Beispiel #1
0
 def _parameter_conversion(self, dataset):
     # NOTE We are here once again convert from (x,y,z) parametrization to (r,\theta,\phi) parametrization
     for i in [1,2]:
         # Magnitude a_i
         dataset["a_{}".format(i)] = xp.sqrt(dataset["spin_{}x".format(i)]**2 + dataset["spin_{}y".format(i)]**2 + dataset["spin_{}z".format(i)]**2)
         # Tilt angle tilt_i
         dataset["tilt_{}".format(i)] = xp.arccos(dataset["spin_{}z".format(i)]/dataset["a_{}".format(i)])
 def angle(self, A, B):
     fp = A * B
     fp = cp.sum(fp)
     norm_a = cp.sqrt(cp.sum(A * A))
     norm_b = cp.sqrt(cp.sum(B * B))
     cos_theta = fp / (norm_a * norm_b)
     return cp.arccos(cos_theta)
def acos(x: Array, /) -> Array:
    """
    Array API compatible wrapper for :py:func:`np.arccos <numpy.arccos>`.

    See its docstring for more information.
    """
    if x.dtype not in _floating_dtypes:
        raise TypeError("Only floating-point dtypes are allowed in acos")
    return Array._new(np.arccos(x._array))
Beispiel #4
0
 def nvst_ringotf_cal(self, fre):
     cp.cuda.Device(0).use
     rh0 = fre
     if (rh0 < 0) or (rh0 > 1):
         return 0.0
     if (self.diaratio < 0) or (self.diaratio > 1):
         return 0.0
     R = 0.5
     A = self.diaratio
     if (rh0 < 2.0 * R):
         c = 2.0 * cp.arccos(
             rh0 / (R * 2.0)) * R**2 - rh0 * cp.sqrt(R**2 - rh0**2 / 4.0)
     else:
         c = 0.0
     if (rh0 < 2.0 * R * A):
         E = 2.0 * cp.arccos(rh0 /
                             (A * R * 2.0)) * (R * A)**2 - rh0 * cp.sqrt(
                                 (R * A)**2 - rh0**2 / 4.0)
     else:
         E = 0.0
     if (rh0 <= R + R * A) and (rh0 > R - R * A):
         s1 = 0.5 * cp.arccos(((R * A)**2 + rh0**2 - R**2) /
                              (2.0 * A * R * rh0)) * (R * A)**2
         s2 = 0.5 * cp.arccos(
             (rh0**2 + R**2 - (R * A)**2) / (2.0 * R * rh0)) * R**2
         s3 = 0.5 * cp.sin(
             cp.arccos(((R * A)**2 + rh0**2 - R**2) /
                       (2.0 * A * R * rh0))) * A * R * rh0
         D = s1 + s2 - s3
         D = D * 2
     elif (rh0 <= R - A * R):
         D = cp.pi * (A * R)**2
     else:
         D = 0.0
     H = (C + E - 2 * D) / (cp.pi * R**2)
     if rh0 == 0:
         H = 1 - A**2
     return float(H)
Beispiel #5
0
 def random_in_unit_sphere_list(size: int) -> Vec3List:
     u = random_float_list(size)
     v = random_float_list(size)
     theta = u * 2 * cp.pi
     phi = cp.arccos(2 * v - 1)
     r = cp.cbrt(random_float_list(size))
     sinTheta = cp.sin(theta)
     cosTheta = cp.cos(theta)
     sinPhi = cp.sin(phi)
     cosPhi = cp.cos(phi)
     x = r * sinPhi * cosTheta
     y = r * sinPhi * sinTheta
     z = r * cosPhi
     return Vec3List(cp.transpose(cp.array([x, y, z])))
Beispiel #6
0
 def random_in_unit_sphere():
     u = random_float()
     v = random_float()
     theta = u * 2 * cp.pi
     phi = cp.arccos(2 * v - 1)
     r = cp.cbrt(random_float())
     sinTheta = cp.sin(theta)
     cosTheta = cp.cos(theta)
     sinPhi = cp.sin(phi)
     cosPhi = cp.cos(phi)
     x = r * sinPhi * cosTheta
     y = r * sinPhi * sinTheta
     z = r * cosPhi
     return Vec3(x, y, z)
Beispiel #7
0
 def random_in_unit_sphere() -> Vec3:
     """
     This method is modified from:
     https://karthikkaranth.me/blog/generating-random-points-in-a-sphere/#better-choice-of-spherical-coordinates
     """
     u = random_float()
     v = random_float()
     theta = u * 2 * cp.pi
     phi = cp.arccos(2 * v - 1)
     r = cp.cbrt(random_float())
     sinTheta = cp.sin(theta)
     cosTheta = cp.cos(theta)
     sinPhi = cp.sin(phi)
     cosPhi = cp.cos(phi)
     x = r * sinPhi * cosTheta
     y = r * sinPhi * sinTheta
     z = r * cosPhi
     return Vec3(x, y, z)
Beispiel #8
0
    def negative_sampling(self, embeddings, is_test):
        if is_test:
            prepare_func = self.prepare_test_negative_sampling()
        else:
            prepare_func = self.prepare_train_negative_sampling()
        for _ in range(self.model.negative_sample_num):
            negative_sampling_matrix = prepare_func
            negative_embeddings = negative_sampling_matrix.dot(embeddings)
            norm_embeddings = cp.linalg.norm(embeddings, axis=1).reshape(
                (embeddings.shape[0], 1))

            target_negative_cos = cp.cos(embeddings, negative_embeddings)
            new_embeddings = embeddings + (embeddings - negative_embeddings) \
                             * self.model.target_negative_weight * cp.cos(cp.arccos(target_negative_cos) / 2)
            norm_new_embeddings = cp.linalg.norm(new_embeddings,
                                                 axis=1).reshape(
                                                     (new_embeddings.shape[0],
                                                      1))
            embeddings = new_embeddings / norm_new_embeddings * norm_embeddings
Beispiel #9
0
def IsoScatter(photon_state):
    ####################################################################################
    # This function takes the photons and scatters them off the dust in the atmosphere
    # using an isotropic scattering method.
    #
    # photon_state -- A CuPy array consisting of [x,y,z,phi,theta,lambda] for each photon.
    ####################################################################################

    # Theta is the polar angle with respect to the lab frame z-axis.
    # theta = 2*cp.pi*cp.random.rand(len(photon_state))

    # Generate a uniform distribution for mu
    mu = 2 * cp.random.rand(len(photon_state)) - 1

    # Extract the angle theta from that uniform distribution
    theta = cp.arccos(mu)

    # Phi is the azimuthal angle, scattering should always be isotropic in phi.
    phi = 2 * cp.pi * cp.random.rand(len(photon_state))

    return theta, phi
Beispiel #10
0
def arcsec__(z):
    return cp.arccos(1.0/z)
Beispiel #11
0
def eig_special_3d(S, full=False):
    """Eigensolution for symmetric real 3-by-3 matrices.

    Arguments:
        S: array-like
            A floating point array with shape ``(6, ...)`` containing structure tensor.
            Use float64 to avoid numerical errors. When using lower precision, ensure
            that the values of S are not very small/large. Pass cupy.ndarray to avoid copying S.
        full: bool, optional
            A flag indicating that all three eigenvalues should be returned.

    Returns:
        val: cupy.ndarray
            An array with shape ``(3, ...)`` containing sorted eigenvalues
        vec: cupy.ndarray
            An array with shape ``(3, ...)`` containing eigenvector corresponding to
            the smallest eigenvalue. If full, vec has shape ``(3, 3, ...)`` and contains
            all three eigenvectors.

    More:
        An analytic solution of eigenvalue problem for real symmetric matrix,
        using an affine transformation and a trigonometric solution of third
        order polynomial. See https://en.wikipedia.org/wiki/Eigenvalue_algorithm
        which refers to Smith's algorithm https://dl.acm.org/citation.cfm?id=366316.

    Authors: [email protected], 2019; [email protected], 2019-2020
    """
    S = np.asarray(S)

    # Check data type. Must be floating point.
    if not np.issubdtype(S.dtype, np.floating):
        raise ValueError('S must be floating point type.')

    # Flatten S.
    input_shape = S.shape
    S = S.reshape(6, -1)

    # Create v vector.
    v = np.array([[2 * np.pi / 3], [4 * np.pi / 3]], dtype=S.dtype)

    # Computing eigenvalues.

    # Allocate vec and val. We will use them for intermediate computations as well.
    if full:
        val = np.empty((3, ) + S.shape[1:], dtype=S.dtype)
        vec = np.empty((9, ) + S.shape[1:], dtype=S.dtype)
        tmp = np.empty((4, ) + S.shape[1:], dtype=S.dtype)
        B03 = val
        B36 = vec[:3]
    else:
        val = np.empty((3, ) + S.shape[1:], dtype=S.dtype)
        vec = np.empty((3, ) + S.shape[1:], dtype=S.dtype)
        tmp = np.empty((4, ) + S.shape[1:], dtype=S.dtype)
        B03 = val
        B36 = vec

    # Views for B.
    B0 = B03[0]
    B1 = B03[1]
    B2 = B03[2]
    B3 = B36[0]
    B4 = B36[1]
    B5 = B36[2]

    # Compute q, mean of diagonal. We need to use q multiple times later.
    # Using np.mean has precision issues.
    q = np.add(S[0], S[1], out=tmp[0])
    q += S[2]
    q /= 3

    # Compute S minus q. Insert it directly into B where it'll stay.
    Sq = np.subtract(S[:3], q, out=B03)

    # Compute s, off-diagonal elements. Store in part of B not yet used.
    s = np.sum(np.multiply(S[3:], S[3:], out=B36), axis=0, out=tmp[1])
    s *= 2

    # Compute p.
    p = np.sum(np.multiply(Sq, Sq, out=B36), axis=0, out=tmp[2])
    del Sq  # Last use of Sq.
    p += s

    p *= 1 / 6
    np.sqrt(p, out=p)

    # Compute inverse p, while avoiding 0 division.
    # Reuse s allocation and delete s to ensure we don't efter it's been reused.
    p_inv = s
    del s
    non_zero_p_mask = p == 0
    np.divide(1, p, out=p_inv)
    p_inv[non_zero_p_mask] = 0

    # Compute B. First part is already filled.
    B03 *= p_inv
    np.multiply(S[3:], p_inv, out=B36)

    # Compute d, determinant of B.
    d = np.prod(B03, axis=0, out=tmp[3])

    # Reuse allocation for p_inv and delete variable.
    d_tmp = p_inv
    del p_inv
    # Computation of d.
    np.multiply(B2, B3, d_tmp)
    d_tmp *= B3
    d -= d_tmp
    np.multiply(B4, B4, out=d_tmp)
    d_tmp *= B1
    d -= d_tmp
    np.prod(B36, axis=0, out=d_tmp)
    d_tmp *= 2
    d += d_tmp
    np.multiply(B5, B5, out=d_tmp)
    d_tmp *= B0
    d -= d_tmp
    d *= 0.5
    # Ensure -1 <= d/2 <= 1.
    np.clip(d, -1, 1, out=d)

    # Compute phi. Beware that we reuse d variable!
    phi = d
    del d
    phi = np.arccos(phi, out=phi)
    phi /= 3

    # Compute val, ordered eigenvalues. Resuing B allocation.
    del B03, B36, B0, B1, B2, B3, B4, B5

    np.add(v, phi[np.newaxis], out=val[:2])
    val[2] = phi
    np.cos(val, out=val)
    p *= 2
    val *= p
    val += q

    # Remove all variable using tmp allocation.
    del q
    del p
    del phi
    del d_tmp

    # Computing eigenvectors -- either only one or all three.
    if full:
        l = val
        vec = vec.reshape(3, 3, -1)
        vec_tmp = tmp[:3]
    else:
        l = val[0]
        vec_tmp = tmp[2]

    # Compute vec. The tmp variable can be reused.

    # u = S[4] * S[5] - (S[2] - l) * S[3]
    u = np.subtract(S[2], l, out=vec[0])
    np.multiply(u, S[3], out=u)
    u_tmp = np.multiply(S[4], S[5], out=tmp[3])
    np.subtract(u_tmp, u, out=u)
    # Put values of u into vector 2 aswell.

    # v = S[3] * S[5] - (S[1] - l) * S[4]
    v = np.subtract(S[1], l, out=vec_tmp)
    np.multiply(v, S[4], out=v)
    v_tmp = np.multiply(S[3], S[5], out=tmp[3])
    np.subtract(v_tmp, v, out=v)

    # w = S[3] * S[4] - (S[0] - l) * S[5]
    w = np.subtract(S[0], l, out=vec[2])
    np.multiply(w, S[5], out=w)
    w_tmp = np.multiply(S[3], S[4], out=tmp[3])
    np.subtract(w_tmp, w, out=w)

    vec[1] = u
    np.multiply(u, v, out=vec[0])
    u = vec[1]
    np.multiply(u, w, out=vec[1])
    np.multiply(v, w, out=vec[2])

    # Remove u, v, w and l variables.
    del u
    del v
    del w
    del l

    # Normalizing -- depends on number of vectors.
    if full:
        # vec is [x1 x2 x3, y1 y2 y3, z1 z2 z3]
        l = np.sum(np.square(vec), axis=0, out=vec_tmp)[:, np.newaxis]
        vec = np.swapaxes(vec, 0, 1)
    else:
        # vec is [x1 y1 z1] = v1
        l = np.sum(np.square(vec, out=tmp[:3]), axis=0, out=vec_tmp)

    cp.rsqrt(l, out=l)
    vec *= l

    return val.reshape(val.shape[:-1] +
                       input_shape[1:]), vec.reshape(vec.shape[:-1] +
                                                     input_shape[1:])
Beispiel #12
0
    def start_from_function(self, vc, g, c):

        latmin = xp.min(g.latCell[:])
        latmax = xp.max(g.latCell[:])
        lonmin = xp.min(g.lonCell[:])
        lonmax = xp.max(g.lonCell[:])

        latmid = 0.5 * (latmin + latmax)
        latwidth = latmax - latmin

        lonmid = 0.5 * (lonmin + lonmax)
        lonwidth = lonmax - lonmin

        pi = np.pi
        sin = np.sin
        exp = np.exp
        r = c.sphere_radius

        if c.test_case == 1:
            a = c.sphere_radius
            R = a / 3
            u0 = 2 * np.pi * a / (12 * 86400)
            h0 = 1000.
            lon_c = .5 * np.pi
            lat_c = 0.
            r = a*xp.arccos(xp.sin(lat_c)*xp.sin(g.latCell[:]) + \
                xp.cos(lat_c)*xp.cos(g.latCell[:])*xp.cos(g.lonCell[:]-lon_c))
            self.thickness[:] = xp.where(
                r <= R, 0.25 * h0 * (1 + xp.cos(np.pi * r / R)), 0.) + h0

            self.vorticity[:] = 2 * u0 / a * np.sin(g.latCell[:])
            self.divergence[:] = 0.
            #self.compute_diagnostics(g, c)

            self.SS0 = xp.sum((self.thickness + g.bottomTopographyCell) *
                              g.areaCell) / xp.sum(g.areaCell)

        elif c.test_case == 2:
            # SWSTC #2, with a stationary analytic solution
            a = c.sphere_radius
            u0 = 2 * np.pi * a / (12 * 86400)
            gh0 = 2.94e4
            gh = xp.sin(g.latCell[:])**2
            gh = -(a * c.Omega0 * u0 + 0.5 * u0 * u0) * gh + gh0
            self.thickness[:] = gh / c.gravity
            h0 = gh0 / c.gravity

            self.vorticity[:] = 2 * u0 / a * xp.sin(g.latCell[:])
            self.divergence[:] = 0.

            self.psi_cell[:] = -a * h0 * u0 * xp.sin(g.latCell[:])
            self.psi_cell[:] += a * u0 / c.gravity * (
                a * c.Omega0 * u0 + 0.5 * u0**2) * (xp.sin(
                    g.latCell[:]))**3 / 3.
            self.psi_cell -= self.psi_cell[0]
            self.phi_cell[:] = 0.

            self.SS0 = xp.sum((self.thickness + g.bottomTopographyCell) *
                              g.areaCell) / xp.sum(g.areaCell)

            ## For debugging ##
            if False:
                # To check the consistency between psi_cell and vorticity
                self.thickness_edge = vc.cell2edge(self.thickness)
                self.psi_vertex[:] = vc.cell2vertex(self.psi_cell)
                nVelocity = vc.discrete_grad_n(self.phi_cell)
                nVelocity -= vc.discrete_grad_td(self.psi_vertex)
                nVelocity /= self.thickness_edge
                vorticity1 = vc.vertex2cell(vc.discrete_curl_t(nVelocity))
                err = vorticity1 - self.vorticity
                print("vorticity computed using normal vel.")
                print("relative error = %e" % (xp.sqrt(
                    xp.sum(err**2 * g.areaCell) /
                    xp.sum(self.vorticity**2 * g.areaCell))))

                self.phi_vertex[:] = vc.cell2vertex(self.phi_cell)
                tVelocity = vc.discrete_grad_n(self.psi_cell)
                tVelocity += vc.discrete_grad_tn(self.phi_vertex)
                tVelocity /= self.thickness_edge
                vorticity2 = vc.discrete_curl_v(tVelocity)
                err = vorticity2 - self.vorticity
                print("vorticity computed using tang vel.")
                print("relative error = %e" % (xp.sqrt(
                    xp.sum(err**2 * g.areaCell) /
                    xp.sum(self.vorticity**2 * g.areaCell))))

                err = 0.5 * (vorticity1 + vorticity2) - self.vorticity
                print("vorticity computed using both normal and tang vel.")
                print("relative error = %e" % (xp.sqrt(
                    xp.sum(err**2 * g.areaCell) /
                    xp.sum(self.vorticity**2 * g.areaCell))))
                raise ValueError(
                    "Testing the consistency between streamfunction and vorticity."
                )
                ## End of debugging ##

        elif c.test_case == 5:
            #SWSTC #5: zonal flow over a mountain topography

            a = c.sphere_radius
            u0 = 20.

            h0 = 5960.
            gh = c.gravity * h0 - xp.sin(
                g.latCell[:])**2 * (a * c.Omega0 * u0 + 0.5 * u0 * u0)
            h = gh / c.gravity

            # Define the mountain topography
            h_s0 = 2000.
            R = np.pi / 9
            lat_c = np.pi / 6.
            lon_c = -.5 * np.pi
            r = xp.sqrt((g.latCell[:] - lat_c)**2 + (g.lonCell[:] - lon_c)**2)
            r = xp.where(r < R, r, R)
            g.bottomTopographyCell[:] = h_s0 * (1 - r / R)
            self.thickness[:] = h[:] - g.bottomTopographyCell[:]
            self.vorticity[:] = 2 * u0 / a * xp.sin(g.latCell[:])
            self.divergence[:] = 0.

            self.SS0 = xp.sum((self.thickness + g.bottomTopographyCell) *
                              g.areaCell) / xp.sum(g.areaCell)

            self.curlWind_cell[:] = 0.
            self.divWind_cell[:] = 0.

        elif c.test_case == 6:
            # Setup shallow water test case 6: Rossby-Haurwitz Wave
            #
            # Reference: Williamson, D.L., et al., "A Standard Test Set for Numerical
            #            Approximations to the Shallow Water Equations in Spherical
            #            Geometry" J. of Comp. Phys., 102, pp. 211--224

            a = c.sphere_radius
            h0 = 8000.
            w = 7.848e-6
            K = 7.848e-6
            R = 4

            cos_lat = xp.cos(g.latCell)
            A = 0.5*w*(2*c.Omega0 + w)*cos_lat**2 + \
                0.25*K**2*cos_lat**(2*R) * ( \
                (R+1)*cos_lat**2 + \
                (2*R**2 - R -2) - \
                2*R**2 / (cos_lat**2) )
            B = 2*(c.Omega0 + w)*K/(R+1)/(R+2)*cos_lat**R * \
                (R**2 + 2*R + 2 - (R+1)**2*cos_lat**2)
            C = 0.25*K**2*cos_lat**(2*R) * \
                ((R+1)*cos_lat**2 - (R+2))

            # The thickness field
            self.thickness[:] = c.gravity * h0 + a**2*A + \
                                a**2*B*xp.cos(R*g.lonCell) + \
                                a**2*C*xp.cos(2*R*g.lonCell)
            self.thickness[:] /= c.gravity

            # Vorticity and divergence fields
            self.vorticity[:] = 2*w*xp.sin(g.latCell) - \
                                K*xp.sin(g.latCell)*cos_lat**R* \
                                (R**2 + 3*R + 2)*xp.cos(R*g.lonCell)
            self.divergence[:] = 0.

            self.SS0 = xp.sum((self.thickness + g.bottomTopographyCell) *
                              g.areaCell) / xp.sum(g.areaCell)

        elif c.test_case == 7:
            # Setup shallow water test case 7: Height and wind at 500 mb
            #
            # Reference: Williamson, D.L., et al., "A Standard Test Set for Numerical
            #            Approximations to the Shallow Water Equations in Spherical
            #            Geometry" J. of Comp. Phys., 102, pp. 211--224
            ini_dat = np.loadtxt('tc7-init-on-%d.dat' % g.nCells)
            self.thickness[:] = ini_dat[:, 2]
            self.vorticity[:] = ini_dat[:, 3]
            self.divergence[:] = ini_dat[:, 4]

            self.SS0 = xp.sum((self.thickness + g.bottomTopographyCell) *
                              g.areaCell) / xp.sum(g.areaCell)

        elif c.test_case == 8:
            # Setup shallow water test case 8: barotropic instability test case
            #
            # Reference:
            self.divergence[:] = 0.

            # Compute the vorticity field
            if c.use_gpu:
                self.vorticity[:] = xp.asarray(
                    cmp.compute_swtc8_vort(g.latCell.get()))
            else:
                self.vorticity[:] = cmp.compute_swtc8_vort(g.latCell)

            # Compute the thickness field, and shift it towards 10km average depth
            if c.use_gpu:
                gh = xp.asarray(cmp.compute_swtc8_gh(g.latCell.get()))
            else:
                gh = cmp.compute_swtc8_gh(g.latCell)

            gh += 10000. * c.gravity - xp.sum(gh * g.areaCell) / xp.sum(
                g.areaCell)
            self.thickness[:] = gh / c.gravity

            # Add a small perturbation to the thickness field
            pert = 120 * xp.cos(g.latCell)
            if xp.max(g.lonCell) > 1.1 * np.pi:
                print("Found the range of lonCell to be [0, 2pi]")
                print("Shifting it to [-pi, pi]")
                g.lonCell[:] = xp.where(g.lonCell > np.pi,
                                        g.lonCell - 2 * np.pi, g.lonCell)
                g.lonVertex[:] = xp.where(g.lonVertex > np.pi,
                                          g.lonVertex - 2 * np.pi, g.lonVertex)
                g.lonEdge[:] = np.where(g.lonEdge > np.pi,
                                        g.lonEdge - 2 * np.pi, g.lonEdge)
            pert *= xp.exp(-(g.lonCell * 3)**2)
            pert *= xp.exp(-((g.latCell - np.pi / 4) * 15)**2)
            #            self.thickness[:] += pert

            self.SS0 = xp.sum((self.thickness + g.bottomTopographyCell) *
                              g.areaCell) / xp.sum(g.areaCell)

        elif c.test_case == 12:
            # SWSTC #2, with a stationary analytic solution, modified for the northern hemisphere
            a = c.sphere_radius
            u0 = 2 * np.pi * a / (12 * 86400)
            gh0 = 2.94e4
            gh = xp.sin(g.latCell[:])**2
            gh = -(a * c.Omega0 * u0 + 0.5 * u0 * u0) * gh + gh0
            self.thickness[:] = gh / c.gravity
            h0 = gh0 / c.gravity

            self.vorticity[:] = 2 * u0 / a * xp.sin(g.latCell[:])
            self.divergence[:] = 0.

            self.psi_cell[:] = -a * h0 * u0 * xp.sin(g.latCell[:])
            self.psi_cell[:] += a * u0 / c.gravity * (
                a * c.Omega0 * u0 + 0.5 * u0**2) * (xp.sin(
                    g.latCell[:]))**3 / 3.
            self.phi_cell[:] = 0.

            self.SS0 = xp.sum((self.thickness + g.bottomTopographyCell) *
                              g.areaCell) / xp.sum(g.areaCell)

        elif c.test_case == 15:
            # Zonal flow over a mountain topography, on the northern hemisphere,
            # modeled after SWSTC #5

            a = c.sphere_radius
            u0 = 20.

            h0 = 5960.
            gh = c.gravity * h0 - xp.sin(
                g.latCell[:])**2 * (a * c.Omega0 * u0 + 0.5 * u0 * u0)
            h = gh / c.gravity

            # Define the mountain topography
            h_s0 = 2000.
            R = np.pi / 9
            lat_c = np.pi / 6.
            lon_c = .5 * np.pi
            r = xp.sqrt((g.latCell[:] - lat_c)**2 + (g.lonCell[:] - lon_c)**2)
            r = xp.where(r < R, r, R)
            g.bottomTopographyCell[:] = h_s0 * (1 - r / R)
            self.thickness[:] = h[:] - g.bottomTopographyCell[:]
            self.vorticity[:] = 2 * u0 / a * xp.sin(g.latCell[:])
            self.divergence[:] = 0.

            self.SS0 = xp.sum((self.thickness + g.bottomTopographyCell) *
                              g.areaCell) / xp.sum(g.areaCell)

            self.curlWind_cell[:] = 0.
            self.divWind_cell[:] = 0.

        elif c.test_case == 21:
            # A wind-driven gyre at mid-latitude in the northern hemisphere
            tau0 = 1.e-4

            latmin = xp.min(g.latCell[:])
            latmax = xp.max(g.latCell[:])
            lonmin = xp.min(g.lonCell[:])
            lonmax = xp.max(g.lonCell[:])

            latmid = 0.5 * (latmin + latmax)
            latwidth = latmax - latmin

            lonmid = 0.5 * (lonmin + lonmax)
            lonwidth = lonmax - lonmin

            r = c.sphere_radius

            self.vorticity[:] = 0.
            self.divergence[:] = 0.
            self.thickness[:] = 4000.

            self.psi_cell[:] = 0.0
            self.psi_vertex[:] = 0.0

            # Initialize wind
            self.curlWind_cell[:] = -tau0 * np.pi/(latwidth*r) * \
                                    xp.sin(np.pi*(g.latCell[:]-latmin) / latwidth)
            self.divWind_cell[:] = 0.

            self.SS0 = xp.sum((self.thickness + g.bottomTopographyCell) *
                              g.areaCell) / xp.sum(g.areaCell)

        elif c.test_case == 22:
            # One gyre with no forcing, for a bounded domain over NA
            d = xp.sqrt(32 * (g.latCell[:] - latmid)**2 / latwidth**2 + 4 *
                        (g.lonCell[:] - (-1.1))**2 / .3**2)
            f0 = xp.mean(g.fCell)
            self.thickness[:] = 4000.
            self.psi_cell[:] = 2 * xp.exp(-d**2) * 0.5 * (1 -
                                                          xp.tanh(20 *
                                                                  (d - 1.5)))
            #            self.psi_cell[:] -= np.sum(self.psi_cell * g.areaCell) / np.sum(g.areaCell)
            self.psi_cell *= c.gravity / f0 * self.thickness
            self.phi_cell[:] = 0.
            self.vorticity = vc.discrete_laplace_v(self.psi_cell)
            self.vorticity /= self.thickness
            self.divergence[:] = 0.

            # Initialize wind
            self.curlWind_cell[:] = 0.
            self.divWind_cell[:] = 0.

            # Eliminate bottom drag
            #c.bottomDrag = 0.

            # Eliminate lateral diffusion
            #c.delVisc = 0.
            #c.del2Visc = 0.

            self.SS0 = xp.sum((self.thickness + g.bottomTopographyCell) *
                              g.areaCell) / xp.sum(g.areaCell)

        else:
            raise ValueError("Invaid choice for the test case.")

        # Set time to zero
        self.time = 0.0