16 #ifdef SERIALBOX_HAS_NETCDF 23 #include <boost/algorithm/string.hpp> 27 #include <netcdf_meta.h> 28 #include <unordered_map> 34 #define NETCDF_CHECK(functionCall) \ 35 if((errorCode = functionCall)) \ 36 throw serialbox::Exception("NetCDFArchive: %s", nc_strerror(errorCode)); 43 static int typeID2NcType(
TypeID type) {
60 template <
bool Int64IsLong>
61 struct DispatchInt64Impl {
62 template <
class FunctionForLong,
class FunctionForLongLong,
typename... Args>
63 int operator()(FunctionForLong&& functionForLong, FunctionForLongLong&& functionForLongLong,
64 Args&&... args) noexcept {
65 (void)functionForLongLong;
66 return functionForLong(args...);
71 struct DispatchInt64Impl<false> {
72 template <
class FunctionForLong,
class FunctionForLongLong,
typename... Args>
73 int operator()(FunctionForLong&& functionForLong, FunctionForLongLong&& functionForLongLong,
74 Args&&... args) noexcept {
75 (void)functionForLong;
76 return functionForLongLong(args...);
82 template <
class FunctionForLong,
class FunctionForLongLong,
typename... Args>
83 static int dispatchInt64(FunctionForLong&& functionForLong,
84 FunctionForLongLong&& functionForLongLong, Args&&... args) {
85 return DispatchInt64Impl<std::is_same<std::int64_t, long>::value>()(
86 std::forward<FunctionForLong>(functionForLong),
87 std::forward<FunctionForLongLong>(functionForLongLong), std::forward<Args>(args)...);
91 static void write(
int ncID,
int varID,
const std::vector<std::size_t>& startp,
92 const std::vector<std::size_t>& countp,
93 const std::vector<std::ptrdiff_t>& stridep,
94 const std::vector<std::ptrdiff_t>& imapp,
const StorageView& storageView) {
97 TypeID type = storageView.type();
100 case TypeID::Boolean:
101 NETCDF_CHECK(nc_put_varm_text(ncID, varID, startp.data(), countp.data(), stridep.data(),
102 imapp.data(), storageView.originPtr()));
105 NETCDF_CHECK(nc_put_varm_int(ncID, varID, startp.data(), countp.data(), stridep.data(),
106 imapp.data(), storageView.originPtrAs<
int>()));
108 case TypeID::Int64: {
109 NETCDF_CHECK(internal::dispatchInt64(nc_put_varm_long, nc_put_varm_longlong, ncID, varID,
110 startp.data(), countp.data(), stridep.data(), imapp.data(),
111 storageView.originPtrAs<std::int64_t>()));
114 case TypeID::Float32:
115 NETCDF_CHECK(nc_put_varm_float(ncID, varID, startp.data(), countp.data(), stridep.data(),
116 imapp.data(), storageView.originPtrAs<
float>()));
118 case TypeID::Float64:
119 NETCDF_CHECK(nc_put_varm_double(ncID, varID, startp.data(), countp.data(), stridep.data(),
120 imapp.data(), storageView.originPtrAs<
double>()));
128 static void read(
int ncID,
int varID,
const std::vector<std::size_t>& startp,
129 const std::vector<std::size_t>& countp,
const std::vector<std::ptrdiff_t>& stridep,
130 const std::vector<std::ptrdiff_t>& imapp, StorageView& storageView) {
133 TypeID type = storageView.type();
136 case TypeID::Boolean:
137 NETCDF_CHECK(nc_get_varm_text(ncID, varID, startp.data(), countp.data(), stridep.data(),
138 imapp.data(), storageView.originPtr()));
141 NETCDF_CHECK(nc_get_varm_int(ncID, varID, startp.data(), countp.data(), stridep.data(),
142 imapp.data(), storageView.originPtrAs<
int>()));
145 NETCDF_CHECK(internal::dispatchInt64(nc_get_varm_long, nc_get_varm_longlong, ncID, varID,
146 startp.data(), countp.data(), stridep.data(), imapp.data(),
147 storageView.originPtrAs<std::int64_t>()));
149 case TypeID::Float32:
150 NETCDF_CHECK(nc_get_varm_float(ncID, varID, startp.data(), countp.data(), stridep.data(),
151 imapp.data(), storageView.originPtrAs<
float>()));
153 case TypeID::Float64:
154 NETCDF_CHECK(nc_get_varm_double(ncID, varID, startp.data(), countp.data(), stridep.data(),
155 imapp.data(), storageView.originPtrAs<
double>()));
169 const std::string& prefix)
170 : mode_(mode), directory_(directory), prefix_(prefix) {
172 LOG(info) <<
"Creating NetCDFArchive (mode = " << mode_ <<
") based on NetCDF (" << NC_VERSION
173 <<
") from directory " << directory_;
175 metaDatafile_ = directory_ / (
"ArchiveMetaData-" + prefix_ +
".json");
178 bool isDir = filesystem::is_directory(directory_);
182 case OpenModeKind::Read:
184 throw Exception(
"no such directory: '%s'", directory_.string());
187 case OpenModeKind::Write:
188 case OpenModeKind::Append:
190 filesystem::create_directories(directory_);
193 }
catch(filesystem::filesystem_error& e) {
200 if(mode_ == OpenModeKind::Write)
207 LOG(info) <<
"Update MetaData of NetCDF Archive";
212 json_[
"serialbox_version"] =
213 100 * SERIALBOX_VERSION_MAJOR + 10 * SERIALBOX_VERSION_MINOR + SERIALBOX_VERSION_PATCH;
218 for(
auto it = fieldMap_.begin(), end = fieldMap_.end(); it != end; ++it)
219 json_[
"field_map"][it->first] = it->second;
223 std::ofstream fs(metaDatafile_.string(), std::ios::out | std::ios::trunc);
226 throw Exception(
"cannot open file: %s", metaDatafile_);
228 fs << json_.dump(2) << std::endl;
233 LOG(info) <<
"Reading MetaData for NetCDF archive ... ";
236 if(!filesystem::exists(metaDatafile_)) {
237 if(mode_ != OpenModeKind::Read)
239 throw Exception(
"archive meta data not found in directory '%s'", directory_.string());
242 std::ifstream fs(metaDatafile_.string(), std::ios::in);
246 int serialboxVersion = json_[
"serialbox_version"];
247 std::string archiveName = json_[
"archive_name"];
248 int archiveVersion = json_[
"archive_version"];
252 throw Exception(
"serialbox version of NetCDF archive (%s) is not compatible with the version " 253 "of the library (%s)",
256 if(archiveName != NetCDFArchive::Name)
257 throw Exception(
"archive is not a NetCDF archive");
259 if(archiveVersion > NetCDFArchive::Version)
260 throw Exception(
"NetCDF archive version (%s) does not match the version of the library (%s)",
261 archiveVersion, NetCDFArchive::Version);
264 if(json_.count(
"field_map")) {
266 for(
auto it = json_[
"field_map"].begin(); it != json_[
"field_map"].end(); ++it)
267 fieldMap_.insert({it.key(), static_cast<int>(it.value())});
276 const std::shared_ptr<FieldMetainfoImpl> info) {
277 if(mode_ == OpenModeKind::Read)
278 throw Exception(
"Archive is not initialized with OpenModeKind set to 'Write' or 'Append'");
280 LOG(info) <<
"Attempting to write field \"" << field <<
"\" to NetCDF archive ...";
282 int ncID, varID, errorCode;
286 std::vector<int> dims;
287 std::vector<int> strides;
288 for(
size_t i = 0; i < storageView.
dims().size(); ++i) {
289 if(storageView.
dims()[i] > 0) {
290 dims.push_back(storageView.
dims()[i]);
291 strides.push_back(storageView.
strides()[i]);
295 std::size_t numDims = dims.size();
296 std::size_t numDimsID = numDims + 1;
298 auto it = fieldMap_.find(field);
301 filesystem::path filename = directory_ / (prefix_ +
"_" + field +
".nc");
303 if(it != fieldMap_.end()) {
305 fieldID.id = it->second;
308 NETCDF_CHECK(nc_open(filename.c_str(), NC_WRITE, &ncID));
311 NETCDF_CHECK(nc_inq_varid(ncID, field.c_str(), &varID));
315 NETCDF_CHECK(nc_create(filename.c_str(), NC_NETCDF4, &ncID));
318 std::vector<int> dimsID(numDimsID);
320 NETCDF_CHECK(nc_def_dim(ncID,
"fieldID", NC_UNLIMITED, &dimsID[0]));
321 for(
int i = 1; i < dimsID.size(); ++i)
323 nc_def_dim(ncID, (
"d" + std::to_string(i - 1)).c_str(), dims[i - 1], &dimsID[i]));
326 NETCDF_CHECK(nc_def_var(ncID, field.c_str(), internal::typeID2NcType(type), numDimsID,
327 dimsID.data(), &varID));
332 fieldMap_.insert({fieldID.name, fieldID.id});
336 std::vector<std::size_t> startp(numDimsID, 0), countp(numDimsID);
337 std::vector<std::ptrdiff_t> stridep(numDimsID, 1), imapp(numDimsID);
339 startp[0] = fieldID.id;
341 imapp[0] = storageView.
size();
343 for(
int i = 0; i < numDims; ++i) {
344 countp[i + 1] = dims[i];
345 imapp[i + 1] = strides[i];
348 internal::write(ncID, varID, startp, countp, stridep, imapp, storageView);
356 LOG(info) <<
"Successfully wrote field \"" << fieldID.name <<
"\" (id = " << fieldID.id <<
") to " 357 << filename.filename();
362 const std::string& field) {
363 int ncID, varID, errorCode;
366 const std::vector<int>& dims = storageView.
dims();
367 const std::vector<int>& strides = storageView.
strides();
369 std::size_t numDims = dims.size();
372 NETCDF_CHECK(nc_create(filename.c_str(), NC_NETCDF4, &ncID));
375 std::vector<int> dimsID(numDims);
376 for(
int i = 0; i < dimsID.size(); ++i)
377 NETCDF_CHECK(nc_def_dim(ncID, (
"d" + std::to_string(i)).c_str(), dims[i], &dimsID[i]));
380 NETCDF_CHECK(nc_def_var(ncID, field.c_str(), internal::typeID2NcType(type), dimsID.size(),
381 dimsID.data(), &varID));
387 std::vector<std::size_t> startp(numDims, 0), countp(numDims);
388 std::vector<std::ptrdiff_t> stridep(numDims, 1), imapp(numDims);
389 for(
int i = 0; i < numDims; ++i) {
391 imapp[i] = strides[i];
394 internal::write(ncID, varID, startp, countp, stridep, imapp, storageView);
405 std::shared_ptr<FieldMetainfoImpl> info)
const {
406 LOG(info) <<
"Attempting to read field \"" << fieldID.
name <<
"\" (id = " << fieldID.
id 407 <<
") via NetCDFArchive ... ";
409 int ncID, varID, errorCode;
411 const std::vector<int>& dims = storageView.
dims();
412 const std::vector<int>& strides = storageView.
strides();
414 std::size_t numDims = dims.size();
415 std::size_t numDimsID = numDims + 1;
418 auto it = fieldMap_.find(fieldID.
name);
419 if(it == fieldMap_.end())
420 throw Exception(
"no field '%s' registered in NetCDFArchive", fieldID.
name);
423 if(fieldID.
id > it->second)
424 throw Exception(
"invalid id '%i' of field '%s'", fieldID.
id, fieldID.
name);
426 filesystem::path filename = directory_ / (prefix_ +
"_" + fieldID.
name +
".nc");
429 NETCDF_CHECK(nc_open(filename.c_str(), NC_NOWRITE, &ncID));
435 std::vector<std::size_t> startp(numDimsID, 0), countp(numDimsID);
436 std::vector<std::ptrdiff_t> stridep(numDimsID, 1), imapp(numDimsID);
438 startp[0] = fieldID.
id;
440 imapp[0] = storageView.
size();
442 for(
int i = 0; i < numDims; ++i) {
443 countp[i + 1] = dims[i];
444 imapp[i + 1] = strides[i];
447 internal::read(ncID, varID, startp, countp, stridep, imapp, storageView);
452 LOG(info) <<
"Successfully read field \"" << fieldID.
name <<
"\" (id = " << fieldID.
id <<
")";
456 const std::string& field) {
458 if(!filesystem::exists(filename))
459 throw Exception(
"cannot open %s: file does not exist", filename);
461 int ncID, varID, errorCode;
463 const std::vector<int>& dims = storageView.
dims();
464 const std::vector<int>& strides = storageView.
strides();
466 std::size_t numDims = dims.size();
469 NETCDF_CHECK(nc_open(filename.c_str(), NC_NOWRITE, &ncID));
472 NETCDF_CHECK(nc_inq_varid(ncID, field.c_str(), &varID));
475 std::vector<std::size_t> startp(numDims, 0), countp(numDims);
476 std::vector<std::ptrdiff_t> stridep(numDims, 1), imapp(numDims);
478 for(
int i = 0; i < numDims; ++i) {
480 imapp[i] = strides[i];
483 internal::read(ncID, varID, startp, countp, stridep, imapp, storageView);
490 filesystem::directory_iterator end;
491 for(filesystem::directory_iterator it(directory_); it != end; ++it) {
492 if(filesystem::is_regular_file(it->path()) &&
493 boost::algorithm::starts_with(it->path().filename().string(), prefix_ +
"_") &&
494 filesystem::path(it->path()).extension() ==
".nc") {
496 if(!filesystem::remove(it->path()))
497 LOG(warning) <<
"NetCDFArchive: cannot remove file " << it->path();
504 stream <<
"NetCDFArchive = {\n";
505 stream <<
" directory: " << directory_.string() <<
"\n";
506 stream <<
" mode: " << mode_ <<
"\n";
507 stream <<
" prefix: " << prefix_ <<
"\n";
508 stream <<
" fieldMap = {\n";
509 for(
auto it = fieldMap_.begin(), end = fieldMap_.end(); it != end; ++it)
510 stream <<
" " << it->first <<
": " << it->second <<
"\n";
517 const std::string&
prefix) {
523 #endif // SERIALBOX_HAS_NETCDF virtual void updateMetaData() override
Update the meta-data on disk.
virtual OpenModeKind mode() const override
Open-policy of the archive.
static std::string toString(int version)
Convert to string.
static const int Version
Revision of the NetCDF archive.
unsigned int id
ID within the field.
virtual FieldID write(const StorageView &storageView, const std::string &fieldID, const std::shared_ptr< FieldMetainfoImpl > info) override
Write the field given by storageView to disk.
virtual void clear() override
Clear the archive i.e remove all data from disk and reset the internal data-structures.
virtual std::string directory() const override
Directory to write/read files.
virtual void read(StorageView &storageView, const FieldID &fieldID, std::shared_ptr< FieldMetainfoImpl > info) const override
Read the field identified by fieldID and given by storageView from disk.
#define NETCDF_CHECK(functionCall)
Check return type of NetCDF functions.
static const std::string Name
Name of the NetCDF archive.
#define LOG(severity)
Logging infrastructure.
Represent a mutable view to a multi-dimensional storage.
TypeID type() const noexcept
Get type.
Namespace of the serialbox library.
TypeID
Type-id of types recognized by serialbox.
static void writeToFile(std::string filename, const StorageView &storageView, const std::string &field)
Directly write field (given by storageView) to file.
void writeMetaDataToJson()
Convert meta-data to JSON and serialize to file.
Uniquely identifiy a field.
std::string name
Name of the field.
const std::vector< int > & dims() const noexcept
Get dimensions.
virtual std::ostream & toStream(std::ostream &stream) const override
Convert the archive to stream.
static std::string toString(TypeID id)
Convert to string.
void readMetaDataFromJson()
Load meta-data from JSON file.
static void readFromFile(std::string filename, StorageView &storageView, const std::string &field)
Directly read field (given by storageView) from file.
std::size_t size() const noexcept
Size of the allocated, sliced data (without padding)
OpenModeKind
Policy for opening files in the Serializer and Archive.
NetCDFArchive(OpenModeKind mode, const std::string &directory, const std::string &prefix)
Initialize the archive.
const std::vector< int > & strides() const noexcept
Get strides.
static bool isCompatible(int version) noexcept
Check if the given version is compatible with the current library version (i.e. is older) ...
static std::unique_ptr< Archive > create(OpenModeKind mode, const std::string &directory, const std::string &prefix)
Create a NetCDFArchive.
#define serialbox_unreachable(msg)
Marks that the current location is not supposed to be reachable.
virtual std::string prefix() const override
Prefix of all files.
Exception class which stores a human-readable error description.