def _soft_fuse(self):
        """Fuse consecutive loops over the same iteration set by concatenating
        kernel bodies and creating new :class:`ParLoop` objects representing
        the fused sequence.

        The conditions under which two loops over the same iteration set can
        be soft fused are:

            * They are both direct, OR
            * One is direct and the other indirect

        This is detailed in the paper::

            "Mesh Independent Loop Fusion for Unstructured Mesh Applications"

        from C. Bertolli et al.
        """

        def fuse(self, loops, loop_chain_index):
            # Naming convention: here, we are fusing ASTs in /fuse_asts/ within
            # /base_ast/. Same convention will be used in the /hard_fuse/ method
            kernels = [l.kernel for l in loops]
            fuse_asts = [k._original_ast if k._code else k._ast for k in kernels]
            # Fuse the actual kernels' bodies
            base_ast = dcopy(fuse_asts[0])
            retval = FindInstances.default_retval()
            base_fundecl = FindInstances(ast.FunDecl).visit(base_ast, ret=retval)[ast.FunDecl]
            if len(base_fundecl) != 1:
                raise RuntimeError("Fusing kernels, but found unexpected AST")
            base_fundecl = base_fundecl[0]
            for unique_id, _fuse_ast in enumerate(fuse_asts[1:], 1):
                fuse_ast = dcopy(_fuse_ast)
                retval = FindInstances.default_retval()
                fuse_fundecl = FindInstances(ast.FunDecl).visit(fuse_ast, ret=retval)[ast.FunDecl]
                if len(fuse_fundecl) != 1:
                    raise RuntimeError("Fusing kernels, but found unexpected AST")
                fuse_fundecl = fuse_fundecl[0]
                # 1) Extend function name
                base_fundecl.name = "%s_%s" % (base_fundecl.name, fuse_fundecl.name)
                # 2) Concatenate the arguments in the signature
                base_fundecl.args.extend(fuse_fundecl.args)
                # 3) Uniquify symbols identifiers
                retval = SymbolReferences.default_retval()
                fuse_symbols = SymbolReferences().visit(fuse_ast, ret=retval)
                for decl in fuse_fundecl.args:
                    for symbol, _ in fuse_symbols[decl.sym.symbol]:
                        symbol.symbol = "%s_%d" % (symbol.symbol, unique_id)
                # 4) Scope and concatenate bodies
                base_fundecl.children[0] = ast.Block(
                    [ast.Block(base_fundecl.children[0].children, open_scope=True),
                     ast.FlatBlock("\n\n// Begin of fused kernel\n\n"),
                     ast.Block(fuse_fundecl.children[0].children, open_scope=True)])
            # Eliminate redundancies in the fused kernel's signature
            self._filter_kernel_args(loops, base_fundecl)
            # Naming convention
            fused_ast = base_ast
            return Kernel(kernels, fused_ast, loop_chain_index)

        fused, fusing = [], [self._loop_chain[0]]
        for i, loop in enumerate(self._loop_chain[1:]):
            base_loop = fusing[-1]
            if base_loop.it_space != loop.it_space or \
                    (base_loop.is_indirect and loop.is_indirect):
                # Fusion not legal
                fused.append((fuse(self, fusing, len(fused)), i+1))
                fusing = [loop]
            elif (base_loop.is_direct and loop.is_direct) or \
                    (base_loop.is_direct and loop.is_indirect) or \
                    (base_loop.is_indirect and loop.is_direct):
                # This loop is fusible. Also, can speculative go on searching
                # for other loops to fuse
                fusing.append(loop)
            else:
                raise RuntimeError("Unexpected loop chain structure while fusing")
        if fusing:
            fused.append((fuse(self, fusing, len(fused)), len(self._loop_chain)))

        fused_kernels, offsets = zip(*fused)
        self._schedule = FusionSchedule(fused_kernels, offsets)
        self._loop_chain = self._schedule(self._loop_chain)
dataset/ETHPy150Open OP2/PyOP2/pyop2/fusion.py/Inspector._soft_fuse/original