Skip to content
🎉 Welcome to Aerosand!.

08_io

Important

Visit https://aerosand.cc for the latest updates.

0. Preface

Through the eight discussions in the first phase, I believe readers have gained a certain understanding of OpenFOAM’s project architecture and compilation principles. Next, we will further discuss some fundamentals necessary for solver projects.

Tip

It is worth reiterating personal recommendations for C++ learning for beginners in OpenFOAM at this stage.

  • What to do: First, learn the fundamentals of object-oriented C++, then continuously study C++ and gradually deepen your knowledge, accumulating C++ experience through ongoing practice.
  • What not to do: There is no need to master advanced C++ features at this stage, nor to learn complex algorithms; do not wait to finish learning C++ before starting OpenFOAM.

This section primarily discusses:

  • Understanding information input and output in OpenFOAM
  • Implementing information input and output using C++
  • Compiling and running an IO project

1. Reference OF Input/Output

We know that C++ can implement information input and output through I/O streams. Let us examine the data format of input and output in OpenFOAM.

Run the following command in the terminal to navigate to the run folder path:

terminal
run

If the terminal indicates that this folder is missing, run the following commands to create the run folder and navigate to it:

terminal
mkdir -p $WM_PROJECT_USER_DIR/run
run

Run the following command to copy the native test case cavity to the run folder path:

terminal
cp -r $FOAM_TUTORIALS/incompressible/icoFoam/cavity/cavity .

Run the following command to open the test case with VS Code and view its files:

terminal
code cavity

1.1. Input Files

Run the following command in the VS Code terminal to view the file structure of the test case and its initial files:

terminal
tree

The terminal output is as follows:

terminal
.
├── 0
│   ├── p
│   └── U
├── constant
│   └── transportProperties
└── system
    ├── blockMeshDict
    ├── controlDict
    ├── decomposeParDict
    ├── fvSchemes
    ├── fvSolution
    └── PDRblockMeshDict

File explanations are as follows:

  • The folder 0/ contains the initial field files for the test case (i.e., the time step at time 0)
    • For example, it includes the pressure field p and velocity field U for this test case
    • Both include corresponding internal fields and boundary conditions
  • The folder constant/ contains dictionaries related to geometry, physics, etc.
    • “Dictionary” here refers to OpenFOAM’s input file format, which can be simply understood as OpenFOAM’s input files
    • For example, it includes the transportProperties file, which typically specifies physical parameters such as fluid viscosity and diffusion coefficients
    • Mesh information files generated later will be saved in this folder
  • The folder system/ contains dictionaries for controlling the computation
    • For example, it includes the blockMeshDict file, which specifies parameters for mesh generation
    • For example, it includes the controlDict file, which specifies parameters such as time step, start and end times
    • For example, it includes the fvSchemes file, which specifies spatial and temporal discretization schemes
    • For example, it includes the fvSolution file, which specifies solver selection, relaxation factors, etc.
    • Other dictionary files will not be explored at this stage

Use VS Code to view the transportProperties dictionary:

cavity/constant/transportProperties
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
FoamFile // FoamFile{} describes the metadata of the file, helping OpenFOAM correctly read and identify dictionary files
{
    version     2.0; // File format version
    format      ascii; // Data storage format: ascii for human-readable files, binary for faster, more compact files
    class       dictionary; // Dictionary class
    object      transportProperties; // Corresponding object name, typically matching the file name
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //

nu              0.01;
// Kinematic viscosity for incompressible fluids, units [m^2.s^-1]

// For compressible fluids, dynamic viscosity mu = nu * rho is typically used, units [kg.m^-1.s^-1]

Use VS Code to view the controlDict dictionary:

cavity/system/controlDict
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
FoamFile
{
    version     2.0;
    format      ascii;
    class       dictionary;
    object      controlDict;
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //

application     icoFoam; // Application name

startFrom       startTime; 
// Starting point for computation, options include:
// - startTime: start at the time specified by startTime
// - latestTime: start from the latest time step results

startTime       0; // Specify the start time as 0 seconds, corresponding to the 0/ folder

stopAt          endTime; 
// Stopping condition for computation, options include:
// - endTime: stop at the time specified by endTime
// - writeNow: write results and stop immediately
// - noWriteNow: stop immediately without writing results

endTime         0.5; // Specify the stop time as 0.5 seconds

deltaT          0.005; // Time step size of 0.005 seconds

writeControl    timeStep;
// Write control, options include:
// - timeStep: write at fixed time step intervals
// - runTime: write at fixed physical time intervals
// - adjustableRunTime: automatically adjusted (commonly used for parallel computing)

writeInterval   20; // Write interval; here, results are written every 20 time steps

purgeWrite      0; // Whether to overwrite writes; here, it is disabled

writeFormat     ascii;
// Write format, options include:
// - ascii: text file (human-readable)
// - binary: binary file (faster and more compact but not human-readable)

writePrecision  6; // Precision for written data (number of significant digits)

writeCompression off; // Whether to compress written files

timeFormat      general;
// Format for naming time folders, options include:
// - general: general format (default scientific notation)
// - fixed: fixed number of digits (used with timePrecision)

timePrecision   6; // Precision for time output

runTimeModifiable true; // Whether dictionary files can be modified during computation

In simple terms, all files in the native test case (including mesh information generated later) are input files, and their information will be transferred into the OpenFOAM application.

1.2. Output Files

Let us quickly compute this test case.

Run the following commands in the terminal to generate the mesh and compute:

terminal
blockMesh
icoFoam

Run the following command to view the file structure after computation:

terminal
tree -L 2
.
├── 0
│   ├── p
│   └── U
├── 0.1
│   ├── p
│   ├── phi
│   ├── U
│   └── uniform
...
├── 0.5
│   ├── p
│   ├── phi
│   ├── U
│   └── uniform
├── constant
│   ├── polyMesh
│   └── transportProperties
└── system
    ├── blockMeshDict
    ├── controlDict
    ├── decomposeParDict
    ├── fvSchemes
    ├── fvSolution
    └── PDRblockMeshDict

It can be seen that after computation, results are stored as specified in the dictionary: 0.005 * 20 = 0.1 s, every 0.1 seconds until stopping at 0.5 seconds. The stored content is also determined by the solver (e.g., phi, etc.), which we will not delve into at this stage. Additionally, we can see the mesh information folder polyMesh saved after mesh generation.

Let us examine the data format of the velocity field results in the 0.5/ folder:

cavity/0.5/U
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
FoamFile
{
    version     2.0;
    format      ascii;
    arch        "LSB;label=32;scalar=64"; // Machine-dependent information for computation results
    class       volVectorField; // volVectorField class
    location    "0.5";
    object      U;
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //

dimensions      [0 1 -1 0 0 0 0]; // Units of the velocity field [m.s^-1]

internalField   nonuniform List<vector> // Internal field information, internal field data format
400 // Total number of internal field data points
(
(0.000253405 -0.000250456 0) // Velocity vector composed of values in three directions
(0.000141206 0.000111424 0)
(-0.00117704 0.000564623 0)
(-0.00348128 0.00088502 0)
...
)
;

boundaryField // Boundary field
{
    movingWall
    {
        type            fixedValue;
        value           uniform (1 0 0);
    }
    fixedWalls
    {
        type            noSlip;
    }
    frontAndBack
    {
        type            empty;
    }
}

Tip

The entire process is:

  • Writing information from external files into the project
  • Project execution (computation)
  • Writing information from the project to external files

2. Implementing Input/Output Using C++

In the early stages of learning, we learned that C++ provides the iostream standard library, which includes cin and cout methods for reading information streams from standard input and writing information streams to standard output. Additionally, C++ provides the fstream standard library for interaction between external files and information streams.

  • ofstream: Output file stream, used to create files and write information to them
  • ifstream: Input file stream, used to read information from files
  • fstream: General file stream, capable of both input and output operations

We will use C++ to implement input and output.

2.1. Project Preparation

Run the following command in the terminal to create the project for this section:

terminal
ofsp

Open this project using VS Code’s C/C++ Project Generator.

Run the following command in the terminal to test the project template:

terminal
make run

The project runs successfully, indicating that the initial project template is functioning correctly.

2.2. Main Source Code

Through this project, we simulate how a solver reads parameters from case dictionary files.

The main source code in ofsp_08_io/src/main.cpp is as follows:

/src/main.cpp
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
#include <iostream> // I/O standard library
#include <string> // String library
#include <fstream> // File stream library
#include <cassert> // Assertion library

int main(int argc, char *argv[])
{
	// Prepare variables for external parameters
	double viscosity;
    std::string application;
    double deltaT;
    int writeInterval;
    bool purgeWrite;
    std::string var[5];

    std::fstream infile; // Define a file stream
    infile.open("ofspProperties", std::fstream::in); // Open the file and prepare for reading
    if (!infile) { // Exception handling: if opening fails, execute the following
        std::cout << "# WARNING: NO input!" << std::endl;
        assert(infile); // Terminate the program and throw an error message (frequent calls affect performance)
    }
    infile >> var[0] >> viscosity;
    infile >> var[1] >> application;
    infile >> var[2] >> deltaT;
    infile >> var[3] >> writeInterval;
    infile >> var[4] >> purgeWrite;
    infile.close(); // Must close the file

    std::cout << std::endl
        << var[0] << "\t\t" << viscosity << std::endl
        << var[1] << "\t" << application << std::endl
        << var[2] << "\t\t" << deltaT << std::endl
        << var[3] << "\t" << writeInterval << std::endl
        << var[4] << "\t" << purgeWrite << std::endl
        << std::endl;


    // Simulate the computation of the velocity field
    int nCells = 4;
    int dimension = 3;
    double initVelocity = 0.1;
    double fieldU[nCells][dimension] = {0};
    for (int i = 0; i < nCells; ++i)
    {
        for (int j = 0; j < dimension; ++j)
        {
            fieldU[i][j] = initVelocity * i * deltaT;
        }
    }

    // Output the computed velocity field
    std::fstream outfile;
    outfile.open("U", std::fstream::out); // Open the file and prepare for writing
    outfile << "internalField\tnonuniform List<vector>" << std::endl;
    outfile << nCells << "\n(" <<  std::endl;
    for (int i = 0; i < nCells; ++i)
    {
        outfile << "( ";
        for (int j = 0; j < dimension; ++j)
        {
            outfile << fieldU[i][j] << " ";
        }
        outfile << ")\n";
    }
    outfile << ")\n;";

    outfile.close(); // Must close the file

	// In modern C++, return 0 can be omitted
}

2.3. Documentation File

Since the current project is relatively simple, readers can prepare documentation files according to their own needs. This will not be elaborated further.

2.4. Compilation and Execution

Run the following command in the terminal to check the compilation:

terminal
make

The terminal output is as follows:

terminal
g++ -std=c++17 -Wall -Wextra -g -Iinclude -c -MMD src/main.cpp  -o src/main.o
src/main.cpp: In function ‘int main(int, char**)’:
src/main.cpp:6:14: warning: unused parameter ‘argc’ [-Wunused-parameter]
    6 | int main(int argc, char *argv[])
      |          ~~~~^~~~
src/main.cpp:6:26: warning: unused parameter ‘argv’ [-Wunused-parameter]
    6 | int main(int argc, char *argv[])
      |                    ~~~~~~^~~~~~
g++ -std=c++17 -Wall -Wextra -g -Iinclude -o output/main src/main.o  -Llib
Executing all complete!

The terminal output shows a warning warning: unused parameter ‘argc’. Since we indeed did not use argc, this can be ignored. The final message Executing all complete! indicates successful compilation with no issues.

Create a file similar to OpenFOAM dictionaries in the root directory of the project. The ofspProperties file content is as follows (located at /ofsp/ofsp_08_io/ofspProperties):

/ofspProperties
nu                  0.01
application         laplacianFoam
deltaT              0.005
writeInterval       20
purgeWrite          0

Run the following command in the terminal to execute this project:

terminal
./output/main

Alternatively, clean and recompile, then run:

terminal
make clean
make run

The terminal output is as follows:

terminal
nu              0.01
application     laplacianFoam
deltaT          0.005
writeInterval   20
purgeWrite      0

We can see that the parameters following each keyword in the external file have been read into the program, meaning these parameters can participate in the project’s computation, and the project ultimately writes out the results.

The written file U is also located in the project root directory, with the following content:

/U
1
2
3
4
5
6
7
8
9
internalField	nonuniform List<vector>
4
(
( 0 0 0 )
( 0.0005 0.0005 0.0005 )
( 0.001 0.001 0.001 )
( 0.0015 0.0015 0.0015 )
)
;

The format is similar to OpenFOAM’s velocity field output format.

3. Summary

Through this project, we can gain a simple understanding of how OpenFOAM solvers obtain parameter values specified by dictionary files. These parameters participate in the project’s computation. Subsequently, the project writes the computation results to external files. The written files essentially conform to OpenFOAM’s format requirements and meet the format requirements of post-processing software, serving as data files awaiting further analysis.

This section has completed the following discussions:

  • Understanding information input and output in OpenFOAM
  • Implementing information input and output using C++
  • Compiling and running an IO project

Support us

Tip

Hopefully, the sharing here can be helpful to you.

If you find this content helpful, your comments or donations would be greatly appreciated. Your support helps ensure the ongoing updates, corrections, refinements, and improvements to this and future series, ultimately benefiting new readers as well.

The information and message provided during donation will be displayed as an acknowledgment of your support.

Copyright @ 2026 Aerosand

Last updated on • Aerosand