def diagonal_block(context,layers,layer_id,impedance):
    """Create a diagonal block associated with a given layer_id
    
    """

    plc = layers[layer_id]['spaces']['l']
    pwc = layers[layer_id]['spaces']['c']
    k = layers[layer_id]['k']
    kappa = layers[layer_id]['kappa']

    if layer_id == 0:
        I_00  = lib.createIdentityOperator(context,plc,pwc,plc,label="I00")
        I_01 = lib.createIdentityOperator(context,pwc,pwc,plc,label="I01")
        I_10 = lib.createIdentityOperator(context,plc,plc,pwc,label="I10")
        K = lib.createModifiedHelmholtz3dDoubleLayerBoundaryOperator(context,plc,plc,pwc,k,label="K10")
        S = 1./kappa*lib.createModifiedHelmholtz3dSingleLayerBoundaryOperator(context,pwc,plc,pwc,k,label="S11")

        return lib.createBlockedBoundaryOperator(context,[[I_00,impedance*I_01],[-.5*I_10-K,S]])
    else:
        kf = layers[layers[layer_id]['father']]['k']
        kappaf = layers[layers[layer_id]['father']]['kappa']
        DD = (-kappaf*lib.createModifiedHelmholtz3dHypersingularBoundaryOperator(context,plc,pwc,plc,kf,label="Df"+str(layer_id)+"_"+str(layer_id))
              -kappa*lib.createModifiedHelmholtz3dHypersingularBoundaryOperator(context,plc,pwc,plc,k,label="D"+str(layer_id)+"_"+str(layer_id)))
        TT = (-lib.createModifiedHelmholtz3dAdjointDoubleLayerBoundaryOperator(context,pwc,pwc,plc,kf,label="Tf"+str(layer_id)+"_"+str(layer_id))
              -lib.createModifiedHelmholtz3dAdjointDoubleLayerBoundaryOperator(context,pwc,pwc,plc,k,label="T"+str(layer_id)+"_"+str(layer_id)))
        KK = (lib.createModifiedHelmholtz3dDoubleLayerBoundaryOperator(context,plc,plc,pwc,kf,label="Kf"+str(layer_id)+"_"+str(layer_id))
              +lib.createModifiedHelmholtz3dDoubleLayerBoundaryOperator(context,plc,plc,pwc,k,label="K"+str(layer_id)+"_"+str(layer_id)))
        SS = (-1./kappaf*lib.createModifiedHelmholtz3dSingleLayerBoundaryOperator(context,pwc,plc,pwc,kf,label="Sf"+str(layer_id)+"_"+str(layer_id))
              -1./kappa*lib.createModifiedHelmholtz3dSingleLayerBoundaryOperator(context,pwc,plc,pwc,k,label="S"+str(layer_id)+"_"+str(layer_id)))
        
        return lib.createBlockedBoundaryOperator(context,[[DD,TT],[KK,SS]])
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)
def diagonal_block(context, layers, layer_id, impedance):
    """Create a diagonal block associated with a given layer_id
    
    """

    plc = layers[layer_id]['spaces']['l']
    pwc = layers[layer_id]['spaces']['c']
    k = layers[layer_id]['k']
    kappa = layers[layer_id]['kappa']

    if layer_id == 0:
        I_00 = lib.createIdentityOperator(context, plc, pwc, plc, label="I00")
        I_01 = lib.createIdentityOperator(context, pwc, pwc, plc, label="I01")
        I_10 = lib.createIdentityOperator(context, plc, plc, pwc, label="I10")
        K = lib.createModifiedHelmholtz3dDoubleLayerBoundaryOperator(
            context, plc, plc, pwc, k, label="K10")
        S = 1. / kappa * lib.createModifiedHelmholtz3dSingleLayerBoundaryOperator(
            context, pwc, plc, pwc, k, label="S11")

        return lib.createBlockedBoundaryOperator(
            context, [[I_00, impedance * I_01], [-.5 * I_10 - K, S]])
    else:
        kf = layers[layers[layer_id]['father']]['k']
        kappaf = layers[layers[layer_id]['father']]['kappa']
        DD = (
            -kappaf *
            lib.createModifiedHelmholtz3dHypersingularBoundaryOperator(
                context,
                plc,
                pwc,
                plc,
                kf,
                label="Df" + str(layer_id) + "_" + str(layer_id)) -
            kappa * lib.createModifiedHelmholtz3dHypersingularBoundaryOperator(
                context,
                plc,
                pwc,
                plc,
                k,
                label="D" + str(layer_id) + "_" + str(layer_id)))
        TT = (-lib.createModifiedHelmholtz3dAdjointDoubleLayerBoundaryOperator(
            context,
            pwc,
            pwc,
            plc,
            kf,
            label="Tf" + str(layer_id) + "_" + str(layer_id)) -
              lib.createModifiedHelmholtz3dAdjointDoubleLayerBoundaryOperator(
                  context,
                  pwc,
                  pwc,
                  plc,
                  k,
                  label="T" + str(layer_id) + "_" + str(layer_id)))
        KK = (lib.createModifiedHelmholtz3dDoubleLayerBoundaryOperator(
            context,
            plc,
            plc,
            pwc,
            kf,
            label="Kf" + str(layer_id) + "_" + str(layer_id)) +
              lib.createModifiedHelmholtz3dDoubleLayerBoundaryOperator(
                  context,
                  plc,
                  plc,
                  pwc,
                  k,
                  label="K" + str(layer_id) + "_" + str(layer_id)))
        SS = (-1. / kappaf *
              lib.createModifiedHelmholtz3dSingleLayerBoundaryOperator(
                  context,
                  pwc,
                  plc,
                  pwc,
                  kf,
                  label="Sf" + str(layer_id) + "_" + str(layer_id)) - 1. /
              kappa * lib.createModifiedHelmholtz3dSingleLayerBoundaryOperator(
                  context,
                  pwc,
                  plc,
                  pwc,
                  k,
                  label="S" + str(layer_id) + "_" + str(layer_id)))

        return lib.createBlockedBoundaryOperator(context, [[DD, TT], [KK, SS]])