Skip to content

Support list of DG-0 + other space in VTXWriter - #3375

Merged
jorgensd merged 11 commits into
FEniCS:mainfrom
ordinary-slim:vtx-cg1+dg0
Sep 25, 2025
Merged

Support list of DG-0 + other space in VTXWriter#3375
jorgensd merged 11 commits into
FEniCS:mainfrom
ordinary-slim:vtx-cg1+dg0

Conversation

@ordinary-slim

Copy link
Copy Markdown
Contributor

Continuation of #3107 so that we're able to output both DG0 + another space in the same bp file. The previous PR by @jorgensd says:

I've decided to keep it such that only single function-space functions should be saved in a single bp file.
This could of course be changed now, to store a mixture of DG-0 and a single other element function.

This is up for discussion.

Since VTX is the recommended writer, I think it's necessary to support this feature. This code doesn't modify previous behavior regarding the written post files.

MWE

from dolfinx import fem, mesh, io
from mpi4py import MPI

cells_per_side = 4

domain  = mesh.create_unit_square(MPI.COMM_WORLD,
                                  cells_per_side,
                                  cells_per_side,
                                  )

cg1 = fem.functionspace(domain, ("Lagrange", 1),)
dg0 = fem.functionspace(domain, ("Discontinuous Lagrange", 0),)

u_cg1 = fem.Function(cg1, name="u_cg1")
u_dg0 = fem.Function(dg0, name="u_dg0")

expression =  lambda x : x[0]*x[1] 
u_cg1.interpolate(expression)
u_dg0.interpolate(expression)

writer = io.VTXWriter(domain.comm,
                      "result_folder/post.bp",
                      output=[u_cg1,u_dg0])
writer.write(0.0)
writer.close()

@jorgensd

jorgensd commented Sep 2, 2024

Copy link
Copy Markdown
Member

Currently DG-0 support in VTX is limited by: ornladios/ADIOS2#4179

@ordinary-slim

Copy link
Copy Markdown
Contributor Author

Currently DG-0 support in VTX is limited by: ornladios/ADIOS2#4179

Thank you for the information, a bit of a bummer. This PR could still be useful for steady data.

@mscroggs mscroggs added this to the 0.10.0 milestone Sep 3, 2024
@garth-wells

Copy link
Copy Markdown
Member

We're directing efforts towards VTKHDF5 - it is actively developed/maintained.

@ordinary-slim

Copy link
Copy Markdown
Contributor Author

What is the recommended way to output both CG1 and DG0 time dependent data?

@jorgensd

Copy link
Copy Markdown
Member

What is the recommended way to output both CG1 and DG0 time dependent data?

I would interpolate the CG1 and DG0 data into a compatible function space (DG1).

As Paraview 6.0 was just released (https://www.paraview.org/download/) it might be worthwhile getting this PR merged (as VTKHDF currently has very limited visualization capabilities of time dependent data in Paraview).
@ordinary-slim do you mind testing if time-dependent data is rendering correctly in paraview 6.0?

@jorgensd jorgensd reopened this May 29, 2025
@ordinary-slim

ordinary-slim commented May 29, 2025

Copy link
Copy Markdown
Contributor Author

@ordinary-slim do you mind testing if time-dependent data is rendering correctly in paraview 6.0?

I've been using this branch without issues with Paraview 5.13. I can confirm it also works with 6.0 @jorgensd . I am using this example:

from dolfinx import fem, mesh, io
from mpi4py import MPI
import numpy as np

cells_per_side = 4

domain  = mesh.create_unit_square(MPI.COMM_WORLD,
                                  cells_per_side,
                                  cells_per_side,
                                  )

cg1 = fem.functionspace(domain, ("Lagrange", 1),)
dg0 = fem.functionspace(domain, ("Discontinuous Lagrange", 0),)

u_cg1 = fem.Function(cg1, name="u_cg1")
u_dg0 = fem.Function(dg0, name="u_dg0")

expression =  lambda x : x[0]*x[1] 
u_cg1.interpolate(expression)
u_dg0.interpolate(expression)

writer = io.VTXWriter(domain.comm,
                      "result_folder/post.bp",
                      output=[u_cg1,u_dg0])

for i in range(10):
    time = np.float64(i)
    u_cg1.x.array[:] *= (time + 1.0)
    u_dg0.x.array[:] *= (time + 1.0)
    writer.write(time)

writer.close()
``

Comment on lines +481 to +498
bool has_V0_changed = false;
for (auto& v : u)
{
std::visit(
[&V0, &has_V0_changed](auto& u)
{
auto V = u->function_space().get();
assert(V);
if (!impl::is_cellwise(*V->element()))
{
V0 = V;
has_V0_changed = true;
}
},
v);
if (has_V0_changed)
break;
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ordinary-slim could you elaborate on this logic?
Here you look through all input functions, and replace the first function space with any (the last) function space if it is not a "constant" space. Is this so that you can mix the non-constant and constant spaces?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This V0 is used to check that all functions have the same dofmap. In the vtk scheme, we can provide both point data and cell data. Since the DG0 functions have this separate path, we only have to verify that the remaining non-DG0 functions use the same dofmap.

@chrisrichardson chrisrichardson removed this from the 0.10.0 milestone Jul 18, 2025
@jorgensd

Copy link
Copy Markdown
Member

This doesn't seem to give the right result for me if using a vector-function for the non-constant space.

Minimal test that I added to python/test/unit/io/test_adios2.py:

    def test_dg_0_data(self, tempdir):
        """Test reusage of mesh by VTXWriter."""
        from dolfinx.io import VTXWriter

        adios2 = pytest.importorskip("adios2", minversion="2.10.0")
        if not adios2.is_built_with_mpi:
            pytest.skip("Require adios2 built with MPI support")

        mesh = generate_mesh(2, False)
        v = Function(functionspace(mesh, ("Lagrange", 2, (2, ))))
        filename = Path(tempdir, "v.bp")
        v.name = "v"
        v.interpolate(lambda x: (x[0], x[1]))
        z = Function(v.function_space)
        z.name = "z"
        z.interpolate(lambda x : (np.sin(x[0]), np.cos(x[1])))


        q = Function(functionspace(mesh, ("DG", 0)))
        q.name = "q"
        q.x.array[:] = np.arange(q.x.array.size, dtype=q.x.array.dtype)
 
        # Save three steps
        writer = VTXWriter(mesh.comm, filename, [v,q])
        writer.write(0)
        writer.close()

When not storing q, I get
image
but when storing q it becomes
image

Comment thread cpp/dolfinx/io/ADIOS2Writers.h Outdated

@jorgensd jorgensd left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've made a slight change to ensure that this is working as expected when mixing different function spaces.
Other than that, I think this is good to go. @jhale, shall we merge?:)

@jorgensd

jorgensd commented Sep 15, 2025

Copy link
Copy Markdown
Member

This doesn't seem to give the right result for me if using a vector-function for the non-constant space.

Resolved in 09af8db

@jorgensd
jorgensd enabled auto-merge September 25, 2025 07:31
@jorgensd
jorgensd added this pull request to the merge queue Sep 25, 2025
Merged via the queue into FEniCS:main with commit d276096 Sep 25, 2025
16 of 17 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants