Exemplo n.º 1
0
def cartesian_grid(xn, yn, zn):
    """
    Spawns a 3D Cartesian grid from three arrays with node coordinates.
    """
    # Compute cell resolutions
    nx = len(xn)-1
    ny = len(yn)-1
    nz = len(zn)-1

    # Create matrices for cell dimensions ...
    dx = empty((nx, ny, nz))
    dy = empty((nx, ny, nz))
    dz = empty((nx, ny, nz))

    # ... and fill them up!
    dx[:] = dif(xn).reshape(nx, 1, 1)
    dy[:] = dif(yn).reshape(1, ny, 1)
    dz[:] = dif(zn).reshape(1, 1, nz)

    # Compute resolutions for cell-centered and all collocated variables
    rc = nx, ny, nz
    ru = nx-1, ny, nz
    rv = nx, ny-1, nz
    rw = nx, ny, nz-1

    return nx, ny, nz, \
           dx, dy, dz, \
           rc, ru, rv, rw  # end of function
Exemplo n.º 2
0
def corr_uvw(uvw, p, rho, dt, dxyz, obst):
    # --------------------------------------------------------------------------
    """
    Docstring.
    """
    # Unpack received tuples
    dx, dy, dz = dxyz
    # Compute pressure correction gradients
    p_x = dif(X, p.val) / avg(X, dx)
    p_y = dif(Y, p.val) / avg(Y, dy)
    p_z = dif(Z, p.val) / avg(Z, dz)

    # Set to zero in obst
    if obst.any() != 0:
        p_x = obst_zero_val(X, p_x, obst)
        p_y = obst_zero_val(Y, p_y, obst)
        p_z = obst_zero_val(Z, p_z, obst)

    # Pad with boundary values by expanding from interior
    # (This is done only for collocated formulation)
    if uvw[X].pos == C:
        p_x = avg(X, cat(X, (p_x[:1, :, :], p_x, p_x[-1:, :, :])))
        p_y = avg(Y, cat(Y, (p_y[:, :1, :], p_y, p_y[:, -1:, :])))
        p_z = avg(Z, cat(Z, (p_z[:, :, :1], p_z, p_z[:, :, -1:])))

    # Correct the velocities
    uvw[X].val[:] = uvw[X].val[:] - dt / avg(uvw[X].pos, rho) * p_x
    uvw[Y].val[:] = uvw[Y].val[:] - dt / avg(uvw[Y].pos, rho) * p_y
    uvw[Z].val[:] = uvw[Z].val[:] - dt / avg(uvw[Z].pos, rho) * p_z

    return  # end of function
Exemplo n.º 3
0
def vol_balance(uvwf, dxyz, obst):
    """
    Computes the volume balance, which is essentially a right hand side in
    the Poisson's equation for pressure.

    Note that "obst" is an optional parameter.  If it is not sent, the source
    won't be zeroed inside the obstacle.  That is important for calculation
    of pressure, see function "calc_p".

    """
    # Unpack tuples
    dx, dy, dz = dxyz
    uf, vf, wf = uvwf

    # Compute it throughout the domain
    src = - dif(X, cat(X, (uf.bnd[W].val, uf.val, uf.bnd[E].val)))*dy*dz  \
          - dif(Y, cat(Y, (vf.bnd[S].val, vf.val, vf.bnd[N].val)))*dx*dz  \
          - dif(Z, cat(Z, (wf.bnd[B].val, wf.val, wf.bnd[T].val)))*dx*dy

    # Zero it inside obstacles, if obstacle is sent as parameter
    if obst.any() != 0:
        src = obst_zero_val(C, src, obst)

    return src  # end of function
Exemplo n.º 4
0
def calc_uvw(uvw, uvwf, rho, mu, p_tot, e_f, dt, dxyz, obst):
    """
    Calculate uvw
    TODO: Write useful docstring

    :param uvw:
    :param uvwf:
    :param rho:
    :param mu:
    :param p_tot:
    :param e_f:
    :param dt:
    :param dxyz:
    :param obst:
    :return:
    """

    # pylint: disable=invalid-name, too-many-locals, too-many-statements

    # Unpack tuples
    u, v, w = uvw
    uf, vf, wf = uvwf
    dx, dy, dz = dxyz
    e_x, e_y, e_z = e_f

    # Fetch resolutions
    ru = u.val.shape
    rv = v.val.shape
    rw = w.val.shape

    # Pre-compute geometrical quantities
    dv = dx * dy * dz

    d = u.pos

    # Create linear systems
    A_u, b_u = create_matrix(u, rho / dt, mu, dxyz, obst, 'd')
    A_v, b_v = create_matrix(v, rho / dt, mu, dxyz, obst, 'd')
    A_w, b_w = create_matrix(w, rho / dt, mu, dxyz, obst, 'd')

    # Advection terms for momentum
    c_u = advection(rho, u, uvwf, dxyz, dt, 'superbee')
    c_v = advection(rho, v, uvwf, dxyz, dt, 'superbee')
    c_w = advection(rho, w, uvwf, dxyz, dt, 'superbee')

    # Innertial term for momentum (this works for collocated and staggered)
    i_u = u.old * avg(u.pos, rho) * avg(u.pos, dv) / dt
    i_v = v.old * avg(v.pos, rho) * avg(v.pos, dv) / dt
    i_w = w.old * avg(w.pos, rho) * avg(w.pos, dv) / dt

    # Compute staggered pressure gradients
    p_tot_x = dif(X, p_tot) / avg(X, dx)
    p_tot_y = dif(Y, p_tot) / avg(Y, dy)
    p_tot_z = dif(Z, p_tot) / avg(Z, dz)

    # Make pressure gradients cell-centered
    if d == C:
        p_tot_x = avg(X,
                      cat(X, (p_tot_x[:1, :, :], p_tot_x, p_tot_x[-1:, :, :])))
        p_tot_y = avg(Y,
                      cat(Y, (p_tot_y[:, :1, :], p_tot_y, p_tot_y[:, -1:, :])))
        p_tot_z = avg(Z,
                      cat(Z, (p_tot_z[:, :, :1], p_tot_z, p_tot_z[:, :, -1:])))

    # Total pressure gradients (this works for collocated and staggered)
    p_st_u = p_tot_x * avg(u.pos, dv)
    p_st_v = p_tot_y * avg(v.pos, dv)
    p_st_w = p_tot_z * avg(w.pos, dv)

    # Full force terms for momentum equations (collocated and staggered)
    f_u = b_u - c_u + i_u - p_st_u + e_x * avg(u.pos, dv)
    f_v = b_v - c_v + i_v - p_st_v + e_y * avg(v.pos, dv)
    f_w = b_w - c_w + i_w - p_st_w + e_z * avg(w.pos, dv)

    # Take care of obsts in the domian
    if obst.any() != 0:
        f_u = obst_zero_val(u.pos, f_u, obst)
        f_v = obst_zero_val(v.pos, f_v, obst)
        f_w = obst_zero_val(w.pos, f_w, obst)

    # Solve for velocities
    res0 = bicgstab(A_u, reshape(f_u, prod(ru)), tol=TOL)
    res1 = bicgstab(A_v, reshape(f_v, prod(rv)), tol=TOL)
    res2 = bicgstab(A_w, reshape(f_w, prod(rw)), tol=TOL)
    u.val[:] = reshape(res0[0], ru)
    v.val[:] = reshape(res1[0], rv)
    w.val[:] = reshape(res2[0], rw)

    # Update velocities in boundary cells
    adj_o_bnds((u, v, w), (dx, dy, dz), dt)

    # Update face velocities (also substract cell-centered pressure gradients
    #                         and add staggered pressure gradients)
    if d == C:
        uf.val[:] = (avg(X, u.val + dt / rho * (p_tot_x)) - dt / avg(X, rho) *
                     (dif(X, p_tot) / avg(X, dx)))
        vf.val[:] = (avg(Y, v.val + dt / rho * (p_tot_y)) - dt / avg(Y, rho) *
                     (dif(Y, p_tot) / avg(Y, dy)))
        wf.val[:] = (avg(Z, w.val + dt / rho * (p_tot_z)) - dt / avg(Z, rho) *
                     (dif(Z, p_tot) / avg(Z, dz)))

        for j in (W, E):
            uf.bnd[j].val[:] = u.bnd[j].val[:]
            vf.bnd[j].val[:] = avg(Y, v.bnd[j].val[:])
            wf.bnd[j].val[:] = avg(Z, w.bnd[j].val[:])
        for j in (S, N):
            uf.bnd[j].val[:] = avg(X, u.bnd[j].val[:])
            vf.bnd[j].val[:] = v.bnd[j].val[:]
            wf.bnd[j].val[:] = avg(Z, w.bnd[j].val[:])
        for j in (B, T):
            uf.bnd[j].val[:] = avg(X, u.bnd[j].val[:])
            vf.bnd[j].val[:] = avg(Y, v.bnd[j].val[:])
            wf.bnd[j].val[:] = w.bnd[j].val[:]

    else:
        uf.val[:] = u.val[:]
        vf.val[:] = v.val[:]
        wf.val[:] = w.val[:]
        for j in (W, E, S, N, B, T):
            uf.bnd[j].val[:] = u.bnd[j].val[:]
            vf.bnd[j].val[:] = v.bnd[j].val[:]
            wf.bnd[j].val[:] = w.bnd[j].val[:]

    if obst.any() != 0:
        uf.val[:] = obst_zero_val(X, uf.val, obst)
        vf.val[:] = obst_zero_val(Y, vf.val, obst)
        wf.val[:] = obst_zero_val(Z, wf.val, obst)

    return  # end of function
Exemplo n.º 5
0
def advection(rho, phi, uvwf, dxyz, dt, lim_name):
    """
    Docstring.
    """
    res = phi.val.shape
    nx, ny, nz = res

    # Unpack tuples
    uf, vf, wf = uvwf
    dx, dy, dz = dxyz

    d = phi.pos

    # Pre-compute geometrical quantities
    sx = dy * dz
    sy = dx * dz
    sz = dx * dy

    # -------------------------------------------------
    # Specific for cell-centered transported variable
    # -------------------------------------------------
    if d == C:

        # Facial values of physical properties including boundary cells
        rho_x_fac = cat(
            X, (rho[:1, :, :], avg(X, rho), rho[-1:, :, :]))  # nxp,ny, nz
        rho_y_fac = cat(
            Y, (rho[:, :1, :], avg(Y, rho), rho[:, -1:, :]))  # nx, nyp,nz
        rho_z_fac = cat(
            Z, (rho[:, :, :1], avg(Z, rho), rho[:, :, -1:]))  # nx, ny, nzp

        # Facial values of areas including boundary cells
        a_x_fac = cat(X, (sx[:1, :, :], avg(X, sx), sx[-1:, :, :]))
        a_y_fac = cat(Y, (sy[:, :1, :], avg(Y, sy), sy[:, -1:, :]))
        a_z_fac = cat(Z, (sz[:, :, :1], avg(Z, sz), sz[:, :, -1:]))

        del_x = avg(X, dx)
        del_y = avg(Y, dy)
        del_z = avg(Z, dz)

        # Facial values of velocities without boundary values
        u_fac = uf.val  # nxm,ny, nz
        v_fac = vf.val  # nx, nym,nz
        w_fac = wf.val  # nx, ny, nzm

        # Boundary velocity values
        u_bnd_W = uf.bnd[W].val
        u_bnd_E = uf.bnd[E].val
        v_bnd_S = vf.bnd[S].val
        v_bnd_N = vf.bnd[N].val
        w_bnd_B = wf.bnd[B].val
        w_bnd_T = wf.bnd[T].val

    # ------------------------------------------------------------
    # Specific for transported variable staggered in x direction
    # ------------------------------------------------------------
    if d == X:
        # Facial values of physical properties including boundary cells
        rho_x_fac = rho  # nx, ny, nz
        rho_nod_y = avg(X, avg(Y, rho))  # nxm,nym,nz
        rho_y_fac = cat(Y, (rho_nod_y[:, :1, :],     \
                            rho_nod_y[:, :, :],     \
                            rho_nod_y[:, -1:, :]))     # nxm,nyp,nz
        rho_nod_z = avg(X, avg(Z, rho))  # nxm,ny,nzm
        rho_z_fac = cat(Z, (rho_nod_z[:, :, :1],      \
                            rho_nod_z[:, :, :],      \
                            rho_nod_z[:, :, -1:]))     # nxm,ny,nzp

        # Facial values of areas including boundary cells
        a_x_fac = sx
        a_y_fac = cat(Y, ( \
                  avg(X, sy[:, :1, :]),\
                  avg(X, avg(Y, sy)), \
                  avg(X, sy[:, -1:, :])))
        a_z_fac = cat(Z, ( \
                  avg(X, sz[:, :, :1]), \
                  avg(X, avg(Z, sz)), \
                  avg(X, sz[:, :, -1:])))

        del_x = dx[1:-1, :, :]
        del_y = avg(X, avg(Y, dy))
        del_z = avg(X, avg(Z, dz))

        # Facial values of velocities without boundary values
        u_fac = avg(X, uf.val)  # nxmm,ny, nz
        v_fac = avg(X, vf.val)  # nxm, nym,nz
        w_fac = avg(X, wf.val)  # nxm, ny, nzm

        # Boundary velocity values
        u_bnd_W = uf.bnd[W].val
        u_bnd_E = uf.bnd[E].val
        v_bnd_S = avg(X, vf.bnd[S].val)
        v_bnd_N = avg(X, vf.bnd[N].val)
        w_bnd_B = avg(X, wf.bnd[B].val)
        w_bnd_T = avg(X, wf.bnd[T].val)

    # ------------------------------------------------------------
    # Specific for transported variable staggered in y direction
    # ------------------------------------------------------------
    if d == Y:
        # Facial values of physical properties including boundary cells
        rho_nod_x = avg(Y, avg(X, rho))  # nxm,nym,nz
        rho_x_fac = cat(X, (rho_nod_x[:1, :, :],      \
                            rho_nod_x[:, :, :],      \
                            rho_nod_x[-1:, :, :]))     # nxp,nym,nz
        rho_y_fac = rho  # nx, ny, nz
        rho_nod_z = avg(Y, avg(Z, rho))  # nx, nym,nzm
        rho_z_fac = cat(Z, (rho_nod_z[:, :, :1],      \
                            rho_nod_z[:, :, :],      \
                            rho_nod_z[:, :, -1:]))     # nx, nym,nzp

        # Facial values of areas including boundary cells
        a_x_fac = cat(X, (             \
                  avg(Y, sx[:1, :, :]),  \
                  avg(Y, avg(X, sx)),   \
                  avg(Y, sx[-1:, :, :])))
        a_y_fac = sy
        a_z_fac = cat(Z, (             \
                  avg(Y, sz[:, :, :1]),  \
                  avg(Y, avg(Z, sz)),   \
                  avg(Y, sz[:, :, -1:])))

        del_x = avg(Y, avg(X, dx))
        del_y = dy[:, 1:-1, :]
        del_z = avg(Y, avg(Z, dz))

        # Facial values of velocities without boundary values
        u_fac = avg(Y, uf.val)  # nxm,nym, nz
        v_fac = avg(Y, vf.val)  # nx, nymm,nz
        w_fac = avg(Y, wf.val)  # nx, nym, nzm

        # Facial values of velocities with boundary values
        u_bnd_W = avg(Y, uf.bnd[W].val)
        u_bnd_E = avg(Y, uf.bnd[E].val)
        v_bnd_S = vf.bnd[S].val
        v_bnd_N = vf.bnd[N].val
        w_bnd_B = avg(Y, wf.bnd[B].val)
        w_bnd_T = avg(Y, wf.bnd[T].val)

    # ------------------------------------------------------------
    # Specific for transported variable staggered in z direction
    # ------------------------------------------------------------
    if d == Z:

        # Facial values of physical properties including boundary cells
        rho_nod_x = avg(Z, avg(X, rho))  # nxm,ny, nzm
        rho_x_fac = cat(X, (rho_nod_x[ :1, :, :],      \
                            rho_nod_x[  :, :, :],      \
                            rho_nod_x[-1:, :, :]))     # nxp,ny, nzm
        rho_nod_y = avg(Z, avg(Y, rho))  # nx, nym,nzm
        rho_y_fac = cat(Y, (rho_nod_y[:, :1, :],      \
                            rho_nod_y[:, :, :],      \
                            rho_nod_y[:, -1:, :]))     # nx, nyp,nzm
        rho_z_fac = rho  # nx, ny, nz

        # Facial values of areas including boundary cells
        a_x_fac = cat(X, (             \
                  avg(Z, sx[:1, :, :]),  \
                  avg(Z, avg(X, sx)),   \
                  avg(Z, sx[-1:, :, :])))
        a_y_fac = cat(Y, (             \
                  avg(Z, sy[:, :1, :]),  \
                  avg(Z, avg(Y, sy)),   \
                  avg(Z, sy[:, -1:, :])))
        a_z_fac = sz

        del_x = avg(Z, avg(X, dx))
        del_y = avg(Z, avg(Y, dy))
        del_z = dz[:, :, 1:-1]

        # Facial values of velocities without boundary values
        u_fac = avg(Z, uf.val)  # nxm,ny,  nzm
        v_fac = avg(Z, vf.val)  # nx, nym, nzm
        w_fac = avg(Z, wf.val)  # nx, ny,  nzmm

        # Facial values of velocities with boundary values
        u_bnd_W = avg(Z, uf.bnd[W].val)
        u_bnd_E = avg(Z, uf.bnd[E].val)
        v_bnd_S = avg(Z, vf.bnd[S].val)
        v_bnd_N = avg(Z, vf.bnd[N].val)
        w_bnd_B = wf.bnd[B].val
        w_bnd_T = wf.bnd[T].val

    # ------------------------------
    # Common part of the algorithm
    # ------------------------------

    # ------------------------------------------------------------
    #
    #    |-o-|-o-|-o-|-o-|-o-|-o-|-o-|-o-|-o-|-o-|
    #      1   2   3   4   5   6   7   8   9   10     phi
    #        x---x---x---x---x---x---x---x---x
    #        1   2   3   4   5   6   7   8   9        d_x initial
    #    0---x---x---x---x---x---x---x---x---x---0
    #    1   2   3   4   5   6   7   8   9  10  11    d_x padded
    #
    # ------------------------------------------------------------

    # Compute consecutive differences (and avoid division by zero)
    d_x = dif(X, phi.val)  # nxm, ny, nz
    d_x[(d_x > -TINY) & (d_x <= 0.0)] = -TINY
    d_x[(d_x >= 0.0) & (d_x < +TINY)] = +TINY
    d_x = cat(X, (d_x[:1, :, :], d_x, d_x[-1:, :, :]))  # nxp, ny, nz

    d_y = dif(Y, phi.val)  # nx, nym, nz
    d_y[(d_y > -TINY) & (d_y <= 0.0)] = -TINY
    d_y[(d_y >= 0.0) & (d_y < +TINY)] = +TINY
    d_y = cat(Y, (d_y[:, :1, :], d_y, d_y[:, -1:, :]))  # nx, nyp, nz

    d_z = dif(Z, phi.val)  # nx, ny, nzm
    d_z[(d_z > -TINY) & (d_z <= 0.0)] = -TINY
    d_z[(d_z >= 0.0) & (d_z < +TINY)] = +TINY
    d_z = cat(Z, (d_z[:, :, :1], d_z, d_z[:, :, -1:]))  # nx, ny, nzp

    # Ratio of consecutive gradients for positive and negative flow
    r_x_we = d_x[1:-1, :, :] / d_x[0:-2, :, :]  # nxm,ny, nz
    r_x_ew = d_x[2:, :, :] / d_x[1:-1, :, :]  # nxm,ny, nz
    r_y_sn = d_y[:, 1:-1, :] / d_y[:, 0:-2, :]  # nx, nym,nz
    r_y_ns = d_y[:, 2:, :] / d_y[:, 1:-1, :]  # nx, nym,nz
    r_z_bt = d_z[:, :, 1:-1] / d_z[:, :, 0:-2]  # nx, ny, nzm
    r_z_tb = d_z[:, :, 2:] / d_z[:, :, 1:-1]  # nx, ny, nzm

    flow_we = u_fac >= 0
    flow_ew = lnot(flow_we)
    flow_sn = v_fac >= 0
    flow_ns = lnot(flow_sn)
    flow_bt = w_fac >= 0
    flow_tb = lnot(flow_bt)

    r_x = r_x_we * flow_we + r_x_ew * flow_ew
    r_y = r_y_sn * flow_sn + r_y_ns * flow_ns
    r_z = r_z_bt * flow_bt + r_z_tb * flow_tb

    # Apply a limiter
    if lim_name == 'upwind':
        psi_x = r_x * 0.0
        psi_y = r_y * 0.0
        psi_z = r_z * 0.0
    elif lim_name == 'minmod':
        psi_x = mx(zeros(r_x.shape), mn(r_x, ones(r_x.shape)))
        psi_y = mx(zeros(r_y.shape), mn(r_y, ones(r_y.shape)))
        psi_z = mx(zeros(r_z.shape), mn(r_z, ones(r_z.shape)))
    elif lim_name == 'superbee':
        psi_x = mx(zeros(r_x.shape), mn(2. * r_x, ones(r_x.shape)),
                   mn(r_x, 2.))
        psi_y = mx(zeros(r_y.shape), mn(2. * r_y, ones(r_y.shape)),
                   mn(r_y, 2.))
        psi_z = mx(zeros(r_z.shape), mn(2. * r_z, ones(r_z.shape)),
                   mn(r_z, 2.))
    elif lim_name == 'koren':
        psi_x = mx(zeros(r_x.shape), mn(2.*r_x, (2.+r_x)/3., \
                         2.*ones(r_x.shape)))
        psi_y = mx(zeros(r_y.shape), mn(2.*r_y, (2.+r_y)/3., \
                         2.*ones(r_y.shape)))
        psi_z = mx(zeros(r_z.shape), mn(2.*r_z, (2.+r_z)/3., \
                         2.*ones(r_z.shape)))

    flux_fac_lim_x = phi.val[0:-1, :, :] * u_fac * flow_we                 \
                     +   phi.val[1:, :, :] * u_fac * flow_ew               \
                     +   0.5 * abs(u_fac) * (1 - abs(u_fac) * dt / del_x)  \
                     *  (psi_x[:, :, :] * d_x[0:nx-1, :, :] * flow_we      \
                          + psi_x[:, :, :] * d_x[1:nx, :, :] * flow_ew)
    flux_fac_lim_y = phi.val[:, 0:-1, :] * v_fac * flow_sn                 \
                     +   phi.val[:, 1:, :] * v_fac * flow_ns               \
                     +   0.5 * abs(v_fac) * (1 - abs(v_fac) * dt / del_y)  \
                     *  (psi_y[:, :, :] * d_y[:, 0:ny-1, :] * flow_sn      \
                          + psi_y[:, :, :] * d_y[:, 1:ny, :] * flow_ns)
    flux_fac_lim_z = phi.val[:, :, 0:-1] * w_fac * flow_bt                 \
                     +   phi.val[:, :, 1:  ] * w_fac * flow_tb             \
                     +   0.5 * abs(w_fac) * (1 - abs(w_fac) * dt / del_z)  \
                     *  (psi_z[:, :, :] * d_z[:, :, 0:nz-1] * flow_bt      \
                          + psi_z[:, :, :] * d_z[:, :, 1:nz] * flow_tb)

    # Pad with boundary values
    flux_fac_lim_x = cat(X, (phi.bnd[W].val * u_bnd_W,      \
                               flux_fac_lim_x,              \
                               phi.bnd[E].val * u_bnd_E))
    flux_fac_lim_y = cat(Y, (phi.bnd[S].val * v_bnd_S,      \
                               flux_fac_lim_y,              \
                               phi.bnd[N].val * v_bnd_N))
    flux_fac_lim_z = cat(Z, (phi.bnd[B].val * w_bnd_B,      \
                               flux_fac_lim_z,              \
                               phi.bnd[T].val * w_bnd_T))

    # Multiply with face areas
    flux_fac_lim_x = rho_x_fac * flux_fac_lim_x * a_x_fac
    flux_fac_lim_y = rho_y_fac * flux_fac_lim_y * a_y_fac
    flux_fac_lim_z = rho_z_fac * flux_fac_lim_z * a_z_fac

    # Sum contributions from all directions up
    c = dif(X, flux_fac_lim_x) + \
        dif(Y, flux_fac_lim_y) + \
        dif(Z, flux_fac_lim_z)

    return c  # end of function
Exemplo n.º 6
0
def obst_mod_matrix(phi, c, obst, obc):
    """
    Adjusts the system matrix for obstacles and cell centered varaibles
    (such as pressure)

    phi  - variable
    c    - coefficients in system matrix
    obst - obstacle array
    obc  - obstacles's boundary condition, ('n' - Neumann, 'd' - Dirichlet)
    """
    pos = phi.pos
    #--------------------------
    #
    # For collocated variables
    #
    #--------------------------
    if pos == C:
        #------------------------------------
        # Neumann's boundary on the obstacle
        #------------------------------------
        if obc == 'n':

            # Correct west and east
            sol_x = dif(X, obst)  # will be +1 east of obst, -1 west of obst
            corr = 1 - (sol_x < 0)
            c.W[1:, :, :] = c.W[1:, :, :] * corr
            corr = 1 - (sol_x > 0)
            c.E[:-1, :, :] = c.E[:-1, :, :] * corr

            # Correct south and north
            sol_y = dif(Y, obst)  # will be +1 north of obst, -1 south of obst
            corr = 1 - (sol_y < 0)
            c.S[:, 1:, :] = c.S[:, 1:, :] * corr
            corr = 1 - (sol_y > 0)
            c.N[:, :-1, :] = c.N[:, :-1, :] * corr

            # Correct bottom and top
            sol_z = dif(Z, obst)  # will be +1 north of obst, -1 south of obst
            corr = 1 - (sol_z < 0)
            c.B[:, :, 1:] = c.B[:, :, 1:] * corr
            corr = 1 - (sol_z > 0)
            c.T[:, :, :-1] = c.T[:, :, :-1] * corr

        #--------------------------------------
        # Dirichlet's boundary on the obstacle
        #--------------------------------------
        elif obc == 'd':
            # Set central coefficient to 1 in obst, unchanged elsewhere
            c.P[:] = c.P[:] * lnot(obst) + obst

            # Set neighbour coefficients to zero in obst
            c.W[:] = c.W[:] * lnot(obst)
            c.E[:] = c.E[:] * lnot(obst)
            c.S[:] = c.S[:] * lnot(obst)
            c.N[:] = c.N[:] * lnot(obst)
            c.B[:] = c.B[:] * lnot(obst)
            c.T[:] = c.T[:] * lnot(obst)

            # Increase coefficients close to obst (makes sense for momentum)
            sol_x = dif(X, obst)  # will be +1 east of obst, -1 west of obst
            corr = 1 + (sol_x > 0)
            c.E[:-1, :, :] = c.E[:-1, :, :] * corr
            corr = 1 + (sol_x < 0)
            c.W[1:, :, :] = c.W[1:, :, :] * corr

            sol_y = dif(Y, obst)  # will be +1 north of obst, -1 south of obst
            corr = 1 + (sol_y > 0)
            c.N[:, :-1, :] = c.N[:, :-1, :] * corr
            corr = 1 + (sol_y < 0)
            c.S[:, 1:, :] = c.S[:, 1:, :] * corr

            sol_z = dif(Z, obst)  # will be +1 top of obst, -1 bottom of obst
            corr = 1 + (sol_z > 0)
            c.T[:, :, :-1] = c.T[:, :, :-1] * corr
            corr = 1 + (sol_z < 0)
            c.B[:, :, 1:] = c.B[:, :, 1:] * corr

        #-------------------------
        #
        # For staggered variables
        #
        #-------------------------
        elif pos == X:
            # Set central coefficient to 1 in obst, unchanged elsewhere
            obst_x = mx(obst[:-1, :, :], obst[1:, :, :])
            c.P[:] = c.P[:] * lnot(obst_x) + obst_x

            # Set neighbour coefficients to zero in obst
            c.W[:] = c.W[:] * lnot(obst_x)
            c.E[:] = c.E[:] * lnot(obst_x)
            c.S[:] = c.S[:] * lnot(obst_x)
            c.N[:] = c.N[:] * lnot(obst_x)
            c.B[:] = c.B[:] * lnot(obst_x)
            c.T[:] = c.T[:] * lnot(obst_x)

            # Increase coefficients close to obst (makes sense for momentum)
            sol_y = dif(Y, obst_x)  # will be +1 north of obst,-1 south of obst
            corr = 1 + (sol_y > 0)
            c.N[:, :-1, :] = c.N[:, :-1, :] * corr
            corr = 1 + (sol_y < 0)
            c.S[:, 1:, :] = c.S[:, 1:, :] * corr

            sol_z = dif(Z, obst_x)  # will be +1 top of obst, -1 bottom of obst
            corr = 1 + (sol_z > 0)
            c.T[:, :, :-1] = c.T[:, :, :-1] * corr
            corr = 1 + (sol_z < 0)
            c.B[:, :, 1:] = c.B[:, :, 1:] * corr

        elif pos == Y:

            # Set central coefficient to 1 in obst, unchanged elsewhere
            obst_y = mx(obst[:, :-1, :], obst[:, 1:, :])
            c.P[:] = c.P[:] * lnot(obst_y) + obst_y

            # Set neighbour coefficients to zero in obst
            c.W[:] = c.W[:] * lnot(obst_y)
            c.E[:] = c.E[:] * lnot(obst_y)
            c.S[:] = c.S[:] * lnot(obst_y)
            c.N[:] = c.N[:] * lnot(obst_y)
            c.B[:] = c.B[:] * lnot(obst_y)
            c.T[:] = c.T[:] * lnot(obst_y)

            # Increase coefficients close to obst (makes sense for momentum)
            sol_x = dif(X, obst_y)  # will be +1 north of obst,-1 south of obst
            corr = 1 + (sol_x > 0)
            c.E[:-1, :, :] = c.E[:-1, :, :] * corr
            corr = 1 + (sol_x < 0)
            c.W[1:, :, :] = c.W[1:, :, :] * corr

            sol_z = dif(Z, obst_y)  # will be +1 north of obst,-1 south of obst
            corr = 1 + (sol_z > 0)
            c.T[:, :, :-1] = c.T[:, :, :-1] * corr
            corr = 1 + (sol_z < 0)
            c.B[:, :, 1:] = c.B[:, :, 1:] * corr

        elif pos == Z:

            # Set central coefficient to 1 in obst, unchanged elsewhere
            obst_z = mx(obst[:, :, :-1], obst[:, :, 1:])
            c.P[:] = c.P[:] * lnot(obst_z) + obst_z

            # Set neighbour coefficients to zero in obst
            c.W[:] = c.W[:] * lnot(obst_z)
            c.E[:] = c.E[:] * lnot(obst_z)
            c.S[:] = c.S[:] * lnot(obst_z)
            c.N[:] = c.N[:] * lnot(obst_z)
            c.B[:] = c.B[:] * lnot(obst_z)
            c.T[:] = c.T[:] * lnot(obst_z)

            # Increase coefficients close to obst (makes sense for momentum)
            sol_x = dif(X, obst_z)  # will be +1 north of obst,-1 south of obst
            corr = 1 + (sol_x > 0)
            c.E[:-1, :, :] = c.E[:-1, :, :] * corr
            corr = 1 + (sol_x < 0)
            c.W[1:, :, :] = c.W[1:, :, :] * corr

            sol_y = dif(Y, obst_z)  # will be +1 north of obst,-1 south of obst
            corr = 1 + (sol_y > 0)
            c.N[:, :-1, :] = c.N[:, :-1, :] * corr
            corr = 1 + (sol_y < 0)
            c.S[:, 1:, :] = c.S[:, 1:, :] * corr

    return c  # end of function