Adding a custom backend#

io4dolfinx is designed to be backend-agnostic, meaning you can implement custom readers and writers for different file formats by adhering to a specific protocol.

The IOBackend Protocol#

Any backend must implement the IOBackend protocol defined in io4dolfinx.backends. This protocol ensures that the backend provides all necessary methods for reading and writing meshes, functions, and attributes.

To use a custom backend, you simply pass the python module name (as a string) to the backend argument of any io4dolfinx function. The library will attempt to import the module and use it as the backend.

Required Data Structures#

Your backend will interact with several data classes defined in io4dolfinx.structures. You should import these to type-hint your implementation correctly:

  • MeshData: Contains local geometry, topology, and partitioning information for writing meshes.

  • ReadMeshData: A container for returning mesh data (cells, geometry, etc.) when reading.

  • FunctionData: Contains function values, dofmaps, and permutation info for writing functions.

  • MeshTagsData: Contains indices, values, and metadata for mesh tags.

Implementation Checklist#

Your backend module must implement the functions listed below. Note that comm is always an MPI.Intracomm and filename is a pathlib.Path or str.

General Configuration#

Attribute IO#

  • write_attributes()

    • Writes a dictionary of attributes (key-value pairs) to the file under the specified name (group).

  • read_attributes()

    • Reads and returns attributes associated with name.

Mesh IO#

  • write_mesh()

    • Writes mesh geometry, topology, and optionally partitioning data.

    • Must handle FileMode.write (new file) and FileMode.append.

  • read_mesh_data()

    • Reads mesh geometry and topology at a specific time.

    • If read_from_partition is True, it should read pre-calculated partitioning data to avoid re-partitioning.

MeshTags IO#

Function IO#

  • write_function()

    • Writes function values, global dofmaps, and cell permutations.

  • read_dofmap()

    • Reads the dofmap (connectivity) for the function name.

  • read_dofs()

    • Reads the local chunk of function values for a specific time.

    • Returns the array of values and the global starting index of that chunk.

  • read_cell_perms()

    • Reads cell permutation data used to map input cells to the current mesh.

  • read_timestamps()

    • Returns all available time-steps for a given function.

Legacy Support (Optional but defined in protocol)#

Snapshots#

  • snapshot_checkpoint()

    • Handles lightweight N-to-N checkpointing where data is saved exactly as distributed in memory without global reordering.

Example Skeleton#

from typing import Any
from pathlib import Path
from mpi4py import MPI
import numpy as np
import dolfinx
from io4dolfinx.structures import MeshData, FunctionData, MeshTagsData, ReadMeshData
from io4dolfinx.backends import FileMode

def get_default_backend_args(arguments: dict[str, Any] | None) -> dict[str, Any]:
    return arguments or {}

def write_mesh(filename: Path | str, comm: MPI.Intracomm, mesh: MeshData,
               backend_args: dict[str, Any] | None, mode: FileMode, time: float):
    # Implementation here
    pass

# ... Implement all other methods defined in IOBackend ...