.. _usage-firstwrite:
First Write
===========
Step-by-step: how to write scientific data with openPMD-api?
.. raw:: html
Include / Import
----------------
After successful :ref:`installation `, you can start using openPMD-api as follows:
C++17
^^^^^
.. code-block:: cpp
#include
// example: data handling
#include // std::iota
#include // std::vector
namespace io = openPMD;
Python
^^^^^^
.. code-block:: python3
import openpmd_api as io
# example: data handling
import numpy as np
Open
----
Write into a new openPMD series in ``myOutput/data_<00...N>.h5``.
Further file formats than ``.h5`` (`HDF5 `_) are supported:
``.bp`` (`ADIOS2 `_) or ``.json`` (`JSON `_).
C++17
^^^^^
.. code-block:: cpp
auto series = io::Series(
"myOutput/data_%05T.h5",
io::Access::CREATE);
Python
^^^^^^
.. code-block:: python3
series = io.Series(
"myOutput/data_%05T.h5",
io.Access.create)
Iteration
---------
Grouping by an arbitrary, nonnegative integer number ```` in a series:
C++17
^^^^^
.. code-block:: cpp
auto i = series.iterations[42];
Python
^^^^^^
.. code-block:: python3
i = series.iterations[42]
Attributes
----------
Everything in openPMD can be extended and user-annotated.
Let us try this by writing some meta data:
C++17
^^^^^
.. code-block:: cpp
series.setAuthor(
"Axel Huebl ");
series.setMachine(
"Hall Probe 5000, Model 3");
series.setAttribute(
"dinner", "Pizza and Coke");
i.setAttribute(
"vacuum", true);
Python
^^^^^^
.. code-block:: python3
series.author = \
"Axel Huebl "
series.machine = "Hall Probe 5000, Model 3"
series.set_attribute(
"dinner", "Pizza and Coke")
i.set_attribute(
"vacuum", True)
Data
----
Let's prepare some data that we want to write.
For example, a magnetic field slice :math:`\vec B(i, j)` in two spatial dimensions with three components :math:`(B_x, B_y, B_z)^\intercal` of which the :math:`B_y` component shall be constant for all :math:`(i, j)` indices.
C++17
^^^^^
.. code-block:: cpp
std::vector x_data(
150 * 300);
std::iota(
x_data.begin(),
x_data.end(),
0.);
float y_data = 4.f;
std::vector z_data(x_data);
for( auto& c : z_data )
c -= 8000.f;
Python
^^^^^^
.. code-block:: python3
x_data = np.arange(
150 * 300,
dtype=np.float64
).reshape(150, 300)
y_data = 4.
z_data = x_data.copy() - 8000.
Record
------
An openPMD record can be either structured (mesh) or unstructured (particles).
We prepared a vector field in 2D above, which is a mesh:
C++17
^^^^^
.. code-block:: cpp
// record
auto B = i.meshes["B"];
// record components
auto B_x = B["x"];
auto B_y = B["y"];
auto B_z = B["z"];
auto dataset = io::Dataset(
io::determineDatatype(),
{150, 300});
B_x.resetDataset(dataset);
B_y.resetDataset(dataset);
B_z.resetDataset(dataset);
Python
^^^^^^
.. code-block:: python3
# record
B = i.meshes["B"]
# record components
B_x = B["x"]
B_y = B["y"]
B_z = B["z"]
dataset = io.Dataset(
x_data.dtype,
x_data.shape)
B_x.reset_dataset(dataset)
B_y.reset_dataset(dataset)
B_z.reset_dataset(dataset)
Units
-----
Let's describe this magnetic field :math:`\vec B` in more detail.
Independent of the absolute unit system, a magnetic field has the `physical dimension `_ of [mass (M)\ :sup:`1` :math:`\cdot` electric current (I)\ :sup:`-1` :math:`\cdot` time (T)\ :sup:`-2`].
Ouch, our magnetic field was measured in `cgs units `_!
Quick, let's also store the conversion factor 10\ :sup:`-4` from `Gauss `_ (cgs) to `Tesla `_ (SI).
C++17
^^^^^
.. code-block:: cpp
// unit system agnostic dimension
B.setUnitDimension({
{io::UnitDimension::M, 1},
{io::UnitDimension::I, -1},
{io::UnitDimension::T, -2}
});
// conversion to SI
B_x.setUnitSI(1.e-4);
B_y.setUnitSI(1.e-4);
B_z.setUnitSI(1.e-4);
Python
^^^^^^
.. code-block:: python3
# unit system agnostic dimension
B.unit_dimension = {
io.Unit_Dimension.M: 1,
io.Unit_Dimension.I: -1,
io.Unit_Dimension.T: -2
}
# conversion to SI
B_x.unit_SI = 1.e-4
B_y.unit_SI = 1.e-4
B_z.unit_SI = 1.e-4
.. tip::
Annotating the *physical dimension* (``unitDimension``) of a record allows us to read data sets with *arbitrary names* and understand their purpose simply by `dimensional analysis `_.
The dimensional `base quantities `_ in openPMD are length (``L``), mass (``M``), time (``T``), electric current (``I``), thermodynamic temperature (``theta``), amount of substance (``N``), luminous intensity (``J``) after the international system of quantities (ISQ).
The *factor to SI* (``unitSI``) on the other hand allows us to convert values between absolute unit systems.
Register Chunk
--------------
We can write record components partially and in parallel or at once.
Writing very small data one by one is is a performance killer for I/O.
Therefore, we register all data to be written first and then flush it out collectively.
C++17
^^^^^
.. code-block:: cpp
B_x.storeChunk(
x_data, {0, 0}, {150, 300});
B_z.storeChunk(
z_data, {0, 0}, {150, 300});
B_y.makeConstant(y_data);
Python
^^^^^^
.. code-block:: python3
B_x[:, :] = x_data
B_z[:, :] = z_data
B_y.make_constant(y_data)
.. attention::
After registering a data chunk such as ``x_data`` and ``y_data``, it MUST NOT be modified or deleted until the ``flush()`` step is performed!
Flush Chunk
-----------
We now flush the registered data chunks to the I/O backend.
Flushing several chunks at once allows to increase I/O performance significantly.
After that, the variables ``x_data`` and ``y_data`` can be used again.
C++17
^^^^^
.. code-block:: cpp
series.flush();
Python
^^^^^^
.. code-block:: python3
series.flush()
Close
-----
Finally, the Series is fully closed (and newly registered data or attributes since the last ``.flush()`` is written) when its destructor is called.
C++17
^^^^^
.. code-block:: cpp
series.close()
Python
^^^^^^
.. code-block:: python3
series.close()