Instruction:
Given the following Python 3 function, write the documentation
def sulfide_type(structure):
    structure = structure.copy()
    structure.remove_oxidation_states()
    s = Element("S")
    comp = structure.composition
    if comp.is_element or s not in comp:
        return None

    finder = SpacegroupAnalyzer(structure, symprec=0.1)
    symm_structure = finder.get_symmetrized_structure()
    s_sites = [sites[0] for sites in symm_structure.equivalent_sites if
               sites[0].specie == s]

    def process_site(site):

        # in an exceptionally rare number of structures, the search
        # radius needs to be increased to find a neighbor atom
        search_radius = 4
        neighbors = []
        while len(neighbors) == 0:
            neighbors = structure.get_neighbors(site, search_radius)
            search_radius *= 2
            if search_radius > max(structure.lattice.abc)*2:
                break

        neighbors = sorted(neighbors, key=lambda n: n[1])
        nn, dist = neighbors[0]
        coord_elements = [site.specie for site, d in neighbors
                          if d < dist + 0.4][:4]
        avg_electroneg = np.mean([e.X for e in coord_elements])
        if avg_electroneg > s.X:
            return "sulfate"
        elif avg_electroneg == s.X and s in coord_elements:
            return "polysulfide"
        else:
            return "sulfide"

    types = set([process_site(site) for site in s_sites])
    if "sulfate" in types:
        return None
    elif "polysulfide" in types:
        return "polysulfide"
    else:
        return "sulfide"

Response: Determines if a structure is a sulfide or polysulfide.


Instruction:
Can you implement a function in Python 3 that
performs Voronoi analysis and returns the polyhedra around the center atom n in Schlaefli notation.

Response:
def analyze(self, structure, n=0):
        """
        Performs Voronoi analysis and returns the polyhedra around atom n
        in Schlaefli notation.

        Args:
            structure (Structure): structure to analyze
            n (int): index of the center atom in structure

        Returns:
            voronoi index of n: <c3,c4,c6,c6,c7,c8,c9,c10>
                where c_i denotes number of facets with i vertices.
        """
        center = structure[n]
        neighbors = structure.get_sites_in_sphere(center.coords, self.cutoff)
        neighbors = [i[0] for i in sorted(neighbors, key=lambda s: s[1])]
        qvoronoi_input = np.array([s.coords for s in neighbors])
        voro = Voronoi(qvoronoi_input, qhull_options=self.qhull_options)
        vor_index = np.array([0, 0, 0, 0, 0, 0, 0, 0])

        for key in voro.ridge_dict:
            if 0 in key:  # This means if the center atom is in key
                if -1 in key:  # This means if an infinity point is in key
                    raise ValueError("Cutoff too short.")
                else:
                    try:
                        vor_index[len(voro.ridge_dict[key]) - 3] += 1
                    except IndexError:
                        # If a facet has more than 10 edges, it's skipped here.
                        pass
        return vor_index


Instruction:
Explain what the following Python 3 code does
def analyze_structures(self, structures, step_freq=10,
                           most_frequent_polyhedra=15):
        """
        Perform Voronoi analysis on a list of Structures.
        Note that this might take a significant amount of time depending on the
        size and number of structures.

        Args:
            structures (list): list of Structures
            cutoff (float: cutoff distance around an atom to search for
                neighbors
            step_freq (int): perform analysis every step_freq steps
            qhull_options (str): options to pass to qhull
            most_frequent_polyhedra (int): this many unique polyhedra with
                highest frequences is stored.

        Returns:
            A list of tuples in the form (voronoi_index,frequency)
        """
        voro_dict = {}
        step = 0
        for structure in structures:
            step += 1
            if step % step_freq != 0:
                continue

            v = []
            for n in range(len(structure)):
                v.append(str(self.analyze(structure, n=n).view()))
            for voro in v:
                if voro in voro_dict:
                    voro_dict[voro] += 1
                else:
                    voro_dict[voro] = 1
        return sorted(voro_dict.items(),
                      key=lambda x: (x[1], x[0]),
                      reverse=True)[:most_frequent_polyhedra]

Response: Perform Voronoi analysis on a list of Structures.


Instruction:
Write a Python 3 script to
return the percentage volume change.

Response:
def get_percentage_volume_change(self):
        """
        Returns the percentage volume change.

        Returns:
            Volume change in percentage, e.g., 0.055 implies a 5.5% increase.
        """
        initial_vol = self.initial.lattice.volume
        final_vol = self.final.lattice.volume
        return final_vol / initial_vol - 1


Instruction:
Implement a function in Python 3 to
return the percentage change in lattice parameter changes.

Response:
def get_percentage_lattice_parameter_changes(self):
        """
        Returns the percentage lattice parameter changes.

        Returns:
            A dict of the percentage change in lattice parameter, e.g.,
            {'a': 0.012, 'b': 0.021, 'c': -0.031} implies a change of 1.2%,
            2.1% and -3.1% in the a, b and c lattice parameters respectively.
        """
        initial_latt = self.initial.lattice
        final_latt = self.final.lattice
        d = {l: getattr(final_latt, l) / getattr(initial_latt, l) - 1
             for l in ["a", "b", "c"]}
        return d


Instruction:
Can you generate the documentation for the following Python 3 function
def get_percentage_bond_dist_changes(self, max_radius=3.0):
        data = collections.defaultdict(dict)
        for inds in itertools.combinations(list(range(len(self.initial))), 2):
            (i, j) = sorted(inds)
            initial_dist = self.initial[i].distance(self.initial[j])
            if initial_dist < max_radius:
                final_dist = self.final[i].distance(self.final[j])
                data[i][j] = final_dist / initial_dist - 1
        return data

Response: Returns the percentage bond distance changes for each site up to max_radius for nearest neighbors.


Instruction:
Can you implement a function in Python 3 that
provides connectivity array. Returns: connectivity: An array of shape [atomi, atomj, imagej]. atomi is the index of the atom in the input structure. Since the second atom can be outside of the unit cell, it must be described by both an atom index and an image index. Array data is the solid angle of polygon between atomi and imagej of atomj

Response:
def connectivity_array(self):
        """
        Provides connectivity array.

        Returns:
            connectivity: An array of shape [atomi, atomj, imagej]. atomi is
            the index of the atom in the input structure. Since the second
            atom can be outside of the unit cell, it must be described
            by both an atom index and an image index. Array data is the
            solid angle of polygon between atomi and imagej of atomj
        """
        # shape = [site, axis]
        cart_coords = np.array(self.s.cart_coords)
        # shape = [site, image, axis]
        all_sites = cart_coords[:, None, :] + self.cart_offsets[None, :, :]
        vt = Voronoi(all_sites.reshape((-1, 3)))
        n_images = all_sites.shape[1]
        cs = (len(self.s), len(self.s), len(self.cart_offsets))
        connectivity = np.zeros(cs)
        vts = np.array(vt.vertices)
        for (ki, kj), v in vt.ridge_dict.items():
            atomi = ki // n_images
            atomj = kj // n_images

            imagei = ki % n_images
            imagej = kj % n_images

            if imagei != n_images // 2 and imagej != n_images // 2:
                continue

            if imagei == n_images // 2:
                # atomi is in original cell
                val = solid_angle(vt.points[ki], vts[v])
                connectivity[atomi, atomj, imagej] = val

            if imagej == n_images // 2:
                # atomj is in original cell
                val = solid_angle(vt.points[kj], vts[v])
                connectivity[atomj, atomi, imagei] = val

            if -10.101 in vts[v]:
                warn('Found connectivity with infinite vertex. '
                     'Cutoff is too low, and results may be '
                     'incorrect')
        return connectivity


Instruction:
Create a Python 3 function for
returning a list of site pairs that are Voronoi Neighbors along the cluster with their real - space distances.

Response:
def get_connections(self):
        """
        Returns a list of site pairs that are Voronoi Neighbors, along
        with their real-space distances.
        """
        con = []
        maxconn = self.max_connectivity
        for ii in range(0, maxconn.shape[0]):
            for jj in range(0, maxconn.shape[1]):
                if maxconn[ii][jj] != 0:
                    dist = self.s.get_distance(ii, jj)
                    con.append([ii, jj, dist])
        return con


Instruction:
Given the following Python 3 function, write the documentation
def get_sitej(self, site_index, image_index):
        atoms_n_occu = self.s[site_index].species
        lattice = self.s.lattice
        coords = self.s[site_index].frac_coords + self.offsets[image_index]
        return PeriodicSite(atoms_n_occu, coords, lattice)

Response: Returns the sitei and sitej for a given site and image.


Instruction:
Create a Python 3 function for
parsing an oxide structure and returns the type of oxide or hydroxide.

Response:
def parse_oxide(self):
        """
        Determines if an oxide is a peroxide/superoxide/ozonide/normal oxide.

        Returns:
            oxide_type (str): Type of oxide
            ozonide/peroxide/superoxide/hydroxide/None.
            nbonds (int): Number of peroxide/superoxide/hydroxide bonds in
            structure.
        """
        structure = self.structure
        relative_cutoff = self.relative_cutoff
        o_sites_frac_coords = []
        h_sites_frac_coords = []
        lattice = structure.lattice

        if isinstance(structure.composition.elements[0], Element):
            comp = structure.composition
        elif isinstance(structure.composition.elements[0], Specie):
            elmap = collections.defaultdict(float)
            for site in structure:
                for species, occu in site.species.items():
                    elmap[species.element] += occu
            comp = Composition(elmap)
        if Element("O") not in comp or comp.is_element:
            return "None", 0

        for site in structure:
            syms = [sp.symbol for sp in site.species.keys()]
            if "O" in syms:
                o_sites_frac_coords.append(site.frac_coords)
            if "H" in syms:
                h_sites_frac_coords.append(site.frac_coords)

        if h_sites_frac_coords:
            dist_matrix = lattice.get_all_distances(o_sites_frac_coords,
                                                    h_sites_frac_coords)
            if np.any(dist_matrix < relative_cutoff * 0.93):
                return "hydroxide", len(
                    np.where(dist_matrix < relative_cutoff * 0.93)[0]) / 2.0
        dist_matrix = lattice.get_all_distances(o_sites_frac_coords,
                                                o_sites_frac_coords)
        np.fill_diagonal(dist_matrix, 1000)
        is_superoxide = False
        is_peroxide = False
        is_ozonide = False
        if np.any(dist_matrix < relative_cutoff * 1.35):
            bond_atoms = np.where(dist_matrix < relative_cutoff * 1.35)[0]
            is_superoxide = True
        elif np.any(dist_matrix < relative_cutoff * 1.49):
            is_peroxide = True
            bond_atoms = np.where(dist_matrix < relative_cutoff * 1.49)[0]
        if is_superoxide:
            if len(bond_atoms) > len(set(bond_atoms)):
                is_superoxide = False
                is_ozonide = True
        try:
            nbonds = len(set(bond_atoms))
        except UnboundLocalError:
            nbonds = 0.0
        if is_ozonide:
            str_oxide = "ozonide"
        elif is_superoxide:
            str_oxide = "superoxide"
        elif is_peroxide:
            str_oxide = "peroxide"
        else:
            str_oxide = "oxide"
        if str_oxide == "oxide":
            nbonds = comp["O"]
        return str_oxide, nbonds


Instruction:
Write a Python 3 function that can
identify non - H non - C atoms in the MoleculeGraph returning a list of ints representing node indices.

Response:
def get_heteroatoms(self, elements=None):
        """
        Identify non-H, non-C atoms in the MoleculeGraph, returning a list of
        their node indices.

        :param elements: List of elements to identify (if only certain
            functional groups are of interest).
        :return: set of ints representing node indices
        """

        heteroatoms = set()

        for node in self.molgraph.graph.nodes():
            if elements is not None:
                if str(self.species[node]) in elements:
                    heteroatoms.add(node)
            else:
                if str(self.species[node]) not in ["C", "H"]:
                    heteroatoms.add(node)

        return heteroatoms


Instruction:
Can you write a function in Python 3 where it
returns a set of ints representing the nodes that are special carbon atoms.

Response:
def get_special_carbon(self, elements=None):
        """
        Identify Carbon atoms in the MoleculeGraph that fit the characteristics
        defined Ertl (2017), returning a list of their node indices.

        The conditions for marking carbon atoms are (quoted from Ertl):
            "- atoms connected by non-aromatic double or triple bond to any
            heteroatom
            - atoms in nonaromatic carbon–carbon double or triple bonds
            - acetal carbons, i.e. sp3 carbons connected to two or more oxygens,
            nitrogens or sulfurs; these O, N or S atoms must have only single bonds
            - all atoms in oxirane, aziridine and thiirane rings"

        :param elements: List of elements that will qualify a carbon as special
            (if only certain functional groups are of interest).
            Default None.
        :return: set of ints representing node indices
        """

        specials = set()

        # For this function, only carbons are considered
        carbons = [n for n in self.molgraph.graph.nodes if
                 str(self.species[n]) == "C"]

        # Condition one: double/triple bonds to heteroatoms
        for node in carbons:
            neighbors = self.molgraph.graph[node]

            for neighbor, attributes in neighbors.items():
                if elements is not None:
                    if str(self.species[neighbor]) in elements and \
                            int(attributes[0]["weight"]) in [2, 3]:
                        specials.add(node)
                else:
                    if str(self.species[neighbor]) not in ["C", "H"] and \
                            int(attributes[0]["weight"]) in [2, 3]:
                        specials.add(node)

        # Condition two: carbon-carbon double & triple bonds
        for node in carbons:
            neighbors = self.molgraph.graph[node]

            for neighbor, attributes in neighbors.items():
                if str(self.species[neighbor]) == "C"\
                        and int(attributes[0]["weight"]) in [2, 3]:
                    specials.add(node)
                    specials.add(neighbor)

        # Condition three: Acetal carbons
        for node in carbons:
            neighbors = self.molgraph.graph[node]

            neighbor_spec = [str(self.species[n]) for n in neighbors.keys()]

            ons = len([n for n in neighbor_spec if n in ["O", "N", "S"]])

            if len(neighbors.keys()) == 4 and ons >= 2:
                specials.add(node)

        # Condition four: oxirane/aziridine/thiirane rings
        rings = self.molgraph.find_rings()
        rings_indices = [set(sum(ring, ())) for ring in rings]

        for ring in rings_indices:
            ring_spec = sorted([str(self.species[node]) for node in ring])
            # All rings of interest are three-member rings
            if len(ring) == 3 and ring_spec in [["C", "C", "O"],
                                                ["C", "C", "N"],
                                                ["C", "C", "S"]]:
                for node in ring:
                    if node in carbons:
                        specials.add(node)

        return specials


Instruction:
Implement a Python 3 function for
giving a set of marked atoms return a list of disjoint groups of those marked atoms.

Response:
def link_marked_atoms(self, atoms):
        """
        Take a list of marked "interesting" atoms (heteroatoms, special carbons)
        and attempt to connect them, returning a list of disjoint groups of
        special atoms (and their connected hydrogens).

        :param atoms: set of marked "interesting" atoms, presumably identified
            using other functions in this class.
        :return: list of sets of ints, representing groups of connected atoms
        """

        # We will add hydrogens to functional groups
        hydrogens = {n for n in self.molgraph.graph.nodes if
                     str(self.species[n]) == "H"}

        # Graph representation of only marked atoms
        subgraph = self.molgraph.graph.subgraph(list(atoms)).to_undirected()

        func_grps_no_h = [x for x in nx.connected_components(subgraph)]

        func_grps = []
        for func_grp in func_grps_no_h:
            grp_hs = set()
            for node in func_grp:
                neighbors = self.molgraph.graph[node]
                for neighbor in neighbors.keys():
                    # Add all associated hydrogens into the functional group
                    if neighbor in hydrogens:
                        grp_hs.add(neighbor)
            func_grp = func_grp.union(grp_hs)

            func_grps.append(func_grp)

        return func_grps


Instruction:
Here you have a function in Python 3, explain what it does
def get_basic_functional_groups(self, func_groups=None):

        strat = OpenBabelNN()

        hydrogens = {n for n in self.molgraph.graph.nodes if
                     str(self.species[n]) == "H"}

        carbons = [n for n in self.molgraph.graph.nodes if
                   str(self.species[n]) == "C"]

        if func_groups is None:
            func_groups = ["methyl", "phenyl"]

        results = []

        if "methyl" in func_groups:
            for node in carbons:
                neighbors = strat.get_nn_info(self.molecule, node)
                hs = {n["site_index"] for n in neighbors if n["site_index"] in hydrogens}
                # Methyl group is CH3, but this will also catch methane
                if len(hs) >= 3:
                    hs.add(node)
                    results.append(hs)

        if "phenyl" in func_groups:
            rings_indices = [set(sum(ring, ())) for ring in
                             self.molgraph.find_rings()]

            possible_phenyl = [r for r in rings_indices if len(r) == 6]

            for ring in possible_phenyl:
                # Phenyl group should have only one (0 for benzene) member whose
                # neighbors are not two carbons and one hydrogen
                num_deviants = 0
                for node in ring:
                    neighbors = strat.get_nn_info(self.molecule, node)
                    neighbor_spec = sorted([str(self.species[n["site_index"]])
                                            for n in neighbors])
                    if neighbor_spec != ["C", "C", "H"]:
                        num_deviants += 1

                if num_deviants <= 1:
                    for node in ring:
                        ring_group = copy.deepcopy(ring)
                        neighbors = self.molgraph.graph[node]

                        # Add hydrogens to the functional group
                        for neighbor in neighbors.keys():
                            if neighbor in hydrogens:
                                ring_group.add(neighbor)

                    results.append(ring_group)

        return results

Response:
Return a list of sets of ints representing the basic functional groups of the functional groups that can be identified by the Ertl method
        of get_special_carbon and get_heteroatoms.


Instruction:
Create a Python 3 function for
getting all functional groups of a specific set of elements.

Response:
def get_all_functional_groups(self, elements=None, func_groups=None,
                                  catch_basic=True):
        """
        Identify all functional groups (or all within a certain subset) in the
        molecule, combining the methods described above.

        :param elements: List of elements that will qualify a carbon as special
            (if only certain functional groups are of interest).
            Default None.
        :param func_groups: List of strs representing the functional groups of
            interest. Default to None, meaning that all of the functional groups
            defined in this function will be sought.
        :param catch_basic: bool. If True, use get_basic_functional_groups and
            other methods
        :return: list of sets of ints, representing groups of connected atoms
        """

        heteroatoms = self.get_heteroatoms(elements=elements)
        special_cs = self.get_special_carbon(elements=elements)
        groups = self.link_marked_atoms(heteroatoms.union(special_cs))

        if catch_basic:
            groups += self.get_basic_functional_groups(func_groups=func_groups)

        return groups


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

        categories = {}

        em = iso.numerical_edge_match("weight", 1)
        nm = iso.categorical_node_match("specie", "C")

        for group in groups:
            atoms = [self.molecule[a] for a in group]
            species = [a.specie for a in atoms]
            coords = [a.coords for a in atoms]

            adaptor = BabelMolAdaptor(Molecule(species, coords))
            # Use Canonical SMILES to ensure uniqueness
            smiles = adaptor.pybel_mol.write("can").strip()

            if smiles in categories:
                this_subgraph = self.molgraph.graph.subgraph(list(group)).to_undirected()
                for other in categories[smiles]["groups"]:
                    other_subgraph = self.molgraph.graph.subgraph(list(other)).to_undirected()

                    if not nx.is_isomorphic(this_subgraph, other_subgraph,
                                            edge_match=em, node_match=nm):
                        break

                    if group not in categories[smiles]["groups"]:
                        categories[smiles]["groups"].append(group)
                        categories[smiles]["count"] += 1

            else:
                categories[smiles] = {"groups": [group],
                                      "count": 1}

        return categories

Response: Determines classes of functional groups present in a set of functional groups.


Instruction:
Can you generate the documentation for the following Python 3 function
def raise_error_if_unphysical(f):
    def wrapper(self, *args, **kwargs):
        if self.k_vrh < 0 or self.g_vrh < 0:
            raise ValueError("Bulk or shear modulus is negative, property "
                             "cannot be determined")
        return f(self, *args, **kwargs)
    return wrapper

Response: A decorator that raises an error if the tensor is unphysical.


Instruction:
Can you generate the documentation for the following Python 3 function
def diff_fit(strains, stresses, eq_stress=None, order=2, tol=1e-10):
    strain_state_dict = get_strain_state_dict(
        strains, stresses, eq_stress=eq_stress, tol=tol,
        add_eq=True, sort=True)

    # Collect derivative data
    c_list = []
    dei_dsi = np.zeros((order - 1, 6, len(strain_state_dict)))
    for n, (strain_state, data) in enumerate(strain_state_dict.items()):
        hvec = data["strains"][:, strain_state.index(1)]
        for i in range(1, order):
            coef = get_diff_coeff(hvec, i)
            dei_dsi[i - 1, :, n] = np.dot(coef, data["stresses"])

    m, absent = generate_pseudo(list(strain_state_dict.keys()), order)
    for i in range(1, order):
        cvec, carr = get_symbol_list(i+1)
        svec = np.ravel(dei_dsi[i-1].T)
        cmap = dict(zip(cvec, np.dot(m[i-1], svec)))
        c_list.append(v_subs(carr, cmap))
    return [Tensor.from_voigt(c) for c in c_list]

Response: This function calculates the nth order elastic constant fitting function based on the given strains and stress.


Instruction:
Write a Python 3 function for
finding stress corresponding to zero strain state in the order of strains and stresses.

Response:
def find_eq_stress(strains, stresses, tol=1e-10):
    """
    Finds stress corresponding to zero strain state in stress-strain list

    Args:
        strains (Nx3x3 array-like): array corresponding to strains
        stresses (Nx3x3 array-like): array corresponding to stresses
        tol (float): tolerance to find zero strain state
    """
    stress_array = np.array(stresses)
    strain_array = np.array(strains)
    eq_stress = stress_array[np.all(abs(strain_array)<tol, axis=(1,2))]

    if eq_stress.size != 0:
        all_same = (abs(eq_stress - eq_stress[0]) < 1e-8).all()
        if len(eq_stress) > 1 and not all_same:
            raise ValueError("Multiple stresses found for equilibrium strain"
                             " state, please specify equilibrium stress or  "
                             " remove extraneous stresses.")
        eq_stress = eq_stress[0]
    else:
        warnings.warn("No eq state found, returning zero voigt stress")
        eq_stress = Stress(np.zeros((3, 3)))
    return eq_stress


Instruction:
Write a Python 3 function for
generating the pseudoinverse for a given set of strains.

Response:
def generate_pseudo(strain_states, order=3):
    """
    Generates the pseudoinverse for a given set of strains.

    Args:
        strain_states (6xN array like): a list of voigt-notation
            "strain-states", i. e. perturbed indices of the strain
            as a function of the smallest strain e. g. (0, 1, 0, 0, 1, 0)
        order (int): order of pseudoinverse to calculate

    Returns:
        mis: pseudo inverses for each order tensor, these can
            be multiplied by the central difference derivative
            of the stress with respect to the strain state
        absent_syms: symbols of the tensor absent from the PI
            expression
    """
    s = sp.Symbol('s')
    nstates = len(strain_states)
    ni = np.array(strain_states)*s
    mis, absent_syms = [], []
    for degree in range(2, order + 1):
        cvec, carr = get_symbol_list(degree)
        sarr = np.zeros((nstates, 6), dtype=object)
        for n, strain_v in enumerate(ni):
            # Get expressions
            exps = carr.copy()
            for i in range(degree - 1):
                exps = np.dot(exps, strain_v)
            exps /= np.math.factorial(degree - 1)
            sarr[n] = [sp.diff(exp, s, degree - 1) for exp in exps]
        svec = sarr.ravel()
        present_syms = set.union(*[exp.atoms(sp.Symbol) for exp in svec])
        absent_syms += [set(cvec) - present_syms]
        m = np.zeros((6*nstates, len(cvec)))
        for n, c in enumerate(cvec):
            m[:, n] = v_diff(svec, c)
        mis.append(np.linalg.pinv(m))
    return mis, absent_syms


Instruction:
Make a summary of the following Python 3 code
def get_symbol_list(rank, dim=6):
    indices = list(
        itertools.combinations_with_replacement(range(dim), r=rank))
    c_vec = np.zeros(len(indices), dtype=object)
    c_arr = np.zeros([dim]*rank, dtype=object)
    for n, idx in enumerate(indices):
        c_vec[n] = sp.Symbol('c_'+''.join([str(i) for i in idx]))
        for perm in itertools.permutations(idx):
            c_arr[perm] = c_vec[n]
    return c_vec, c_arr

Response:
Returns a symbolic representation of the voigt - notation
    tensor that places identical symbols for entries related to the entry relatedto the entry with index transposition i. e. C_1121 = C_1211 etc.


Instruction:
Explain what the following Python 3 code does
def calculate_stress(self, strain):
        strain = np.array(strain)
        if strain.shape == (6,):
            strain = Strain.from_voigt(strain)
        assert strain.shape == (3, 3), "Strain must be 3x3 or voigt-notation"
        stress_matrix = self.einsum_sequence([strain]*(self.order - 1)) \
                / factorial(self.order - 1)
        return Stress(stress_matrix)

Response: Calculates a given elastic tensor s contribution to the


Instruction:
Explain what the following Python 3 code does
def energy_density(self, strain, convert_GPa_to_eV=True):
        e_density = np.sum(self.calculate_stress(strain)*strain) / self.order
        if convert_GPa_to_eV:
            e_density *= self.GPa_to_eV_A3  # Conversion factor for GPa to eV/A^3
        return e_density

Response: Calculates the elastic energy density due to a strain


Instruction:
Can you generate the documentation for the following Python 3 function
def compliance_tensor(self):
        s_voigt = np.linalg.inv(self.voigt)
        return ComplianceTensor.from_voigt(s_voigt)

Response: returns the Voigt - notation compliance tensor


Instruction:
Write a Python 3 function that can
return the G_v shear modulus

Response:
def g_voigt(self):
        """
        returns the G_v shear modulus
        """
        return (2. * self.voigt[:3, :3].trace() -
                np.triu(self.voigt[:3, :3]).sum() +
                3 * self.voigt[3:, 3:].trace()) / 15.


Instruction:
Create a Python 3 function to
return the G_r shear modulus

Response:
def g_reuss(self):
        """
        returns the G_r shear modulus
        """
        return 15. / (8. * self.compliance_tensor.voigt[:3, :3].trace() -
                      4. * np.triu(self.compliance_tensor.voigt[:3, :3]).sum() +
                      3. * self.compliance_tensor.voigt[3:, 3:].trace())


Instruction:
Can you write a function in Python 3 where it
calculates the Young s modulus using the Voigt - Reuss - Hill averages of bulk and shear moduli

Response:
def y_mod(self):
        """
        Calculates Young's modulus (in SI units) using the
        Voigt-Reuss-Hill averages of bulk and shear moduli
        """
        return 9.e9 * self.k_vrh * self.g_vrh / (3. * self.k_vrh + self.g_vrh)


Instruction:
Can you create a Python 3 function that
calculates the poisson ratio for a specific directional entry.

Response:
def directional_poisson_ratio(self, n, m, tol=1e-8):
        """
        Calculates the poisson ratio for a specific direction
        relative to a second, orthogonal direction

        Args:
            n (3-d vector): principal direction
            m (3-d vector): secondary direction orthogonal to n
            tol (float): tolerance for testing of orthogonality
        """
        n, m = get_uvec(n), get_uvec(m)
        if not np.abs(np.dot(n, m)) < tol:
            raise ValueError("n and m must be orthogonal")
        v = self.compliance_tensor.einsum_sequence([n]*2 + [m]*2)
        v *= -1 / self.compliance_tensor.einsum_sequence([n]*4)
        return v


Instruction:
Given the following Python 3 function, write the documentation
def trans_v(self, structure):
        nsites = structure.num_sites
        volume = structure.volume
        natoms = structure.composition.num_atoms
        weight = float(structure.composition.weight)
        mass_density = 1.6605e3 * nsites * weight / (natoms * volume)
        if self.g_vrh < 0:
            raise ValueError("k_vrh or g_vrh is negative, "
                             "sound velocity is undefined")
        return (1e9 * self.g_vrh / mass_density) ** 0.5

Response:
Calculates the transverse sound velocity using the the
            Voigt - Reuss - Hill average bulk modulus.


Instruction:
Can you tell what is the following Python 3 function doing
def snyder_ac(self, structure):
        nsites = structure.num_sites
        volume = structure.volume
        natoms = structure.composition.num_atoms
        num_density = 1e30 * nsites / volume
        tot_mass = sum([e.atomic_mass for e in structure.species])
        avg_mass = 1.6605e-27 * tot_mass / natoms
        return 0.38483*avg_mass * \
            ((self.long_v(structure) + 2.*self.trans_v(structure))/3.) ** 3.\
            / (300.*num_density ** (-2./3.) * nsites ** (1./3.))

Response: Calculates Snyder s acoustic sound velocity in SI units.


Instruction:
Write a Python 3 function for
calculating Snyder s optical sound velocity

Response:
def snyder_opt(self, structure):
        """
        Calculates Snyder's optical sound velocity (in SI units)

        Args:
            structure: pymatgen structure object

        Returns: Snyder's optical sound velocity (in SI units)

        """
        nsites = structure.num_sites
        volume = structure.volume
        num_density = 1e30 * nsites / volume
        return 1.66914e-23 * \
            (self.long_v(structure) + 2.*self.trans_v(structure))/3. \
            / num_density ** (-2./3.) * (1 - nsites ** (-1./3.))


Instruction:
Explain what the following Python 3 code does
def clarke_thermalcond(self, structure):
        nsites = structure.num_sites
        volume = structure.volume
        tot_mass = sum([e.atomic_mass for e in structure.species])
        natoms = structure.composition.num_atoms
        weight = float(structure.composition.weight)
        avg_mass = 1.6605e-27 * tot_mass / natoms
        mass_density = 1.6605e3 * nsites * weight / (natoms * volume)
        return 0.87 * 1.3806e-23 * avg_mass**(-2./3.) \
            * mass_density**(1./6.) * self.y_mod**0.5

Response: Calculates the Clarke s thermal conductivity for a given structure object.


Instruction:
Can you implement a function in Python 3 that
estimates the debye temperature from longitudinal and transverse sound velocities

Response:
def debye_temperature(self, structure):
        """
        Estimates the debye temperature from longitudinal and
        transverse sound velocities

        Args:
            structure: pymatgen structure object

        Returns: debye temperature (in SI units)

        """
        v0 = (structure.volume * 1e-30 / structure.num_sites)
        vl, vt = self.long_v(structure), self.trans_v(structure)
        vm = 3**(1./3.) * (1 / vl**3 + 2 / vt**3)**(-1./3.)
        td = 1.05457e-34 / 1.38065e-23 * vm * (6 * np.pi**2 / v0) ** (1./3.)
        return td


Instruction:
Create a Python 3 function to
return the universal anisotropy value

Response:
def universal_anisotropy(self):
        """
        returns the universal anisotropy value
        """
        return 5. * self.g_voigt / self.g_reuss + \
            self.k_voigt / self.k_reuss - 6.


Instruction:
Create a Python 3 function to
return the homogeneous poisson ratio of the current species.

Response:
def homogeneous_poisson(self):
        """
        returns the homogeneous poisson ratio
        """
        return (1. - 2. / 3. * self.g_vrh / self.k_vrh) / \
               (2. + 2. / 3. * self.g_vrh / self.k_vrh)


Instruction:
Can you tell what is the following Python 3 function doing
def get_structure_property_dict(self, structure, include_base_props=True,
                                    ignore_errors=False):
        """
        returns a dictionary of properties derived from the elastic tensor
        and an associated structure

        Args:
            structure (Structure): structure object for which to calculate
                associated properties
            include_base_props (bool): whether to include base properties,
                like k_vrh, etc.
            ignore_errors (bool): if set to true, will set problem properties
                that depend on a physical tensor to None, defaults to False
        """
        s_props = ["trans_v", "long_v", "snyder_ac", "snyder_opt",
                   "snyder_total", "clarke_thermalcond", "cahill_thermalcond",
                   "debye_temperature"]
        if ignore_errors and (self.k_vrh < 0 or self.g_vrh < 0):
            sp_dict = {prop: None for prop in s_props}
        else:
            sp_dict = {prop: getattr(self, prop)(structure) for prop in s_props}
        sp_dict["structure"] = structure
        if include_base_props:
            sp_dict.update(self.property_dict)
        return sp_dict

Response:
Returns a dictionary of properties derived from the elastic tensor
            and an associated structure


Instruction:
Create a Python 3 function to
class method to fit an elastic tensor from stress/strain data. Method uses Moore-Penrose pseudoinverse to invert the s = C*e equation with elastic tensor, stress, and strain in voigt notation Args: stresses (Nx3x3 array-like): list or array of stresses strains (Nx3x3 array-like): list or array of strains

Response:
def from_pseudoinverse(cls, strains, stresses):
        """
        Class method to fit an elastic tensor from stress/strain
        data.  Method uses Moore-Penrose pseudoinverse to invert
        the s = C*e equation with elastic tensor, stress, and
        strain in voigt notation

        Args:
            stresses (Nx3x3 array-like): list or array of stresses
            strains (Nx3x3 array-like): list or array of strains
        """
        # convert the stress/strain to Nx6 arrays of voigt-notation
        warnings.warn("Pseudoinverse fitting of Strain/Stress lists may yield "
                      "questionable results from vasp data, use with caution.")
        stresses = np.array([Stress(stress).voigt for stress in stresses])
        with warnings.catch_warnings(record=True):
            strains = np.array([Strain(strain).voigt for strain in strains])

        voigt_fit = np.transpose(np.dot(np.linalg.pinv(strains), stresses))
        return cls.from_voigt(voigt_fit)


Instruction:
Can you tell what is the following Python 3 function doing
def from_independent_strains(cls, strains, stresses, eq_stress=None,
                                 vasp=False, tol=1e-10):
        """
        Constructs the elastic tensor least-squares fit of independent strains
        Args:
            strains (list of Strains): list of strain objects to fit
            stresses (list of Stresses): list of stress objects to use in fit
                corresponding to the list of strains
            eq_stress (Stress): equilibrium stress to use in fitting
            vasp (boolean): flag for whether the stress tensor should be
                converted based on vasp units/convention for stress
            tol (float): tolerance for removing near-zero elements of the
                resulting tensor
        """
        strain_states = [tuple(ss) for ss in np.eye(6)]
        ss_dict = get_strain_state_dict(strains, stresses, eq_stress=eq_stress)
        if not set(strain_states) <= set(ss_dict.keys()):
            raise ValueError("Missing independent strain states: "
                             "{}".format(set(strain_states) - set(ss_dict)))
        if len(set(ss_dict.keys()) - set(strain_states)) > 0:
            warnings.warn("Extra strain states in strain-stress pairs "
                          "are neglected in independent strain fitting")
        c_ij = np.zeros((6, 6))
        for i in range(6):
            istrains = ss_dict[strain_states[i]]["strains"]
            istresses = ss_dict[strain_states[i]]["stresses"]
            for j in range(6):
                c_ij[i, j] = np.polyfit(istrains[:, i], istresses[:, j], 1)[0]
        if vasp:
            c_ij *= -0.1  # Convert units/sign convention of vasp stress tensor
        c = cls.from_voigt(c_ij)
        c = c.zeroed(tol)
        return c

Response: Constructs the elastic tensor least - squares fit of independent strains and stress pairs.


Instruction:
Can you write a function in Python 3 where it
generates an elastic tensor expansion from a diffusion fit.

Response:
def from_diff_fit(cls, strains, stresses, eq_stress=None,
                      tol=1e-10, order=3):
        """
        Generates an elastic tensor expansion via the fitting function
        defined below in diff_fit
        """
        c_list = diff_fit(strains, stresses, eq_stress, order, tol)
        return cls(c_list)


Instruction:
Write a Python 3 function for
calculating the elastic energy density due to a strain

Response:
def energy_density(self, strain, convert_GPa_to_eV=True):
        """
        Calculates the elastic energy density due to a strain
        """
        return sum([c.energy_density(strain, convert_GPa_to_eV)
                    for c in self])


Instruction:
Given the following Python 3 function, write the documentation
def get_ggt(self, n, u):
        gk = self[0].einsum_sequence([n, u, n, u])
        result = -(2*gk*np.outer(u, u) + self[0].einsum_sequence([n, n])
                   + self[1].einsum_sequence([n, u, n, u])) / (2*gk)
        return result

Response: Gets the Generalized Gruneisen tensor for a given ArcGIS 3x1 array - like object.


Instruction:
Create a Python 3 function for
getting the thermodynamic Gruneisen tensor by using an integration of the GGT and the directional heat capacity.

Response:
def get_tgt(self, temperature=None, structure=None, quad=None):
        """
        Gets the thermodynamic Gruneisen tensor (TGT) by via an
        integration of the GGT weighted by the directional heat
        capacity.

        See refs:
            R. N. Thurston and K. Brugger, Phys. Rev. 113, A1604 (1964).
            K. Brugger Phys. Rev. 137, A1826 (1965).

        Args:
            temperature (float): Temperature in kelvin, if not specified
                will return non-cv-normalized value
            structure (float): Structure to be used in directional heat
                capacity determination, only necessary if temperature
                is specified
            quad (dict): quadrature for integration, should be
                dictionary with "points" and "weights" keys defaults
                to quadpy.sphere.Lebedev(19) as read from file
        """
        if temperature and not structure:
            raise ValueError("If using temperature input, you must also "
                             "include structure")

        quad = quad if quad else DEFAULT_QUAD
        points = quad['points']
        weights = quad['weights']
        num, denom, c = np.zeros((3, 3)), 0, 1
        for p, w in zip(points, weights):
            gk = ElasticTensor(self[0]).green_kristoffel(p)
            rho_wsquareds, us = np.linalg.eigh(gk)
            us = [u / np.linalg.norm(u) for u in np.transpose(us)]
            for u in us:
                # TODO: this should be benchmarked
                if temperature:
                    c = self.get_heat_capacity(temperature, structure, p, u)
                num += c*self.get_ggt(p, u) * w
                denom += c * w
        return SquareTensor(num / denom)


Instruction:
How would you code a function in Python 3 to
get the single average gruneisen parameter from the TGT.

Response:
def get_gruneisen_parameter(self, temperature=None, structure=None,
                                quad=None):
        """
        Gets the single average gruneisen parameter from the TGT.

        Args:
            temperature (float): Temperature in kelvin, if not specified
                will return non-cv-normalized value
            structure (float): Structure to be used in directional heat
                capacity determination, only necessary if temperature
                is specified
            quad (dict): quadrature for integration, should be
                dictionary with "points" and "weights" keys defaults
                to quadpy.sphere.Lebedev(19) as read from file
        """
        return np.trace(self.get_tgt(temperature, structure, quad)) / 3.


Instruction:
Make a summary of the following Python 3 code
def get_heat_capacity(self, temperature, structure, n, u, cutoff=1e2):
        k = 1.38065e-23
        kt = k*temperature
        hbar_w = 1.05457e-34*self.omega(structure, n, u)
        if hbar_w > kt * cutoff:
            return 0.0
        c = k * (hbar_w / kt) ** 2
        c *= np.exp(hbar_w / kt) / (np.exp(hbar_w / kt) - 1)**2
        return c * 6.022e23

Response: Gets the directional heat capacity for a higher order tensor in the degree of the modulation.


Instruction:
Write a Python 3 function for
calculating the directional frequency contribution to the heat Privileged class

Response:
def omega(self, structure, n, u):
        """
        Finds directional frequency contribution to the heat
        capacity from direction and polarization

        Args:
            structure (Structure): Structure to be used in directional heat
                capacity determination
            n (3x1 array-like): direction for Cv determination
            u (3x1 array-like): polarization direction, note that
                no attempt for verification of eigenvectors is made
        """
        l0 = np.dot(np.sum(structure.lattice.matrix, axis=0), n)
        l0 *= 1e-10 # in A
        weight = float(structure.composition.weight) * 1.66054e-27 # in kg
        vol = structure.volume * 1e-30 # in m^3
        vel = (1e9 * self[0].einsum_sequence([n, u, n, u])
               / (weight / vol)) ** 0.5
        return vel / l0


Instruction:
Write a Python 3 script to
return thermal expansion coefficient from third - order constants.

Response:
def thermal_expansion_coeff(self, structure, temperature, mode="debye"):
        """
        Gets thermal expansion coefficient from third-order constants.

        Args:
            temperature (float): Temperature in kelvin, if not specified
                will return non-cv-normalized value
            structure (Structure): Structure to be used in directional heat
                capacity determination, only necessary if temperature
                is specified
            mode (string): mode for finding average heat-capacity,
                current supported modes are 'debye' and 'dulong-petit'
        """
        soec = ElasticTensor(self[0])
        v0 = (structure.volume * 1e-30 / structure.num_sites)
        if mode == "debye":
            td = soec.debye_temperature(structure)
            t_ratio = temperature / td
            integrand = lambda x: (x**4 * np.exp(x)) / (np.exp(x) - 1)**2
            cv = 9 * 8.314 * t_ratio**3 * quad(integrand, 0, t_ratio**-1)[0]
        elif mode == "dulong-petit":
            cv = 3 * 8.314
        else:
            raise ValueError("Mode must be debye or dulong-petit")
        tgt = self.get_tgt(temperature, structure)
        alpha = np.einsum('ijkl,ij', soec.compliance_tensor, tgt)
        alpha *= cv / (1e9 * v0 * 6.022e23)
        return SquareTensor(alpha)


Instruction:
Can you write a function in Python 3 where it
gets a compliance tensor expansion from the elastic entry.

Response:
def get_compliance_expansion(self):
        """
        Gets a compliance tensor expansion from the elastic
        tensor expansion.
        """
        # TODO: this might have a general form
        if not self.order <= 4:
            raise ValueError("Compliance tensor expansion only "
                             "supported for fourth-order and lower")
        ce_exp = [ElasticTensor(self[0]).compliance_tensor]
        einstring = "ijpq,pqrsuv,rskl,uvmn->ijklmn"
        ce_exp.append(np.einsum(einstring, -ce_exp[-1], self[1],
                                ce_exp[-1], ce_exp[-1]))
        if self.order == 4:
            # Four terms in the Fourth-Order compliance tensor
            einstring_1 = "pqab,cdij,efkl,ghmn,abcdefgh"
            tensors_1 = [ce_exp[0]]*4 + [self[-1]]
            temp = -np.einsum(einstring_1, *tensors_1)
            einstring_2 = "pqab,abcdef,cdijmn,efkl"
            einstring_3 = "pqab,abcdef,efklmn,cdij"
            einstring_4 = "pqab,abcdef,cdijkl,efmn"
            for es in [einstring_2, einstring_3, einstring_4]:
                temp -= np.einsum(es, ce_exp[0], self[-2], ce_exp[1], ce_exp[0])
            ce_exp.append(temp)
        return TensorCollection(ce_exp)


Instruction:
How would you code a function in Python 3 to
get the strain from a stress state according to the compliance expansion corresponding to the tensor expansion.

Response:
def get_strain_from_stress(self, stress):
        """
        Gets the strain from a stress state according
        to the compliance expansion corresponding to the
        tensor expansion.
        """
        compl_exp = self.get_compliance_expansion()
        strain = 0
        for n, compl in enumerate(compl_exp):
            strain += compl.einsum_sequence([stress]*(n+1)) / factorial(n+1)
        return strain


Instruction:
Here you have a function in Python 3, explain what it does
def get_effective_ecs(self, strain, order=2):
        ec_sum = 0
        for n, ecs in enumerate(self[order-2:]):
            ec_sum += ecs.einsum_sequence([strain] * n) / factorial(n)
        return ec_sum

Response: Returns the effective elastic constants for the elastic tensor expansion.


Instruction:
Write a Python 3 function that can
get the Wallace Tensor for determining yield strength for the given time - step tau

Response:
def get_wallace_tensor(self, tau):
        """
        Gets the Wallace Tensor for determining yield strength
        criteria.

        Args:
            tau (3x3 array-like): stress at which to evaluate
                the wallace tensor
        """
        b = 0.5 * (np.einsum("ml,kn->klmn", tau, np.eye(3)) +
                   np.einsum("km,ln->klmn", tau, np.eye(3)) +
                   np.einsum("nl,km->klmn", tau, np.eye(3)) +
                   np.einsum("kn,lm->klmn", tau, np.eye(3)) +
                   -2*np.einsum("kl,mn->klmn", tau, np.eye(3)))
        strain = self.get_strain_from_stress(tau)
        b += self.get_effective_ecs(strain)
        return b


Instruction:
Write a Python 3 function that can
get the symmetrized wallace tensor for determining the yield strength criteria.

Response:
def get_symmetric_wallace_tensor(self, tau):
        """
        Gets the symmetrized wallace tensor for determining
        yield strength criteria.

        Args:
            tau (3x3 array-like): stress at which to evaluate
                the wallace tensor.
        """
        wallace = self.get_wallace_tensor(tau)
        return Tensor(0.5 * (wallace + np.transpose(wallace, [2, 3, 0, 1])))


Instruction:
Implement a Python 3 function for
getting the stability criteria from the symmetric Wallace tensor from an input vector and stress value.

Response:
def get_stability_criteria(self, s, n):
        """
        Gets the stability criteria from the symmetric
        Wallace tensor from an input vector and stress
        value.

        Args:
            s (float): Stress value at which to evaluate
                the stability criteria
            n (3x1 array-like): direction of the applied
                stress
        """
        n = get_uvec(n)
        stress = s * np.outer(n, n)
        sym_wallace = self.get_symmetric_wallace_tensor(stress)
        return np.linalg.det(sym_wallace.voigt)


Instruction:
Can you generate the documentation for the following Python 3 function
def get_yield_stress(self, n):
        # TODO: root finding could be more robust
        comp = root(self.get_stability_criteria, -1, args=n)
        tens = root(self.get_stability_criteria, 1, args=n)
        return (comp.x, tens.x)

Response: Gets the yield stress for a given direction


Instruction:
Can you tell what is the following Python 3 function doing
def symmetry_measure(points_distorted, points_perfect):
    # When there is only one point, the symmetry measure is 0.0 by definition
    if len(points_distorted) == 1:
        return {'symmetry_measure': 0.0, 'scaling_factor': None, 'rotation_matrix': None}
    # Find the rotation matrix that aligns the distorted points to the perfect points in a least-square sense.
    rot = find_rotation(points_distorted=points_distorted,
                           points_perfect=points_perfect)
    # Find the scaling factor between the distorted points and the perfect points in a least-square sense.
    scaling_factor, rotated_coords, points_perfect = find_scaling_factor(points_distorted=points_distorted,
                                                                         points_perfect=points_perfect,
                                                                         rot=rot)
    # Compute the continuous symmetry measure [see Eq. 1 in Pinsky et al., Inorganic Chemistry 37, 5575 (1998)]
    rotated_coords = scaling_factor * rotated_coords
    diff = points_perfect - rotated_coords
    num = np.tensordot(diff, diff)
    denom = np.tensordot(points_perfect, points_perfect)
    return {'symmetry_measure': num / denom * 100.0, 'scaling_factor': scaling_factor, 'rotation_matrix': rot}

Response: Computes continuous symmetry measure of a set of points points_distorted and points_perfect.


Instruction:
Here you have a function in Python 3, explain what it does
def find_scaling_factor(points_distorted, points_perfect, rot):
    rotated_coords = np.matmul(rot, points_distorted.T).T
    num = np.tensordot(rotated_coords, points_perfect)
    denom = np.tensordot(rotated_coords, rotated_coords)
    return num / denom, rotated_coords, points_perfect

Response: This function finds the scaling factor between two sets of points points_distorted and the set of points points_perfect and the rotation matrix rot.


Instruction:
How would you explain what the following Python 3 function does
def setup_structure(self, structure):
        self.initial_structure = structure.copy()
        if self.structure_refinement == self.STRUCTURE_REFINEMENT_NONE:
            self.structure = structure.copy()
            self.spg_analyzer = None
            self.symmetrized_structure = None
        else:
            self.spg_analyzer = SpacegroupAnalyzer(self.initial_structure,
                                                   symprec=
                                                   self.spg_analyzer_options[
                                                       'symprec'],
                                                   angle_tolerance=
                                                   self.spg_analyzer_options[
                                                       'angle_tolerance'])
            if self.structure_refinement == self.STRUCTURE_REFINEMENT_REFINED:
                self.structure = self.spg_analyzer.get_refined_structure()
                self.symmetrized_structure = None
            elif self.structure_refinement == self.STRUCTURE_REFINEMENT_SYMMETRIZED:
                self.structure = self.spg_analyzer.get_refined_structure()
                self.spg_analyzer_refined = SpacegroupAnalyzer(self.structure,
                                                               symprec=
                                                               self.spg_analyzer_options[
                                                                   'symprec'],
                                                               angle_tolerance=
                                                               self.spg_analyzer_options
                                                               [
                                                                   'angle_tolerance'])
                self.symmetrized_structure = self.spg_analyzer_refined.get_symmetrized_structure()

Response: Sets up the structure for which the coordination geometries have to be identified.


Instruction:
How would you code a function in Python 3 to
set up the pymatgen structure for which the coordination geometries have to be identified starting from the structure .

Response:
def set_structure(self, lattice, species, coords, coords_are_cartesian):
        """
        Sets up the pymatgen structure for which the coordination geometries have to be identified starting from the
        lattice, the species and the coordinates
        :param lattice: The lattice of the structure
        :param species: The species on the sites
        :param coords: The coordinates of the sites
        :param coords_are_cartesian: If set to True, the coordinates are given in cartesian coordinates
        """
        self.setup_structure(
            Structure(lattice, species, coords, coords_are_cartesian))


Instruction:
Explain what the following Python 3 code does
def compute_structure_environments(self,
                                       excluded_atoms=None,
                                       only_atoms=None,
                                       only_cations=True,
                                       only_indices=None,
                                       maximum_distance_factor=PRESETS['DEFAULT']['maximum_distance_factor'],
                                       minimum_angle_factor=PRESETS['DEFAULT']['minimum_angle_factor'],
                                       max_cn=None,
                                       min_cn=None,
                                       only_symbols=None,
                                       valences='undefined',
                                       additional_conditions=None,
                                       info=None,
                                       timelimit=None,
                                       initial_structure_environments=None,
                                       get_from_hints=False,
                                       voronoi_normalized_distance_tolerance=PRESETS['DEFAULT']
                                       ['voronoi_normalized_distance_tolerance'],
                                       voronoi_normalized_angle_tolerance=PRESETS['DEFAULT']
                                       ['voronoi_normalized_angle_tolerance'],
                                       recompute=None,
                                       optimization=PRESETS['DEFAULT']['optimization']):
        """
        Computes and returns the StructureEnvironments object containing all the information about the coordination
        environments in the structure
        :param excluded_atoms: Atoms for which the coordination geometries does not have to be identified
        :param only_atoms: If not set to None, atoms for which the coordination geometries have to be identified
        :param only_cations: If set to True, will only compute environments for cations
        :param only_indices: If not set to None, will only compute environments the atoms of the given indices
        :param maximum_distance_factor: If not set to None, neighbors beyond
                                        maximum_distance_factor*closest_neighbor_distance are not considered
        :param minimum_angle_factor: If not set to None, neighbors for which the angle is lower than
                                     minimum_angle_factor*largest_angle_neighbor are not considered
        :param max_cn: maximum coordination number to be considered
        :param min_cn: minimum coordination number to be considered
        :param only_symbols: if not set to None, consider only coordination environments with the given symbols
        :param valences: valences of the atoms
        :param additional_conditions: additional conditions to be considered in the bonds (example : only bonds
                                      between cation and anion
        :param info: additional info about the calculation
        :param timelimit: time limit (in secs) after which the calculation of the StructureEnvironments object stops
        :param initial_structure_environments: initial StructureEnvironments object (most probably incomplete)
        :param get_from_hints: whether to add neighbors sets from "hints" (e.g. capped environment => test the
                               neighbors without the cap)
        :param voronoi_normalized_distance_tolerance: tolerance for the normalized distance used to distinguish
                                                      neighbors sets
        :param voronoi_normalized_angle_tolerance: tolerance for the normalized angle used to distinguish
                                                   neighbors sets
        :param recompute: whether to recompute the sites already computed (when initial_structure_environments
                          is not None)
        :param optimization: optimization algorithm
        :return: The StructureEnvironments object containing all the information about the coordination
        environments in the structure
        """
        time_init = time.clock()
        if info is None:
            info = {}
        info.update({'local_geometry_finder':
                         {'parameters':
                              {'centering_type': self.centering_type,
                               'include_central_site_in_centroid': self.include_central_site_in_centroid,
                               'structure_refinement': self.structure_refinement,
                               'spg_analyzer_options': self.spg_analyzer_options
                               }
                          }
                     })
        if only_symbols is not None:
            self.allcg = AllCoordinationGeometries(
                permutations_safe_override=self.permutations_safe_override,
                only_symbols=only_symbols)

        self.valences = valences

        # Get a list of indices of unequivalent sites from the initial structure
        self.equivalent_sites = [[site] for site in self.structure]
        self.struct_sites_to_irreducible_site_list_map = list(
            range(len(self.structure)))
        self.sites_map = list(range(len(self.structure)))
        indices = list(range(len(self.structure)))

        # Get list of unequivalent sites with valence >= 0
        if only_cations and self.valences != 'undefined':
            sites_indices = [isite for isite in indices if
                             self.valences[isite] >= 0]
        else:
            sites_indices = [isite for isite in indices]

        # Include atoms that are in the list of "only_atoms" if it is provided
        if only_atoms is not None:
            sites_indices = [isite for isite in sites_indices
                             if any([at in [sp.symbol for sp in self.structure[
                    isite].species]
                                     for at in only_atoms])]

        # Exclude atoms that are in the list of excluded atoms
        if excluded_atoms:
            sites_indices = [isite for isite in sites_indices
                             if not any([at in [sp.symbol for sp in
                                                self.structure[
                                                    isite].species]
                                         for at in excluded_atoms])]

        if only_indices is not None:
            sites_indices = [isite for isite in indices if
                             isite in only_indices]

        # Get the VoronoiContainer for the sites defined by their indices (sites_indices)
        logging.info('Getting DetailedVoronoiContainer')
        if voronoi_normalized_distance_tolerance is None:
            normalized_distance_tolerance = DetailedVoronoiContainer.default_normalized_distance_tolerance
        else:
            normalized_distance_tolerance = voronoi_normalized_distance_tolerance
        if voronoi_normalized_angle_tolerance is None:
            normalized_angle_tolerance = DetailedVoronoiContainer.default_normalized_angle_tolerance
        else:
            normalized_angle_tolerance = voronoi_normalized_angle_tolerance
        self.detailed_voronoi = DetailedVoronoiContainer(self.structure,
                                                         isites=sites_indices,
                                                         valences=self.valences,
                                                         maximum_distance_factor=maximum_distance_factor,
                                                         minimum_angle_factor=minimum_angle_factor,
                                                         additional_conditions=additional_conditions,
                                                         normalized_distance_tolerance=normalized_distance_tolerance,
                                                         normalized_angle_tolerance=normalized_angle_tolerance)
        logging.info('DetailedVoronoiContainer has been set up')

        # Initialize the StructureEnvironments object (either from initial_structure_environments or from scratch)
        if initial_structure_environments is not None:
            se = initial_structure_environments
            if se.structure != self.structure:
                raise ValueError('Structure is not the same in initial_structure_environments')
            if se.voronoi != self.detailed_voronoi:
                if self.detailed_voronoi.is_close_to(se.voronoi):
                    self.detailed_voronoi = se.voronoi
                else:
                    raise ValueError('Detailed Voronoi is not the same in initial_structure_environments')
            se.info = info
        else:
            se = StructureEnvironments(voronoi=self.detailed_voronoi, valences=self.valences,
                                       sites_map=self.sites_map, equivalent_sites=self.equivalent_sites,
                                       ce_list=[None]*len(self.structure), structure=self.structure,
                                       info=info)

        # Set up the coordination numbers that have to be computed based on min_cn, max_cn and possibly the settings
        # for an update (argument "recompute") of an existing StructureEnvironments
        if min_cn is None:
            min_cn = 1
        if max_cn is None:
            max_cn = 13
        all_cns = range(min_cn, max_cn+1)
        do_recompute = False
        if recompute is not None:
            if 'cns' in recompute:
                cns_to_recompute = recompute['cns']
                all_cns = list(set(all_cns).intersection(cns_to_recompute))
            do_recompute = True

        # Variables used for checking timelimit
        max_time_one_site = 0.0
        breakit = False

        if optimization > 0:
            self.detailed_voronoi.local_planes = [None]*len(self.structure)
            self.detailed_voronoi.separations = [None]*len(self.structure)

        # Loop on all the sites
        for isite in range(len(self.structure)):
            if isite not in sites_indices:
                logging.info(' ... in site #{:d}/{:d} ({}) : '
                             'skipped'.format(isite, len(self.structure),
                                              self.structure[isite].species_string))
                continue
            if breakit:
                logging.info(' ... in site #{:d}/{:d} ({}) : '
                             'skipped (timelimit)'.format(isite, len(self.structure),
                                                          self.structure[isite].species_string))
                continue
            logging.info(' ... in site #{:d}/{:d} ({})'.format(isite, len(self.structure),
                                                               self.structure[isite].species_string))
            t1 = time.clock()
            if optimization > 0:
                self.detailed_voronoi.local_planes[isite] = OrderedDict()
                self.detailed_voronoi.separations[isite] = {}
            se.init_neighbors_sets(isite=isite, additional_conditions=additional_conditions, valences=valences)

            to_add_from_hints = []
            nb_sets_info = {}

            for cn, nb_sets in se.neighbors_sets[isite].items():
                if cn not in all_cns:
                    continue
                for inb_set, nb_set in enumerate(nb_sets):
                    logging.debug('    ... getting environments for nb_set ({:d}, {:d})'.format(cn, inb_set))
                    tnbset1 = time.clock()
                    ce = self.update_nb_set_environments(se=se, isite=isite, cn=cn, inb_set=inb_set, nb_set=nb_set,
                                                         recompute=do_recompute, optimization=optimization)
                    tnbset2 = time.clock()
                    if cn not in nb_sets_info:
                        nb_sets_info[cn] = {}
                    nb_sets_info[cn][inb_set] = {'time': tnbset2 - tnbset1}
                    if get_from_hints:
                        for cg_symbol, cg_dict in ce:
                            cg = self.allcg[cg_symbol]
                            # Get possibly missing neighbors sets
                            if cg.neighbors_sets_hints is None:
                                continue
                            logging.debug('       ... getting hints from cg with mp_symbol "{}" ...'.format(cg_symbol))
                            hints_info = {'csm': cg_dict['symmetry_measure'],
                                          'nb_set': nb_set,
                                          'permutation': cg_dict['permutation']}
                            for nb_sets_hints in cg.neighbors_sets_hints:
                                suggested_nb_set_voronoi_indices = nb_sets_hints.hints(hints_info)
                                for inew, new_nb_set_voronoi_indices in enumerate(suggested_nb_set_voronoi_indices):
                                    logging.debug('           hint # {:d}'.format(inew))
                                    new_nb_set = se.NeighborsSet(structure=se.structure, isite=isite,
                                                                 detailed_voronoi=se.voronoi,
                                                                 site_voronoi_indices=new_nb_set_voronoi_indices,
                                                                 sources={'origin': 'nb_set_hints',
                                                                          'hints_type': nb_sets_hints.hints_type,
                                                                          'suggestion_index': inew,
                                                                          'cn_map_source': [cn, inb_set],
                                                                          'cg_source_symbol': cg_symbol})
                                    cn_new_nb_set = len(new_nb_set)
                                    if max_cn is not None and cn_new_nb_set > max_cn:
                                        continue
                                    if min_cn is not None and cn_new_nb_set < min_cn:
                                        continue
                                    if new_nb_set in [ta['new_nb_set'] for ta in to_add_from_hints]:
                                        has_nb_set = True
                                    elif not cn_new_nb_set in se.neighbors_sets[isite]:
                                        has_nb_set = False
                                    else:
                                        has_nb_set = new_nb_set in se.neighbors_sets[isite][cn_new_nb_set]
                                    if not has_nb_set:
                                        to_add_from_hints.append({'isite': isite,
                                                                  'new_nb_set': new_nb_set,
                                                                  'cn_new_nb_set': cn_new_nb_set})
                                        logging.debug('              => to be computed'.format(inew))
                                    else:
                                        logging.debug('              => already present'.format(inew))
            logging.debug('    ... getting environments for nb_sets added from hints')
            for missing_nb_set_to_add in to_add_from_hints:
                se.add_neighbors_set(isite=isite, nb_set=missing_nb_set_to_add['new_nb_set'])
            for missing_nb_set_to_add in to_add_from_hints:
                isite_new_nb_set = missing_nb_set_to_add['isite']
                cn_new_nb_set = missing_nb_set_to_add['cn_new_nb_set']
                new_nb_set = missing_nb_set_to_add['new_nb_set']
                inew_nb_set = se.neighbors_sets[isite_new_nb_set][cn_new_nb_set].index(new_nb_set)
                logging.debug('    ... getting environments for nb_set ({:d}, {:d}) - '
                              'from hints'.format(cn_new_nb_set, inew_nb_set))
                tnbset1 = time.clock()
                self.update_nb_set_environments(se=se,
                                                isite=isite_new_nb_set,
                                                cn=cn_new_nb_set,
                                                inb_set=inew_nb_set,
                                                nb_set=new_nb_set,
                                                optimization=optimization)
                tnbset2 = time.clock()
                if cn not in nb_sets_info:
                    nb_sets_info[cn] = {}
                nb_sets_info[cn][inew_nb_set] = {'time': tnbset2 - tnbset1}
            t2 = time.clock()
            se.update_site_info(isite=isite, info_dict={'time': t2 - t1, 'nb_sets_info': nb_sets_info})
            if timelimit is not None:
                time_elapsed = t2 - time_init
                time_left = timelimit - time_elapsed
                if time_left < 2.0 * max_time_one_site:
                    breakit = True
            max_time_one_site = max(max_time_one_site, t2 - t1)
            logging.info('    ... computed in {:.2f} seconds'.format(t2 - t1))
        time_end = time.clock()
        logging.info('    ... compute_structure_environments ended in {:.2f} seconds'.format(time_end-time_init))
        return se

Response: Computes and returns a StructureEnvironments object containing all the information about the coordination environments in the structure.


Instruction:
How would you code a function in Python 3 to
set up the AbstractGeometry for the local geometry of site with index isite.

Response:
def setup_local_geometry(self, isite, coords, optimization=None):
        """
        Sets up the AbstractGeometry for the local geometry of site with index isite.
        :param isite: Index of the site for which the local geometry has to be set up
        :param coords: The coordinates of the (local) neighbors
        """
        self.local_geometry = AbstractGeometry(
            central_site=self.structure.cart_coords[isite],
            bare_coords=coords,
            centering_type=self.centering_type,
            include_central_site_in_centroid=
            self.include_central_site_in_centroid,
            optimization=optimization)


Instruction:
Implement a function in Python 3 to
set up a purely random structure with a given coordination number.

Response:
def setup_random_structure(self, coordination):
        """
        Sets up a purely random structure with a given coordination.
        :param coordination: coordination number for the random structure
        """
        aa = 0.4
        bb = -0.2
        coords = list()
        for ii in range(coordination + 1):
            coords.append(aa * np.random.random_sample(3, ) + bb)
        self.set_structure(
            lattice=np.array([[10, 0, 0], [0, 10, 0], [0, 0, 10]], np.float),
            species=["Si"] * (coordination + 1),
            coords=coords,
            coords_are_cartesian=False)
        self.setup_random_indices_local_geometry(coordination)


Instruction:
How would you explain what the following Python 3 function does
def setup_random_indices_local_geometry(self, coordination):
        self.icentral_site = 0
        self.indices = list(range(1, coordination + 1))
        np.random.shuffle(self.indices)

Response: Sets up random indices for the local geometry for testing purposes


Instruction:
Write a Python 3 script to
set up ordered indices for the local geometry for testing purposes

Response:
def setup_ordered_indices_local_geometry(self, coordination):
        """
        Sets up ordered indices for the local geometry, for testing purposes
        :param coordination: coordination of the local geometry
        """
        self.icentral_site = 0
        self.indices = list(range(1, coordination + 1))


Instruction:
Can you generate a brief explanation for the following Python 3 code
def setup_explicit_indices_local_geometry(self, explicit_indices):
        self.icentral_site = 0
        self.indices = [ii + 1 for ii in explicit_indices]

Response: Sets up explicit indices for the local geometry for testing purposes


Instruction:
Write a Python 3 function for
returning the continuous symmetry measures of the current local geometry in a dictionary.

Response:
def get_coordination_symmetry_measures(self, only_minimum=True,
                                           all_csms=True, optimization=None):
        """
        Returns the continuous symmetry measures of the current local geometry in a dictionary.
        :return: the continuous symmetry measures of the current local geometry in a dictionary.
        """
        test_geometries = self.allcg.get_implemented_geometries(
            len(self.local_geometry.coords))
        if len(self.local_geometry.coords) == 1:
            if len(test_geometries) == 0:
                return {}
            result_dict = {'S:1': {'csm': 0.0, 'indices': [0], 'algo': 'EXPLICIT',
                                   'local2perfect_map': {0: 0}, 'perfect2local_map': {0: 0},
                                   'scaling_factor': None, 'rotation_matrix': None, 'translation_vector': None}}
            if all_csms:
                for csmtype in ['wocs_ctwocc', 'wocs_ctwcc', 'wocs_csc', 'wcs_ctwocc', 'wcs_ctwcc', 'wcs_csc']:
                    result_dict['S:1']['csm_{}'.format(csmtype)] = 0.0
                    result_dict['S:1']['scaling_factor_{}'.format(csmtype)] = None
                    result_dict['S:1']['rotation_matrix_{}'.format(csmtype)] = None
                    result_dict['S:1']['translation_vector_{}'.format(csmtype)] = None
            return result_dict
        result_dict = {}
        for geometry in test_geometries:
            self.perfect_geometry = AbstractGeometry.from_cg(cg=geometry,
                                                             centering_type=self.centering_type,
                                                             include_central_site_in_centroid=
                                                             self.include_central_site_in_centroid)
            points_perfect = self.perfect_geometry.points_wcs_ctwcc()
            cgsm = self.coordination_geometry_symmetry_measures(geometry,
                                                                points_perfect=points_perfect,
                                                                optimization=optimization)
            result, permutations, algos, local2perfect_maps, perfect2local_maps = cgsm
            if only_minimum:
                if len(result) > 0:
                    imin = np.argmin([rr['symmetry_measure'] for rr in result])
                    if geometry.algorithms is not None:
                        algo = algos[imin]
                    else:
                        algo = algos
                    result_dict[geometry.mp_symbol] = {'csm': result[imin]['symmetry_measure'],
                                                       'indices': permutations[
                                                           imin],
                                                       'algo': algo,
                                                       'local2perfect_map':
                                                           local2perfect_maps[
                                                               imin],
                                                       'perfect2local_map':
                                                           perfect2local_maps[
                                                               imin],
                                                       'scaling_factor': 1.0 / result[imin]['scaling_factor'],
                                                       'rotation_matrix':
                                                           np.linalg.inv(result[imin]['rotation_matrix']),
                                                       'translation_vector': result[imin]['translation_vector']}
                    if all_csms:
                        self._update_results_all_csms(result_dict, permutations, imin, geometry)
            else:
                result_dict[geometry.mp_symbol] = {'csm': result,
                                                   'indices': permutations,
                                                   'algo': algos,
                                                   'local2perfect_map': local2perfect_maps,
                                                   'perfect2local_map': perfect2local_maps}
        return result_dict


Instruction:
Can you generate the documentation for the following Python 3 function
def get_coordination_symmetry_measures_optim(self, only_minimum=True,
                                                 all_csms=True, nb_set=None, optimization=None):
        """
        Returns the continuous symmetry measures of the current local geometry in a dictionary.
        :return: the continuous symmetry measures of the current local geometry in a dictionary.
        """
        cn = len(self.local_geometry.coords)
        test_geometries = self.allcg.get_implemented_geometries(cn)
        if all([cg.algorithms[0].algorithm_type == EXPLICIT_PERMUTATIONS for cg in test_geometries]):
            return self.get_coordination_symmetry_measures(only_minimum=only_minimum, all_csms=all_csms,
                                                           optimization=optimization)
        if not all([all([algo.algorithm_type == SEPARATION_PLANE
                         for algo in cg.algorithms]) for cg in test_geometries]):
            raise ValueError('All algorithms should be EXPLICIT_PERMUTATIONS or SEPARATION_PLANE')

        result_dict = {}
        for geometry in test_geometries:
            self.perfect_geometry = AbstractGeometry.from_cg(cg=geometry,
                                                             centering_type=self.centering_type,
                                                             include_central_site_in_centroid=
                                                             self.include_central_site_in_centroid)
            points_perfect = self.perfect_geometry.points_wcs_ctwcc()
            cgsm = self.coordination_geometry_symmetry_measures_sepplane_optim(geometry,
                                                                               points_perfect=points_perfect,
                                                                               nb_set=nb_set,
                                                                               optimization=optimization)
            result, permutations, algos, local2perfect_maps, perfect2local_maps = cgsm
            if only_minimum:
                if len(result) > 0:
                    imin = np.argmin([rr['symmetry_measure'] for rr in result])
                    if geometry.algorithms is not None:
                        algo = algos[imin]
                    else:
                        algo = algos
                    result_dict[geometry.mp_symbol] = {'csm': result[imin]['symmetry_measure'],
                                                       'indices': permutations[
                                                           imin],
                                                       'algo': algo,
                                                       'local2perfect_map':
                                                           local2perfect_maps[
                                                               imin],
                                                       'perfect2local_map':
                                                           perfect2local_maps[
                                                               imin],
                                                       'scaling_factor': 1.0 / result[imin]['scaling_factor'],
                                                       'rotation_matrix':
                                                           np.linalg.inv(result[imin]['rotation_matrix']),
                                                       'translation_vector': result[imin]['translation_vector']}
                    if all_csms:
                        self._update_results_all_csms(result_dict, permutations, imin, geometry)
        return result_dict

Response: This method returns the continuous symmetry measures of the current local geometry in a dictionary.


Instruction:
Can you write a function in Python 3 where it
returns the symmetry measures of a given coordination_geometry for a set of permutations depending on the permutation setup. Depending on the parameters of the LocalGeometryFinder and on the coordination geometry, different methods are called. :param coordination_geometry: Coordination geometry for which the symmetry measures are looked for :return: the symmetry measures of a given coordination_geometry for a set of permutations :raise: NotImplementedError if the permutation_setup does not exists

Response:
def coordination_geometry_symmetry_measures(self, coordination_geometry,
                                                tested_permutations=False,
                                                points_perfect=None,
                                                optimization=None):
        """
        Returns the symmetry measures of a given coordination_geometry for a set of permutations depending on
        the permutation setup. Depending on the parameters of the LocalGeometryFinder and on the coordination
         geometry, different methods are called.
        :param coordination_geometry: Coordination geometry for which the symmetry measures are looked for
        :return: the symmetry measures of a given coordination_geometry for a set of permutations
        :raise: NotImplementedError if the permutation_setup does not exists
        """
        if tested_permutations:
            tested_permutations = set()
        if self.permutations_safe_override:
            raise ValueError('No permutations safe override anymore')
        csms = []
        permutations = []
        algos = []
        local2perfect_maps = []
        perfect2local_maps = []
        for algo in coordination_geometry.algorithms:
            if algo.algorithm_type == EXPLICIT_PERMUTATIONS:
                return self.coordination_geometry_symmetry_measures_standard(
                    coordination_geometry, algo,
                    points_perfect=points_perfect,
                    optimization=optimization)
            if algo.algorithm_type == SEPARATION_PLANE:
                cgsm = self.coordination_geometry_symmetry_measures_separation_plane(
                    coordination_geometry,
                    algo,
                    tested_permutations=tested_permutations,
                    points_perfect=points_perfect)
                csm, perm, algo, local2perfect_map, perfect2local_map = cgsm

                csms.extend(csm)
                permutations.extend(perm)
                algos.extend(algo)
                local2perfect_maps.extend(local2perfect_map)
                perfect2local_maps.extend(perfect2local_map)
        return csms, permutations, algos, local2perfect_maps, perfect2local_maps


Instruction:
Explain what the following Python 3 code does
def coordination_geometry_symmetry_measures_sepplane_optim(self, coordination_geometry,
                                                               points_perfect=None,
                                                               nb_set=None, optimization=None):
        """
        Returns the symmetry measures of a given coordination_geometry for a set of permutations depending on
        the permutation setup. Depending on the parameters of the LocalGeometryFinder and on the coordination
         geometry, different methods are called.
        :param coordination_geometry: Coordination geometry for which the symmetry measures are looked for
        :return: the symmetry measures of a given coordination_geometry for a set of permutations
        :raise: NotImplementedError if the permutation_setup does not exists
        """
        csms = []
        permutations = []
        algos = []
        local2perfect_maps = []
        perfect2local_maps = []
        for algo in coordination_geometry.algorithms:
            if algo.algorithm_type == SEPARATION_PLANE:
                cgsm = self.coordination_geometry_symmetry_measures_separation_plane_optim(
                    coordination_geometry,
                    algo,
                    points_perfect=points_perfect,
                    nb_set=nb_set,
                    optimization=optimization)
                csm, perm, algo, local2perfect_map, perfect2local_map = cgsm

                csms.extend(csm)
                permutations.extend(perm)
                algos.extend(algo)
                local2perfect_maps.extend(local2perfect_map)
                perfect2local_maps.extend(perfect2local_map)
        return csms, permutations, algos, local2perfect_maps, perfect2local_maps

Response: This method returns the symmetry measures of a given coordination geometry for a set of permutations and a set of local symmetry algorithms.


Instruction:
Explain what the following Python 3 code does
def coordination_geometry_symmetry_measures_standard(self,
                                                         coordination_geometry,
                                                         algo,
                                                         points_perfect=None,
                                                         optimization=None):
        """
        Returns the symmetry measures for a set of permutations (whose setup depends on the coordination geometry)
        for the coordination geometry "coordination_geometry". Standard implementation looking for the symmetry
        measures of each permutation

        :param coordination_geometry: The coordination geometry to be investigated
        :return: The symmetry measures for the given coordination geometry for each permutation investigated
        """
        # permutations_symmetry_measures = np.zeros(len(algo.permutations),
        #                                           np.float)
        if optimization == 2:
            permutations_symmetry_measures = [None] * len(algo.permutations)
            permutations = list()
            algos = list()
            local2perfect_maps = list()
            perfect2local_maps = list()
            for iperm, perm in enumerate(algo.permutations):

                local2perfect_map = {}
                perfect2local_map = {}
                permutations.append(perm)
                for iperfect, ii in enumerate(perm):
                    perfect2local_map[iperfect] = ii
                    local2perfect_map[ii] = iperfect
                local2perfect_maps.append(local2perfect_map)
                perfect2local_maps.append(perfect2local_map)

                points_distorted = self.local_geometry.points_wcs_ctwcc(
                    permutation=perm)

                sm_info = symmetry_measure(points_distorted=points_distorted,
                                              points_perfect=points_perfect)
                sm_info['translation_vector'] = self.local_geometry.centroid_with_centre

                permutations_symmetry_measures[iperm] = sm_info
                algos.append(str(algo))
            return permutations_symmetry_measures, permutations, algos, local2perfect_maps, perfect2local_maps
        else:
            permutations_symmetry_measures = [None] * len(algo.permutations)
            permutations = list()
            algos = list()
            local2perfect_maps = list()
            perfect2local_maps = list()
            for iperm, perm in enumerate(algo.permutations):

                local2perfect_map = {}
                perfect2local_map = {}
                permutations.append(perm)
                for iperfect, ii in enumerate(perm):
                    perfect2local_map[iperfect] = ii
                    local2perfect_map[ii] = iperfect
                local2perfect_maps.append(local2perfect_map)
                perfect2local_maps.append(perfect2local_map)

                points_distorted = self.local_geometry.points_wcs_ctwcc(
                    permutation=perm)

                sm_info = symmetry_measure(points_distorted=points_distorted,
                                           points_perfect=points_perfect)
                sm_info['translation_vector'] = self.local_geometry.centroid_with_centre

                permutations_symmetry_measures[iperm] = sm_info
                algos.append(str(algo))
            return permutations_symmetry_measures, permutations, algos, local2perfect_maps, perfect2local_maps

Response: This method returns the symmetry measures for a set of permutations and a set of algorithms that are in the coordination geometry.


Instruction:
Can you create a Python 3 function that
returns the symmetry measures of the given coordination geometry "coordination_geometry" using separation facets to reduce the complexity of the system. Caller to the refined 2POINTS, 3POINTS and other ... :param coordination_geometry: The coordination geometry to be investigated :return: The symmetry measures for the given coordination geometry for each plane and permutation investigated

Response:
def coordination_geometry_symmetry_measures_separation_plane(self,
                                                                 coordination_geometry,
                                                                 separation_plane_algo,
                                                                 testing=False,
                                                                 tested_permutations=False,
                                                                 points_perfect=None):

        """
        Returns the symmetry measures of the given coordination geometry "coordination_geometry" using separation
        facets to reduce the complexity of the system. Caller to the refined 2POINTS, 3POINTS and other ...
        :param coordination_geometry: The coordination geometry to be investigated
        :return: The symmetry measures for the given coordination geometry for each plane and permutation investigated
        """
        permutations = list()
        permutations_symmetry_measures = list()
        plane_separations = list()
        algos = list()
        perfect2local_maps = list()
        local2perfect_maps = list()
        if testing:
            separation_permutations = list()
        nplanes = 0
        for npoints in range(separation_plane_algo.minimum_number_of_points,
                             min(separation_plane_algo.maximum_number_of_points,
                                 4) + 1):
            for points_combination in itertools.combinations(
                    self.local_geometry.coords, npoints):
                if npoints == 2:
                    if collinear(points_combination[0], points_combination[1],
                                 self.local_geometry.central_site,
                                 tolerance=0.25):
                        continue
                    plane = Plane.from_3points(points_combination[0],
                                               points_combination[1],
                                               self.local_geometry.central_site)
                elif npoints == 3:
                    if collinear(points_combination[0], points_combination[1],
                                 points_combination[2], tolerance=0.25):
                        continue
                    plane = Plane.from_3points(points_combination[0],
                                               points_combination[1],
                                               points_combination[2])
                elif npoints > 3:
                    plane = Plane.from_npoints(points_combination,
                                               best_fit='least_square_distance')
                else:
                    raise ValueError(
                        'Wrong number of points to initialize separation plane')
                cgsm = self._cg_csm_separation_plane(
                    coordination_geometry=coordination_geometry,
                    sepplane=separation_plane_algo,
                    local_plane=plane,
                    plane_separations=plane_separations,
                    dist_tolerances=DIST_TOLERANCES,
                    testing=testing,
                    tested_permutations=tested_permutations,
                    points_perfect=points_perfect)
                csm, perm, algo = cgsm[0], cgsm[1], cgsm[2]

                if csm is not None:
                    permutations_symmetry_measures.extend(csm)
                    permutations.extend(perm)
                    for thisperm in perm:
                        p2l = {}
                        l2p = {}
                        for i_p, pp in enumerate(thisperm):
                            p2l[i_p] = pp
                            l2p[pp] = i_p
                        perfect2local_maps.append(p2l)
                        local2perfect_maps.append(l2p)
                    algos.extend(algo)
                    if testing:
                        separation_permutations.extend(cgsm[3])
                    nplanes += 1
            if nplanes > 0:
                break
        if nplanes == 0:
            return self.coordination_geometry_symmetry_measures_fallback_random(
                coordination_geometry,
                points_perfect=points_perfect)
        if testing:
            return permutations_symmetry_measures, permutations, separation_permutations
        return permutations_symmetry_measures, permutations, algos, local2perfect_maps, perfect2local_maps