Hi,
I need to implement a long-range coupling between disconnected subdomains, that represent springs between them. My problem is similar to this simple one:
\Omega = \Omega_1 \cup \Omega_2,
\int_\Omega \nabla u : \nabla \delta u = \int_\Omega \delta u q,
with the spring force q(x) = -k(u(x) - u(T(x))),
k a parameter,
and T(x) a mapping from \Omega_1 to \Omega_2 and from \Omega_2 to \Omega_1.
The way I am solving it now and I’d like to improve involves working with an extended function space that contains copies of all subdomain fields in each subdomain. Then, I use dolfinx_mpc to express that all these are copies, and I solve the problem involving only the master dofs.
So, for the above equations, that would be:
elt = basix.ufl.element(...)
N = 2
# V = fem.functionspace(domain, elt) # that would be the "natural" function space
V_big = fem.functionspace(domain, basix.ufl.mixed_element([elt] * N)) # here each subdomain is copied on all other subdomains
u = ufl.TrialFunctions(V_big) # len(...) = N -> u_n=u[n]
du = ufl.TestFunctions(V_big)
# K = ufl.inner(ufl.grad(u), ufl.grad(du)) * dx # if dealing with V
K = ufl.inner(ufl.grad(u[0]), ufl.grad(du[0])) * dx(0) + ufl.inner(ufl.grad(u[1]), ufl.grad(du[1])) * dx(1)
# long-range coupling
q0 = -k * (u[0] - u[1])
q1 = -k * (u[1] - u[0])
W_ext = ufl.inner(q0, du[0]) * dx(0) + ufl.inner(q1, du[1]) * dx(1)
K_total = K - W_ext
and then comes a dolfinx_mpc.MultiPointConstraint(V_big) to express that u[0] on tag 1 is mapped to u[0] on tag 0 through a given mapping T, and similar for u[1].
Now, this works, but I don’t find it very elegant so I am wondering whether there could be a better way. Drawbacks: the slave dofs grow as N^2 for N subdomains; the “classical” part of the equation (here K) does not express very nicely; and mostly I am forced to treat this as a multiphysics problem if there are other subdomains that are not long-range coupled.
I think ideally I would like to work with two function spaces simultaneously, and define and mapping between them. Say, something like:
V = fem.functionspace(domain, elt)
u = ufl.TrialFunction(V)
du = ufl.TestFunction(V)
K = ufl.inner(ufl.grad(u), ufl.grad(du)) * dx
# long-range coupling
fictitious_subdomain = ...
V_not_bigger = fem.functionspace(fictitious_subdomain, basix.ufl.mixed_element([elt] * N))
u_f = ufl.TrialFunctions(V)
du_f = ufl.TestFunctions(V)
q0 = -k * (u_f[0] - u_f[1])
q1 = -k * (u_f[1] - u_f[0])
W_ext = ufl.inner(q0, du_f[0]) * dx + ufl.inner(q1, du_f[1]) * dx
# and then a way to define the mapping T(x) between u{tag n} and u_f[n]
Is this doable?