def off_diagonal_block_same_layer(context,layers,id1,id2):
    """Off-diagonal interaction between two different layers. Returns a tuple of two operators.
    """

    pwc_id1 = layers[id1]['spaces']['c']
    plc_id1 = layers[id1]['spaces']['l']
    pwc_id2 = layers[id2]['spaces']['c']
    plc_id2 = layers[id2]['spaces']['l']

    k = layers[layers[id1]['father']]['k']
    kappa = layers[layers[id1]['father']]['kappa']

    D = kappa*lib.createModifiedHelmholtz3dHypersingularBoundaryOperator(context,plc_id2,pwc_id1,plc_id1,k)
    T = lib.createModifiedHelmholtz3dAdjointDoubleLayerBoundaryOperator(context,pwc_id2,pwc_id1,plc_id1,k)
    K = lib.createModifiedHelmholtz3dDoubleLayerBoundaryOperator(context,plc_id2,plc_id1,pwc_id1,k)
    S = 1./kappa*lib.createModifiedHelmholtz3dSingleLayerBoundaryOperator(context,pwc_id2,plc_id1,pwc_id1,k)

    D2 = lib.adjoint(D,pwc_id2)
    T2 = lib.adjoint(K,pwc_id2)
    K2  = lib.adjoint(T,plc_id2)
    S2  = lib.adjoint(S,plc_id2)

    op1 = lib.createBlockedBoundaryOperator(context,[[-D,-T],[K,-S]])
    op2 = lib.createBlockedBoundaryOperator(context,[[-D2,-T2],[K2,-S2]])
    
    return (op1,op2)
def off_diagonal_block_same_layer(context, layers, id1, id2):
    """Off-diagonal interaction between two different layers. Returns a tuple of two operators.
    """

    pwc_id1 = layers[id1]['spaces']['c']
    plc_id1 = layers[id1]['spaces']['l']
    pwc_id2 = layers[id2]['spaces']['c']
    plc_id2 = layers[id2]['spaces']['l']

    k = layers[layers[id1]['father']]['k']
    kappa = layers[layers[id1]['father']]['kappa']

    D = kappa * lib.createModifiedHelmholtz3dHypersingularBoundaryOperator(
        context, plc_id2, pwc_id1, plc_id1, k)
    T = lib.createModifiedHelmholtz3dAdjointDoubleLayerBoundaryOperator(
        context, pwc_id2, pwc_id1, plc_id1, k)
    K = lib.createModifiedHelmholtz3dDoubleLayerBoundaryOperator(
        context, plc_id2, plc_id1, pwc_id1, k)
    S = 1. / kappa * lib.createModifiedHelmholtz3dSingleLayerBoundaryOperator(
        context, pwc_id2, plc_id1, pwc_id1, k)

    D2 = lib.adjoint(D, pwc_id2)
    T2 = lib.adjoint(K, pwc_id2)
    K2 = lib.adjoint(T, plc_id2)
    S2 = lib.adjoint(S, plc_id2)

    op1 = lib.createBlockedBoundaryOperator(context, [[-D, -T], [K, -S]])
    op2 = lib.createBlockedBoundaryOperator(context, [[-D2, -T2], [K2, -S2]])

    return (op1, op2)
def off_diagonal_block_father_son(context,layers,father_id,son_id):
    """Off-diagonal interaction between two different layers. Returns a tuple of two operators.
    """

    plc_father = layers[father_id]['spaces']['l']
    pwc_father = layers[father_id]['spaces']['c']
    plc_son = layers[son_id]['spaces']['l']
    pwc_son = layers[son_id]['spaces']['c']

    kf = layers[father_id]['k']
    kappaf = layers[father_id]['kappa']
    
    if father_id==0:
        null_00 = lib.createNullOperator(context,plc_son,pwc_father,plc_father,label="Null_00")
        null_01 = lib.createNullOperator(context,pwc_son,pwc_father,plc_father,label="Null_01")
        Kf = lib.createModifiedHelmholtz3dDoubleLayerBoundaryOperator(context,plc_son,plc_father,pwc_father,kf,label="Kf0_"+str(son_id))
        Sf = 1./kappaf*lib.createModifiedHelmholtz3dSingleLayerBoundaryOperator(context,pwc_son,plc_father,pwc_father,kf,label="Sf0_"+str(son_id))

        op_father_son = lib.createBlockedBoundaryOperator(context,[[null_00,null_01],[Kf,-Sf]])

        Ds = kappaf*lib.createModifiedHelmholtz3dHypersingularBoundaryOperator(context,plc_father,pwc_son,plc_son,kf,label="Ds"+str(son_id)+"_0")
        Ts = lib.adjoint(Kf,pwc_son)
        Ks = lib.createModifiedHelmholtz3dDoubleLayerBoundaryOperator(context,plc_father,plc_son,pwc_son,kf,label="Ks"+str(son_id)+"_0")
        Ss = lib.adjoint(Sf,plc_son)

        op_son_father = lib.createBlockedBoundaryOperator(context,[[Ds,Ts],[-Ks,Ss]])

    else:
        Df = kappaf*lib.createModifiedHelmholtz3dHypersingularBoundaryOperator(context,plc_son,pwc_father,plc_father,kf)
        Tf = lib.createModifiedHelmholtz3dAdjointDoubleLayerBoundaryOperator(context,pwc_son,pwc_father,plc_father,kf)
        Kf = lib.createModifiedHelmholtz3dDoubleLayerBoundaryOperator(context,plc_son,plc_father,pwc_father,kf)
        Sf = 1./kappaf*lib.createModifiedHelmholtz3dDoubleLayerBoundaryOperator(context,pwc_son,plc_father,pwc_father,kf)

        op_father_son = lib.createBlockedBoundaryOperator(context,[[Df,Tf],[-Kf,Sf]])

        Ds = lib.adjoint(Df,pwc_son)
        Ts = lib.adjoint(Kf,pwc_son)
        Ks = lib.adjoint(Tf,plc_son)
        Ss = lib.adjoint(Sf,plc_son)

        op_son_father = lib.createBlockedBoundaryOperator(context,[[Ds,Ts],[-Ks,Ss]])

    return (op_father_son,op_son_father)
def off_diagonal_block_father_son(context, layers, father_id, son_id):
    """Off-diagonal interaction between two different layers. Returns a tuple of two operators.
    """

    plc_father = layers[father_id]['spaces']['l']
    pwc_father = layers[father_id]['spaces']['c']
    plc_son = layers[son_id]['spaces']['l']
    pwc_son = layers[son_id]['spaces']['c']

    kf = layers[father_id]['k']
    kappaf = layers[father_id]['kappa']

    if father_id == 0:
        null_00 = lib.createNullOperator(context,
                                         plc_son,
                                         pwc_father,
                                         plc_father,
                                         label="Null_00")
        null_01 = lib.createNullOperator(context,
                                         pwc_son,
                                         pwc_father,
                                         plc_father,
                                         label="Null_01")
        Kf = lib.createModifiedHelmholtz3dDoubleLayerBoundaryOperator(
            context,
            plc_son,
            plc_father,
            pwc_father,
            kf,
            label="Kf0_" + str(son_id))
        Sf = 1. / kappaf * lib.createModifiedHelmholtz3dSingleLayerBoundaryOperator(
            context,
            pwc_son,
            plc_father,
            pwc_father,
            kf,
            label="Sf0_" + str(son_id))

        op_father_son = lib.createBlockedBoundaryOperator(
            context, [[null_00, null_01], [Kf, -Sf]])

        Ds = kappaf * lib.createModifiedHelmholtz3dHypersingularBoundaryOperator(
            context,
            plc_father,
            pwc_son,
            plc_son,
            kf,
            label="Ds" + str(son_id) + "_0")
        Ts = lib.adjoint(Kf, pwc_son)
        Ks = lib.createModifiedHelmholtz3dDoubleLayerBoundaryOperator(
            context,
            plc_father,
            plc_son,
            pwc_son,
            kf,
            label="Ks" + str(son_id) + "_0")
        Ss = lib.adjoint(Sf, plc_son)

        op_son_father = lib.createBlockedBoundaryOperator(
            context, [[Ds, Ts], [-Ks, Ss]])

    else:
        Df = kappaf * lib.createModifiedHelmholtz3dHypersingularBoundaryOperator(
            context, plc_son, pwc_father, plc_father, kf)
        Tf = lib.createModifiedHelmholtz3dAdjointDoubleLayerBoundaryOperator(
            context, pwc_son, pwc_father, plc_father, kf)
        Kf = lib.createModifiedHelmholtz3dDoubleLayerBoundaryOperator(
            context, plc_son, plc_father, pwc_father, kf)
        Sf = 1. / kappaf * lib.createModifiedHelmholtz3dDoubleLayerBoundaryOperator(
            context, pwc_son, plc_father, pwc_father, kf)

        op_father_son = lib.createBlockedBoundaryOperator(
            context, [[Df, Tf], [-Kf, Sf]])

        Ds = lib.adjoint(Df, pwc_son)
        Ts = lib.adjoint(Kf, pwc_son)
        Ks = lib.adjoint(Tf, plc_son)
        Ss = lib.adjoint(Sf, plc_son)

        op_son_father = lib.createBlockedBoundaryOperator(
            context, [[Ds, Ts], [-Ks, Ss]])

    return (op_father_son, op_son_father)