def quasilinear_eigenvalues(self, q, x, t, n):
        g = self.gravity_constant
        p = swme.get_primitive_variables(q)
        h = p[0]
        u = p[1]
        # Eigenvalues are u |pm \sqrt{gh + sum{i = 1}{N}{3 alpha_i^2 / (2i + 1)}}
        # The rest of the eigenvalues are u
        if self.num_moments == 0:
            eigenvalues = np.array([u - np.sqrt(g * h), u + np.sqrt(g * h)])
        elif self.num_moments >= 1:
            c = np.sqrt(
                g * h
                + sum(
                    [
                        3 * p[i + 2] * p[i + 2] / (2 * i + 3)
                        for i in range(self.num_moments)
                    ]
                )
            )

            eigenvalues = np.array(
                [u - c] + [u for i in range(self.num_moments)] + [u + c]
            )

        return eigenvalues
    def roe_averaged_states(self, left_state, right_state, x, t):
        errors.MissingImplementation(self.class_str, "roe_averaged_states")
        p_left = swme.get_primitive_variables(left_state)
        p_right = swme.get_primitive_variables(right_state)

        # roe averaged primitive variables
        p_avg = np.zeros(p_left.shape)
        # h_avg
        p_avg[0] = 0.5 * (p_left[0] + p_right[0])
        d = np.sqrt(p_left[0]) + np.sqrt(p_right[0])
        for i in range(1, self.num_moments + 2):
            # u_avg, s_avg, k_avg, m_avg
            p_avg[i] = (
                np.sqrt(p_left[0]) * p_left[i] + np.sqrt(p_right[0]) * p_right[i]
            ) / d

        # transform back to conserved variables
        return swme.get_conserved_variables(p_avg)
    def function(self, q):
        # q.shape (num_eqns, points_shape)
        num_eqns = q.shape[0]  # also num_moments + 2
        points_shape = q.shape[1:]

        Q = np.zeros((num_eqns, num_eqns, 1) + points_shape)
        p = swme.get_primitive_variables(q)
        u = p[1]
        for i in range(self.num_moments):
            Q[i + 2, i + 2, 0] = -u

        return Q
    def quasilinear_eigenvectors_left(self, q, x, t):
        # TODO: needs to be implemented
        errors.MissingImplementation(self.class_str, "quasilinear_eigenvectors_left")
        g = self.gravity_constant
        p = swme.get_primitive_variables(q)
        h = p[0]
        u = p[1]
        eigenvectors = np.zeros((self.num_moments + 2, self.num_moments + 2))
        if self.num_moments == 0:
            sqrt_gh = np.sqrt(g * h)
            eigenvectors[0, 0] = 0.5 * (u + sqrt_gh) / sqrt_gh
            eigenvectors[0, 1] = -0.5 / sqrt_gh

            eigenvectors[1, 0] = -0.5 * (u - sqrt_gh) / sqrt_gh
            eigenvectors[1, 1] = 0.5 / sqrt_gh
        elif self.num_moments == 1:
            s = p[2]
            ghs2 = g * h + s * s
            sqrt_gh_s2 = np.sqrt(ghs2)
            eigenvectors[0, 0] = (
                1.0 / 6.0 * (3.0 * g * h - s * s + 3.0 * sqrt_gh_s2 * u) / ghs2
            )
            eigenvectors[0, 1] = -0.5 / sqrt_gh_s2
            eigenvectors[0, 2] = 1.0 / 3.0 * s / ghs2

            eigenvectors[1, 0] = 4.0 / 3.0 * s * s / ghs2
            eigenvectors[1, 1] = 0.0
            eigenvectors[1, 2] = -2.0 / 3.0 * s / ghs2

            eigenvectors[2, 0] = (
                -1.0
                / 6.0
                * (3.0 * ghs2 * u - (3.0 * g * h - s * s) * sqrt_gh_s2)
                / np.power(ghs2, 1.5)
            )
            eigenvectors[2, 1] = 0.5 / sqrt_gh_s2
            eigenvectors[2, 2] = 1.0 / 3.0 * s / ghs2

        elif self.num_moments == 2:
            raise errors.NotImplementedParameter(
                "ShallowWaterLinearizedMomentEquations.quasilinear_eigenvectors_left",
                "num_moments",
                2,
            )
        elif self.num_moments == 3:
            raise errors.NotImplementedParameter(
                "ShallowWaterLinearizedMomentEquations.quasilinear_eigenvectors_left",
                "num_moments",
                3,
            )

        return eigenvectors
    def function(self, q):
        # q.shape (num_eqns, points.shape)
        # return shape (num_eqns, 1, points.shape)
        num_eqns = q.shape[0]
        points_shape = q.shape[1:]
        g = self.gravity_constant
        p = swme.get_primitive_variables(q)
        h = p[0]
        u = p[1]
        f = np.zeros((num_eqns, 1) + points_shape)
        f[0, 0] = h * u
        f[1, 0] = h * u * u + 0.5 * g * h * h
        for i in range(self.num_moments):
            alpha_i = p[i + 2]
            f[1, 0] += 1.0 / (2.0 * i + 3.0) * h * alpha_i * alpha_i
            f[i + 2, 0] = 2.0 * alpha_i * h * u

        return f
    def quasilinear_eigenvectors_right(self, q, x, t):
        # TODO: needs to be implemented
        errors.MissingImplementation(self.class_str, "quasilinear_eigenvectors_right")
        g = self.gravity_constant
        p = swme.get_primitive_variables(q)
        h = p[0]
        u = p[1]
        eigenvectors = np.zeros((self.num_moments + 2, self.num_moments + 2))
        if self.num_moments == 0:
            sqrt_gh = np.sqrt(g * h)
            eigenvectors[0, 0] = 1.0
            eigenvectors[1, 0] = u - sqrt_gh

            eigenvectors[0, 1] = 1.0
            eigenvectors[1, 1] = u + sqrt_gh
        elif self.num_moments == 1:
            s = p[2]
            sqrt_gh_s2 = np.sqrt(g * h + s * s)
            eigenvectors[0, 0] = 1.0
            eigenvectors[1, 0] = u - sqrt_gh_s2
            eigenvectors[2, 0] = 2.0 * s

            eigenvectors[0, 1] = 1.0
            eigenvectors[1, 1] = u
            eigenvectors[2, 1] = -0.5 * (3.0 * g * h - s * s) / s

            eigenvectors[0, 2] = 1.0
            eigenvectors[1, 2] = u + sqrt_gh_s2
            eigenvectors[2, 2] = 2.0 * s
        elif self.num_moments == 2:
            raise errors.NotImplementedParameter(
                "ShallowWaterLinearizedMomentEquations.quasilinear_eigenvalues_right",
                "num_moments",
                2,
            )
        elif self.num_moments == 3:
            raise errors.NotImplementedParameter(
                "ShallowWaterLinearizedMomentEquations.quasilinear_eigenvectors_right",
                "num_moments",
                3,
            )

        return eigenvectors
    def do_q_jacobian(self, q):
        # q may be shape (num_eqns, points.shape)
        # result should be (num_eqns, num_eqns, 1, points.shape) or (num_eqns, num_eqns)
        num_eqns = q.shape[0]
        points_shape = q.shape[1:]
        g = self.gravity_constant
        p = swme.get_primitive_variables(q)
        h = p[0]
        u = p[1]

        num_eqns = self.num_moments + 2
        result = np.zeros((num_eqns, num_eqns, 1) + points_shape)
        result[0, 1, 0] = 1.0
        result[1, 0, 0] = g * h - u * u
        result[1, 1, 0] = 2.0 * u
        for i in range(self.num_moments):
            alpha_i = p[i + 2]
            result[1, 0, 0] += -1.0 / (2.0 * i + 3.0) * alpha_i * alpha_i
            result[1, i + 2, 0] = 2.0 / (2.0 * i + 3.0) * alpha_i
            result[i + 2, 0, 0] = -2.0 * u * alpha_i
            result[i + 2, 1, 0] = 2.0 * alpha_i
            result[i + 2, i + 2, 0] = 2.0 * u

        return result