I/O in Rodin
Comprehensive guide to input and output in Rodin.
Introduction
The IO module in Rodin provides facilities for reading and writing meshes, grid functions, and simulation data in various file formats. Proper I/O is essential for:
- Pre-processing: Loading meshes from external generators (MMG)
- Post-processing: Saving solutions for visualization in tools like ParaView
- Checkpointing: Saving and restoring simulation state
- Data exchange: Converting between file formats
Supported File Formats
Rodin supports several file formats for meshes and grid functions. The format is specified explicitly via the IO::
| Format | Extension | Enum Value | Use Case |
|---|---|---|---|
| MFEM | .mesh, .gf | IO::FileFormat::MFEM | MFEM-compatible workflows |
| MEDIT | .mesh | IO::FileFormat::MEDIT | INRIA tools, MMG integration |
| HDF5 | .h5 | IO::FileFormat::HDF5 | High-performance binary I/O |
| XDMF | .xdmf, .h5 | — (via IO:: | Scientific visualization (ParaView) |
Mesh I/O
Loading Meshes
Meshes can be loaded from files in supported formats using the Mesh::load() method. The file format must be explicitly specified via IO::
#include <Rodin/Geometry.h> using namespace Rodin; using namespace Rodin::Geometry; int main() { Mesh mesh; // Load MFEM format mesh.load("domain.mesh", IO::FileFormat::MFEM); // Load MEDIT format (commonly used with MMG) mesh.load("domain.mesh", IO::FileFormat::MEDIT); return 0; }
Saving Meshes
Meshes can be saved in any of the supported formats:
Mesh mesh; mesh = mesh.UniformGrid(Polytope::Type::Triangle, {16, 16}); // Save in MFEM format mesh.save("output.mesh", IO::FileFormat::MFEM); // Save in MEDIT format mesh.save("output.mesh", IO::FileFormat::MEDIT);
Format Conversion
A common workflow is to convert meshes between formats. For example, loading a MEDIT mesh (from MMG) and saving it in MFEM format:
Mesh mesh; mesh.load("input.mesh", IO::FileFormat::MEDIT); mesh.save("output.mesh", IO::FileFormat::MFEM);
Grid Function I/O
Grid functions (finite element solutions) can also be saved and loaded:
#include <Rodin/Geometry.h> #include <Rodin/Variational.h> using namespace Rodin; using namespace Rodin::Geometry; using namespace Rodin::Variational; int main() { Mesh mesh; mesh = mesh.UniformGrid(Polytope::Type::Triangle, {16, 16}); P1 Vh(mesh); GridFunction gf(Vh); gf = [](const Geometry::Point& p) { return p.x() + p.y(); }; // Save mesh and grid function mesh.save("mesh.mesh", IO::FileFormat::MFEM); gf.save("solution.gf", IO::FileFormat::MFEM); return 0; }
XDMF Output for Visualization
The XDMF class provides a powerful and modern way to export simulation data for visualization in tools like ParaView. XDMF (eXtensible Data Model and Format) stores heavy data (coordinates, connectivity, field values) in HDF5 files and lightweight metadata in an XML file.
Why Use XDMF?
XDMF has several advantages over other formats:
- Efficient: Uses HDF5 for binary storage, leading to compact files and fast read/write
- Scalable: Supports very large datasets without excessive memory usage
- ParaView compatible: Directly loadable in ParaView for advanced 3D visualization
- Time series: Built-in support for time-dependent data with temporal collections
- Multiple fields: Export several grid functions (velocity, pressure, temperature, etc.) in a single output
Basic XDMF Usage
The simplest use case is to export a mesh and solution at a single time step:
#include <Rodin/Geometry.h> #include <Rodin/Variational.h> #include <Rodin/IO/XDMF.h> using namespace Rodin; using namespace Rodin::Geometry; using namespace Rodin::Variational; int main() { Mesh mesh; mesh = mesh.UniformGrid(Polytope::Type::Triangle, {16, 16}); mesh.getConnectivity().compute(1, 2); P1 Vh(mesh); TrialFunction u(Vh); TestFunction v(Vh); // ... solve a problem ... // Export to XDMF for visualization in ParaView IO::XDMF xdmf("MySimulation"); xdmf.grid().setMesh(mesh).add("u", u.getSolution()); xdmf.write(); return 0; }
This produces:
MySimulation.xdmf— XML metadata file (open this in ParaView)MySimulation.*.h5— HDF5 data files with mesh and field data
Time-Dependent Output
One of the most powerful features of XDMF is its ability to record time-evolving data. Use write(time) to record snapshots at different time values:
#include <Rodin/Geometry.h> #include <Rodin/Variational.h> #include <Rodin/IO/XDMF.h> #include <Rodin/Math/Constants.h> using namespace Rodin; using namespace Rodin::Geometry; using namespace Rodin::Variational; int main() { const Real dt = 0.05; const size_t Nt = 40; const size_t Nc = 32; // Create mesh Mesh mesh; mesh = mesh.UniformGrid(Polytope::Type::Triangle, { Nc, Nc }); mesh.scale(1.0 / (Nc - 1)); // Create finite element space (vector-valued, 2 components) P1 fes(mesh, 2); // Evolving field GridFunction u(fes); u.setName("u"); // Setup XDMF writer IO::XDMF xdmf("TimeEvolution"); auto grid = xdmf.grid(); grid.setMesh(mesh); grid.add(u); // register the vector field // Time loop for (size_t k = 0; k < Nt; ++k) { const Real t = k * dt; // Update the field at current time u = [t](const Geometry::Point& p) { const Real x = p.x(); const Real y = p.y(); return Math::Vector<Real>{{ std::sin(2.0 * Math::Constants::pi() * (x + t)), std::cos(2.0 * Math::Constants::pi() * (y - t)) }}; }; // Write snapshot at time t xdmf.write(t); } return 0; }
When opened in ParaView, this produces a time slider that lets you animate the field evolution.
Multiple Fields
You can export several fields (scalar, vector) at once:
IO::XDMF xdmf("MultiField"); auto grid = xdmf.grid(); grid.setMesh(mesh); grid.add("velocity", velocity); grid.add("pressure", pressure); grid.add("temperature", temperature); xdmf.write(t);
Multiple Grids
You can also work with multiple named grids, each with its own mesh and attributes:
IO::XDMF xdmf("MultiGrid"); auto fluidGrid = xdmf.grid("Fluid"); fluidGrid.setMesh(fluidMesh); fluidGrid.add("velocity", velocity); auto solidGrid = xdmf.grid("Solid"); solidGrid.setMesh(solidMesh); solidGrid.add("displacement", displacement); xdmf.write(t);
Viewing XDMF Files in ParaView
To visualize XDMF output in ParaView:
- Open ParaView
- Click File → Open and select the
.xdmffile - Click Apply in the Properties panel
- Select the field to display from the dropdown menu
- For time-dependent data, use the play/timeline controls to animate
External Mesh Generators
Using MMG
MMG is used for mesh adaptation and optimization. MMG uses the MEDIT format, which Rodin supports natively:
Mesh mesh; mesh.load("mmg_output.mesh", IO::FileFormat::MEDIT);
See the MMG examples for more details on using Rodin's MMG integration.
Recommended Workflow
For a typical simulation workflow, we recommend using XDMF for output:
#include <Rodin/Solver.h> #include <Rodin/Geometry.h> #include <Rodin/Variational.h> #include <Rodin/IO/XDMF.h> using namespace Rodin; using namespace Rodin::Solver; using namespace Rodin::Geometry; using namespace Rodin::Variational; int main() { // 1. Create or load the mesh Mesh mesh; mesh = mesh.UniformGrid(Polytope::Type::Triangle, {32, 32}); mesh.getConnectivity().compute(1, 2); // 2. Define the finite element space and problem P1 Vh(mesh); TrialFunction u(Vh); TestFunction v(Vh); RealFunction f = 1; Problem poisson(u, v); poisson = Integral(Grad(u), Grad(v)) - Integral(f, v) + DirichletBC(u, Zero()); // 3. Solve CG(poisson).solve(); // 4. Export results via XDMF IO::XDMF xdmf("Results"); xdmf.grid().setMesh(mesh).add("u", u.getSolution()); xdmf.write(); return 0; }
This workflow produces files that can be directly opened in ParaView for visualization and analysis.
Detailed Format Guides
For in-depth information on each format, see:
- XDMF Format - XDMF format for visualization and time series
- Grid Function I/O - Saving and loading grid functions
- Mesh File Formats - MFEM and MEDIT mesh file formats