collect all split/concatenation nodes and group them by target interface

def mergeSplitsOnInterfaces(root: LNode):
    """
    collect all split/concatenation nodes and group them by target interface
    """
    for ch in root.children:
        if ch.children:
            mergeSplitsOnInterfaces(ch)

    ctx = MergeSplitsOnInterfacesCtx()
    for ch in root.children:
        srcPorts = None
        try:
            if ch.name == "CONCAT":
                p = single(ch.east, lambda x: True)
                e = single(p.outgoingEdges, lambda x: True)
                srcPorts = e.dsts
            elif ch.name == "SLICE":
                p = single(ch.west, lambda x: True)
                e = single(p.incomingEdges, lambda x: True)
                srcPorts = e.srcs
        except (DuplicitValueExc, NoValueExc):
            continue

        if srcPorts is not None:
            for srcPort in srcPorts:
                if isinstance(srcPort.parent, LPort):
                    # only for non primitive ports
                    rootPort = getRootIntfPort(srcPort)
                    ctx.register(rootPort, ch, e)

    # join them if it is possible
    for srcPort, splitsAndConcats in ctx.iterPortSplits():
        if len(splitsAndConcats) <= 1:
            continue

        name = "SPLIT" if srcPort.direction == PortType.OUTPUT else "CONCAT"
        newSplitNode = root.addNode(name)
        copyPort(srcPort, newSplitNode, True, "")
        n = splitsAndConcats[0][0]
        for i in range(max(len(n.west),
                           len(n.east))):
            copyPort(
                srcPort, newSplitNode,
                False, "[%d]" % i)

        reconnectPorts(root, srcPort, splitsAndConcats,
                       newSplitNode)