As a follow up to Computing fluxes accurately, where I created a MWE to compute a flux and eventually reached a nice result using the method described by Jeremy Bleyer (Computing consistent reaction forces — Computational Mechanics Numerical Tours with FEniCSx), I face several problems or questions when I apply this method in my real problem.
It is a mixed element problem where I solve for two scalar fields, T and V.
I build up a vector field \vec J=-\sigma \nabla V -\sigma S \nabla T that involves the 2 unknown scalar fields, and I write the weak form of 2 coupled PDEs, the PDEs being \nabla \cdot \vec J =0 and \nabla \cdot (\kappa \nabla T)+\vec J^2/\sigma-T(S_{xx}-S_{yy})\frac{\partial J_x}{\partial x}=0.
I am able to solve for V and T, but then I need to compute fluxes, several kinds of fluxes, on each boundary of the domain. And this is where I have several problems. For example, computing the heat flux through each boundary does not yield very accurate results, where I expect 0, I can get things that differ significantly from 0. And it’s not just fluxes, but also when I integrate the divergence of those fluxes in the whole volume, I do not exactly recover Gauss’s law, i.e. a volume integral of the divergence of a field can be significantly different than the surface integrated flux of that field, which is very bad.
I have several questions.
If I use Jeremy’s method on a boundary, which flux is computed exactly in my problem? In my MWE, it was obvious it was the heat flux, but here, I have a ME space, I have no idea which flux is being computed with this code:
(...)
# Define ME function space
deg = 4
el = FiniteElement("CG", mesh.ufl_cell(), deg)
mel = MixedElement([el, el])
ME = FunctionSpace(mesh, mel)
u, v = TestFunction(ME)
TempVolt = Function(ME)
temp, volt = split(TempVolt)
# Weak form.
J_vector = -sigma * grad(volt) - sigma * Seebeck_tensor * grad(temp)
F_T = dot(-κ * grad(temp), grad(u)) * dx + dot(rho * J_vector, J_vector) * u * dx - temp * (s_xx * J_vector[0].dx(0) + s_yy * J_vector[1].dx(1)) * u * dx
F_V = -dot(grad(v), sigma * grad(volt))*dx -dot(grad(v), sigma * Seebeck_tensor * grad(temp))*dx - v * J_out * ds(out_current_curve) + v * J_inj * ds(inj_current_curve)
weak_form = F_T + F_V
(...)
# Compute heat flux on boundaries more accurately than usual method.
u_bc = fem.Function(ME)
bcs_right = [fem.dirichletbc(u_bc, right_dofs_temp)]
virtual_temp_right = Function(ME)
u_bc.sub(0).interpolate(one)
fem.set_bc(virtual_temp_right.vector, bcs_right)
virtual_heat_flux_form_right = form(action(weak_form, virtual_temp_right))
virtual_heat_flux_right = assemble_scalar(virtual_heat_flux_form_right)
print(f'Virtual flux right computation: {virtual_flux_right}')
The above code returns a number very close to what I obtain by the standard way to compute fluxes, except that I do not know which flux is computed.
Since the heat equation I am solving is in fact worth \nabla \cdot \vec J_U=0 where \vec J_U=-\kappa \nabla T +(ST+V)\vec J is the internal energy flux, I suspect Jeremy’s method gives me the internal energy flux over the boundary I specify. But it’s not clear at all, to me. The “sub(0)” part annoys me somehow.
What happens for example if I do:
u_bc.sub(1).interpolate(one)
either in addition to u_bc.sub(0).interpolate(one)
or on its own?
Ideally, I wish to compute with utmost accuracy the \vec J_U flux across each boundary. And if possible, also \vec J. The usual method is not accurate enough, even when I use a space of degree 7 (Jeremy’s method converges much better than the usual method, but I do not know how to compute a particular flux with it).
My MWE would be around 300 lines of code. I don’t mind sharing the code, but maybe Bitbucket would be more appropriate, if there is any need to share the full code…