<SYSTEM_TASK:>
Solve the following problem using Python, implementing the functions described below, one line at a time
<END_TASK>
<USER_TASK:>
Description:
 def replace_group(self, index, func_grp, strategy, bond_order=1, graph_dict=None, strategy_params=None, reorder=True, extend_structure=True):
 """ Builds off of Molecule.substitute and MoleculeGraph.substitute_group to replace a functional group in self.molecule with a functional group. This method also amends self.graph to incorporate the new functional group. TODO: Figure out how to replace into a ring structure. :param index: Index of atom to substitute. :param func_grp: Substituent molecule. There are three options: 1. Providing an actual molecule as the input. The first atom must be a DummySpecie X, indicating the position of nearest neighbor. The second atom must be the next nearest atom. For example, for a methyl group substitution, func_grp should be X-CH3, where X is the first site and C is the second site. What the code will do is to remove the index site, and connect the nearest neighbor to the C atom in CH3. The X-C bond indicates the directionality to connect the atoms. 2. A string name. The molecule will be obtained from the relevant template in func_groups.json. 3. A MoleculeGraph object. :param strategy: Class from pymatgen.analysis.local_env. :param bond_order: A specified bond order to calculate the bond length between the attached functional group and the nearest neighbor site. Defaults to 1. :param graph_dict: Dictionary representing the bonds of the functional group (format: {(u, v):
 props}, where props is a dictionary of properties, including weight. If None, then the algorithm will attempt to automatically determine bonds using one of a list of strategies defined in pymatgen.analysis.local_env. :param strategy_params: dictionary of keyword arguments for strategy. If None, default parameters will be used. :param reorder: bool, representing if graph nodes need to be reordered following the application of the local_env strategy :param extend_structure: If True (default), then a large artificial box will be placed around the Molecule, because some strategies assume periodic boundary conditions. :return: """


        self.set_node_attributes()
        neighbors = self.get_connected_sites(index)

        # If the atom at index is terminal
        if len(neighbors) == 1:
            self.substitute_group(index, func_grp, strategy,
                                  bond_order=bond_order, graph_dict=graph_dict,
                                  strategy_params=strategy_params,
                                  reorder=reorder,
                                  extend_structure=extend_structure)

        else:
            rings = self.find_rings(including=[index])
            if len(rings) != 0:
                raise RuntimeError("Currently functional group replacement"
                                   "cannot occur at an atom within a ring"
                                   "structure.")

            to_remove = set()
            sizes = dict()
            disconnected = self.graph.to_undirected()
            disconnected.remove_node(index)
            for neighbor in neighbors:
                sizes[neighbor[2]] = len(nx.descendants(disconnected, neighbor[2]))

            keep = max(sizes, key=lambda x: sizes[x])
            for i in sizes.keys():
                if i != keep:
                    to_remove.add(i)

            self.remove_nodes(list(to_remove))

            self.substitute_group(index, func_grp, strategy,
                                  bond_order=bond_order, graph_dict=graph_dict,
                                  strategy_params=strategy_params,
                                  reorder=reorder,
                                  extend_structure=extend_structure)