Exemplo n.º 1
0
def computeVerticalSpeeds(os, sp):
    for i in range(1, os.imax - 1):
        for j in range(1, os.jmax - 1):
            os.W[i, j, os.kmm[i, j]] = 0  # No current through bottom
            for k in range(os.kmm[i, j] - 1, -1, -1):
                # Current through upper boundary is calculated to balance
                # flow rates through the other ones.

                # Calculate mean cell heights on both borders in x and y
                # direction, to find areas of cell interfaces:
                meanHeightU = (
                    0.5 *
                    (os.cellHeights[i - 1, j, k] + os.cellHeights[i, j, k]),
                    0.5 *
                    (os.cellHeights[i, j, k] + os.cellHeights[i + 1, j, k]))
                meanHeightV = (
                    0.5 *
                    (os.cellHeights[i, j - 1, k] + os.cellHeights[i, j, k]),
                    0.5 *
                    (os.cellHeights[i, j, k] + os.cellHeights[i, j + 1, k]))

                flowDiff = os.W[i,j,k+1]*sp.dx*sp.dx \
                 + getUV(os.U,i-1,j,k,0)*sp.dx*meanHeightU[0] - getUV(os.U,i,j,k,0)*sp.dx*meanHeightU[1] \
                 + getUV(os.V,i,j-1,k,0)*sp.dx*meanHeightV[0] - getUV(os.V,i,j,k,0)*sp.dx*meanHeightV[1]
                os.W[i, j, k] = flowDiff / (sp.dx * sp.dx)
Exemplo n.º 2
0
    def calcVerticalMixingRichardson(self, sp):
        for i in range(0,self.imax):
            for j in range(0,self.jmax):
                lowerDepth = 0
                for k in range(0,self.kmax-1):
                    lowerDepth = lowerDepth + self.cellHeights[i,j,k]
                    centerDepth = lowerDepth - 0.5*self.cellHeights[i,j,k]
                    if k<self.kmm[i,j]:
                        k_w = 0.028*((sp.H_wave*sp.H_wave)/sp.T_wave)*math.exp(-0.8*centerDepth/sp.H_wave)
                        meanCellHeights = 0.5*(self.cellHeights[i,j,k] + self.cellHeights[i,j,k+1])
                        # Calculate the vertical gradient of the
                        # density. If density is increasing downwards
                        # we want a positive value:
                        d_rho_dz = (self.rho[i,j,k+1] - self.rho[i,j,k])/meanCellHeights
                        meanU_above = 0.5*(getUV(self.U,i-1,j,k,0) + getUV(self.U,i,j,k,0))
                        meanU_below = 0.5*(getUV(self.U,i-1,j,k+1,0) + getUV(self.U,i,j,k+1,0))
                        meanV_above = 0.5*(getUV(self.V,i,j-1,k,0) + getUV(self.V,i,j,k,0))
                        meanV_below = 0.5*(getUV(self.V,i,j-1,k+1,0) + getUV(self.V,i,j,k+1,0))
                        d_U_dz2 = (math.pow(meanU_above - meanU_below, 2) + math.pow(meanV_above - meanV_below,2))/ \
                                   (meanCellHeights*meanCellHeights)
                        d_U_dz2 = max(d_U_dz2, 1e-6)
                        #if d_U_dz2 == 0:
                        #    Ri = 0
                        #else:
                        Ri = (9.81/self.rho[i,j,k])*d_rho_dz/d_U_dz2
                        if math.isnan(Ri):
                            Ri = 0

                        self.K_v[i,j,k] = sp.KVm*(math.atan(sp.G_vmix*(sp.Ri0-Ri))/math.pi + 0.5) + k_w


                    else:
                        self.K_v[i,j,k] = 0
Exemplo n.º 3
0
def biharmon(os, sp, varU, varV):

    brd = 2
    dx4 = sp.dx * sp.dx * sp.dx * sp.dx

    diffU = np.zeros(varU.shape)
    diffV = np.zeros(varV.shape)

    # Diffusion along x direction:
    for i in range(brd, os.imax - 1 - brd):
        for j in range(brd, os.jmax - brd):
            for k in range(0, os.kmax):
                if not os.maskU[i, j, k]:
                    break
                val = varU[i, j, k]
                s1 = getUV(varU,i+1,j,k,val) + getUV(varU,i,j+1,k,val) \
                    + getUV(varU,i-1,j,k,val) + getUV(varU,i,j-1,k,val)
                s2 = getUV(varU,i+1,j+1,k,val) + getUV(varU,i-1,j+1,k,val) \
                    + getUV(varU,i-1,j-1,k,val) + getUV(varU,i+1,j-1,k,val)
                s3 = os.maskU[i+1,j,k]*getUV(varU,i+2,j,k,val) \
                    + os.maskU[i,j+1,k]*getUV(varU,i,j+2,k,val) \
                    + os.maskU[i-1,j,k]*getUV(varU,i-2,j,k,val) \
                    + os.maskU[i,j-1,k]*getUV(varU,i,j-2,k,val)

                diffU[i, j,
                      k] = sp.KBi * (20 * val - 8 * s1 + 2 * s2 + s3) / dx4

    # Diffusion along y direction:
    for i in range(brd, os.imax - brd):
        for j in range(brd, os.jmax - 1 - brd):
            for k in range(0, os.kmax):
                if not os.maskV[i, j, k]:
                    break
                val = varV[i, j, k]
                s1 = getUV(varV,i+1,j,k,val) + getUV(varV,i,j+1,k,val) \
                    + getUV(varV,i-1,j,k,val) + getUV(varV,i,j-1,k,val)
                s2 = getUV(varV,i+1,j+1,k,val) + getUV(varV,i-1,j+1,k,val) \
                    + getUV(varV,i-1,j-1,k,val) + getUV(varV,i+1,j-1,k,val)
                s3 = os.maskV[i+1,j,k]*getUV(varV,i+2,j,k,val) \
                    + os.maskV[i,j+1,k]*getUV(varV,i,j+2,k,val) \
                    + os.maskV[i-1,j,k]*getUV(varV,i-2,j,k,val) \
                    + os.maskV[i,j-1,k]*getUV(varV,i,j-2,k,val)

                diffV[i, j,
                      k] = sp.KBi * (20 * val - 8 * s1 + 2 * s2 + s3) / dx4

    return (diffU, diffV)
Exemplo n.º 4
0
def stepUVE(os, sp):
    imax = os.imax
    jmax = os.jmax
    kmax = os.kmax

    # Based on the values in os.W[i,j,0] - the vertical speeds calculated at the top edge
    # of the surface layer cells, calculate the updated elevation:
    os.E_next[1:-1, 1:-1] = os.E[1:-1, 1:-1] + sp.dt * os.W[1:-1, 1:-1, 0]

    # Create temporary arrays:
    p_above = sp.p_atm0 * np.ones((imax, jmax))
    p_diff = np.zeros((imax, jmax))
    p_gradx = np.zeros((imax - 1, jmax, kmax))
    p_grady = np.zeros((imax, jmax - 1, kmax))

    # Calculate pressure gradients:
    for k in range(0, kmax):
        for i in range(0, imax):
            for j in range(0, jmax):
                if k < os.kmm[i, j]:
                    p_diff[i, j] = os.cellHeights[i, j,
                                                  k] * 9.81 * os.rho[i, j, k]
                else:
                    p_diff[i, j] = 0

        for i in range(0, imax - 1):
            for j in range(0, jmax):
                if os.maskU[i, j, k] > 0:
                    # height measured from below:
                    meanCellHeight = 0.5 * (os.cellHeights[i, j, k] +
                                            os.cellHeights[i + 1, j, k])
                    if k == 0:  # Surface layer
                        p_gradx[i,j,k] = (p_above[i+1,j] + p_diff[i+1,j]*(os.cellHeights[i+1,j,k]-0.5*meanCellHeight)/os.cellHeights[i+1,j,k] \
                             - (p_above[i,j] + p_diff[i,j]*(os.cellHeights[i,j,k]-0.5*meanCellHeight)/os.cellHeights[i,j,k])) / sp.dx
                    else:  # Mid or bottom layer:
                        p_gradx[i,j,k] = (p_above[i+1,j] + p_diff[i+1,j]*0.5*meanCellHeight/os.cellHeights[i+1,j,k] \
                            - (p_above[i,j] + p_diff[i,j]* 0.5*meanCellHeight/os.cellHeights[i,j,k])) / sp.dx

        for i in range(0, imax):
            for j in range(0, jmax - 1):
                if os.maskV[i, j, k] > 0:
                    # height measured from below:
                    meanCellHeight = 0.5 * (os.cellHeights[i, j, k] +
                                            os.cellHeights[i, j + 1, k])
                    if k == 0:  # Surface layer
                        p_grady[i,j,k] = (p_above[i,j+1] + p_diff[i,j+1]*(os.cellHeights[i,j+1,k]-0.5*meanCellHeight)/os.cellHeights[i,j+1,k] \
                             - (p_above[i,j] + p_diff[i,j]*(os.cellHeights[i,j,k]-0.5*meanCellHeight)/os.cellHeights[i,j,k])) / sp.dx
                    else:  # Mid or bottom layer:
                        p_grady[i,j,k] = (p_above[i,j+1] + p_diff[i,j+1]*0.5*meanCellHeight/os.cellHeights[i,j+1,k] \
                            - (p_above[i,j] + p_diff[i,j]* 0.5*meanCellHeight/os.cellHeights[i,j,k])) / sp.dx

        p_above = p_above + p_diff

    # Calculate horizontal accelerations in U direction:
    for i in range(0, imax - 1):
        for j in range(0, jmax):
            if os.kmm[i, j] >= 0:
                for k in range(0, os.kmax):

                    if not os.maskU[i, j, k]:
                        os.U_next[i, j, k:os.kmax] = math.nan
                        break

                    # Get the value at this point:
                    val = os.U[i, j, k]

                    # Estimate the local V and W values by interpolation:
                    vMean = calcMean(
                        (getUV(os.V, i, j - 1, k,
                               math.nan), getUV(os.V, i, j, k, math.nan),
                         getUV(os.V, i + 1, j - 1, k,
                               math.nan), getUV(os.V, i + 1, j, k, math.nan)))
                    wMean = calcMean(
                        (getUV(os.W, i, j, k,
                               math.nan), getUV(os.W, i + 1, j, k, math.nan)))

                    # Estimate the local d2u/dz2 (double derivative):
                    if k > 0:
                        dz_up = 0.5 * (os.cellHeights[i, j, k] +
                                       os.cellHeights[i, j, k - 1])
                    else:
                        dz_up = os.cellHeights[i, j, k]
                    if k < kmax - 1:
                        dz_down = 0.5 * (os.cellHeights[i, j, k] +
                                         os.cellHeights[i, j, k + 1])
                    else:
                        dz_down = os.cellHeights[i, j, k]
                    d2u_dz2 = ((getUV(os.U,i,j,k-1,val) - val)/dz_up \
                               - (val - getUV(os.U,i,j,k+1,val))/dz_down)/(0.5*(dz_up+dz_down))

                    if sp.biharmonic:
                        # If biharmonic is activated the diffusion is handled later, so we
                        # can set it to 0 for now:
                        diffUV = 0
                    else:
                        # Estimate the local d2u/dx2 (double derivative):
                        d2u_dx2 = (getUV(os.U, i - 1, j, k, val) - 2 * val +
                                   getUV(os.U, i + 1, j, k, val)) / (sp.dx *
                                                                     sp.dx)
                        # Estimate the local d2u/dy2 (double derivative):
                        d2u_dy2 = (getUV(os.U, i, j - 1, k, val) - 2 * val +
                                   getUV(os.U, i, j + 1, k, val)) / (sp.dx *
                                                                     sp.dx)
                        # Calculate diffusion term:
                        diffUV = os.AH[i, j, k] * (d2u_dx2 + d2u_dy2)

                    # Calculate nonlinear (advective) terms:
                    if sp.advectiveTermsOn:
                        # Calculate the advection (nonlinear) terms using the
                        # Superbee flux limiter to limit oscillations while
                        # suppressing numerical diffusion:
                        advU = superbeeAdv(sp.dt, sp.dx,
                                           getUV(os.U, i - 2, j, k, val),
                                           getUV(os.U, i - 1, j, k, val), val,
                                           getUV(os.U, i + 1, j, k, val),
                                           getUV(os.U, i + 2, j, k, val), val,
                                           val)
                        advV = superbeeAdv(sp.dt, sp.dx,
                                           getUV(os.U, i, j - 2, k, val),
                                           getUV(os.U, i, j - 1, k, val), val,
                                           getUV(os.U, i, j + 1, k, val),
                                           getUV(os.U, i, j + 2, k, val),
                                           vMean, vMean)
                        advW = superbeeAdv(sp.dt, sp.dx,
                                           getUV(os.U, i, j, k - 2, val),
                                           getUV(os.U, i, j, k - 1, val), val,
                                           getUV(os.U, i, j, k + 1, val),
                                           getUV(os.U, i, j + 2, k + 2, val),
                                           wMean, wMean)
                    else:
                        advU = 0
                        advV = 0
                        advW = 0

                    # Sum up the terms and calculate next time step value:
                    os.U_next[i, j, k] = os.U[i, j, k] + sp.dt * (
                        -p_gradx[i, j, k] / sp.rho_0  # Pressure term
                        + advU + advV + advW  # Advective terms
                        + sp.A_z * d2u_dz2  # Vertical eddy viscosity
                        + diffUV  # Horizontal diffusion
                        + 2 * sp.omega * math.sin(sp.phi0) * vMean)  # Coriolis

                    #if np.isnan(os.U_next[i,j,k]) or np.isinf(os.U_next[i,j,k]) or np.isinf(-os.U_next[i,j,k]):
                    #    print("nan")

    # Calculate horizontal accelerations in V direction:
    for i in range(0, imax):
        for j in range(0, jmax - 1):
            if os.kmm[i, j] >= 0:
                for k in range(0, os.kmm[i, j]):

                    if not os.maskV[i, j, k]:
                        os.V_next[i, j, k:os.kmax] = math.nan
                        break

                    # Get the value at this point:
                    val = os.V[i, j, k]

                    # Estimate the local U and W values by interpolation:
                    uMean = calcMean(
                        (getUV(os.U, i - 1, j, k,
                               math.nan), getUV(os.U, i, j, k, math.nan),
                         getUV(os.U, i - 1, j + 1, k,
                               math.nan), getUV(os.U, i, j + 1, k, math.nan)))
                    wMean = calcMean(
                        (getUV(os.W, i, j, k,
                               math.nan), getUV(os.W, i, j + 1, k, math.nan)))

                    # Estimate the local d2u/dz2 (double derivative):
                    if k > 0:
                        dz_up = 0.5 * (os.cellHeights[i, j, k] +
                                       os.cellHeights[i, j, k - 1])
                    else:
                        dz_up = os.cellHeights[i, j, k]
                    if k < kmax - 1:
                        dz_down = 0.5 * (os.cellHeights[i, j, k] +
                                         os.cellHeights[i, j, k + 1])
                    else:
                        dz_down = os.cellHeights[i, j, k]
                    d2u_dz2 = ((getUV(os.V,i,j,k-1,val) - val)/dz_up \
                               - (val - getUV(os.V,i,j,k+1,val))/dz_down)/(0.5*(dz_up+dz_down))

                    if sp.biharmonic:
                        # If biharmonic is activated the diffusion is handled later, so we
                        # can set it to 0 for now:
                        diffUV = 0
                    else:
                        # Estimate the local d2u/dx2 (double derivative):
                        d2u_dx2 = (getUV(os.V, i - 1, j, k, val) - 2 * val +
                                   getUV(os.V, i + 1, j, k, val)) / (sp.dx *
                                                                     sp.dx)
                        # Estimate the local d2u/dy2 (double derivative):
                        d2u_dy2 = (getUV(os.V, i, j - 1, k, val) - 2 * val +
                                   getUV(os.V, i, j + 1, k, val)) / (sp.dx *
                                                                     sp.dx)
                        # Calculate diffusion term:
                        diffUV = os.AH[i, j, k] * (d2u_dx2 + d2u_dy2)

                    # Calculate nonlinear (advective) terms:
                    if sp.advectiveTermsOn:
                        # Calculate the advection (nonlinear) terms using the
                        # Superbee flux limiter to limit oscillations while
                        # suppressing numerical diffusion:
                        advU = superbeeAdv(sp.dt, sp.dx,
                                           getUV(os.V, i - 2, j, k, val),
                                           getUV(os.V, i - 1, j, k, val), val,
                                           getUV(os.V, i + 1, j, k, val),
                                           getUV(os.V, i + 2, j, k, val), val,
                                           val)
                        advV = superbeeAdv(sp.dt, sp.dx,
                                           getUV(os.V, i, j - 2, k, val),
                                           getUV(os.V, i, j - 1, k, val), val,
                                           getUV(os.V, i, j + 1, k, val),
                                           getUV(os.V, i, j + 2, k, val),
                                           vMean, vMean)
                        advW = superbeeAdv(sp.dt, sp.dx,
                                           getUV(os.V, i, j, k - 2, val),
                                           getUV(os.V, i, j, k - 1, val), val,
                                           getUV(os.V, i, j, k + 1, val),
                                           getUV(os.V, i, j + 2, k + 2, val),
                                           wMean, wMean)
                    else:
                        advU = 0
                        advV = 0
                        advW = 0

                    # Sum up the terms and calculate next time step value:
                    os.V_next[i, j, k] = os.V[i, j, k] + sp.dt * (
                        -p_grady[i, j, k] / sp.rho_0  # Pressure term
                        + advU + advV + advW  # Advective terms
                        + sp.A_z * d2u_dz2  # Vertical eddy viscosity
                        + diffUV  # Horizontal diffusion
                        - 2 * sp.omega * math.sin(sp.phi0) * uMean)  # Coriolis

    # If we are using biharmonic diffusion of velocities, do it here:
    if sp.biharmonic:
        (diffU, diffV) = biharmon(os, sp)
        os.U_next = os.U_next - sp.dt * diffU
        os.V_next = os.V_next - sp.dt * diffV

    # Wind stress and bottom friction, U:
    for i in range(0, os.imax - 1):
        for j in range(0, os.jmax):
            if os.maskU[
                    i, j,
                    0] > 0:  # Check if there is a valid current vector at this position
                # Surface cell, average height on cell border:
                dz_mean = 0.5 * (os.cellHeights[i, j, 0] +
                                 os.cellHeights[i + 1, j, 0])
                os.U_next[i, j, 0] = os.U_next[
                    i, j,
                    0] + sp.dt * sp.windStressCoeff * os.windU[i, j] / dz_mean

                # Bottom friction. Apply at the minimum kmax of the
                # neighbouring cells. We need to calculate the absolute value
                # of the current speed here, based on U and interpolated V values:
                k = min(os.kmm[i, j], os.kmm[i + 1, j]) - 1
                # Bottom cell, average height on cell border:
                dz_mean = 0.5 * (os.cellHeights[i, j, k] +
                                 os.cellHeights[i + 1, j, k])
                # V value interpolated here:
                meanV = calcMean([
                    getUV(os.V, i, j - 1, k, math.nan),
                    getUV(os.V, i + 1, j - 1, k, math.nan),
                    getUV(os.V, i, j, k, math.nan),
                    getUV(os.V, i + 1, j, k, math.nan)
                ])
                speed = math.sqrt(os.U[i, j, k] * os.U[i, j, k] +
                                  meanV * meanV)
                os.U_next[i, j, k] = os.U_next[
                    i, j, k] - sp.dt * sp.C_b * os.U[i, j, k] * speed / dz_mean

    # Wind stress and bottom friction, V:
    for i in range(0, os.imax):
        for j in range(0, os.jmax - 1):
            if os.maskV[
                    i, j,
                    0] > 0:  # Check if there is a valid current vector at this position
                # Surface cell, average height on cell border:
                dz_mean = 0.5 * (os.cellHeights[i, j, 0] +
                                 os.cellHeights[i, j + 1, 0])
                os.V_next[i, j, 0] = os.V_next[
                    i, j,
                    0] + sp.dt * sp.windStressCoeff * sp.windV[i, j] / dz_mean

                # Bottom friction. Apply at the minimum kmax of the
                # neighbouring cells. We need to calculate the absolute value
                # of the current speed here, based on U and interpolated V values:
                k = min(os.kmm[i, j], os.kmm[i, j + 1]) - 1
                # Bottom cell, average height on cell border:
                dz_mean = 0.5 * (os.cellHeights[i, j, k] +
                                 os.cellHeights[i, j + 1, k])
                # V value interpolated here:
                meanU = calcMean([
                    getUV(os.U, i - 1, j, k, math.nan),
                    getUV(os.U, i - 1, j + 1, k, math.nan),
                    getUV(os.U, i, j, k, math.nan),
                    getUV(os.U, i, j + 1, k, math.nan)
                ])
                speed = math.sqrt(os.V[i, j, k] * os.V[i, j, k] +
                                  meanV * meanV)
                os.V_next[i, j, k] = os.V_next[
                    i, j, k] - sp.dt * sp.C_b * os.V[i, j, k] * speed / dz_mean
Exemplo n.º 5
0
    def calcHorizontalDiffusivitySmagorinsky(self, sp):
        dx2 = sp.dx*sp.dx
        amLim= 0.0625*dx2/(2*sp.dt)
        for i in range(1,self.imax-1):
            for j in range(1, self.jmax-1):
                for k in range(0, self.kmm[i,j]):
                    AM = sp.CM*dx2*math.sqrt(
                        math.pow(getUV(self.U,i,j,k,0)-getUV(self.U,i-1,j,k,0)/sp.dx,2)
                        + math.pow((getUV(self.V,i,j,k,0)-getUV(self.V,i,j-1,k,0))/sp.dx,2)
                        +.5*math.pow(.25*(getUV(self.U,i-1,j-1,k,0) + getUV(self.U,i-1,j+1,k,0)
                        - getUV(self.U,i,j-1,k,0) - getUV(self.U,i,j+1,k,0))/sp.dx
                        + .25*(getUV(self.V,i-1,j-1,k,0) + getUV(self.V,i+1,j-1,k,0)- getUV(self.V,i-1,j,k,0)
                            - getUV(self.V,i+1,j,k,0))/sp.dx, 2))

                    self.AH[i,j,k] = min((sp.CH/sp.CM)*AM, amLim)

        self.AH[0,:,:] = self.AH[1,:,:]
        self.AH[-1,:,:] = self.AH[-2,:,:]
        self.AH[:,0,:] = self.AH[:,1,:]
        self.AH[:,-1,:] = self.AH[:,-2,:]
        self.AH[0,0,:] = self.AH[1,1,:]
        self.AH[0,-1,:] = self.AH[1,-2,:]
        self.AH[-1,0,:] = self.AH[-2,1,:]
        self.AH[-1,-1,:] = self.AH[-2,-2,:]
Exemplo n.º 6
0
def advectField(trc, trc_next, os, sp):

    for i in range(1, os.imax - 1):
        for j in range(1, os.jmax - 1):
            for k in range(0, os.kmm[i, j]):
                advS = 0
                trc_ij = trc[i, j, k]

                # Find the cell heights interpolated to the cell boundaries:
                vsize = [
                    0.5 *
                    (os.cellHeights[i - 1, j, k] + os.cellHeights[i, j, k]),
                    0.5 *
                    (os.cellHeights[i, j, k] + os.cellHeights[i + 1, j, k]),
                    0.5 *
                    (os.cellHeights[i, j - 1, k] + os.cellHeights[i, j, k]),
                    0.5 *
                    (os.cellHeights[i, j, k] + os.cellHeights[i, j + 1, k])
                ]

                # For each horizontal direction, if current is into this cell,
                # add advection term of the unit (vol/s)*deltaS:
                if os.maskU[i - 1, j, k] and os.U[i - 1, j, k] > 0:
                    advS = advS + os.U[i - 1, j, k] * sp.dx * vsize[0] * (
                        getUV(trc, i - 1, j, k, trc_ij) -
                        getUV(trc, i, j, k, trc_ij))
                if os.maskU[i, j, k] and os.U[i, j, k] < 0:
                    advS = advS - os.U[i, j, k] * sp.dx * vsize[1] * (
                        getUV(trc, i + 1, j, k, trc_ij) -
                        getUV(trc, i, j, k, trc_ij))
                if os.maskV[i, j - 1, k] and os.V[i, j - 1, k] > 0:
                    advS = advS + os.V[i, j - 1, k] * sp.dx * vsize[2] * (
                        getUV(trc, i, j - 1, k, trc_ij) -
                        getUV(trc, i, j, k, trc_ij))
                if os.maskV[i, j, k] and os.V[i, j, k] < 0:
                    advS = advS - os.V[i, j, k] * sp.dx * vsize[3] * (
                        getUV(trc, i, j + 1, k, trc_ij) -
                        getUV(trc, i, j, k, trc_ij))

                # Vertically:
                if os.W[i, j, k + 1] > 0:
                    advS = advS + os.W[i, j, k + 1] * sp.dx * sp.dx * (
                        getUV(trc, i, j, k + 1, trc_ij) -
                        getUV(trc, i, j, k, trc_ij))
                if k > 0:
                    if os.W[i, j, k - 1] < 0:
                        advS = advS - os.W[i, j, k - 1] * sp.dx * sp.dx * (
                            trc[i, j, k - 1] - trc[i, j, k])

                if sp.trcHorizMix:
                    validNb = [0, 0, 0, 0]
                    if os.maskU[i - 1, j, k]:
                        validNb[0] = 1
                    if os.maskU[i, j, k]:
                        validNb[1] = 1
                    if os.maskV[i, j - 1, k]:
                        validNb[2] = 1
                    if os.maskV[i, j, k]:
                        validNb[3] = 1
                    diffU = (validNb[1] * 0.5 *
                             (os.AH[i, j, k] + os.AH[i + 1, j, k]) *
                             (getUV(trc, i + 1, j, k, trc_ij) -
                              getUV(trc, i, j, k, trc_ij)) - validNb[0] * 0.5 *
                             (os.AH[i - 1, j, k] + os.AH[i, j, k]) *
                             (getUV(trc, i, j, k, trc_ij) - getUV(
                                 trc, i - 1, j, k, trc_ij))) / (sp.dx * sp.dx)
                    diffV = (validNb[3] * 0.5 *
                             (os.AH[i, j, k] + os.AH[i, j + 1, k]) *
                             (getUV(trc, i, j + 1, k, trc_ij) -
                              getUV(trc, i, j, k, trc_ij)) - validNb[2] * 0.5 *
                             (os.AH[i, j - 1, k] + os.AH[i, j, k]) *
                             (getUV(trc, i, j, k, trc_ij) - getUV(
                                 trc, i, j - 1, k, trc_ij))) / (sp.dx * sp.dx)
                else:
                    diffU = 0
                    diffV = 0

                # # Vertical mixing:
                # if sp.trcVertMix:
                #
                #     if k==0:
                #         v_above = trc_ij
                #         dz_up = os.cellHeights[i,j,k]
                #         kv_above = 0
                #     else:
                #         v_above = trc[i,j,k-1]
                #         dz_up = 0.5*(os.cellHeights[i,j,k]+os.cellHeights[i,j,k-1])
                #         kv_above = os.K_v[i,j,k-1]
                #     if k==os.kmm[i,j]-1:
                #         v_below = trc_ij
                #         dz_down = os.cellHeights[i,j,k]
                #         kv_below = 0
                #     else:
                #         v_below = trc[i,j,k+1]
                #         dz_down = 0.5*(os.cellHeights[i,j,k]+os.cellHeights[i,j,k+1])
                #         kv_below = os.K_v[i,j,k]
                #     diffS = (kv_above*(v_above-trc_ij)/dz_up - kv_below*(trc_ij - v_below)/dz_down)/(0.5*(dz_up+dz_down))
                # else:
                #     diffS = 0

                trc_next[i, j, k] = trc[i, j, k] + sp.dt * (
                    advS /
                    (sp.dx * sp.dx * os.cellHeights[i, j, k]) + diffU + diffV)

                # if i==1 and j==54 and k==5:
                #     print(str(trc_ij)+" -> "+str(trc_next[i,j,k]))
                #if np.isnan(trc_next[i,j,k]):
                #    print("hei")

    # Implicit calculation of vertical mixing of tracers:
    if sp.trcVertMix:
        for i in range(1, os.imax - 1):
            for j in range(1, os.jmax - 1):
                # Vertical diffusion - implicit calculation:
                if os.kmm[i, j] > 1:
                    kmx = os.kmm[i, j]
                    AP = np.zeros((kmx, ))
                    CP = np.zeros((kmx, ))
                    SP = np.zeros((kmx, ))
                    EP = np.zeros((kmx, ))
                    AP[0] = 0
                    for k in range(1, kmx):
                        AP[k] = os.K_v[i, j, k - 1] * sp.dt / (
                            os.cellHeights[i, j, k] * 0.5 *
                            (os.cellHeights[i, j, k] +
                             os.cellHeights[i, j, k - 1]))
                        CP[k-1] = os.K_v[i,j,k-1]*sp.dt/\
                                  (os.cellHeights[i,j,k-1]*0.5*(os.cellHeights[i,j,k-1]+os.cellHeights[i,j,k]))
                    CP[kmx - 1] = 0

                    SP[0] = 1 + CP[0] + AP[0]
                    EP[0] = trc_next[i, j, 0]
                    for k in range(1, kmx):
                        SP[k] = 1 + AP[k] + CP[k] - AP[k] * CP[k - 1] / SP[k -
                                                                           1]
                        EP[k] = trc_next[i, j,
                                         k] + AP[k] * EP[k - 1] / SP[k - 1]
                    trc_next[i, j, kmx - 1] = EP[kmx - 1] / SP[kmx - 1]
                    for k in range(kmx - 2, -1, -1):
                        trc_next[i, j,
                                 k] = (EP[k] +
                                       CP[k] * trc_next[i, j, k + 1]) / SP[k]
Exemplo n.º 7
0
def integrate(os, sp, scenario, fullDims, fullDepth, pos, splits, slice, t,
              doMpi, comm, rank):
    # Some small precalculations:
    dt = sp.dt / sp.nsub
    dtn = sp.dt
    dx = sp.dx
    dx2 = dx * dx
    dtdx = dt / dx
    dtndx = dtn / dx

    setBounds.setEUVBounds(scenario, fullDims, fullDepth, pos, splits, slice,
                           t, os, sp)

    UB_prelim = os.UB.copy()
    VB_prelim = os.VB.copy()
    AU = np.zeros((os.imax - 1, os.jmax))
    AV = np.zeros((os.imax, os.jmax - 1))

    # If we are using biharmonic diffusion of velocities, compute those for velocity deviations here:
    if sp.biharmonic:
        (diffU, diffV) = biharmon(os, sp, os.UB, os.VB)

    # Then calculate preliminary U deviations for next long time step by including
    # advection (of full velocities), coriolis terms based on deviations, pressure term based on
    # deviations and diffusion of deviations:
    for i in range(0, os.imax - 1):
        for j in range(0, os.jmax):
            # First define the number of layers here:
            kmx = min(os.kmm[i, j], os.kmm[i + 1, j])
            sumD = 0
            for k in range(0, kmx):
                #if not os.maskU[i,j,k]:
                #    break
                # As we go down through the layers we sum up the pressure/rho values in the neighbouring cells:
                if k == 0:
                    # Center of cell boundary:
                    p_C = np.array([
                        9.81 * 0.5 * os.DW[i, j, k] *
                        (os.rho[i, j, k] / sp.rho_0 - 1), 9.81 * 0.5 *
                        os.DW[i, j, k] * (os.rho[i + 1, j, k] / sp.rho_0 - 1)
                    ])
                    # Bottom of cell boundary, stored for next iteration:
                    p_B = 2 * p_C
                else:
                    # Added pressure/rho in this layer:
                    p_h = np.array([
                        9.81 * os.DW[i, j, k] *
                        (os.rho[i, j, k] / sp.rho_0 - 1), 9.81 *
                        os.DW[i, j, k] * (os.rho[i + 1, j, k] / sp.rho_0 - 1)
                    ])
                    # Center of cell boundary:
                    p_C = p_B + 0.5 * p_h
                    # Bottom of cell boundary, stored for next iteration:
                    p_B = p_B + p_h

                # Add pressure term:
                UB_prelim[i, j,
                          k] = UB_prelim[i, j, k] - dtndx * (p_C[1] - p_C[0])

                #if i==10 and j==10:
                #    print("pdiff="+str(p_C[1]-p_C[0])+", p_C[0]="+str(p_C[0])+", sumD="+str(sumD+os.DWD[i,j,k])+
                #          ", rho="+str(os.rho[i,j,k])+", rho/rho0-1="+str(os.rho[i,j,k]/sp.rho_0 - 1))
                #    if k==os.kmax-1:
                #        print()

                # Get the full speed and deviation at this point:
                val = os.UA[i, j] + os.UB[i, j, k]
                valB = os.UB[i, j, k]
                # Estimate the local full V and W values by interpolation:
                vMean = calcMean(
                    (getUV(os.V, i, j - 1, k,
                           math.nan), getUV(os.V, i, j, k, math.nan),
                     getUV(os.V, i + 1, j - 1, k,
                           math.nan), getUV(os.V, i + 1, j, k, math.nan)))
                wMean = calcMean(
                    (getUV(os.W, i, j, k,
                           math.nan), getUV(os.W, i + 1, j, k, math.nan)))
                # Estimate the local V deviation value by interpolation:
                vbMean = calcMean(
                    (getUV(os.VB, i, j - 1, k,
                           math.nan), getUV(os.VB, i, j, k, math.nan),
                     getUV(os.VB, i + 1, j - 1, k,
                           math.nan), getUV(os.VB, i + 1, j, k, math.nan)))
                # Get the absolute speed value:
                absSpeed = math.sqrt(val * val + vMean * vMean)

                # Calculate nonlinear (advective) terms:
                if sp.advectiveTermsOn:
                    # Calculate the advection (nonlinear) terms using the
                    # Superbee flux limiter to limit oscillations while
                    # suppressing numerical diffusion:
                    advU = superbeeAdv(dt, sp.dx,
                                       getUV(os.U, i - 2, j, k, val),
                                       getUV(os.U, i - 1, j, k, val), val,
                                       getUV(os.U, i + 1, j, k, val),
                                       getUV(os.U, i + 2, j, k, val), val, val)
                    advV = superbeeAdv(dt, sp.dx,
                                       getUV(os.U, i, j - 2, k, val),
                                       getUV(os.U, i, j - 1, k, val), val,
                                       getUV(os.U, i, j + 1, k, val),
                                       getUV(os.U, i, j + 2, k,
                                             val), vMean, vMean)
                    advW = superbeeAdv(dt, sp.dx,
                                       getUV(os.U, i, j, k - 2, val),
                                       getUV(os.U, i, j, k - 1, val), val,
                                       getUV(os.U, i, j, k + 1, val),
                                       getUV(os.U, i, j + 2, k + 2,
                                             val), wMean, wMean)
                else:
                    advU = 0
                    advV = 0
                    advW = 0

                # Add advective terms:
                UB_prelim[i, j,
                          k] = UB_prelim[i, j, k] + dtn * (advU + advV + advW)

                # Diffusion:
                if sp.biharmonic:
                    UB_prelim[i, j,
                              k] = UB_prelim[i, j, k] - dtn * diffU[i, j, k]
                else:
                    # Estimate the local d2u/dx2 (double derivative):
                    d2u_dx2 = (getUV(os.UB, i - 1, j, k, valB) - 2 * valB +
                               getUV(os.UB, i + 1, j, k, valB)) / dx2
                    # Estimate the local d2u/dy2 (double derivative):
                    d2u_dy2 = (getUV(os.UB, i, j - 1, k, valB) - 2 * valB +
                               getUV(os.UB, i, j + 1, k, valB)) / dx2
                    # Calculate diffusion term:
                    UB_prelim[i, j, k] = UB_prelim[
                        i, j, k] + dtn * os.AH[i, j, k] * (d2u_dx2 + d2u_dy2)

                # Coriolis:
                UB_prelim[i, j, k] = UB_prelim[
                    i, j, k] + dtn * 2 * sp.omega * math.sin(sp.phi0) * vbMean

                # Wind stress. Only if this is the surface layer:
                if k == 0:
                    UB_prelim[i, j, k] = UB_prelim[
                        i, j, k] + dtn * sp.windStressCoeff * os.windU[
                            i, j] / os.DWD[i, j, k]

                # Bottom friction. Only if this is bottom layer:
                if k == kmx - 1:
                    UB_prelim[i, j, k] = UB_prelim[
                        i, j,
                        k] - dtn * sp.C_b * val * absSpeed / os.DWD[i, j, k]

                # Vertical diffusion.
                if kmx > 1 and not sp.implicitVerticalDiff:
                    # Estimate the local d2u/dz2 (double derivative):
                    if k > 0:
                        dz_up = 0.5 * (os.DWD[i, j, k] + os.DWD[i, j, k - 1])
                    else:
                        dz_up = os.DWD[i, j, k]
                    if k < os.kmax - 1 and os.maskU[i, j, k + 1]:
                        dz_down = 0.5 * (os.DWD[i, j, k] + os.DWD[i, j, k + 1])
                    else:
                        dz_down = os.DW[i, j, k]
                    d2u_dz2 = ((getUV(os.UB,i,j,k-1,valB) - valB)/dz_up \
                               - (valB - getUV(os.UB,i,j,k+1,valB))/dz_down)/(0.5*(dz_up+dz_down))
                    UB_prelim[i, j,
                              k] = UB_prelim[i, j, k] + dtn * sp.A_z * d2u_dz2

                if math.isnan(UB_prelim[i, j, k]):
                    print(i)

                # Find AU (depth integrated UB for this time step):
                AU[i, j] = AU[i, j] + os.DWD[i, j, k] * UB_prelim[i, j, k]
                sumD = sumD + os.DWD[i, j, k]

            # Divide AU by sum depth and time step:
            if sumD > 0:
                AU[i, j] = AU[i, j] / (dtn * sumD)

                # Subtract AU in all layers:
                for k in range(0, kmx):
                    UB_prelim[i, j, k] = UB_prelim[i, j, k] - AU[i, j] * dtn

            # Vertical diffusion - implicit calculation:
            if sp.implicitVerticalDiff and kmx > 1:
                AP = np.zeros((kmx, ))
                CP = np.zeros((kmx, ))
                SP = np.zeros((kmx, ))
                EP = np.zeros((kmx, ))
                AP[0] = 0
                for k in range(1, kmx):
                    AP[k] = sp.A_z * dtn / (
                        os.DWD[i, j, k] * 0.5 *
                        (os.DWD[i, j, k] + os.DWD[i, j, k - 1]))
                    CP[k - 1] = sp.A_z * dtn / (
                        os.DWD[i, j, k - 1] * 0.5 *
                        (os.DWD[i, j, k - 1] + os.DWD[i, j, k]))
                CP[kmx - 1] = 0

                SP[0] = 1 + CP[0] + AP[0]
                EP[0] = UB_prelim[i, j, 0]
                for k in range(1, kmx):
                    SP[k] = 1 + AP[k] + CP[k] - AP[k] * CP[k - 1] / SP[k - 1]
                    EP[k] = UB_prelim[i, j, k] + AP[k] * EP[k - 1] / SP[k - 1]
                UB_prelim[i, j, kmx - 1] = EP[kmx - 1] / SP[kmx - 1]
                for k in range(kmx - 2, -1, -1):
                    UB_prelim[i, j,
                              k] = (EP[k] +
                                    CP[k] * UB_prelim[i, j, k + 1]) / SP[k]

    # Calculate preliminary V deviations;
    for i in range(0, os.imax):
        for j in range(0, os.jmax - 1):
            # First define the number of layers here:
            kmx = min(os.kmm[i, j], os.kmm[i, j + 1])
            sumD = 0
            for k in range(0, kmx):
                #if not os.maskV[i,j,k]:
                #    break
                # As we go down through the layers we sum up the pressure/rho values in the neighbouring cells:
                if k == 0:
                    # Center of cell boundary:
                    p_C = np.array([
                        9.81 * 0.5 * os.DS[i, j, k] *
                        (os.rho[i, j, k] / sp.rho_0 - 1), 9.81 * 0.5 *
                        os.DS[i, j, k] * (os.rho[i, j + 1, k] / sp.rho_0 - 1)
                    ])
                    # Bottom of cell boundary, stored for next iteration:
                    p_B = 2 * p_C
                else:
                    # Added pressure/rho in this layer:
                    p_h = np.array([
                        9.81 * os.DS[i, j, k] *
                        (os.rho[i, j, k] / sp.rho_0 - 1), 9.81 *
                        os.DS[i, j, k] * (os.rho[i, j + 1, k] / sp.rho_0 - 1)
                    ])
                    # Center of cell boundary:
                    p_C = p_B + 0.5 * p_h
                    # Bottom of cell boundary, stored for next iteration:
                    p_B = p_B + p_h

                # Add pressure term:
                VB_prelim[i, j,
                          k] = VB_prelim[i, j, k] - dtndx * (p_C[1] - p_C[0])

                # Get the full speed and deviation at this point:
                val = os.V[i, j, k]
                valB = os.VB[i, j, k]
                # Estimate the local U and W values by interpolation:
                uMean = calcMean(
                    (getUV(os.U, i - 1, j, k,
                           math.nan), getUV(os.U, i, j, k, math.nan),
                     getUV(os.U, i - 1, j + 1, k,
                           math.nan), getUV(os.U, i, j + 1, k, math.nan)))
                wMean = calcMean(
                    (getUV(os.W, i, j, k,
                           math.nan), getUV(os.W, i, j + 1, k, math.nan)))
                # Estimate the local U deviation value by interpolation:
                ubMean = calcMean(
                    (getUV(os.UB, i - 1, j, k,
                           math.nan), getUV(os.UB, i, j, k, math.nan),
                     getUV(os.UB, i - 1, j + 1, k,
                           math.nan), getUV(os.UB, i, j + 1, k, math.nan)))
                # Get the absolute speed value:
                absSpeed = math.sqrt(val * val + uMean * uMean)

                # Calculate nonlinear (advective) terms:
                if sp.advectiveTermsOn:
                    # Calculate the advection (nonlinear) terms using the
                    # Superbee flux limiter to limit oscillations while
                    # suppressing numerical diffusion:
                    advU = superbeeAdv(dt, sp.dx,
                                       getUV(os.V, i - 2, j, k, val),
                                       getUV(os.V, i - 1, j, k, val), val,
                                       getUV(os.V, i + 1, j, k, val),
                                       getUV(os.V, i + 2, j, k,
                                             val), uMean, uMean)
                    advV = superbeeAdv(dt, sp.dx,
                                       getUV(os.V, i, j - 2, k, val),
                                       getUV(os.V, i, j - 1, k, val), val,
                                       getUV(os.V, i, j + 1, k, val),
                                       getUV(os.V, i, j + 2, k, val), val, val)
                    advW = superbeeAdv(dt, sp.dx,
                                       getUV(os.V, i, j, k - 2, val),
                                       getUV(os.V, i, j, k - 1, val), val,
                                       getUV(os.V, i, j, k + 1, val),
                                       getUV(os.V, i, j + 2, k + 2,
                                             val), wMean, wMean)
                else:
                    advU = 0
                    advV = 0
                    advW = 0

                # Add advective terms:
                VB_prelim[i, j,
                          k] = VB_prelim[i, j, k] + dtn * (advU + advV + advW)

                # Diffusion:
                if sp.biharmonic:
                    VB_prelim[i, j,
                              k] = VB_prelim[i, j, k] - dtn * diffV[i, j, k]
                else:
                    # Estimate the local d2v/dx2 (double derivative):
                    d2v_dx2 = (getUV(os.VB, i - 1, j, k, valB) - 2 * valB +
                               getUV(os.VB, i + 1, j, k, valB)) / dx2
                    # Estimate the local d2v/dy2 (double derivative):
                    d2v_dy2 = (getUV(os.VB, i, j - 1, k, valB) - 2 * valB +
                               getUV(os.VB, i, j + 1, k, valB)) / dx2
                    # Calculate diffusion term:
                    VB_prelim[i, j, k] = VB_prelim[
                        i, j, k] + dtn * os.AH[i, j, k] * (d2v_dx2 + d2v_dy2)

                # Coriolis:
                VB_prelim[i, j, k] = VB_prelim[
                    i, j, k] - dtn * 2 * sp.omega * math.sin(sp.phi0) * ubMean

                # Wind stress. Only if this is the surface layer:
                if k == 0:
                    VB_prelim[i, j, k] = VB_prelim[
                        i, j, k] + dtn * sp.windStressCoeff * os.windV[
                            i, j] / os.DSD[i, j, k]

                # Bottom friction. Only if this is bottom layer:
                if k == kmx - 1:
                    VB_prelim[i, j, k] = VB_prelim[
                        i, j, k] - sp.C_b * val * absSpeed / os.DSD[i, j, k]

                # Vertical diffusion.
                if kmx > 1 and not sp.implicitVerticalDiff:
                    # Estimate the local d2u/dz2 (double derivative):
                    if k > 0:
                        dz_up = 0.5 * (os.DSD[i, j, k] + os.DSD[i, j, k - 1])
                    else:
                        dz_up = os.DSD[i, j, k]
                    if k < os.kmax - 1 and os.maskV[i, j, k + 1]:
                        dz_down = 0.5 * (os.DSD[i, j, k] + os.DSD[i, j, k + 1])
                    else:
                        dz_down = os.DS[i, j, k]
                    d2u_dz2 = (
                        (getUV(os.VB, i, j, k - 1, valB) - valB) / dz_up -
                        (valB - getUV(os.VB, i, j, k + 1, valB)) / dz_down) / (
                            0.5 * (dz_up + dz_down))
                    VB_prelim[i, j, k] = VB_prelim[i, j, k] + sp.A_z * d2u_dz2

                # Find AV (depth integrated VB for this time step):
                AV[i, j] = AV[i, j] + os.DSD[i, j, k] * VB_prelim[i, j, k]
                sumD = sumD + os.DSD[i, j, k]

            # Divide AV by sum depth and time step:
            if sumD > 0:
                AV[i, j] = AV[i, j] / (dtn * sumD)

                # Subtract AU in all layers:
                for k in range(0, kmx):
                    VB_prelim[i, j, k] = VB_prelim[i, j, k] - AV[i, j] * dtn

            # Vertical diffusion - implicit calculation:
            if sp.implicitVerticalDiff and kmx > 1:
                AP = np.zeros((kmx, ))
                CP = np.zeros((kmx, ))
                SP = np.zeros((kmx, ))
                EP = np.zeros((kmx, ))
                AP[0] = 0
                for k in range(1, kmx):
                    AP[k] = sp.A_z * dtn / (
                        os.DSD[i, j, k] * 0.5 *
                        (os.DSD[i, j, k] + os.DSD[i, j, k - 1]))
                    CP[k - 1] = sp.A_z * dtn / (
                        os.DSD[i, j, k - 1] * 0.5 *
                        (os.DSD[i, j, k - 1] + os.DSD[i, j, k]))
                CP[kmx - 1] = 0

                SP[0] = 1 + CP[0] + AP[0]
                EP[0] = VB_prelim[i, j, 0]
                for k in range(1, kmx):
                    SP[k] = 1 + AP[k] + CP[k] - AP[k] * CP[k - 1] / SP[k - 1]
                    EP[k] = VB_prelim[i, j, k] + AP[k] * EP[k - 1] / SP[k - 1]
                VB_prelim[i, j, kmx - 1] = EP[kmx - 1] / SP[kmx - 1]
                for k in range(kmx - 2, -1, -1):
                    VB_prelim[i, j,
                              k] = (EP[k] +
                                    CP[k] * VB_prelim[i, j, k + 1]) / SP[k]

    # Update 3D speeds:
    os.UB[...] = UB_prelim[...]
    os.VB[...] = VB_prelim[...]

    # Communicate UB/VB between processes:
    #if doMpi:
    #    mpi.communicate3D(comm, rank, pos, splits, os, sp)

    # Vertically integrated model:
    for subt in range(0, sp.nsub):

        # Communicate 2D fields between processes:
        if doMpi:
            mpi.communicate2D(comm, rank, pos, splits, os, sp)

        deltU = np.zeros(os.HUA.shape)
        deltV = np.zeros(os.HVA.shape)

        if sp.biharmonic:
            (diffU, diffV) = biharmon2D(os, sp, os.UA, os.VA)

        # U direction:
        for i in range(0, os.imax - 1):
            for j in range(1, os.jmax - 1):
                if not os.maskU[i, j, 0]:
                    continue

                # Local values:
                kmx = min(os.kmm[i, j], os.kmm[i + 1,
                                               j]) - 1  # Number of wet layers
                dwad = os.DWA[i, j] + 0.5 * (os.E[i, j] + os.E[i + 1, j])
                val = os.UA[i, j]
                hval = os.HUA[i, j]
                # Weighted average of V:
                averHV = 0
                if os.maskV[i, j - 1, 0]:
                    averHV = averHV + getUV2(os.HVA, i, j - 1,
                                             0) / os.hvSqr[i, j - 1]
                if os.maskV[i + 1, j - 1, 0]:
                    averHV = averHV + getUV2(os.HVA, i + 1, j - 1,
                                             0) / os.hvSqr[i + 1, j - 1]
                if os.maskV[i, j, 0]:
                    averHV = averHV + getUV2(os.HVA, i, j, 0) / os.hvSqr[i, j]
                if os.maskV[i + 1, j, 0]:
                    averHV = averHV + getUV2(os.HVA, i + 1, j,
                                             0) / os.hvSqr[i + 1, j]
                averHV = 0.25 * averHV * os.huSqr[i, j]
                # Unweighted:
                averVA = 0.25 * (getUV2(os.VA, i, j - 1, 0) + getUV2(
                    os.VA, i + 1, j - 1, 0) + getUV2(os.VA, i, j, 0) +
                                 getUV2(os.VA, i + 1, j, 0))
                averVB = 0.25 * (getUV(os.VB, i, j - 1, kmx, 0) + getUV(
                    os.VB, i + 1, j - 1, kmx, 0) + getUV(os.VB, i, j, kmx, 0) +
                                 getUV(os.VB, i + 1, j, kmx, 0))
                # Get bottom current to calculate bottom drag:
                ubot = val + os.UB[i, j, kmx]
                vbot = averVA + averVB
                vvec = math.sqrt(ubot * ubot + vbot * vbot)

                # TODO: depth-integrated equation, support for variable atmospheric pressure

                deltU[i, j] = (
                    dt * 2 * sp.omega * math.sin(sp.phi0) * averHV  # Coriolis
                    + dtdx * 9.81 * dwad *
                    (os.E[i, j] - os.E[i + 1, j])  # External gravity waves
                    - dt * sp.C_b * ubot * vvec  # Bottom friction
                    + dt * dwad * AU[i, j]
                )  # Average rate of change extracted from 3D step

                if sp.biharmonic:
                    deltU[i, j] = deltU[i, j] - dt * dwad * diffU[i, j]
                else:
                    # Estimate the local d2u/dx2 (double derivative):
                    d2u_dx2 = (getUV2(os.UA, i - 1, j, val) - 2 * val +
                               getUV2(os.UA, i + 1, j, val)) / dx2
                    # Estimate the local d2u/dy2 (double derivative):
                    d2u_dy2 = (getUV2(os.UA, i, j - 1, val) - 2 * val +
                               getUV2(os.UA, i, j + 1, val)) / dx2
                    # Calculate diffusion term:
                    deltU[i, j] = deltU[i, j] + dt * os.AM2D[i, j] * (d2u_dx2 +
                                                                      d2u_dy2)

        # V direction:
        for i in range(1, os.imax - 1):
            for j in range(0, os.jmax - 1):
                if not os.maskV[i, j, 0]:
                    continue

                # Local values:
                kmx = min(os.kmm[i, j],
                          os.kmm[i, j + 1]) - 1  # Number of wet layers
                dsad = os.DSA[i, j] + 0.5 * (os.E[i, j] + os.E[i, j + 1])
                val = os.VA[i, j]
                hval = os.HVA[i, j]
                # Weighted average of U:
                averHU = 0
                if os.maskU[i - 1, j, 0]:
                    averHU = averHU + getUV2(os.HUA, i - 1, j,
                                             0) / os.huSqr[i - 1, j]
                if os.maskU[i, j, 0]:
                    averHU = averHU + getUV2(os.HUA, i, j, 0) / os.huSqr[i, j]
                if os.maskU[i - 1, j + 1, 0]:
                    averHU = averHU + getUV2(os.HUA, i - 1, j + 1,
                                             0) / os.huSqr[i - 1, j + 1]
                if os.maskU[i, j + 1, 0]:
                    averHU = averHU + getUV2(os.HUA, i, j + 1,
                                             0) / os.huSqr[i, j + 1]
                averHU = 0.25 * averHU * os.hvSqr[i, j]
                # Unweighted:
                averUA = 0.25 * (getUV2(os.UA, i - 1, j, 0) + getUV2(
                    os.UA, i, j, 0) + getUV2(os.UA, i - 1, j + 1, 0) +
                                 getUV2(os.UA, i, j + 1, 0))
                averUB = 0.25 * (getUV(os.UB, i - 1, j, kmx, 0) + getUV(
                    os.UB, i, j, kmx, 0) + getUV(os.UB, i - 1, j + 1, kmx, 0) +
                                 getUV(os.UB, i, j + 1, kmx, 0))
                # Get bottom current to calculate bottom drag:
                vbot = val + os.VB[i, j, kmx]
                ubot = averUA + averUB
                vvec = math.sqrt(ubot * ubot + vbot * vbot)

                # TODO: depth-integrated equation, support for variable atmospheric pressure

                deltV[i, j] = (
                    -dt * 2 * sp.omega * math.sin(sp.phi0) * averHU  # Coriolis
                    + dtdx * 9.81 * dsad *
                    (os.E[i, j] - os.E[i, j + 1])  # External gravity waves
                    - dt * sp.C_b * vbot * vvec  # Bottom friction
                    + dt * dsad * AV[i, j]
                )  # Average rate of change extracted from 3D step

                if sp.biharmonic:
                    deltV[i, j] = deltV[i, j] - dt * dsad * diffV[i, j]
                else:
                    # Estimate the local d2u/dx2 (double derivative):
                    d2u_dx2 = (getUV2(os.VA, i - 1, j, val) - 2 * val +
                               getUV2(os.VA, i + 1, j, val)) / dx2
                    # Estimate the local d2u/dy2 (double derivative):
                    d2u_dy2 = (getUV2(os.VA, i, j - 1, val) - 2 * val +
                               getUV2(os.VA, i, j + 1, val)) / dx2
                    # Calculate diffusion term:
                    deltV[i, j] = deltV[i, j] + dt * os.AM2D[i, j] * (d2u_dx2 +
                                                                      d2u_dy2)

        # Update HUA:
        os.HUA = os.HUA + deltU

        # Calculate new UA:
        for i in range(0, os.imax - 1):
            for j in range(0, os.jmax):
                if not os.maskU[i, j, 0]:
                    continue
                dwad = os.DWA[i, j] + 0.5 * (os.E[i, j] + os.E[i + 1, j])
                os.UA[i, j] = os.HUA[i, j] / dwad

        # Update HVA:
        os.HVA = os.HVA + deltV

        # Calculate new VA:
        for i in range(0, os.imax):
            for j in range(0, os.jmax - 1):
                if not os.maskV[i, j, 0]:
                    continue
                dsad = os.DSA[i, j] + 0.5 * (os.E[i, j] + os.E[i, j + 1])
                os.VA[i, j] = os.HVA[i, j] / dsad

        # Done with short time step for UA/HUA and VA/HVA.
        # Calculate new elevation:
        for i in range(1, os.imax - 1):
            for j in range(1, os.jmax - 1):
                if os.kmm[i, j] == 0:
                    continue
                os.E[i,
                     j] = os.E[i,
                               j] + dtdx * (os.HUA[i - 1, j] - os.HUA[i, j] +
                                            os.HVA[i, j - 1] - os.HVA[i, j])

        # Update local time variable:
        t = t + dt
        # Update EUV bounds:
        setBounds.setEUVBounds(scenario, fullDims, fullDepth, pos, splits,
                               slice, t, os, sp)

    # Done with depth integrated (short) time steps

    # Recompute U:
    for i in range(0, os.imax - 1):
        for j in range(0, os.jmax):
            for k in range(0, os.kmax):
                if not os.maskU[i, j, k]:
                    # Check we are at the surface layer and there is still current. If so, it's a river outlet.
                    if k == 0 and os.DW[i, j, 0] > 0 and os.HUA[i, j] != 0:
                        os.U[i, j, k] = os.HUA[i, j] / os.DW[i, j, 0]
                    break
                os.U[i, j, k] = os.UA[i, j] + os.UB[i, j, k]

    # Recompute V:
    for i in range(0, os.imax):
        for j in range(0, os.jmax - 1):
            for k in range(0, os.kmax):
                if not os.maskV[i, j, k]:
                    # Check we are at the surface layer and there is still current. If so, it's a river outlet.
                    if k == 0 and os.DS[i, j, 0] > 0 and os.HVA[i, j] != 0:
                        os.V[i, j, k] = os.HVA[i, j] / os.DS[i, j, 0]
                    break
                os.V[i, j, k] = os.VA[i, j] + os.VB[i, j, k]

    # Recompute vertical speeds:
    computeVerticalSpeeds(os, sp)