Arcane  4.1.12.0
User documentation
Loading...
Searching...
No Matches
How it works

The use of TimeHistory is extremely simple.

At each iteration, a value will be recorded for each value history. If there is no explicit value recording during an iteration, a 0 will be recorded. If there are two values recorded during the same iteration for a value history, the last value recorded will be the one that is actually saved.

Historically, value histories were managed only by process 0. Today, this is no longer the case. Each process can have its own history, using the same key. Furthermore, a history can be linked to a mesh. Thus, we can have a history per mesh, always using the same key.

Warning
To enable multi-process recording, it is necessary to define the environment variable ARCANE_ENABLE_NON_IO_MASTER_CURVES=1.

GlobalTimeHistoryAdder

The first structure allowing the management of value histories is the GlobalTimeHistoryAdder. It allows adding values to a history. "Global" means that the internal variables used to manage this history are global, linked to the sub-domains.

Suppose we have a mesh shared across four sub-domains (SD0, SD1, SD2, SD3). Each cell has a pressure. We want to have, for each iteration, the average pressure of each sub-domain. And in addition, we want to have the average pressure of the entire domain.

Let's use avg_pressure as the key:

Each sub-domain has an average pressure and there is a global average pressure. The image presents a single iteration: iteration 0.

To obtain a history like this, we can do this:

// Calculation of the subdomain pressure average.
Real avg_pressure_subdomain = 0;
ENUMERATE_ (Cell, icell, mesh()->ownCells()) {
avg_pressure_subdomain += m_pressure[icell];
}
if (!mesh()->ownCells().empty()) {
avg_pressure_subdomain /= mesh()->ownCells().size();
}
ISubDomain* sd = subDomain();
IParallelMng* pm = parallelMng();
Integer my_rank = pm->commRank();
Integer nb_proc = pm->commSize();
// Calculation of the global average pressure.
Real avg_pressure_global = pm->reduce(IParallelMng::eReduceType::ReduceSum, avg_pressure_subdomain);
avg_pressure_global /= nb_proc;
// Creation of the object allowing values to be added to a value history.
// Adding the avg_pressure_subdomain value to the "avg_pressure" history. One history per subdomain.
global_adder.addValue(TimeHistoryAddValueArg("avg_pressure", true, my_rank), avg_pressure_subdomain);
// Adding the avg_pressure_global value to the "avg_pressure" history. Global history.
global_adder.addValue(TimeHistoryAddValueArg("avg_pressure"), avg_pressure_global);
Remarks
Internally, GlobalTimeHistoryAdder uses the internal part of the ITimeHistoryMng passed as a parameter. The GlobalTimeHistoryAdder object can therefore be destroyed without problems.
Note
To use GlobalTimeHistoryAdder, do not forget to import the necessary headers:
#include <arcane/core/ITimeHistoryMng.h>
#include <arcane/core/GlobalTimeHistoryAdder.h>

This piece of code, if called at every iteration, allows obtaining the averages at each iteration.

MeshTimeHistoryAdder

The second structure allowing the management of value histories is the MeshTimeHistoryAdder. Like the first structure, it allows adding values to a history. "Mesh" means that the internal variables used to manage this history are linked to the desired mesh. Therefore, each mesh can have a different variable with the same name.

Let's take the example above but with two meshes. These two meshes are distributed across four sub-domains. We want to have, for each sub-domain, the average pressure of the cells of each mesh. But we still want, as above, the average pressure of each sub-domain.

Let's use the same key: avg_pressure:

We can see that in addition to the avg_pressure of each sub-domain and the global one, there are avg_pressure for the two meshes.

Here is a code example to perform this calculation:

ISubDomain* sd = subDomain();
IParallelMng* pm = parallelMng();
Integer my_rank = pm->commRank();
Integer nb_proc = pm->commSize();
Integer nb_mesh = sd->meshes().size();
// Will contain the subdomain pressure average ("(2)" in the image).
Real avg_pressure_subdomain = 0;
// Will contain the pressure average of the entire domain ("(4)" in the image)
Real avg_pressure_global = 0;
// For each mesh.
for (auto mesh : sd->meshes()) {
// Will contain the subdomain and mesh "mesh" pressure average ("(1)" in the image).
Real avg_pressure_subdomain_mesh = 0;
ENUMERATE_ (Cell, icell, mesh->ownCells()) {
avg_pressure_subdomain_mesh += m_pressure[icell];
}
if (!mesh->ownCells().empty()) {
avg_pressure_subdomain_mesh /= mesh->ownCells().size();
}
// Will contain the pressure average of the entire domain and mesh "mesh" ("(3)" in the image).
Real avg_pressure_global_mesh = pm->reduce(IParallelMng::eReduceType::ReduceSum, avg_pressure_subdomain_mesh);
avg_pressure_global_mesh /= nb_proc;
// Creation of the object allowing values to be added to a value history linked to mesh "mesh".
MeshTimeHistoryAdder mesh_adder(sd->timeHistoryMng(), mesh->handle());
// Adding the avg_pressure_subdomain_mesh value to the "avg_pressure" history linked to mesh "mesh".
// One history per subdomain.
mesh_adder.addValue(TimeHistoryAddValueArg("avg_pressure", true, my_rank), avg_pressure_subdomain_mesh);
// Adding the avg_pressure_global value to the "avg_pressure" history linked to mesh "mesh".
// Global history.
mesh_adder.addValue(TimeHistoryAddValueArg("avg_pressure"), avg_pressure_global_mesh);
avg_pressure_subdomain += avg_pressure_subdomain_mesh;
avg_pressure_global += avg_pressure_global_mesh;
}
if (nb_mesh != 0) {
avg_pressure_subdomain /= nb_mesh;
avg_pressure_global /= nb_mesh;
}
// Creation of the object allowing values to be added to a value history.
// Adding the avg_pressure_subdomain value to the "avg_pressure" history. One history per subdomain.
global_adder.addValue(TimeHistoryAddValueArg("avg_pressure", true, my_rank), avg_pressure_subdomain);
// Adding the avg_pressure_global value to the "avg_pressure" history. Global history.
global_adder.addValue(TimeHistoryAddValueArg("avg_pressure"), avg_pressure_global);

The difference here is that we iterate over the meshes. To create the MeshTimeHistoryAdder, in addition to an ITimeHistoryMng*, we must provide a mesh handle. This allows linking the history to the mesh.

Remarks
Internally, MeshTimeHistoryAdder uses the internal part of the ITimeHistoryMng passed as a parameter. The MeshTimeHistoryAdder object can therefore be destroyed without problems.
Note
To use MeshTimeHistoryAdder, do not forget to import the necessary headers:
#include <arcane/core/ITimeHistoryMng.h>
#include <arcane/core/MeshTimeHistoryAdder.h>
The TimeHistoryMng manages checkpoints, so the user does not have to worry about it.