Proper Approach for Time-dependent Bilinear and Linear Forms in FEniCSx

Hello FEniCSx Community,

I am working on a time-dependent problem where both the bilinear and linear forms vary with time. Based on the dynamics of my problem, I understand that I should create matrix and vector outside the time loop but update and reassemble them within each time step due to their time-dependent nature.

Based on this example https://jsdokken.com/dolfinx-tutorial/chapter2/ns_code2.html, it seems that I have successfully executed my code. However, the following code is what I am confused about:

......
A1 = create_matrix(a1)
b1 = create_vector(L1)
solver1 = PETSc.KSP().create(mesh.comm)
solver1.setOperators(A1)
solver1.setType(PETSc.KSP.Type.GMRES)
for i in range(num_steps):
    A1.zeroEntries()
    assemble_matrix(A1, a1)
    A1.assemble()
    with b1.localForm() as loc:
        loc.set(0)
    assemble_vector(b1, L1)
    b1.ghostUpdate(addv=PETSc.InsertMode.ADD_VALUES, mode=PETSc.ScatterMode.REVERSE)
    solver1.solve(b1, u_s.vector)
    u_s.x.scatter_forward()

Assuming a1, L1 and some others are already defined, with boundary conditions being Neumann zero conditions, is every line of code in the time loop necessary? Especially in this line,

b1.ghostUpdate(addv=PETSc.InsertMode.ADD_VALUES, mode=PETSc.ScatterMode.REVERSE)

if I don’t applly Dirichlet boundary conditions, I need not to use apply_lifting(), but it seems like b1.ghostUpdate() must be added, otherwise the result will be incorrect.

Overall, my confusion lies in what are the necessary and unnecessary function calls for manually assembling PETSc problem.

If you have to re-assemble A at every time-step, you could just define a dolfinx.fem.petsc.LinearProblem outside the loop and use that.
I would only split into manual PETSc steps if I wanted to avoid such calls (say that you have a time-independent matrix, but a time dependent RHS).

The ghostUpdate reverse with addition is needed to accumulate contributions from all dofs when assembling the local array. It is similar to what happens within A1.assemble().

Sorry for not describing it completely. I need to update matrix that changes over time, but also need to use matrix constructed in variational form for some other calculations. I’m not sure, is there any way to obtain the compiled matrix if LinearProblem() is used.

There’s one more question:
In the absence of any Dirichlet boundary conditions, is it appropriate to entirely omit apply_lifting() and set_bc() from the assembly and solving process?

You can access it as an attribute, i.e. problem.A: dolfinx.fem.petsc — DOLFINx 0.8.0 documentation

Yes, for any standard variational problem, apply_lifting and set_bc is exclusively used for assigning Dirichlet boundary conditions.