Пример #1
0
    def test_cmscr1d_weak_solution_zero_Dirichlet(self):
        print("Running test 'test_cmscr1d_weak_solution_zero_Dirichlet'")
        # Define temporal and spatial sample points.
        m, n = 10, 20

        # Define mesh and function space.
        mesh = UnitSquareMesh(m - 1, n - 1)
        V = dh.create_function_space(mesh, 'default')
        W = dh.create_vector_function_space(mesh, 'default')

        # Define boundary conditions for velocity.
        bc = DirichletBC(W.sub(0), Constant(0), dh.DirichletBoundary())

        # Create zero function.
        f = Function(V)

        # Compute velocity.
        v, k, res, fun, converged = cmscr1d_weak_solution(W, f,
                                                          f.dx(0), f.dx(1),
                                                          1.0, 1.0,
                                                          1.0, 1.0, 1.0,
                                                          bcs=bc)
        v = v.vector().get_local()
        k = k.vector().get_local()

        np.testing.assert_allclose(v.shape, m * n)
        np.testing.assert_allclose(v, np.zeros_like(v))
        np.testing.assert_allclose(k.shape, m * n)
        np.testing.assert_allclose(k, np.zeros_like(k))
Пример #2
0
    def test_cms1d_weak_solution_given_velocity_default(self):
        # Define temporal and spatial sample points.
        m, n = 10, 20

        # Define mesh and function space.
        mesh = UnitSquareMesh(m - 1, n - 1)
        V = dh.create_function_space(mesh, 'default')

        # Create zero function.
        f = Function(V)

        # Create velocity.
        v = Function(V)
        vx = Function(V)

        # Compute source.
        k, res, fun = cms1d_weak_solution_given_velocity(
            V, f, f.dx(0), f.dx(1), v, vx, 1.0, 1.0)
        k = k.vector().get_local()

        np.testing.assert_allclose(k.shape, m * n)
        np.testing.assert_allclose(k, np.zeros_like(k))

        V = dh.create_function_space(mesh, 'periodic')

        # Create zero function.
        f = Function(V)

        # Compute source.
        k, res, fun = cms1d_weak_solution_given_velocity(
            V, f, f.dx(0), f.dx(1), v, vx, 1.0, 1.0)
        k = k.vector().get_local()

        np.testing.assert_allclose(k.shape, m * (n - 1))
        np.testing.assert_allclose(k, np.zeros_like(k))
Пример #3
0
    def test_cm1d_weak_solution_default(self):
        # Define temporal and spatial sample points.
        m, n = 10, 20

        # Define mesh and function space.
        mesh = UnitSquareMesh(m - 1, n - 1)
        V = dh.create_function_space(mesh, 'default')

        # Create zero function.
        f = Function(V)

        # Compute velocity.
        v, res, fun = cm1d_weak_solution(V, f, f.dx(0), f.dx(1), 1.0, 1.0)
        v = v.vector().get_local()

        np.testing.assert_allclose(v.shape, m * n)
        np.testing.assert_allclose(v, np.zeros_like(v))
        np.testing.assert_allclose(res, 0)
        np.testing.assert_allclose(fun, 0)

        V = dh.create_function_space(mesh, 'periodic')

        # Create zero function.
        f = Function(V)

        # Compute velocity.
        v, res, fun = cm1d_weak_solution(V, f, f.dx(0), f.dx(1), 1.0, 1.0)
        v = v.vector().get_local()

        np.testing.assert_allclose(v.shape, m * (n - 1))
        np.testing.assert_allclose(v, np.zeros_like(v))
        np.testing.assert_allclose(res, 0)
        np.testing.assert_allclose(fun, 0)
Пример #4
0
def cmscr1d_img_pb(img: np.array,
                   alpha0: float, alpha1: float,
                   alpha2: float, alpha3: float,
                   beta: float, deriv='mesh') \
                   -> (np.array, np.array, float, float, bool):
    """Computes the L2-H1 mass conserving flow with source for a 1D image
    sequence with spatio-temporal and convective regularisation with periodic
    spatial boundary.

    Args:
        img (np.array): 1D image sequence of shape (m, n), where m is the
                        number of time steps and n is the number of pixels.
        alpha0 (float): The spatial regularisation parameter for v.
        alpha1 (float): The temporal regularisation parameter for v.
        alpha2 (float): The spatial regularisation parameter for k.
        alpha3 (float): The temporal regularisation parameter for k.
        beta (float): The convective regularisation parameter.
        deriv (str): Specifies how to approximate pertial derivatives.
                     When set to 'mesh' it uses FEniCS built in function.

    Returns:
        v (np.array): A velocity array of shape (m, n).
        k (np.array): A source array of shape (m, n).
        res (float): The residual.
        fun (float): The function value.
        converged (bool): True if Newton's method converged.

    """
    # Check for valid arguments.
    valid = {'mesh'}
    if deriv not in valid:
        raise ValueError("Argument 'deriv' must be one of %r." % valid)

    # Create mesh.
    m, n = img.shape
    mesh = UnitSquareMesh(m - 1, n - 1)

    # Define function space.
    V = dh.create_function_space(mesh, 'periodic')
    W = dh.create_vector_function_space(mesh, 'periodic')

    # Convert array to function.
    f = Function(V)
    f.vector()[:] = dh.img2funvec_pb(img)

    # Compute partial derivatives.
    ft, fx = f.dx(0), f.dx(1)

    # Compute velocity.
    v, k, res, fun, converged = cmscr1d_weak_solution(W, f, ft, fx, alpha0,
                                                      alpha1, alpha2, alpha3,
                                                      beta)

    # Convert back to array and return.
    v = dh.funvec2img(v.vector().get_local(), m, n)
    k = dh.funvec2img(k.vector().get_local(), m, n)
    return v, k, res, fun, converged
Пример #5
0
def of1d_img(img: np.array, alpha0: float, alpha1: float, deriv) \
             -> (np.array, float, float):
    """Computes the L2-H1 optical flow for a 1D image sequence.

    Takes a one-dimensional image sequence and returns a minimiser of the
    Horn-Schunck functional with spatio-temporal regularisation.

    Allows to specify how to approximate partial derivatives of f numerically.

    Args:
        img (np.array): 1D image sequence of shape (m, n), where m is the
                        number of time steps and n is the number of pixels.
        alpha0 (float): Spatial regularisation parameter.
        alpha1 (float): Temporal regularisation parameter.
        deriv (str): Specifies how to approximate pertial derivatives.
                     When set to 'mesh' it uses FEniCS built in function.
                     When set to 'fd' it uses finite differences.

    Returns:
        v (np.array): A velocity array of shape (m, n).
        res (float): The residual.
        fun (float): The function value.
    """
    # Check for valid arguments.
    valid = {'mesh', 'fd'}
    if deriv not in valid:
        raise ValueError("Argument 'deriv' must be one of %r." % valid)

    # Create mesh.
    m, n = img.shape
    mesh = UnitSquareMesh(m - 1, n - 1)

    # Define function space.
    V = dh.create_function_space(mesh, 'default')

    # Convert array to function.
    f = Function(V)
    f.vector()[:] = dh.img2funvec(img)

    # Compute partial derivatives.
    if deriv is 'mesh':
        ft, fx = f.dx(0), f.dx(1)
    if deriv is 'fd':
        imgt, imgx = nh.partial_derivatives(img)
        ft, fx = Function(V), Function(V)
        ft.vector()[:] = dh.img2funvec(imgt)
        fx.vector()[:] = dh.img2funvec(imgx)

    # Compute velocity.
    v, res, fun = of1d_weak_solution(V, f, ft, fx, alpha0, alpha1)

    # Convert to array and return.
    return dh.funvec2img(v.vector().get_local(), m, n), res, fun
Пример #6
0
def plot_deltas_2D(vertex_vector, mesh_path, save_dir):
    mesh = Mesh()
    with XDMFFile(mesh_path) as f:
        f.read(mesh)
    V = FunctionSpace(mesh, 'CG', 1)
    value = Function(V)
    value.vector()[:] = vertex_vector[dof_to_vertex_map(V)]
    delta_S = project(value.dx(0), V)
    delta_file = XDMFFile(save_dir)
    delta_file.write_checkpoint(delta_S, 'delta_S', 0, XDMFFile.Encoding.HDF5,
                                True)
    delta_v = project(value.dx(1), V)
    delta_file.write_checkpoint(delta_v, 'delta_v', 0, XDMFFile.Encoding.HDF5,
                                True)
Пример #7
0
def cm1d_img_pb(img: np.array,
                alpha0: float,
                alpha1: float,
                deriv='mesh') -> np.array:
    """Computes the L2-H1 mass conserving flow for a 1D image sequence with
    periodic spatial boundary.

    Allows to specify how to approximate partial derivatives of f numerically.

    Note that the last column of img is ignored.

    Args:
        img (np.array): 1D image sequence of shape (m, n), where m is the
                        number of time steps and n is the number of pixels.
        alpha0 (float): Spatial regularisation parameter.
        alpha1 (float): Temporal regularisation parameter.
        deriv (str): Specifies how to approximate pertial derivatives.
                     When set to 'mesh' it uses FEniCS built in function.

    Returns:
        v (np.array): A velocity array of shape (m, n).
        res (float): The residual.
        func (float): The value of the functional.

    """
    # Check for valid arguments.
    valid = {'mesh'}
    if deriv not in valid:
        raise ValueError("Argument 'deriv' must be one of %r." % valid)

    # Create mesh.
    m, n = img.shape
    mesh = UnitSquareMesh(m - 1, n - 1)

    # Define function space.
    V = dh.create_function_space(mesh, 'periodic')

    # Convert array to function.
    f = Function(V)
    f.vector()[:] = dh.img2funvec_pb(img)

    # Compute partial derivatives.
    ft, fx = f.dx(0), f.dx(1)

    # Compute velocity.
    v, res, fun = cm1d_weak_solution(V, f, ft, fx, alpha0, alpha1)

    # Convert to array and return.
    return dh.funvec2img_pb(v.vector().get_local(), m, n), res, fun
Пример #8
0
def compute_velocity_correction(
    ui, p0, p1, u_bcs, rho, mu, dt, rotational_form, my_dx, tol, verbose
):
    """Compute the velocity correction according to

    .. math::

        U = u_0 - \\frac{dt}{\\rho} \\nabla (p_1-p_0).
    """
    W = ui.function_space()
    P = p1.function_space()

    u = TrialFunction(W)
    v = TestFunction(W)
    a3 = dot(u, v) * my_dx
    phi = Function(P)
    phi.assign(p1)
    if p0:
        phi -= p0
    if rotational_form:
        r = SpatialCoordinate(W.mesh())[0]
        div_ui = 1 / r * (r * ui[0]).dx(0) + ui[1].dx(1)
        phi += mu * div_ui
    L3 = dot(ui, v) * my_dx - dt / rho * (phi.dx(0) * v[0] + phi.dx(1) * v[1]) * my_dx
    u1 = Function(W)
    solve(
        a3 == L3,
        u1,
        bcs=u_bcs,
        solver_parameters={
            "linear_solver": "iterative",
            "symmetric": True,
            "preconditioner": "hypre_amg",
            "krylov_solver": {
                "relative_tolerance": tol,
                "absolute_tolerance": 0.0,
                "maximum_iterations": 100,
                "monitor_convergence": verbose,
            },
        },
    )
    # u = project(ui - k/rho * grad(phi), V)
    # div_u = 1/r * div(r*u)
    r = SpatialCoordinate(W.mesh())[0]
    div_u1 = 1.0 / r * (r * u1[0]).dx(0) + u1[1].dx(1)
    info("||u||_div = {:e}".format(sqrt(assemble(div_u1 * div_u1 * my_dx))))
    return u1
Пример #9
0
def cm1dvelocity(img: np.array, vel: np.array, alpha0: float,
                 alpha1: float) -> np.array:
    """Computes the source for a L2-H1 mass conserving flow for a 1D image
    sequence and a given velocity.

    Takes a one-dimensional image sequence and a velocity, and returns a
    minimiser of the L2-H1 mass conservation functional with spatio-temporal
    regularisation.

    Args:
        img (np.array): A 1D image sequence of shape (m, n), where m is the
                        number of time steps and n is the number of pixels.
        vel (np.array): A 1D image sequence of shape (m, n).
        alpha0 (float): The spatial regularisation parameter.
        alpha1 (float): The temporal regularisation parameter.

    Returns:
        k: A source array of shape (m, n).

    """
    # Create mesh.
    [m, n] = img.shape
    mesh = UnitSquareMesh(m - 1, n - 1)

    # Define function space and functions.
    V = FunctionSpace(mesh, 'CG', 1)
    k = TrialFunction(V)
    w = TestFunction(V)

    # Convert image to function.
    f = Function(V)
    f.vector()[:] = dh.img2funvec(img)

    # Convert velocity to function.
    v = Function(V)
    v.vector()[:] = dh.img2funvec(vel)

    # Define derivatives of data.
    ft = Function(V)
    ftv = np.diff(img, axis=0) * (m - 1)
    ftv = np.concatenate((ftv, ftv[-1, :].reshape(1, n)), axis=0)
    ft.vector()[:] = dh.img2funvec(ftv)

    fx = Function(V)
    fxv = np.gradient(img, 1 / (n - 1), axis=1)
    fx.vector()[:] = dh.img2funvec(fxv)

    # Define weak formulation.
    A = k * w * dx + alpha0 * k.dx(1) * w.dx(1) * dx + alpha1 * k.dx(0) * w.dx(
        0) * dx
    b = (ft + v.dx(1) * f + v * fx) * w * dx

    # Compute solution.
    k = Function(V)
    solve(A == b, k)

    # Convert back to array.
    k = dh.funvec2img(k.vector().get_local(), m, n)
    return k
Пример #10
0
    def test_cmscr1d_weak_solution_default(self):
        print("Running test 'test_cmscr1d_weak_solution_default'")
        # Define temporal and spatial sample points.
        m, n = 10, 20

        # Define mesh and function space.
        mesh = UnitSquareMesh(m - 1, n - 1)
        V = dh.create_function_space(mesh, 'default')
        W = dh.create_vector_function_space(mesh, 'default')

        # Create zero function.
        f = Function(V)

        # Compute velocity.
        v, k, res, fun, converged = cmscr1d_weak_solution(W, f,
                                                          f.dx(0), f.dx(1),
                                                          1.0, 1.0,
                                                          1.0, 1.0, 1.0)
        v = v.vector().get_local()
        k = k.vector().get_local()

        np.testing.assert_allclose(v.shape, m * n)
        np.testing.assert_allclose(v, np.zeros_like(v))
        np.testing.assert_allclose(k.shape, m * n)
        np.testing.assert_allclose(k, np.zeros_like(k))

        V = dh.create_function_space(mesh, 'periodic')

        # Create zero function.
        f = Function(V)

        # Compute velocity.
        v, k, res, fun, converged = cmscr1d_weak_solution(W, f,
                                                          f.dx(0), f.dx(1),
                                                          1.0, 1.0, 1.0,
                                                          1.0, 1.0)
        v = v.vector().get_local()
        k = k.vector().get_local()

        np.testing.assert_allclose(v.shape, m * n)
        np.testing.assert_allclose(v, np.zeros_like(v))
        np.testing.assert_allclose(k.shape, m * n)
        np.testing.assert_allclose(k, np.zeros_like(k))
Пример #11
0
def cm1dsource(img: np.array, k: np.array, alpha0: float,
               alpha1: float) -> np.array:
    """Computes the L2-H1 mass conserving flow for a 1D image sequence and a
    given source.

    Takes a one-dimensional image sequence and a source, and returns a
    minimiser of the L2-H1 mass conservation functional with spatio-temporal
    regularisation.

    Args:
        img (np.array): A 1D image sequence of shape (m, n), where m is the
                        number of time steps and n is the number of pixels.
        k (np.array):   A 1D image sequence of shape (m, n).
        alpha0 (float): The spatial regularisation parameter.
        alpha1 (float): The temporal regularisation parameter.

    Returns:
        v: A velocity array of shape (m, n).

    """
    # Create mesh.
    [m, n] = img.shape
    mesh = UnitSquareMesh(m - 1, n - 1)

    # Define function space and functions.
    V = FunctionSpace(mesh, 'CG', 1)
    v = TrialFunction(V)
    w = TestFunction(V)

    # Convert image to function.
    f = Function(V)
    f.vector()[:] = dh.img2funvec(img)

    # Convert source to function.
    g = Function(V)
    g.vector()[:] = dh.img2funvec(k)

    # Define derivatives of data.
    ft = Function(V)
    ftv = np.diff(img, axis=0) * (m - 1)
    ftv = np.concatenate((ftv, ftv[-1, :].reshape(1, n)), axis=0)
    ft.vector()[:] = dh.img2funvec(ftv)

    fx = Function(V)
    fxv = np.gradient(img, 1 / (n - 1), axis=1)
    fx.vector()[:] = dh.img2funvec(fxv)

    ft = f.dx(0)
    fx = f.dx(1)

    # Define weak formulation.
    A = - (fx*v + f*v.dx(1)) * (fx*w + f*w.dx(1))*dx \
        - alpha0*v.dx(1)*w.dx(1)*dx - alpha1*v.dx(0)*w.dx(0)*dx
    b = ft * (fx * w + f * w.dx(1)) * dx - g * (fx * w + f * w.dx(1)) * dx

    # Compute solution.
    v = Function(V)
    solve(A == b, v)

    # Convert back to array.
    vel = dh.funvec2img(v.vector().get_local(), m, n)
    return vel
Пример #12
0
def of2dmcs(img1: np.array, img2: np.array, alpha0: float, alpha1: float,
            beta0: float, beta1: float) -> (np.array, np.array):
    """Computes the L2-H1 optical flow for a 2D two-channel image sequence and
    source for the second channel (optical flow 2d multi-channel with source).

    Takes a two-dimensional image sequence and returns a minimiser of the
    Horn-Schunck functional with source and with spatio-temporal
    regularisation for both velocity and source.

    Args:
        img1 (np.array): A 2D image sequence of shape (t, m, n), where t is the
                         number of time steps and (n, n) is the number of
                         pixels.
        img2 (np.array): A 2D image sequence of shape (t, m, n), where t is the
                         number of time steps and (n, n) is the number of
                         pixels.
        alpha0 (float):  The spatial regularisation parameter.
        alpha1 (float):  The temporal regularisation parameter.

    Returns:
        v (np.array): A velocity array of shape (t, m, n, 2).
        k (np.array): A source array of shape (t, m, n).

    """
    # Create mesh.
    [t, m, n] = img1.shape
    mesh = UnitCubeMesh(t-2, m-1, n-1)

    # Define function space and functions.
    V = FunctionSpace(mesh, 'CG', 1)
    W = VectorFunctionSpace(mesh, 'CG', 1, dim=3)
    v1, v2, k = TrialFunctions(W)
    w1, w2, w3 = TestFunctions(W)

    # Convert image to function.
    f1, f2 = Function(V), Function(V)
    f1.vector()[:] = dh.imgseq2funvec(img1[0:-1])
    f2.vector()[:] = dh.imgseq2funvec(img2[0:-1])

    # Define function to compute temporal derivative.
    def time_deriv(img: np.array) -> Function:
        # Evaluate function at vertices.
        mc = mesh.coordinates().reshape((-1, 3))
        hx, hy, hz = 1./(t-2), 1./(m-1), 1./(n-1)
        x = np.array(mc[:, 0]/hx, dtype=int)
        y = np.array(mc[:, 1]/hy, dtype=int)
        z = np.array(mc[:, 2]/hz, dtype=int)

        # Map pixel values to vertices.
        d2v = dof_to_vertex_map(V)

        # Compute derivative wrt. time.
        imgt = img[1:] - img[0:-1]
        ftv = imgt[x, y, z]

        # Create function.
        ft = Function(V)
        ft.vector()[:] = ftv[d2v]
        return ft

    # Compute temporal derivatives.
    f1t = time_deriv(img1)
    f2t = time_deriv(img2)

    # Define derivatives of data.
    f1x, f1y = f1.dx(1), f1.dx(2)
    f2x, f2y = f2.dx(1), f2.dx(2)

    # Define weak formulation.
    A = f1x*(f1x*v1 + f1y*v2)*w1*dx + f2x*(f2x*v1 + f2y*v2 - k)*w1*dx \
        + f1y*(f1x*v1 + f1y*v2)*w2*dx + f2y*(f2x*v1 + f2y*v2 - k)*w2*dx \
        - (f2x*v1 + f2y*v2 - k)*w3*dx \
        + alpha0*v1.dx(1)*w1.dx(1)*dx + alpha0*v1.dx(2)*w1.dx(2)*dx \
        + alpha1*v1.dx(0)*w1.dx(0)*dx \
        + alpha0*v2.dx(1)*w2.dx(1)*dx + alpha0*v2.dx(2)*w2.dx(2)*dx \
        + alpha1*v2.dx(0)*w2.dx(0)*dx \
        + beta0*k.dx(1)*w3.dx(1)*dx + beta0*k.dx(2)*w3.dx(2)*dx \
        + beta1*k.dx(0)*w3.dx(0)*dx
    b = - f1x*f1t*w1*dx - f2x*f2t*w1*dx \
        - f1y*f1t*w2*dx - f2y*f2t*w2*dx \
        + f2t*w3*dx

    # Compute solution.
    v = Function(W)
    solve(A == b, v, [], solver_parameters={"linear_solver": "cg"})

    # Split solution into functions.
    v1, v2, k = v.split(deepcopy=True)

    # Convert back to arrays.
    v1 = dh.funvec2imgseq(v1.vector().get_local(), t-1, m, n)
    v2 = dh.funvec2imgseq(v2.vector().get_local(), t-1, m, n)
    k = dh.funvec2imgseq(k.vector().get_local(), t-1, m, n)
    return (np.stack((v1, v2), axis=3), k)
    def step(self,
             dt,
             u1, p1,
             u, p0,
             u_bcs, p_bcs,
             f0=None, f1=None,
             verbose=True,
             tol=1.0e-10
             ):
        '''General pressure projection scheme as described in section 3.4 of
        :cite:`GMS06`.
        '''
        # Some initial sanity checkups.
        assert dt > 0.0

        # Define coefficients
        k = Constant(dt)

        # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        # Compute tentative velocity step.
        # rho (u[-1] + (u.\nabla)u) = mu 1/r \div(r \nabla u) + rho g.
        with Message('Computing tentative velocity'):
            solver = NewtonSolver()
            solver.parameters['maximum_iterations'] = 5
            solver.parameters['absolute_tolerance'] = tol
            solver.parameters['relative_tolerance'] = 0.0
            solver.parameters['report'] = True
            # The nonlinear term makes the problem generally nonsymmetric.
            solver.parameters['linear_solver'] = 'gmres'
            # If the nonsymmetry is too strong, e.g., if u[-1] is large, then
            # AMG preconditioning might not work very well.
            # Use HYPRE-Euclid instead of ILU for parallel computation.
            solver.parameters['preconditioner'] = 'hypre_euclid'
            solver.parameters['krylov_solver']['relative_tolerance'] = tol
            solver.parameters['krylov_solver']['absolute_tolerance'] = 0.0
            solver.parameters['krylov_solver']['maximum_iterations'] = 1000
            solver.parameters['krylov_solver']['monitor_convergence'] = verbose

            ui = Function(self.W)
            step_problem = \
                TentativeVelocityProblem(ui,
                                         self.theta,
                                         self.rho, self.mu,
                                         u, p0, k,
                                         u_bcs,
                                         f0, f1,
                                         stabilization=self.stabilization,
                                         dx=self.dx
                                         )

            # Take u[-1] as initial guess.
            ui.assign(u[-1])
            solver.solve(step_problem, ui.vector())
            #div_u = 1/r * div(r*ui)
            #plot(div_u)
            #interactive()
        # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        with Message('Computing pressure correction'):
            # The pressure correction is based on the update formula
            #
            #                           (    dphi/dr    )
            #     rho/dt (u_{n+1}-u*) + (    dphi/dz    ) = 0
            #                           (1/r dphi/dtheta)
            #
            # with
            #
            #     phi = p_{n+1} - p*
            #
            # and
            #
            #      1/r d/dr    (r ur_{n+1})
            #    +     d/dz    (  uz_{n+1})
            #    + 1/r d/dtheta(  utheta_{n+1}) = 0
            #
            # With the assumption that u does not change in the direction
            # theta, one derives
            #
            #  - 1/r * div(r * \nabla phi) = 1/r * rho/dt div(r*(u_{n+1} - u*))
            #  - 1/r * n. (r * \nabla phi) = 1/r * rho/dt  n.(r*(u_{n+1} - u*))
            #
            # In its weak form, this is
            #
            #   \int r * \grad(phi).\grad(q) *2*pi =
            #       - rho/dt \int div(r*u*) q *2*p
            #       - rho/dt \int_Gamma n.(r*(u_{n+1}-u*)) q *2*pi.
            #
            # (The terms 1/r cancel with the volume elements 2*pi*r.)
            # If Dirichlet boundary conditions are applied to both u* and u_n
            # (the latter in the final step), the boundary integral vanishes.
            #
            alpha = 1.0
            #alpha = 3.0 / 2.0
            rotational_form = False
            self._pressure_poisson(p1, p0,
                                   self.mu, ui,
                                   self.rho * alpha * ui / k,
                                   p_bcs=p_bcs,
                                   rotational_form=rotational_form,
                                   tol=tol,
                                   verbose=verbose
                                   )
        #plot(ui, title='u intermediate')
        ##plot(f, title='f')
        ##plot(ui[1], title='u intermediate[1]')
        #plot(div(ui), title='div(u intermediate)')
        #plot(p1, title='p1')
        #interactive()
        # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        # Velocity correction.
        #   U = u[-1] - dt/rho \nabla (p1-p0).
        with Message('Computing velocity correction'):
            u = TrialFunction(self.W)
            v = TestFunction(self.W)
            a3 = dot(u, v) * dx
            phi = Function(self.P)
            phi.assign(p1)
            if p0:
                phi -= p0
            if rotational_form:
                r = Expression('x[0]', degree=1, domain=self.W.mesh())
                div_ui = 1/r * (r * ui[0]).dx(0) + ui[1].dx(1)
                phi += self.mu * div_ui
            L3 = dot(ui, v) * dx \
                - k / self.rho * (phi.dx(0) * v[0] + phi.dx(1) * v[1]) * dx
            # Jacobi preconditioning for mass matrix is order-optimal.
            solve(
                a3 == L3, u1,
                bcs=u_bcs,
                solver_parameters={
                    'linear_solver': 'iterative',
                    'symmetric': True,
                    'preconditioner': 'jacobi',
                    'krylov_solver': {'relative_tolerance': tol,
                                      'absolute_tolerance': 0.0,
                                      'maximum_iterations': 100,
                                      'monitor_convergence': verbose}
                    }
                )
            #u = project(ui - k/rho * grad(phi), V)
            # div_u = 1/r * div(r*u)
            r = Expression('x[0]', degree=1, domain=self.W.mesh())
            div_u1 = 1.0 / r * (r * u1[0]).dx(0) + u1[1].dx(1)
            info('||u||_div = %e' % sqrt(assemble(div_u1 * div_u1 * dx)))
            #plot(div_u)
            #interactive()

            ## Ha! TODO remove
            #u1.vector()[:] = ui.vector()
        return
Пример #14
0
def heston_transform(parameters,
                     get_error=False,
                     file_path=None,
                     plot_deltas=False):
    experiment = parameters.experiment
    domain_name = parameters.domain
    mesh_name = parameters.mesh

    mesh = Mesh()
    mesh_path = parameters.get_mesh_path()
    with XDMFFile(mesh_path) as f:
        f.read(mesh)

    # Create functions in function spaces defined by both original and
    # transformed meshes and copy value function data from one to another
    t_mesh_path = f'meshes/{domain_name}/t_{domain_name}_{mesh_name}.xdmf'
    if not os.path.exists(t_mesh_path):
        mesh2 = get_original_mesh(t_mesh_path, mesh, parameters)
    else:
        mesh2 = Mesh()
        with XDMFFile(t_mesh_path) as f:
            f.read(mesh2)

    V = FunctionSpace(mesh, 'CG', 1)
    t_V = FunctionSpace(mesh2, 'CG', 1)
    value = Function(V)
    control = Function(V)
    t_value = Function(t_V)
    t_control = Function(t_V)
    if not file_path:
        f = XDMFFile(f'out/{experiment}/{domain_name}/{mesh_name}/v.xdmf')
        t_f = XDMFFile(f'out/{experiment}/{domain_name}/{mesh_name}/t_v.xdmf')
    else:
        f = XDMFFile(file_path)
        path, file_name = os.path.split(file_path)
        t_f = XDMFFile(os.path.join(path, f't_{file_name}'))
    # Save Final Time Condition
    f.read_checkpoint(value, 'value_func', 0)
    t_value.vector()[:] = value.vector()[:]
    t_f.write_checkpoint(t_value, 'value_func', parameters.T)

    # Iterate through remaining timesteps (only some of them are
    #  saved to the file)
    i = 1
    parameters.calculate_save_interval()
    dt = parameters.T / parameters.M
    for k in range(parameters.M - 1, -1, -1):
        if k % parameters.save_interval != 0:
            continue
        f.read_checkpoint(value, 'value_func', i)
        f.read_checkpoint(control, 'control', i - 1)
        t_value.vector()[:] = value.vector()[:]
        t_control.vector()[:] = control.vector()[:]
        t_f.write_checkpoint(t_value, 'value_func', k * dt,
                             XDMFFile.Encoding.HDF5, True)
        t_f.write_checkpoint(t_control, 'control', k * dt,
                             XDMFFile.Encoding.HDF5, True)
        if plot_deltas:
            delta_S = project(t_value.dx(0), t_V)
            t_f.write_checkpoint(delta_S, 'delta_S', k * dt,
                                 XDMFFile.Encoding.HDF5, True)
            delta_v = project(t_value.dx(1), t_V)
            t_f.write_checkpoint(delta_v, 'delta_v', k * dt,
                                 XDMFFile.Encoding.HDF5, True)
        i += 1

        if k == 0 and get_error:
            error_calc(t_value, parameters.t_v_e, mesh2, f't_{mesh_name}',
                       f'out/{experiment}/{domain_name}/errors.json')
Пример #15
0
def cmscr1d_img(img: np.array,
                alpha0: float, alpha1: float,
                alpha2: float, alpha3: float,
                beta: float, deriv, mesh=None, bc='natural') \
                -> (np.array, np.array, float, float, bool):
    """Computes the L2-H1 mass conserving flow with source for a 1D image
    sequence with spatio-temporal and convective regularisation.

    Allows to specify how to approximate partial derivatives of f numerically.

    Args:
        img (np.array): 1D image sequence of shape (m, n), where m is the
                        number of time steps and n is the number of pixels.
        alpha0 (float): The spatial regularisation parameter for v.
        alpha1 (float): The temporal regularisation parameter for v.
        alpha2 (float): The spatial regularisation parameter for k.
        alpha3 (float): The temporal regularisation parameter for k.
        beta (float): The convective regularisation parameter.
        deriv (str): Specifies how to approximate pertial derivatives.
                     When set to 'mesh' it uses FEniCS built in function.
                     When set to 'fd' it uses finite differences.
        mesh: A custom mesh (optional). Must have (m - 1, n - 1) cells.
        bc (str): One of {'natural', 'zero', 'zerospace'} for boundary
                    conditions for the velocity v (optional).

    Returns:
        v (np.array): A velocity array of shape (m, n).
        k (np.array): A source array of shape (m, n).
        res (float): The residual.
        fun (float): The function value.
        converged (bool): True if Newton's method converged.

    """
    # Check for valid arguments.
    valid = {'mesh', 'fd'}
    if deriv not in valid:
        raise ValueError("Argument 'deriv' must be one of %r." % valid)

    # Create mesh.
    m, n = img.shape
    if mesh is None:
        mesh = UnitSquareMesh(m - 1, n - 1)

    # Define function spaces.
    V = dh.create_function_space(mesh, 'default')
    W = dh.create_vector_function_space(mesh, 'default')

    # Convert array to function.
    f = Function(V)
    f.vector()[:] = dh.img2funvec(img)

    # Compute partial derivatives.
    if deriv is 'mesh':
        ft, fx = f.dx(0), f.dx(1)
    if deriv is 'fd':
        imgt, imgx = nh.partial_derivatives(img)
        ft, fx = Function(V), Function(V)
        ft.vector()[:] = dh.img2funvec(imgt)
        fx.vector()[:] = dh.img2funvec(imgx)

    # Check for valid arguments.
    valid = {'natural', 'zero', 'zerospace'}
    if bc not in valid:
        raise ValueError("Argument 'bc' must be one of %r." % valid)

    # Define boundary conditions for velocity.
    if bc is 'natural':
        bc = []
    if bc is 'zero':
        bc = DirichletBC(W.sub(0), Constant(0), dh.DirichletBoundary())
    if bc is 'zerospace':
        bc = DirichletBC(W.sub(0), Constant(0), dh.DirichletBoundarySpace())

    # Compute velocity.
    v, k, res, fun, converged = cmscr1d_weak_solution(W,
                                                      f,
                                                      ft,
                                                      fx,
                                                      alpha0,
                                                      alpha1,
                                                      alpha2,
                                                      alpha3,
                                                      beta,
                                                      bcs=bc)

    # Convert back to array and return.
    v = dh.funvec2img(v.vector().get_local(), m, n)
    k = dh.funvec2img(k.vector().get_local(), m, n)
    return v, k, res, fun, converged
Пример #16
0
class TVPD():
    """ Total variation using primal-dual Newton """
    def __init__(self, parameters):
        """ 
        TV regularization in primal-dual format
        Input parameters:
            * k = regularization parameter
            * eps = regularization constant (see above)
            * rescaledradiusdual = radius of dual set
            * exact = use full TV (bool)
            * PCGN = use GN Hessian to precondition (bool); 
            not recommended for performance but can help avoid num.instability
            * print (bool)
        """

        self.parameters = {}
        self.parameters['k'] = 1.0
        self.parameters['eps'] = 1e-2
        self.parameters['rescaledradiusdual'] = 1.0
        self.parameters['exact'] = False
        self.parameters['PCGN'] = False
        self.parameters['print'] = False
        self.parameters['correctcost'] = True
        self.parameters['amg'] = 'default'

        assert parameters.has_key('Vm')
        self.parameters.update(parameters)
        self.Vm = self.parameters['Vm']
        k = self.parameters['k']
        eps = self.parameters['eps']
        exact = self.parameters['exact']
        amg = self.parameters['amg']

        self.m = Function(self.Vm)
        testm = TestFunction(self.Vm)
        trialm = TrialFunction(self.Vm)

        # WARNING: should not be changed.
        # As it is, code only works with DG0
        if self.parameters.has_key('Vw'):
            Vw = self.parameters['Vw']
        else:
            Vw = FunctionSpace(self.Vm.mesh(), 'DG', 0)
        self.wx = Function(Vw)
        self.wxrs = Function(Vw)  # re-scaled dual variable
        self.wxhat = Function(Vw)
        self.gwx = Function(Vw)
        self.wy = Function(Vw)
        self.wyrs = Function(Vw)  # re-scaled dual variable
        self.wyhat = Function(Vw)
        self.gwy = Function(Vw)
        self.wxsq = Vector()
        self.wysq = Vector()
        self.normw = Vector()
        self.factorw = Vector()
        testw = TestFunction(Vw)
        trialw = TrialFunction(Vw)

        normm = inner(nabla_grad(self.m), nabla_grad(self.m))
        TVnormsq = normm + Constant(eps)
        TVnorm = sqrt(TVnormsq)
        if self.parameters['correctcost']:
            meshtmp = UnitSquareMesh(self.Vm.mesh().mpi_comm(), 10, 10)
            Vtmp = FunctionSpace(meshtmp, 'CG', 1)
            x = SpatialCoordinate(meshtmp)
            correctioncost = 1. / assemble(sqrt(4.0 * x[0] * x[0]) * dx)
        else:
            correctioncost = 1.0
        self.wkformcost = Constant(k * correctioncost) * TVnorm * dx
        if exact:
            sys.exit(1)
#            self.w = nabla_grad(self.m)/TVnorm # full Hessian
#            self.Htvw = inner(Constant(k) * nabla_grad(testm), self.w) * dx

        self.misfitwx = inner(testw, self.wx * TVnorm - self.m.dx(0)) * dx
        self.misfitwy = inner(testw, self.wy * TVnorm - self.m.dx(1)) * dx

        self.Htvx = assemble(inner(Constant(k) * testm.dx(0), trialw) * dx)
        self.Htvy = assemble(inner(Constant(k) * testm.dx(1), trialw) * dx)

        self.massw = inner(TVnorm * testw, trialw) * dx

        mpicomm = self.Vm.mesh().mpi_comm()
        invMwMat, VDM, VDM = setupPETScmatrix(Vw, Vw, 'aij', mpicomm)
        for ii in VDM.dofs():
            invMwMat[ii, ii] = 1.0
        invMwMat.assemblyBegin()
        invMwMat.assemblyEnd()
        self.invMwMat = PETScMatrix(invMwMat)
        self.invMwd = Vector()
        self.invMwMat.init_vector(self.invMwd, 0)
        self.invMwMat.init_vector(self.wxsq, 0)
        self.invMwMat.init_vector(self.wysq, 0)
        self.invMwMat.init_vector(self.normw, 0)
        self.invMwMat.init_vector(self.factorw, 0)

        u = Function(Vw)
        uflrank = len(u.ufl_shape)
        if uflrank == 0:
            ones = ("1.0")
        elif uflrank == 1:
            ones = (("1.0", "1.0"))
        else:
            sys.exit(1)
        u = interpolate(Constant(ones), Vw)
        self.one = u.vector()

        self.wkformAx = inner(testw, trialm.dx(0) - \
        self.wx * inner(nabla_grad(self.m), nabla_grad(trialm)) / TVnorm) * dx
        self.wkformAxrs = inner(testw, trialm.dx(0) - \
        self.wxrs * inner(nabla_grad(self.m), nabla_grad(trialm)) / TVnorm) * dx
        self.wkformAy = inner(testw, trialm.dx(1) - \
        self.wy * inner(nabla_grad(self.m), nabla_grad(trialm)) / TVnorm) * dx
        self.wkformAyrs = inner(testw, trialm.dx(1) - \
        self.wyrs * inner(nabla_grad(self.m), nabla_grad(trialm)) / TVnorm) * dx

        kovsq = Constant(k) / TVnorm
        self.wkformGNhess = kovsq * inner(nabla_grad(trialm),
                                          nabla_grad(testm)) * dx
        factM = 1e-2 * k
        M = assemble(inner(testm, trialm) * dx)
        self.sMass = M * factM

        self.Msolver = PETScKrylovSolver('cg', 'jacobi')
        self.Msolver.parameters["maximum_iterations"] = 2000
        self.Msolver.parameters["relative_tolerance"] = 1e-24
        self.Msolver.parameters["absolute_tolerance"] = 1e-24
        self.Msolver.parameters["error_on_nonconvergence"] = True
        self.Msolver.parameters["nonzero_initial_guess"] = False
        self.Msolver.set_operator(M)

        if amg == 'default': self.amgprecond = amg_solver()
        else: self.amgprecond = amg

        if self.parameters['print']:
            print '[TVPD] TV regularization -- primal-dual method',
            if self.parameters['PCGN']:
                print ' -- PCGN',
            print ' -- k={}, eps={}'.format(k, eps)
            print '[TVPD] preconditioner = {}'.format(self.amgprecond)
            print '[TVPD] correction cost with factor={}'.format(
                correctioncost)

    def isTV(self):
        return True

    def isPD(self):
        return True

    def cost(self, m):
        """ evaluate the cost functional at m """
        setfct(self.m, m)
        return assemble(self.wkformcost)

    def costvect(self, m_in):
        return self.cost(m_in)

    def _assemble_invMw(self):
        """ Assemble inverse of matrix Mw,
        weighted mass matrix in dual space """
        # WARNING: only works if Mw is diagonal (e.g, DG0)
        Mw = assemble(self.massw)
        Mwd = get_diagonal(Mw)
        as_backend_type(self.invMwd).vec().pointwiseDivide(\
            as_backend_type(self.one).vec(),\
            as_backend_type(Mwd).vec())
        self.invMwMat.set_diagonal(self.invMwd)

    def grad(self, m):
        """ compute the gradient at m """
        setfct(self.m, m)
        self._assemble_invMw()

        self.gwx.vector().zero()
        self.gwx.vector().axpy(1.0, assemble(self.misfitwx))
        normgwx = norm(self.gwx.vector())

        self.gwy.vector().zero()
        self.gwy.vector().axpy(1.0, assemble(self.misfitwy))
        normgwy = norm(self.gwy.vector())

        if self.parameters['print']:
            print '[TVPD] |gw|={}'.format(np.sqrt(normgwx**2 + normgwy**2))

        return self.Htvx*(self.wx.vector() - self.invMwd*self.gwx.vector()) \
        + self.Htvy*(self.wy.vector() - self.invMwd*self.gwy.vector())
        #return assemble(self.Htvw) - self.Htv*(self.invMwd*self.gw.vector())

    def gradvect(self, m_in):
        return self.grad(m_in)

    def assemble_hessian(self, m):
        """ build Hessian matrix at given point m """
        setfct(self.m, m)
        self._assemble_invMw()

        self.Ax = assemble(self.wkformAx)
        Hxasym = MatMatMult(self.Htvx, MatMatMult(self.invMwMat, self.Ax))
        Hx = (Hxasym + Transpose(Hxasym)) * 0.5
        Axrs = assemble(self.wkformAxrs)
        Hxrsasym = MatMatMult(self.Htvx, MatMatMult(self.invMwMat, Axrs))
        Hxrs = (Hxrsasym + Transpose(Hxrsasym)) * 0.5

        self.Ay = assemble(self.wkformAy)
        Hyasym = MatMatMult(self.Htvy, MatMatMult(self.invMwMat, self.Ay))
        Hy = (Hyasym + Transpose(Hyasym)) * 0.5
        Ayrs = assemble(self.wkformAyrs)
        Hyrsasym = MatMatMult(self.Htvy, MatMatMult(self.invMwMat, Ayrs))
        Hyrs = (Hyrsasym + Transpose(Hyrsasym)) * 0.5

        self.H = Hx + Hy
        self.Hrs = Hxrs + Hyrs

        PCGN = self.parameters['PCGN']
        if PCGN:
            HGN = assemble(self.wkformGNhess)
            self.precond = HGN + self.sMass
        else:
            self.precond = self.Hrs + self.sMass

    def hessian(self, mhat):
        return self.Hrs * mhat

    def compute_what(self, mhat):
        """ Compute update direction for what, given mhat """
        self.wxhat.vector().zero()
        self.wxhat.vector().axpy(
            1.0, self.invMwd * (self.Ax * mhat - self.gwx.vector()))
        normwxhat = norm(self.wxhat.vector())

        self.wyhat.vector().zero()
        self.wyhat.vector().axpy(
            1.0, self.invMwd * (self.Ay * mhat - self.gwy.vector()))
        normwyhat = norm(self.wyhat.vector())

        if self.parameters['print']:
            print '[TVPD] |what|={}'.format(
                np.sqrt(normwxhat**2 + normwyhat**2))

    def update_w(self, mhat, alphaLS, compute_what=True):
        """ update dual variable in direction what 
        and update re-scaled version """
        if compute_what: self.compute_what(mhat)
        self.wx.vector().axpy(alphaLS, self.wxhat.vector())
        self.wy.vector().axpy(alphaLS, self.wyhat.vector())

        # rescaledradiusdual=1.0: checked empirically to be max radius acceptable
        rescaledradiusdual = self.parameters['rescaledradiusdual']
        # wx**2
        as_backend_type(self.wxsq).vec().pointwiseMult(\
            as_backend_type(self.wx.vector()).vec(),\
            as_backend_type(self.wx.vector()).vec())
        # wy**2
        as_backend_type(self.wysq).vec().pointwiseMult(\
            as_backend_type(self.wy.vector()).vec(),\
            as_backend_type(self.wy.vector()).vec())
        # |w|
        self.normw = self.wxsq + self.wysq
        as_backend_type(self.normw).vec().sqrtabs()
        # |w|/r
        as_backend_type(self.normw).vec().pointwiseDivide(\
            as_backend_type(self.normw).vec(),\
            as_backend_type(self.one*rescaledradiusdual).vec())
        # max(1.0, |w|/r)
        #        as_backend_type(self.factorw).vec().pointwiseMax(\
        #            as_backend_type(self.one).vec(),\
        #            as_backend_type(self.normw).vec())
        count = pointwiseMaxCount(self.factorw, self.normw, 1.0)
        # rescale wx and wy
        as_backend_type(self.wxrs.vector()).vec().pointwiseDivide(\
            as_backend_type(self.wx.vector()).vec(),\
            as_backend_type(self.factorw).vec())
        as_backend_type(self.wyrs.vector()).vec().pointwiseDivide(\
            as_backend_type(self.wy.vector()).vec(),\
            as_backend_type(self.factorw).vec())

        minf = self.factorw.min()
        maxf = self.factorw.max()
        if self.parameters['print']:
            #            print 'min(factorw)={}, max(factorw)={}'.format(minf, maxf)
            print '[TVPD] perc. dual entries rescaled={:.2f} %, min(factorw)={}, max(factorw)={}'.format(\
            100.*float(count)/self.factorw.size(), minf, maxf)

    def getprecond(self):
        """ Precondition by inverting the TV Hessian """

        solver = PETScKrylovSolver('cg', self.amgprecond)
        solver.parameters["maximum_iterations"] = 2000
        solver.parameters["relative_tolerance"] = 1e-24
        solver.parameters["absolute_tolerance"] = 1e-24
        solver.parameters["error_on_nonconvergence"] = True
        solver.parameters["nonzero_initial_guess"] = False

        # used to compare iterative application of preconditioner
        # with exact application of preconditioner:
        #solver = PETScLUSolver("petsc")
        #solver.parameters['symmetric'] = True
        #solver.parameters['reuse_factorization'] = True

        solver.set_operator(self.precond)

        return solver

    def init_vector(self, u, dim):
        self.sMass.init_vector(u, dim)
Пример #17
0
ds = Measure("ds", subdomain_data=boundary_parts)

# Define forms (dont redefine functions used here)
# step 1
u0 = Function(V)
u1 = Function(V)
p0 = Function(Q)
u_tent = TrialFunction(V)
v = TestFunction(V)
# U_ = 1.5*u0 - 0.5*u1
# nonlinearity = inner(dot(0.5 * (u_tent.dx(0) + u0.dx(0)), U_), v) * dx
# F_tent = (1./dt)*inner(u_tent - u0, v) * dx + nonlinearity\
#     + nu*inner(0.5 * (u_tent.dx(0) + u0.dx(0)), v.dx(0)) * dx + inner(p0.dx(0), v) * dx\
#     - inner(f, v)*dx     # solve to u_
# using explicite scheme: so LHS has interpretation as heat equation, RHS are sources
F_tent = (1./dt)*inner(u_tent - u0, v)*dx + inner(dot(u0.dx(0), u0), v)*dx + nu*inner((u_tent.dx(0) + u0.dx(0)), v.dx(0)) * dx + inner(p0.dx(0), v) * dx\
    - inner(f, v)*dx     # solve to u_
a_tent, L_tent = system(F_tent)
# step 2
u_tent_computed = Function(V)
p = TrialFunction(Q)
q = TestFunction(Q)
F_p = inner(grad(p-p0), grad(q))*dx + (1./dt)*u_tent_computed.dx(0)*q*dx # + 2*p.dx(0)*q*ds(1) # tried to force dp/dn=0 on inflow
# TEST: prescribe Neumann outflow BC
# F_p = inner(grad(p-p0), grad(q))*dx + (1./dt)*u_tent_computed.dx(0)*q*dx + (1./dt)*(v_in_expr-u_tent_computed)*q*ds(2)
a_p, L_p = system(F_p)
A_p = assemble(a_p)
as_backend_type(A_p).set_nullspace(null_space)
print(A_p.array())
# step 2 rotation
# F_p_rot = inner(grad(p), grad(q))*dx + (1./dt)*u_tent_computed.dx(0)*q*dx + (1./dt)*(v_in_expr-u_tent_computed)*q*ds(2)
Пример #18
0
ds = Measure("ds", subdomain_data=boundary_parts)

# Define forms (dont redefine functions used here)
# step 1
u0 = Function(V)
u1 = Function(V)
p0 = Function(Q)
u_tent = TrialFunction(V)
v = TestFunction(V)
# U_ = 1.5*u0 - 0.5*u1
# nonlinearity = inner(dot(0.5 * (u_tent.dx(0) + u0.dx(0)), U_), v) * dx
# F_tent = (1./dt)*inner(u_tent - u0, v) * dx + nonlinearity\
#     + nu*inner(0.5 * (u_tent.dx(0) + u0.dx(0)), v.dx(0)) * dx + inner(p0.dx(0), v) * dx\
#     - inner(f, v)*dx     # solve to u_
# using explicite scheme: so LHS has interpretation as heat equation, RHS are sources
F_tent = (1./dt)*inner(u_tent - u0, v)*dx + inner(dot(u0.dx(0), u0), v)*dx + nu*inner((u_tent.dx(0) + u0.dx(0)), v.dx(0)) * dx + inner(p0.dx(0), v) * dx\
    - inner(f, v)*dx     # solve to u_
a_tent, L_tent = system(F_tent)
# step 2
u_tent_computed = Function(V)
p = TrialFunction(Q)
q = TestFunction(Q)
F_p = inner(grad(p - p0), grad(q)) * dx + (1. / dt) * u_tent_computed.dx(
    0) * q * dx  # + 2*p.dx(0)*q*ds(1) # tried to force dp/dn=0 on inflow
# TEST: prescribe Neumann outflow BC
# F_p = inner(grad(p-p0), grad(q))*dx + (1./dt)*u_tent_computed.dx(0)*q*dx + (1./dt)*(v_in_expr-u_tent_computed)*q*ds(2)
a_p, L_p = system(F_p)
A_p = assemble(a_p)
as_backend_type(A_p).set_nullspace(null_space)
print(A_p.array())
# step 2 rotation