Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TOML Backend #1436

Merged
merged 10 commits into from
Aug 17, 2023
43 changes: 37 additions & 6 deletions docs/source/backends/json.rst
Original file line number Diff line number Diff line change
@@ -1,10 +1,19 @@
.. _backends-json:

JSON
====
JSON/TOML
=========

openPMD supports writing to and reading from JSON files.
The JSON backend is always available.
openPMD supports writing to and reading from JSON and TOML files.
The JSON and TOML backends are always available.

.. note::

Both the JSON and the TOML backends are not intended for large-scale data I/O.

The JSON backend is mainly intended for prototyping and learning, or similar workflows where setting up a large IO backend such as HDF5 or ADIOS2 is perceived as obstructive. It can also be used for small datasets that need to be stored in text format rather than binary.

The TOML backend is intended for exchanging the *structure* of a data series without its "heavy" data fields.
For instance, one can easily create and exchange human-readable, machine-actionable data configurations for experiments and simulations.


JSON File Format
Expand Down Expand Up @@ -43,9 +52,17 @@ Every such attribute is itself a JSON object with two keys:
* ``datatype``: A string describing the type of the value.
* ``value``: The actual value of type ``datatype``.

TOML File Format
----------------

A TOML file uses the file ending ``.toml``. The TOML backend is chosen by creating a ``Series`` object with a filename that has this file ending.

The TOML backend internally works with JSON datasets and converts to/from TOML during I/O.
As a result, data layout and usage are equivalent to the JSON backend.


Restrictions
------------
JSON Restrictions
-----------------

For creation of JSON serializations (i.e. writing), the restrictions of the JSON backend are
equivalent to those of the `JSON library by Niels Lohmann <https://github.com/nlohmann/json>`_
Expand Down Expand Up @@ -77,6 +94,20 @@ The (keys) names ``"attributes"``, ``"data"`` and ``"datatype"`` are reserved an

A parallel (i.e. MPI) implementation is *not* available.

TOML Restrictions
-----------------

Note that the JSON datatype-specific restrictions do not automatically hold for TOML, as those affect only the representation on disk, not the internal representation.

TOML supports most numeric types, with the support for long double and long integer types being platform-defined.
Special floating point values such as NaN are also support.

TOML does not support null values.

The (keys) names ``"attributes"``, ``"data"`` and ``"datatype"`` are reserved and must not be used for base/mesh/particles path, records and their components.

A parallel (i.e. MPI) implementation is *not* available.


Example
-------
Expand Down
1 change: 1 addition & 0 deletions include/openPMD/IO/Format.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ enum class Format
ADIOS2_SST,
ADIOS2_SSC,
JSON,
TOML,
DUMMY
};

Expand Down
7 changes: 6 additions & 1 deletion include/openPMD/IO/JSON/JSONIOHandler.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,12 @@ namespace openPMD
class JSONIOHandler : public AbstractIOHandler
{
public:
JSONIOHandler(std::string path, Access at);
JSONIOHandler(
std::string path,
Access at,
openPMD::json::TracingJSON config,
JSONIOHandlerImpl::FileFormat,
std::string originalExtension);

~JSONIOHandler() override;

Expand Down
45 changes: 33 additions & 12 deletions include/openPMD/IO/JSON/JSONIOHandlerImpl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,10 @@
#include "openPMD/IO/Access.hpp"
#include "openPMD/IO/JSON/JSONFilePosition.hpp"
#include "openPMD/auxiliary/Filesystem.hpp"
#include "openPMD/auxiliary/JSON_internal.hpp"
#include "openPMD/config.hpp"

#include <istream>
#include <nlohmann/json.hpp>

#include <complex>
Expand Down Expand Up @@ -153,7 +155,17 @@ class JSONIOHandlerImpl : public AbstractIOHandlerImpl
using json = nlohmann::json;

public:
explicit JSONIOHandlerImpl(AbstractIOHandler *);
enum class FileFormat
{
Json,
Toml
};

explicit JSONIOHandlerImpl(
AbstractIOHandler *,
openPMD::json::TracingJSON config,
FileFormat,
std::string originalExtension);

~JSONIOHandlerImpl() override;

Expand Down Expand Up @@ -229,15 +241,25 @@ class JSONIOHandlerImpl : public AbstractIOHandlerImpl
// files that have logically, but not physically been written to
std::unordered_set<File> m_dirty;

/*
* Is set by constructor.
*/
FileFormat m_fileFormat{};

std::string m_originalExtension;

// HELPER FUNCTIONS

// will use the IOHandler to retrieve the correct directory
// shared pointer to circumvent the fact that c++ pre 17 does
// not enforce (only allow) copy elision in return statements
std::shared_ptr<FILEHANDLE> getFilehandle(
File,
Access access); //, Access
// m_frontendAccess=this->m_handler->m_frontendAccess);
// will use the IOHandler to retrieve the correct directory.
// first tuple element will be the underlying opened file handle.
// if Access is read mode, then the second tuple element will be the istream
// casted to precision std::numeric_limits<double>::digits10 + 1, else null.
// if Access is write mode, then the second tuple element will be the
// ostream casted to precision std::numeric_limits<double>::digits10 + 1,
// else null. first tuple element needs to be a pointer, since the casted
// streams are references only.
std::tuple<std::unique_ptr<FILEHANDLE>, std::istream *, std::ostream *>
getFilehandle(File, Access access);

// full operating system path of the given file
std::string fullPath(File);
Expand Down Expand Up @@ -272,15 +294,13 @@ class JSONIOHandlerImpl : public AbstractIOHandlerImpl
// essentially: m_i = \prod_{j=0}^{i-1} extent_j
static Extent getMultiplicators(Extent const &extent);

static nlohmann::json initializeNDArray(Extent const &extent);

static Extent getExtent(nlohmann::json &j);

// remove single '/' in the beginning and end of a string
static std::string removeSlashes(std::string);

template <typename KeyT>
static bool hasKey(nlohmann::json &, KeyT &&key);
static bool hasKey(nlohmann::json const &, KeyT &&key);

// make sure that the given path exists in proper form in
// the passed json value
Expand Down Expand Up @@ -366,7 +386,8 @@ class JSONIOHandlerImpl : public AbstractIOHandlerImpl
struct AttributeReader
{
template <typename T>
static void call(nlohmann::json &, Parameter<Operation::READ_ATT> &);
static void
call(nlohmann::json const &, Parameter<Operation::READ_ATT> &);

static constexpr char const *errorMsg = "JSON: writeAttribute";
};
Expand Down
16 changes: 16 additions & 0 deletions include/openPMD/auxiliary/TypeTraits.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include "openPMD/auxiliary/UniquePtr.hpp"

#include <array>
#include <complex>
#include <cstddef> // size_t
#include <memory>
#include <vector>
Expand Down Expand Up @@ -56,6 +57,18 @@ namespace detail
static constexpr bool value = true;
};

template <typename>
struct IsComplex
{
static constexpr bool value = false;
};

template <typename T>
struct IsComplex<std::complex<T>>
{
static constexpr bool value = true;
};

template <typename T>
struct IsPointer
{
Expand Down Expand Up @@ -114,6 +127,9 @@ using IsPointer_t = typename detail::IsPointer<T>::type;
template <typename T>
inline constexpr bool IsContiguousContainer_v = IsVector_v<T> || IsArray_v<T>;

template <typename T>
inline constexpr bool IsComplex_v = detail::IsComplex<T>::value;

namespace
{
// see https://en.cppreference.com/w/cpp/language/if
Expand Down
4 changes: 4 additions & 0 deletions src/Format.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ Format determineFormat(std::string const &filename)
return Format::ADIOS2_SSC;
if (auxiliary::ends_with(filename, ".json"))
return Format::JSON;
if (auxiliary::ends_with(filename, ".toml"))
return Format::TOML;

// Format might still be specified via JSON
return Format::DUMMY;
Expand All @@ -66,6 +68,8 @@ std::string suffix(Format f)
return ".ssc";
case Format::JSON:
return ".json";
case Format::TOML:
return ".toml";
default:
return "";
}
Expand Down
15 changes: 14 additions & 1 deletion src/IO/AbstractIOHandlerHelper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,20 @@ std::unique_ptr<AbstractIOHandler> createIOHandler<json::TracingJSON>(
std::move(originalExtension));
case Format::JSON:
return constructIOHandler<JSONIOHandler, openPMD_HAVE_JSON>(
"JSON", path, access);
"JSON",
path,
access,
std::move(options),
JSONIOHandlerImpl::FileFormat::Json,
std::move(originalExtension));
case Format::TOML:
return constructIOHandler<JSONIOHandler, openPMD_HAVE_JSON>(
"JSON",
path,
access,
std::move(options),
JSONIOHandlerImpl::FileFormat::Toml,
std::move(originalExtension));
default:
throw std::runtime_error(
"Unknown file format! Did you specify a file ending? Specified "
Expand Down
10 changes: 8 additions & 2 deletions src/IO/JSON/JSONIOHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,14 @@ namespace openPMD
{
JSONIOHandler::~JSONIOHandler() = default;

JSONIOHandler::JSONIOHandler(std::string path, Access at)
: AbstractIOHandler{path, at}, m_impl{JSONIOHandlerImpl{this}}
JSONIOHandler::JSONIOHandler(
std::string path,
Access at,
openPMD::json::TracingJSON jsonCfg,
JSONIOHandlerImpl::FileFormat format,
std::string originalExtension)
: AbstractIOHandler{path, at}
, m_impl{this, std::move(jsonCfg), format, std::move(originalExtension)}
{}

std::future<void> JSONIOHandler::flush(internal::ParsedFlushParams &)
Expand Down
Loading
Loading