openPMD-api
 
Loading...
Searching...
No Matches
JSONIOHandlerImpl.hpp
1/* Copyright 2017-2025 Franz Poeschel, Axel Huebl, Luca Fedeli
2 *
3 * This file is part of openPMD-api.
4 *
5 * openPMD-api is free software: you can redistribute it and/or modify
6 * it under the terms of of either the GNU General Public License or
7 * the GNU Lesser General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * openPMD-api is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License and the GNU Lesser General Public License
15 * for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * and the GNU Lesser General Public License along with openPMD-api.
19 * If not, see <http://www.gnu.org/licenses/>.
20 */
21
22#pragma once
23
24#include "openPMD/IO/AbstractIOHandler.hpp"
25#include "openPMD/IO/AbstractIOHandlerImpl.hpp"
26#include "openPMD/IO/Access.hpp"
27#include "openPMD/IO/JSON/JSONFilePosition.hpp"
28#include "openPMD/auxiliary/Filesystem.hpp"
29#include "openPMD/auxiliary/JSON_internal.hpp"
30#include "openPMD/backend/Variant_internal.hpp"
31#include "openPMD/config.hpp"
32
33#include <istream>
34#include <nlohmann/json.hpp>
35#if openPMD_HAVE_MPI
36#include <mpi.h>
37#endif
38
39#include <complex>
40#include <fstream>
41#include <memory>
42#include <stdexcept>
43#include <tuple>
44#include <unordered_map>
45#include <unordered_set>
46#include <vector>
47
48namespace openPMD
49{
50// Wrapper around a shared pointer to:
51// * a filename
52// * and a boolean indicating whether the file still exists
53// The wrapper adds no extra information, but some commodity functions.
54// Invariant for JSONIOHandlerImpl:
55// For any valid filename, there is at any time at most one
56// such shared pointer (wrapper) in the HandlerImpl's data structures
57// (counting by pointer equality)
58// This means, that a file can be invalidated (i.e. deleted or overwritten)
59// by simply searching for one instance of the file e.g. in m_files and
60// invalidating this instance
61// A new instance may hence only be created after making sure that there are
62// no valid instances in the data structures.
63struct File
64{
65 explicit File(std::string s) : fileState{std::make_shared<FileState>(s)}
66 {}
67
68 File() = default;
69
70 struct FileState
71 {
72 explicit FileState(std::string s) : name{std::move(s)}
73 {}
74
75 std::string name;
76 bool valid = true;
77 bool printedReadmeWarningAlready = false;
78 };
79
80 std::shared_ptr<FileState> fileState;
81
82 void invalidate()
83 {
84 fileState->valid = false;
85 }
86
87 bool valid() const
88 {
89 return fileState->valid;
90 }
91
92 File &operator=(std::string const &s)
93 {
94 if (fileState)
95 {
96 fileState->name = s;
97 }
98 else
99 {
100 fileState = std::make_shared<FileState>(s);
101 }
102 return *this;
103 }
104
105 bool operator==(File const &f) const
106 {
107 return this->fileState == f.fileState;
108 }
109
110 std::string &operator*() const
111 {
112 return fileState->name;
113 }
114
115 std::string *operator->() const
116 {
117 return &fileState->name;
118 }
119
120 explicit operator bool() const
121 {
122 return fileState.operator bool();
123 }
124};
125} // namespace openPMD
126
127namespace std
128{
129template <>
130struct hash<openPMD::File>
131{
132 typedef openPMD::File argument_type;
133 typedef std::size_t result_type;
134
135 result_type operator()(argument_type const &s) const noexcept
136 {
137 return std::hash<shared_ptr<openPMD::File::FileState>>{}(s.fileState);
138 }
139};
140
141// std::complex handling
142template <class T>
143void to_json(nlohmann::json &j, const std::complex<T> &p)
144{
145 j = nlohmann::json{p.real(), p.imag()};
146}
147
148template <class T>
149void from_json(const nlohmann::json &j, std::complex<T> &p)
150{
151 p.real(j.at(0));
152 p.imag(j.at(1));
153}
154} // namespace std
155
156namespace openPMD
157{
158class JSONIOHandlerImpl : public AbstractIOHandlerImpl
159{
160 using json = nlohmann::json;
161
162public:
163 enum class FileFormat
164 {
165 Json,
166 Toml
167 };
168
169 explicit JSONIOHandlerImpl(
170 AbstractIOHandler *, FileFormat, std::string originalExtension);
171
172#if openPMD_HAVE_MPI
173 JSONIOHandlerImpl(
175 MPI_Comm,
176 FileFormat,
177 std::string originalExtension);
178#endif
179
180 void init(openPMD::json::TracingJSON config);
181
182 ~JSONIOHandlerImpl() override;
183
184 void
186
188
189 void
191
192 void createDataset(
194
195 void extendDataset(
197
198 void availableChunks(
200
202
203 void
205
206 void openPath(Writable *, Parameter<Operation::OPEN_PATH> const &) override;
207
209
210 void
212
213 void
215
216 void deleteDataset(
218
219 void deleteAttribute(
220 Writable *, Parameter<Operation::DELETE_ATT> const &) override;
221
222 void
224
225 void writeAttribute(
226 Writable *, Parameter<Operation::WRITE_ATT> const &) override;
227
229
231
233
234 void
236
238
239 void
241
242 void touch(Writable *, Parameter<Operation::TOUCH> const &) override;
243
244 std::future<void> flush();
245
246private:
247#if openPMD_HAVE_MPI
248 std::optional<MPI_Comm> m_communicator;
249#endif
250
251 using FILEHANDLE = std::fstream;
252
253 // map each Writable to its associated file
254 // contains only the filename, without the OS path
255 std::unordered_map<Writable *, File> m_files;
256
257 std::unordered_map<File, std::shared_ptr<nlohmann::json>> m_jsonVals;
258
259 // files that have logically, but not physically been written to
260 std::unordered_set<File> m_dirty;
261
262 /*
263 * Is set by constructor.
264 */
265 FileFormat m_fileFormat{};
266
267 /*
268 * Under which key do we find the backend configuration?
269 * -> "json" for the JSON backend, "toml" for the TOML backend.
270 */
271 std::string backendConfigKey() const;
272
273 /*
274 * First return value: The location of the JSON value (either "json" or
275 * "toml") Second return value: The value that was maybe found at this place
276 */
277 std::pair<std::string, std::optional<openPMD::json::TracingJSON>>
278 getBackendConfig(openPMD::json::TracingJSON &) const;
279
280 std::string m_originalExtension;
281
282 /*
283 * Was the config value explicitly user-chosen, or are we still working with
284 * defaults?
285 */
286 enum class SpecificationVia
287 {
288 DefaultValue,
289 Manually
290 };
291
293 // Dataset IO mode //
295
296 enum class DatasetMode
297 {
298 Dataset,
299 Template
300 };
301
302 // IOMode m_mode{};
303 // SpecificationVia m_IOModeSpecificationVia =
304 // SpecificationVia::DefaultValue; bool m_printedSkippedWriteWarningAlready
305 // = false;
306
307 struct DatasetMode_s
308 {
309 // Initialized in init()
310 DatasetMode m_mode{};
311 SpecificationVia m_specificationVia = SpecificationVia::DefaultValue;
312 bool m_skipWarnings = false;
313
314 template <typename A, typename B, typename C>
315 operator std::tuple<A, B, C>()
316 {
317 return std::tuple<A, B, C>{
318 m_mode, m_specificationVia, m_skipWarnings};
319 }
320 };
321 DatasetMode_s m_datasetMode;
322 DatasetMode_s retrieveDatasetMode(openPMD::json::TracingJSON &config) const;
323
325 // Attribute IO mode //
327
328 enum class AttributeMode
329 {
330 Short,
331 Long
332 };
333
334 struct AttributeMode_s
335 {
336 // Will be modified in init() based on the openPMD version and the
337 // active file format (JSON/TOML)
338 AttributeMode m_mode{};
339 SpecificationVia m_specificationVia = SpecificationVia::DefaultValue;
340 };
341 AttributeMode_s m_attributeMode;
342
343 AttributeMode_s
344 retrieveAttributeMode(openPMD::json::TracingJSON &config) const;
345
346 // HELPER FUNCTIONS
347
348 // will use the IOHandler to retrieve the correct directory.
349 // first tuple element will be the underlying opened file handle.
350 // if Access is read mode, then the second tuple element will be the istream
351 // casted to precision std::numeric_limits<double>::digits10 + 1, else null.
352 // if Access is write mode, then the second tuple element will be the
353 // ostream casted to precision std::numeric_limits<double>::digits10 + 1,
354 // else null. first tuple element needs to be a pointer, since the casted
355 // streams are references only.
356 std::tuple<std::unique_ptr<FILEHANDLE>, std::istream *, std::ostream *>
357 getFilehandle(File const &, Access access);
358
359 // full operating system path of the given file
360 std::string fullPath(File const &);
361
362 std::string fullPath(std::string const &);
363
364 // from a path specification /a/b/c, remove the last
365 // "folder" (i.e. modify the string to equal /a/b)
366 static void parentDir(std::string &);
367
368 // Fileposition is assumed to have already been set,
369 // get it in string form
370 static std::string filepositionOf(Writable *w);
371
372 // Execute visitor on each pair of positions in the json value
373 // and the flattened multidimensional array.
374 // Used for writing from the data to JSON and for reading back into
375 // the array from JSON
376 template <typename T, typename Visitor>
377 static void syncMultidimensionalJson(
378 nlohmann::json &j,
379 Offset const &offset,
380 Extent const &extent,
381 Extent const &multiplicator,
382 Visitor visitor,
383 T *data,
384 size_t currentdim = 0);
385
386 // multiplicators: an array [m_0,...,m_n] s.t.
387 // data[i_0]...[i_n] = data[m_0*i_0+...+m_n*i_n]
388 // (m_n = 1)
389 // essentially: m_i = \prod_{j=0}^{i-1} extent_j
390 static Extent getMultiplicators(Extent const &extent);
391
392 static std::pair<Extent, DatasetMode> getExtent(nlohmann::json &j);
393
394 // remove single '/' in the beginning and end of a string
395 static std::string removeSlashes(std::string);
396
397 template <typename KeyT>
398 static bool hasKey(nlohmann::json const &, KeyT &&key);
399
400 // make sure that the given path exists in proper form in
401 // the passed json value
402 static void ensurePath(nlohmann::json *json, std::string const &path);
403
404 // In order not to insert the same file name into the data structures
405 // with a new pointer (e.g. when reopening), search for a possibly
406 // existing old pointer. Construct a new pointer only upon failure.
407 // The bool is true iff the pointer has been newly-created.
408 // The iterator is an iterator for m_files
409 std::tuple<File, std::unordered_map<Writable *, File>::iterator, bool>
410 getPossiblyExisting(std::string const &file);
411
412 // get the json value representing the whole file, possibly reading
413 // from disk
414 std::shared_ptr<nlohmann::json> obtainJsonContents(File const &);
415
416 // get the json value at the writable's fileposition
417 nlohmann::json &obtainJsonContents(Writable *writable);
418
419 // write to disk the json contents associated with the file
420 // remove from m_dirty if unsetDirty == true
421 auto putJsonContents(File const &, bool unsetDirty = true)
422 -> decltype(m_jsonVals)::iterator;
423
424 // figure out the file position of the writable
425 // (preferring the parent's file position) and extend it
426 // by extend. return the modified file position.
427 std::shared_ptr<JSONFilePosition>
428 setAndGetFilePosition(Writable *, std::string const &extend);
429
430 // figure out the file position of the writable
431 // (preferring the parent's file position)
432 // only modify the writable's fileposition when specified
433 std::shared_ptr<JSONFilePosition>
434 setAndGetFilePosition(Writable *, bool write = true);
435
436 // get the writable's containing file
437 // if the parent is associated with another file,
438 // associate the writable with that file and return it
439 File refreshFileFromParent(Writable *writable);
440
441 void associateWithFile(Writable *writable, File);
442
443 // need to check the name too in order to exclude "attributes" key
444 static bool isGroup(nlohmann::json::const_iterator const &it);
445
446 static bool isDataset(nlohmann::json const &j);
447
448 // check whether the json reference contains a valid dataset
449 template <typename Param>
450 DatasetMode verifyDataset(Param const &parameters, nlohmann::json &);
451
452 static nlohmann::json platformSpecifics();
453
454 struct DatasetWriter
455 {
456 template <typename T>
457 static void call(
458 nlohmann::json &json,
459 const Parameter<Operation::WRITE_DATASET> &parameters);
460
461 static constexpr char const *errorMsg = "JSON: writeDataset";
462 };
463
464 struct DatasetReader
465 {
466 template <typename T>
467 static void call(
468 nlohmann::json &json,
470
471 static constexpr char const *errorMsg = "JSON: readDataset";
472 };
473
474 struct AttributeWriter
475 {
476 template <typename T>
477 static void call(nlohmann::json &, attribute_types const &);
478
479 static constexpr char const *errorMsg = "JSON: writeAttribute";
480 };
481
482 struct AttributeReader
483 {
484 template <typename T>
485 static void
486 call(nlohmann::json const &, Parameter<Operation::READ_ATT> &);
487
488 static constexpr char const *errorMsg = "JSON: writeAttribute";
489 };
490
491 template <typename T>
492 struct CppToJSON
493 {
494 nlohmann::json operator()(T const &);
495 };
496
497 template <typename T>
498 struct CppToJSON<std::vector<T>>
499 {
500 nlohmann::json operator()(std::vector<T> const &);
501 };
502
503 template <typename T, int n>
504 struct CppToJSON<std::array<T, n>>
505 {
506 nlohmann::json operator()(std::array<T, n> const &);
507 };
508
509 template <typename T, typename Enable = T>
510 struct JsonToCpp
511 {
512 T operator()(nlohmann::json const &);
513 };
514
515 template <typename T>
516 struct JsonToCpp<std::vector<T>>
517 {
518 std::vector<T> operator()(nlohmann::json const &);
519 };
520
521 template <typename T, int n>
522 struct JsonToCpp<std::array<T, n>>
523 {
524 std::array<T, n> operator()(nlohmann::json const &);
525 };
526
527 template <typename T>
528 struct JsonToCpp<T, typename std::enable_if_t<std::is_floating_point_v<T>>>
529 {
530 T operator()(nlohmann::json const &);
531 };
532};
533
534} // namespace openPMD
Interface for communicating between logical and physically persistent data.
Definition AbstractIOHandler.hpp:206
void createPath(Writable *, Parameter< Operation::CREATE_PATH > const &) override
Create all necessary groups for a path, possibly recursively.
Definition JSONIOHandlerImpl.cpp:552
void availableChunks(Writable *, Parameter< Operation::AVAILABLE_CHUNKS > &) override
Report chunks that are available for loading from the dataset represented by this writable.
Definition JSONIOHandlerImpl.cpp:880
void touch(Writable *, Parameter< Operation::TOUCH > const &) override
Treat this writable's file as open/active/dirty.
Definition JSONIOHandlerImpl.cpp:1643
void listAttributes(Writable *, Parameter< Operation::LIST_ATTS > &) override
List all attributes associated with an object.
Definition JSONIOHandlerImpl.cpp:1617
void checkFile(Writable *, Parameter< Operation::CHECK_FILE > &) override
Check if the file specified by the parameter is already present on disk.
Definition JSONIOHandlerImpl.cpp:536
void openPath(Writable *, Parameter< Operation::OPEN_PATH > const &) override
Open all contained groups in a path, possibly recursively.
Definition JSONIOHandlerImpl.cpp:930
void readDataset(Writable *, Parameter< Operation::READ_DATASET > &) override
Read a chunk of data from an existing dataset.
Definition JSONIOHandlerImpl.cpp:1240
void deleteAttribute(Writable *, Parameter< Operation::DELETE_ATT > const &) override
Delete an existing attribute.
Definition JSONIOHandlerImpl.cpp:1126
void openFile(Writable *, Parameter< Operation::OPEN_FILE > &) override
Open an existing file assuming it conforms to openPMD.
Definition JSONIOHandlerImpl.cpp:890
void openDataset(Writable *, Parameter< Operation::OPEN_DATASET > &) override
Open an existing dataset and determine its datatype and extent.
Definition JSONIOHandlerImpl.cpp:956
void deletePath(Writable *, Parameter< Operation::DELETE_PATH > const &) override
Delete all objects within an existing path.
Definition JSONIOHandlerImpl.cpp:1008
void writeAttribute(Writable *, Parameter< Operation::WRITE_ATT > const &) override
Create a single attribute and fill the value, possibly overwriting an existing attribute.
Definition JSONIOHandlerImpl.cpp:1174
void deregister(Writable *, Parameter< Operation::DEREGISTER > const &) override
Notify the backend that the Writable has been / will be deallocated.
Definition JSONIOHandlerImpl.cpp:1637
void readAttribute(Writable *, Parameter< Operation::READ_ATT > &) override
Read the value of an existing attribute.
Definition JSONIOHandlerImpl.cpp:1532
void createFile(Writable *, Parameter< Operation::CREATE_FILE > const &) override
Create a new file in physical storage, possibly overriding an existing file.
Definition JSONIOHandlerImpl.cpp:463
void writeDataset(Writable *, Parameter< Operation::WRITE_DATASET > &) override
Write a chunk of data into an existing dataset.
Definition JSONIOHandlerImpl.cpp:1142
void deleteFile(Writable *, Parameter< Operation::DELETE_FILE > const &) override
Delete an existing file from physical storage.
Definition JSONIOHandlerImpl.cpp:977
void listPaths(Writable *, Parameter< Operation::LIST_PATHS > &) override
List all paths/sub-groups inside a group, non-recursively.
Definition JSONIOHandlerImpl.cpp:1580
void closeFile(Writable *, Parameter< Operation::CLOSE_FILE > const &) override
Close the file corresponding with the writable and release file handles.
Definition JSONIOHandlerImpl.cpp:912
void listDatasets(Writable *, Parameter< Operation::LIST_DATASETS > &) override
List all datasets inside a group, non-recursively.
Definition JSONIOHandlerImpl.cpp:1599
void createDataset(Writable *, Parameter< Operation::CREATE_DATASET > const &) override
Create a new dataset of given type, extent and storage properties.
Definition JSONIOHandlerImpl.cpp:587
void deleteDataset(Writable *, Parameter< Operation::DELETE_DATASET > const &) override
Delete an existing dataset.
Definition JSONIOHandlerImpl.cpp:1085
void extendDataset(Writable *, Parameter< Operation::EXTEND_DATASET > const &) override
Increase the extent of an existing dataset.
Definition JSONIOHandlerImpl.cpp:707
Layer to mirror structure of logical data and persistent data in file.
Definition Writable.hpp:76
Extend nlohmann::json with tracing of which keys have been accessed by operator[]().
Definition JSON_internal.hpp:69
Public definitions of openPMD-api.
Definition Date.cpp:29
Access
File access mode to use during IO.
Definition Access.hpp:58
@ T
time
Definition UnitDimension.hpp:41
STL namespace.
Definition JSONIOHandlerImpl.hpp:64
Typesafe description of all required arguments for a specified Operation.
Definition IOTask.hpp:148