Confusion about mixed spaces functions

I am a bit confused, I was successful in making my code returns what I think it should, but I don’t understand how/why.

At some point I do:

# Define ME function space
el = FiniteElement("CG", mesh.ufl_cell(), 1)
mel = MixedElement([el, el])
ME = FunctionSpace(mesh, mel)

u, v = TestFunction(ME)
TempVolt = Function(ME)
temp, volt = split(TempVolt)

And then I involve “temp” and “volt” in the weak form, i.e. I solve for temp and volt (I get the temperature distribution as well as the voltage one). So far so good.

However, I cannot write these solutions into files as I used to:

with io.XDMFFile(mesh.comm, "solution/voltage.xdmf", "w") as xdmf:
    xdmf.write_mesh(mesh)
    xdmf.write_function(volt)

would fail returning an error, complaining about the type of functions xdmf.write() can accept.

I fixed my problem by replacing “volt” by with “TempVolt.sub(1)”, and it seems to work well. But I do not understand why this works. As far as I understand, I solve for temp and volt. And I always used to save these solutions in files. Why would I need to invoke “TempVolt.sub(1)”? Why am I not solving for “TempVolt.sub(1)” in the weak form?

This is a common cause of confusion in DOLFIN: fenics-project / DOLFIN / issues / #194 - split(u) versus u.split() — Bitbucket.

split(TempVol) is a ufl-function

In [1]: import dolfin
dolfinx.
In [2]: dolfin.split??

In [3]: dolfin.split?
Signature: dolfin.split(v)
Docstring:
UFL operator: If v is a Coefficient or Argument in a mixed space, returns
a tuple with the function components corresponding to the subelements.
File:      /usr/lib/python3/dist-packages/ufl/split_functions.py
Type:      function

This is to be used in variational forms.

dolfin.split

In [4]: dolfin.Function.split?
Signature: dolfin.Function.split(self, deepcopy=False)
Docstring:
Extract any sub functions.

A sub function can be extracted from a discrete function that
is in a mixed, vector, or tensor FunctionSpace. The sub
function resides in the subspace of the mixed space.

*Arguments*
    deepcopy
        Copy sub function vector instead of sharing
File:      /usr/lib/python3/dist-packages/dolfin/function/function.py
Type:      function

which has the source code:

       num_sub_spaces = self.num_sub_spaces()
        if num_sub_spaces == 1:
            raise RuntimeError("No subfunctions to extract")
        return tuple(self.sub(i, deepcopy) for i in range(num_sub_spaces))

which in turn calls:

In [5]: dolfin.Function.sub?
Signature: dolfin.Function.sub(self, i, deepcopy=False)
Docstring:
Return a sub function.

The sub functions are numbered from i = 0..N-1, where N is the
total number of sub spaces.

*Arguments*
    i : int
        The number of the sub function
File:      /usr/lib/python3/dist-packages/dolfin/function/function.py
Type:      function
1 Like

Hmm so this means I could use

TempVolt = Function(ME)
temp, volt = TempVolt.split()

and use those (temp and volt) in the weak form, as well as saving them into a file with xdmf?

What I would do is:

  1. use the following in your variational forms
TempVolt = dolfinx.fem.Function(ME)
temp, volt = ufl.split(TempVolt)

Use the following in outputting

TempVolt.sub(0)
TempVolt.sub(1)

for each of the components.

2 Likes

Thanks a lot dokken, so that’s exactly what I am doing.

Another thing, I noticed I had to invoke “TempVolt.sub(1)” to compute the electric field. “volt” wouldn’t work.
The code is

from dolfinx.fem import VectorFunctionSpace, Expression
W = VectorFunctionSpace(mesh, ("DG", 0))
E_field = Function(W)
E_field_expr = Expression(-grad(TempVolt.sub(1)), W.element.interpolation_points())