Dear all,
I'd like to speed up the assembly of the "fully coupled" linearized
Navier-Stokes equations discretized with inf-sup stable mixed elements (e.g., P2P1).
For a linearization of type $\vec u^n\cdot\nabla\vec u^{n+1}$ the unknown velocity
components $u^{n+1}_i$ are decoupled. It is sufficient to assemble the
convection term for just one velocity component and then build the complete
matrix by copying the matrix of the scalar into the corresponding blocks.
This is explained in the FEniCS book on p. 428.
I spent some time trying to do this, an "easy way" did not occurr to me.
My naive approach was:
- build the scalar convection matrix
Ac
- extract the values and indices with
(indptr, col, val) =
Ac.getValuesCSR()
- shift the
indptr
and the column index col
in order to match the DOFs of velocity components in the
mixed function space, i.e., W.sub(0).sub(i).dofmap().dofs()
$\forall i=1,...,d$
- write the extracted values into the complete matrix (suppose the full matrix
was assembled once in the beginning) with
A.setValuesCSR(indptr_shift, col_shift, val)
However, there are several problems.
- DOF ordering: not trivial without
parameters["reorder_dofs_serial"] =
False
since the ordering is not the same for all velocity components
- In parallel this approach won't work, at least not without fiddling with
MPI, because the matrices are distributed across the processes...
Does anyone know of a way to implement this? I'd be very glad about any hints.
I wonder if there are other DOLFIN or PETSc functions that would make this
easier..
Thanks!
David
edit: an example to make it clearer:
from dolfin import *
N = 16
mesh = UnitCubeMesh(N, N, N)
P1 = FiniteElement('P', mesh.ufl_cell(), 1)
P2 = VectorElement('P', mesh.ufl_cell(), 2)
W = FunctionSpace(mesh, P2*P1)
bc = DirichletBC(W.sub(0), Constant([0]*3), "on_boundary")
u0 = project(Constant([1]*3), W.sub(0).collapse(), solver_type='cg')
# define trial and test functions
(u, p) = TrialFunctions(W)
(v, q) = TestFunctions(W)
# Build complete convection matrix for u = (u1, u2, u3)
a = dot(grad(u)*u0, v)*dx
K = PETScMatrix()
assemble(a, tensor=K)
# Build convection matrix for just one component "uc"
Vc = FunctionSpace(mesh, FiniteElement('P', mesh.ufl_cell(), 2))
uc = TrialFunction(Vc)
vc = TestFunction(Vc)
ac = dot(u0, nabla_grad(uc))*vc*dx
C = PETScMatrix()
assemble(ac, tensor=C)
# In theory, K = diag((C, C, C))
(ai, aj, av) = C.mat().getValuesCSR()
for i in range(3):
iset = W.sub(0).sub(i).dofmap().dofs()
# some computations to determine what ai, aj mean with respect to the index
# set of the i'th velocity sub space
...
K.mat().setValuesCSR(ai_new, ai_new, av)
K.mat().assemble()