diff --git a/cmake/LoadingExternalGitRepositories.cmake b/cmake/LoadingExternalGitRepositories.cmake index 1d31e9f5..b1c19422 100644 --- a/cmake/LoadingExternalGitRepositories.cmake +++ b/cmake/LoadingExternalGitRepositories.cmake @@ -1,6 +1,65 @@ +include(FetchContent) + +# nanovdb +if(${NEON_USE_NANOVDB}) + FetchContent_GetProperties(nanovdb) + if (NOT nanovdb_POPULATED) + message(STATUS "Fetching nanovdb...") + FetchContent_Declare(nanovdb + GIT_REPOSITORY https://github.com/AcademySoftwareFoundation/openvdb.git + GIT_TAG master + ) + FetchContent_Populate(nanovdb) + + # Configure and build nanovdb with the desired options + add_custom_target(build_nanovdb ALL + COMMAND ${CMAKE_COMMAND} -S ${nanovdb_SOURCE_DIR} -B ${nanovdb_BINARY_DIR} + -DNANOVDB_USE_OPENVDB=OFF + -DUSE_NANOVDB=ON + -DOPENVDB_BUILD_CORE=OFF + -DOPENVDB_BUILD_BINARIES=OFF + -DNANOVDB_USE_TBB=OFF + -DNANOVDB_USE_CUDA=ON + -DNANOVDB_USE_BLOSC=OFF + -DNANOVDB_USE_ZLIB=OFF + -DCMAKE_INSTALL_PREFIX=/usr/local + COMMAND ${CMAKE_COMMAND} --build ${nanovdb_BINARY_DIR} --target install + WORKING_DIRECTORY ${nanovdb_SOURCE_DIR} + ) + + include_directories(${nanovdb_SOURCE_DIR}/nanovdb) + endif () +endif () + +# hdf5 and HighFive +if(${NEON_USE_HDF5}) + find_package(HDF5 REQUIRED COMPONENTS CXX) + if(HDF5_FOUND) + message(STATUS "HDF5 found: ${HDF5_INCLUDE_DIRS}") + + # Include HDF5 directories and libraries globally + set(HDF5_INCLUDE_DIRS ${HDF5_INCLUDE_DIRS}) + set(HDF5_LIBRARIES ${HDF5_LIBRARIES}) + + message(STATUS "Fetching HighFive...") + FetchContent_Declare( + HighFive + GIT_REPOSITORY https://github.com/BlueBrain/HighFive.git + GIT_TAG v2.3.1 # Specify the version you want to use + ) + FetchContent_MakeAvailable(HighFive) + set(HighFive_FOUND TRUE) + set(HighFive_INCLUDE_DIRS ${HighFive_INCLUDE_DIRS}) + set(HighFive_LIBRARIES HighFive) + + # Add definitions + add_definitions(-DNEON_USE_HDF5) + else() + message(FATAL_ERROR "HDF5 not found") + endif() +endif() # spdlog -include(FetchContent) FetchContent_GetProperties(spdlog) if (NOT spdlog_POPULATED) message(STATUS "Fetching spdlog...") diff --git a/docs/learn/io/01-NanoVDB-output.md b/docs/learn/io/01-NanoVDB-output.md new file mode 100644 index 00000000..78a9a432 --- /dev/null +++ b/docs/learn/io/01-NanoVDB-output.md @@ -0,0 +1,31 @@ +# NanoVDB + +## What is NanoVDB? + +As the name indicates it's a mini-version of the much bigger OpenVDB library, both in terms of functionality and scope. In fact, a stand-alone C++11 implementation of NanoVDB is available in the file NanoVDB.h and the C99 equivalent in the files CNanoVDB.h, and PNanoVDB.h. However, NanoVDB offers one major advantage over OpenVDB, namely support for GPUs. In short, NanoVDB is a standalone static-topology implementation of the well-known sparse volumetric VDB data structure. In other words, while values can be modified in a NanoVDB grid its tree topology cannot. + +Additionally, it also can have no external dependencies. + +*The explanation is taken from their documentation* + +## How do I compile a program that uses NanoVDB?: + +When making Neon, you must cmake it with the setting ` -DNEON_USE_NANOVDB=ON `. + +For example, you can make Neon with ` cmake -DNEON_USE_NANOVDB=ON .. `, which will then download and install NanoVDB on your computer. + +## Where is the tool located?: + +At Neon/libNeonCore/include/Neon/core/tools/io/ioToNanoVDB.h + +## How do I use the tool?: + +For the user, you simply have to instantiate the object `Neon::ioToNanoVDB`. At the time of writing, its constructor has 7 arguments. + +1. `filename`: The name of the file you want to outupt to. For example, if it is `coolGrid`, the output file will be written to `coolGrid.nvdb`. +2. `dim`: The dimension of the output. If you set it to `(10, 10, 10)`, there will be 1000 datapoints outputted. +3. `fun`: This is an anonymous function which takes in an index and a cardinality (of types `Neon::Integer_3d` and `int`, respectively), and should output the value you want to be stored at the corresponding index in the output. This function allows this tool to access internal values for your grid/field in the way you specify. +4. `card`: The cardinality of the output. Currently, only cardinalities of `1`, `3`, or `4` are supported. +5. `scalingData`: This is a scalar which scales the voxels in the output by the amount given. +6. `origin`: This is the index where the output starts at. The indices stored will be from `origin` to `origin + dim - (1,1,1)`. +7. `mask`: The anonymous function detailing which indices inside the inclusive range [`origin`, `origin + dim - (1,1,1)`] should be included in the output. This allows for sparse matrices to be stored. It should return `true` for indices that should be outputted, and `false` for those that shouldn't. You only need to consider indices inside that range. diff --git a/docs/learn/io/02-HDF5-output.md b/docs/learn/io/02-HDF5-output.md new file mode 100644 index 00000000..9b8fd0be --- /dev/null +++ b/docs/learn/io/02-HDF5-output.md @@ -0,0 +1,39 @@ +# HDF5 + +## What is HDF5? + +Hierarchical Data Format (HDF) is a set of file formats (HDF4, HDF5) designed to store and organize large amounts of data. Originally developed at the U.S. National Center for Supercomputing Applications, it is supported by The HDF Group, a non-profit corporation whose mission is to ensure continued development of HDF5 technologies and the continued accessibility of data stored in HDF. + +*The definition was taken from Wikipedia https://en.wikipedia.org/wiki/Hierarchical_Data_Format* + + +## What is HighFive? + +It is a modern header-only C++11 friendly interface for libhdf5. It is used in this context for HDF5 output. + +You can read its documentation here: https://bluebrain.github.io/HighFive/ + +## How do I compile a program that uses HDF5?: + +When making Neon, you must cmake it with the setting ` -NEON_USE_HDF5=ON `. + +For example, you can make Neon with ` cmake -NEON_USE_HDF5=ON .. `, which will then download and install HighFive on your computer + +You must also have HDF5 and Boost installed. For Ubuntu, you can do `sudo apt install libhdf5-dev` and `sudo apt install libboost-all-dev` + +## Where is the tool located?: + +At Neon/libNeonCore/include/Neon/core/tools/io/ioToHDF5.h + +## How do I use the tool?: + +For the user, you simply have to instantiate the object `Neon::ioToHDF5`. At the time of writing, its constructor has 7 arguments. + +1. `filename`: The name of the file you want to outupt to. For example, if it is `coolGrid`, the output file will be written to `coolGrid.nvdb`. +2. `dim`: The dimension of the output. If you set it to `(10, 10, 10)`, there will be 1000 datapoints outputted. +3. `fun`: This is an anonymous function which takes in an index and a cardinality (of types `Neon::Integer_3d` and `int`, respectively), and should output the value you want to be stored at the corresponding index in the output. This function allows this tool to access internal values for your grid/field in the way you specify. +4. `card`: The cardinality of the output. Currently, only cardinalities of `1`, `3`, or `4` are supported. +5. `scalingData`: This is a scalar which scales the voxels in the output by the amount given. +6. `origin`: This is the index where the output starts at. The indices stored will be from `origin` to `origin + dim - (1,1,1)`. +7. `chunking`: This is an integer 3d which stores the dimensions for which the HDF5 output should be chunked. You can play around with it to get different optimization results based on your use case. +8. `mask`: The anonymous function detailing which indices inside the inclusive range [`origin`, `origin + dim - (1,1,1)`] should be included in the output. This allows for sparse matrices to be stored. It should return `true` for indices that should be outputted, and `false` for those that shouldn't. You only need to consider indices inside that range. diff --git a/libNeonCore/CMakeLists.txt b/libNeonCore/CMakeLists.txt index baeb5c8f..bc03b67f 100644 --- a/libNeonCore/CMakeLists.txt +++ b/libNeonCore/CMakeLists.txt @@ -23,6 +23,11 @@ target_include_directories(libNeonCore PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/inclu target_include_directories(libNeonCore PUBLIC "${spdlog_SOURCE_DIR}/include") target_include_directories(libNeonCore PUBLIC "${rapidjson_SOURCE_DIR}/include") +if(HDF5_FOUND) + target_include_directories(libNeonCore PUBLIC ${HDF5_INCLUDE_DIRS} ${HighFive_INCLUDE_DIRS}) + target_link_libraries(libNeonCore PUBLIC ${HDF5_LIBRARIES} ${HighFive_LIBRARIES}) +endif() + if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") target_link_libraries(libNeonCore PRIVATE "-lstdc++fs") endif () diff --git a/libNeonCore/include/Neon/core/tools/io/ioToHDF5.h b/libNeonCore/include/Neon/core/tools/io/ioToHDF5.h new file mode 100644 index 00000000..58b87c35 --- /dev/null +++ b/libNeonCore/include/Neon/core/tools/io/ioToHDF5.h @@ -0,0 +1,191 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "cuda_fp16.h" +#include + +#include +#include +#include + + +#include "Neon/core/types/vec.h" + +namespace Neon { + +/** + * Namespace for this tool + */ +namespace ioToHDF5ns { + +/** + * Implicit function that defines the data stores by a user fields + */ +template +using UserFieldAccessGenericFunction_t = std::function&, int componentIdx)>; + +/** + * Implicit function that takes in an index, and returns a boolean for if that index is a valid index in the field or not. + */ +template +using UserFieldAccessMask = std::function&)>; + +/** + * Number of components of the field + */ +using nComponent_t = int; + +/** + * Name of the file where the field will be exported into + */ +using FieldName_t = std::string; + +template +struct UserFieldInformation +{ + UserFieldAccessGenericFunction_t m_userFieldAccessGenericFunction; + nComponent_t m_cardinality; + + UserFieldInformation(const UserFieldAccessGenericFunction_t& fun, nComponent_t card) + : m_userFieldAccessGenericFunction(fun), + m_cardinality(card) + { + } + + UserFieldInformation(const std::tuple, nComponent_t>& tuple) + : m_userFieldAccessGenericFunction(std::get<0>(tuple)), + m_cardinality(std::get<1>(tuple)) + { + } +}; + + +namespace helpNs { + +} // namespace helpNs + +template +void ioToHDF5(const ioToHDF5ns::UserFieldInformation& fieldData /*! User data that defines the field */, + ioToHDF5ns::UserFieldAccessMask mask /*! Stores a mask for which indices in the field should be outputted*/, + const std::string& filename /*! File name */, + const Neon::Integer_3d& dim /*! Dimension of the field */, + [[maybe_unused]] double spacingScale = 1.0 /*! Spacing, i.e. size of a voxel */, + const Neon::Integer_3d& origin = Neon::Integer_3d(0, 0, 0) /*! Origin */, + const Neon::Integer_3d& chunking = Neon::Integer_3d(10, 10, 10) /*! Chunking */, + [[maybe_unused]] int iterationId = -1) +{ + + if (fieldData.m_cardinality != 1) { + std::string msg = std::string("Too many components specified during attempt at creating HDF5 output. It currently only supports 1 component."); + NeonException exception("ioToHDF5"); + exception << msg; + NEON_THROW(exception); + } + + // create the dataset + HighFive::File file(filename, HighFive::File::ReadWrite | HighFive::File::Create | HighFive::File::Truncate); + HighFive::DataSetCreateProps props; + props.add(HighFive::Chunking(std::vector{chunking.x, chunking.y, chunking.z})); + HighFive::DataSet dataset = file.createDataSet(filename, HighFive::DataSpace({dim.x, dim.y, dim.z}), props); + + // write the values to the dataset + for (int i = origin.x; i < origin.x + dim.x; ++i) { + for (int j = origin.y; j < origin.y + dim.y; ++j) { + for (int k = origin.z; k < origin.z + dim.z; ++k) { + if (!mask(Neon::Integer_3d(i, j, k))) { + dataset.select({i - origin.x, j - origin.y, k - origin.z}, {1, 1, 1}).write(fieldData.m_userFieldAccessGenericFunction(Neon::Integer_3d(i, j, k), 0)); + } + } + } + } + +} +} // namespace ioToHDF5ns + +template +struct ioToHDF5 +{ + ioToHDF5(const std::string& filename /*! File name */, + const Neon::Integer_3d& dim /*! IoDense dimension of the field */, + const ioToHDF5ns::UserFieldAccessGenericFunction_t& fun /*! Implicit defintion of the user field */, + const nComponent_t card /*! Field cardinality */, + const double scalingData = 1.0 /*! Spacing, i.e. size of a voxel */, + const Neon::Integer_3d& origin = Neon::Integer_3d(0, 0, 0) /*! Minimum Corner && Origin */, + const Neon::Integer_3d& chunking = Neon::Integer_3d(10, 10, 10) /*1 Chunking size of the output file */, + const Neon::ioToHDF5ns::UserFieldAccessMask mask = [](const Neon::index_3d& idx){return (idx.x == idx.x) ? true: false;}) /*! Used for sparce matrices; returns true for indices that should be included in the output */ + : m_filename(filename), + m_dim(dim), + m_scalingData(scalingData), + m_origin(origin), + m_field(ioToHDF5ns::UserFieldInformation(fun, card)), + m_chunking(chunking), + m_mask(mask) + { + std::ofstream out("metadata2"); + out << "dim: " << m_dim.x << " " << m_dim.y << " " << m_dim.z << std::endl; + } + + virtual ~ioToHDF5() + { + } + + auto flush() -> void + { + std::string filename; + if (m_iteration == -1) { + filename = m_filename; + } else { + std::stringstream ss; + ss << std::setw(5) << std::setfill('0') << m_iteration; + std::string s = ss.str(); + filename = m_filename + s; + } + filename = filename + ".h5"; + ioToHDF5ns::ioToHDF5(m_field, + m_mask, + filename, + m_dim, + m_scalingData, + m_origin, + m_chunking, + m_iteration); + } + + + auto setIteration(int iteration) + { + m_iteration = iteration; + } + + auto setFileName(const std::string& fname) + { + m_filename = fname; + } + + + private: + std::string m_filename /*! File name */; + Neon::Integer_3d m_dim /*! IoDense dimension of the field */; + double m_scalingData = 1.0 /*! Spacing, i.e. size of a voxel */; + Neon::Integer_3d m_origin = Neon::Integer_3d(0, 0, 0) /*! Origin */; + Neon::Integer_3d m_chunking = Neon::Integer_3d(10, 10, 10) /*! Chunking */; + ioToHDF5ns::UserFieldInformation m_field /*! Field data*/; + ioToHDF5ns::UserFieldAccessMask m_mask; + int m_iteration = -1; +}; + +} // namespace Neon diff --git a/libNeonCore/include/Neon/core/tools/io/ioToNanoVDB.h b/libNeonCore/include/Neon/core/tools/io/ioToNanoVDB.h new file mode 100644 index 00000000..14392da7 --- /dev/null +++ b/libNeonCore/include/Neon/core/tools/io/ioToNanoVDB.h @@ -0,0 +1,298 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "cuda_fp16.h" +#include + + +#include +#include +#include +#include +#include + + +#include "Neon/core/types/vec.h" + +namespace Neon { + +/** + * Namespace for this tool + */ +namespace ioToNanoVDBns { + +/* + * nanovdb's 3-component vector + */ +template +using Vec3 = nanovdb::math::Vec3; + +/* + * nanovdb's 4-component vector + */ +template +using Vec4 = nanovdb::math::Vec4; + +/** + * Implicit function that defines the data stores by a user fields + */ +template +using UserFieldAccessGenericFunction_t = std::function&, int componentIdx)>; + +/** + * Implicit function that takes in an index, and returns a boolean for if that index is a valid index in the field or not. + */ +template +using UserFieldAccessMask = std::function&)>; + +/** + * Number of components of the field + */ +using nComponent_t = int; + +/** + * Name of the file where the field will be exported into + */ +using FieldName_t = std::string; + +template +struct UserFieldInformation +{ + UserFieldAccessGenericFunction_t m_userFieldAccessGenericFunction; + nComponent_t m_cardinality; + + UserFieldInformation(const UserFieldAccessGenericFunction_t& fun, nComponent_t card) + : m_userFieldAccessGenericFunction(fun), + m_cardinality(card) + { + } + + UserFieldInformation(const std::tuple, nComponent_t>& tuple) + : m_userFieldAccessGenericFunction(std::get<0>(tuple)), + m_cardinality(std::get<1>(tuple)) + { + } +}; + +/* + * Alias for NanoVDB grid types + */ +template +using Grid1Component = std::shared_ptr>; + +template +using Grid3Components = std::shared_ptr>>; + +template +using Grid4Components = std::shared_ptr>>; + +// The possible kinds of grids (containing 1, 3, or 4 components) that can be used +template +using GridPtrVariant = std::variant< + std::shared_ptr>, + std::shared_ptr>>, + std::shared_ptr>> +>; + +namespace helpNs { + +template +struct BackgroundValueVisitor { + real_tt operator()(const typename Neon::ioToNanoVDBns::Grid1Component) { + return std::numeric_limits::min(); + } + Vec3 operator()(const typename Neon::ioToNanoVDBns::Grid3Components) { + return Vec3(std::numeric_limits::min(), std::numeric_limits::min(), std::numeric_limits::min()); + } + Vec4 operator()(const typename Neon::ioToNanoVDBns::Grid4Components) { + return Vec4(std::numeric_limits::min(), std::numeric_limits::min(), std::numeric_limits::min(), std::numeric_limits::min()); + } +}; + +template +struct GetDataVisitor { + real_tt operator()(const typename Neon::ioToNanoVDBns::Grid1Component, Neon::Integer_3d xyz, const ioToNanoVDBns::UserFieldAccessGenericFunction_t& fieldData) const { + return fieldData(xyz, 0); + } + Vec3 operator()(const typename Neon::ioToNanoVDBns::Grid3Components, Neon::Integer_3d xyz, const ioToNanoVDBns::UserFieldAccessGenericFunction_t& fieldData) const { + return Vec3(fieldData(xyz, 0), fieldData(xyz, 1), fieldData(xyz, 2)); + } + Vec4 operator()(const typename Neon::ioToNanoVDBns::Grid4Components, Neon::Integer_3d xyz, const ioToNanoVDBns::UserFieldAccessGenericFunction_t& fieldData) const { + return Vec4(fieldData(xyz, 0), fieldData(xyz, 1), fieldData(xyz, 2), fieldData(xyz, 3)); + } +}; + +template +struct BuildGridVisitor { + real_tt operator()(const typename Neon::ioToNanoVDBns::Grid1Component grid, Neon::Integer_3d xyz, const ioToNanoVDBns::UserFieldAccessGenericFunction_t& fieldData) const { + return fieldData(xyz, 0); + } + Vec3 operator()(const typename Neon::ioToNanoVDBns::Grid3Components grid, Neon::Integer_3d xyz, const ioToNanoVDBns::UserFieldAccessGenericFunction_t& fieldData) const { + return Vec3(fieldData(xyz, 0), fieldData(xyz, 1), fieldData(xyz, 2)); + } + Vec4 operator()(const typename Neon::ioToNanoVDBns::Grid4Components grid, Neon::Integer_3d xyz, const ioToNanoVDBns::UserFieldAccessGenericFunction_t& fieldData) const { + return Vec4(fieldData(xyz, 0), fieldData(xyz, 1), fieldData(xyz, 2), fieldData(xyz, 3)); + } +}; + + +} // namespace helpNs + +template +void ioToNanoVDB(const ioToNanoVDBns::UserFieldInformation& fieldData /*! User data that defines the field */, + ioToNanoVDBns::UserFieldAccessMask mask /*! Stores a mask for which indices in the field should be outputted*/, + const std::string& filename /*! File name */, + const Neon::Integer_3d& dim /*! Dimension of the field */, + double spacingScale = 1.0 /*! Spacing, i.e. size of a voxel */, + const Neon::Integer_3d& origin = Neon::Integer_3d(0, 0, 0) /*! Origin */, + [[maybe_unused]] int iterationId = -1) +{ + // Create our grid + Neon::ioToNanoVDBns::GridPtrVariant outputGrid; + + // Based on the cardinality, use a scalar, Vec3, or Vec4 + switch (fieldData.m_cardinality) { + case 1: + outputGrid = std::make_shared>( + std::numeric_limits::min(), filename, nanovdb::GridClass::LevelSet); + break; + case 3: + outputGrid = std::make_shared>>( + Vec3(std::numeric_limits::min(), std::numeric_limits::min(), std::numeric_limits::min()), filename, nanovdb::GridClass::LevelSet); + break; + case 4: + outputGrid = std::make_shared>>( + Vec4(std::numeric_limits::min(), std::numeric_limits::min(), std::numeric_limits::min(), std::numeric_limits::min()), filename, nanovdb::GridClass::LevelSet); + break; + default: + std::string msg = std::string("Too many components specified during attempt at creating nanoVDB output"); + NeonException exception("ioToNanoVDB"); + exception << msg; + NEON_THROW(exception); + } + + // through experimentation, it seems that both coordinates are inclusive. So, the upper corner should be subtracted by one. + const int bboxOffset = -1; + nanovdb::CoordBBox bbox(nanovdb::Coord(origin.x, origin.y, origin.z), nanovdb::Coord(origin.x + dim.x + bboxOffset, origin.y + dim.y + bboxOffset, origin.z + dim.z + bboxOffset)); + + // Write the data to the nanoVDB grid + std::visit([&](auto& grid) { + + // Set the voxel scale: + grid->setTransform(spacingScale); + + // Use a nanoVDB functor to set the grid values + (*grid)([&](const nanovdb::Coord &ijk) { + const Neon::Integer_3d xyz(ijk[0], ijk[1], ijk[2]); + + // If the mask shows that the current coordinate is empty, then we will set it with the background value. + // This background value makes this index unset (to accomodate sparse grids) + if (!mask(xyz)) { + return Neon::ioToNanoVDBns::helpNs::BackgroundValueVisitor{}(grid); + } + + return Neon::ioToNanoVDBns::helpNs::GetDataVisitor{}(grid, xyz, fieldData.m_userFieldAccessGenericFunction); + }, bbox); + + }, outputGrid); + + + try { + // Write the grid out to a file + std::visit([&](auto& grid) { + nanovdb::io::writeGrid(filename, nanovdb::tools::createNanoGrid(*grid), nanovdb::io::Codec::NONE); + }, outputGrid); + + // catch possible file IO errors + } catch (...) { + std::string msg = std::string("An error on file operations where encountered when writing nanoVDB data"); + NeonException exception("ioToNanoVDB output exception"); + exception << msg; + NEON_THROW(exception); + } +} +} // namespace ioToNanoVDBns + +template +struct ioToNanoVDB +{ + ioToNanoVDB(const std::string& filename /*! File name */, + const Neon::Integer_3d& dim /*! IoDense dimension of the field */, + const ioToNanoVDBns::UserFieldAccessGenericFunction_t& fun /*! Implicit defintion of the user field */, + const nComponent_t card /*! Field cardinality */, + const double scalingData = 1.0 /*! Spacing, i.e. size of a voxel */, + const Neon::Integer_3d& origin = Neon::Integer_3d(0, 0, 0) /*! Minimum Corner && Origin */, + const Neon::ioToNanoVDBns::UserFieldAccessMask mask = [](const Neon::index_3d& idx){return (idx.x == idx.x) ? true: false;}) /*! Used for sparce matrices; returns true for indices that should be stored in vdb output */ + : m_filename(filename), + m_dim(dim), + m_scalingData(scalingData), + m_origin(origin), + m_field(ioToNanoVDBns::UserFieldInformation(fun, card)), + m_mask(mask) + { + std::ofstream out("metadata2"); + out << "dim: " << m_dim.x << " " << m_dim.y << " " << m_dim.z << std::endl; + } + + virtual ~ioToNanoVDB() + { + } + + auto flush() -> void + { + std::string filename; + if (m_iteration == -1) { + filename = m_filename; + } else { + std::stringstream ss; + ss << std::setw(5) << std::setfill('0') << m_iteration; + std::string s = ss.str(); + filename = m_filename + s; + } + filename = filename + ".nvdb"; + ioToNanoVDBns::ioToNanoVDB(m_field, + m_mask, + filename, + m_dim, + m_scalingData, + m_origin, + m_iteration); + } + + + auto setIteration(int iteration) + { + m_iteration = iteration; + } + + auto setFileName(const std::string& fname) + { + m_filename = fname; + } + + + private: + std::string m_filename /*! File name */; + Neon::Integer_3d m_dim /*! IoDense dimension of the field */; + double m_scalingData = 1.0 /*! Spacing, i.e. size of a voxel */; + Neon::Integer_3d m_origin = Neon::Integer_3d(0, 0, 0) /*! Origin */; + ioToNanoVDBns::UserFieldInformation m_field /*! Field data*/; + ioToNanoVDBns::UserFieldAccessMask m_mask; + int m_iteration = -1; +}; + +} // namespace Neon diff --git a/libNeonDomain/include/Neon/domain/interface/FieldBase.h b/libNeonDomain/include/Neon/domain/interface/FieldBase.h index 1401e98b..4a377ba2 100644 --- a/libNeonDomain/include/Neon/domain/interface/FieldBase.h +++ b/libNeonDomain/include/Neon/domain/interface/FieldBase.h @@ -140,6 +140,18 @@ class FieldBase Neon::IoFileType ioFileType = Neon::IoFileType::ASCII, bool isNodeSpace = false) const -> void; + template + auto ioToNanoVDB(const std::string& fileName) const -> void; + + template + auto ioDomainToNanoVDB(const std::string& fileName) const -> void; + + template + auto ioToHDF5(const std::string& fileName) const -> void; + + template + auto ioDomainToHDF5(const std::string& fileName) const -> void; + private: struct Storage diff --git a/libNeonDomain/include/Neon/domain/interface/FieldBase_imp.h b/libNeonDomain/include/Neon/domain/interface/FieldBase_imp.h index 97d10dc1..0b1ff57a 100644 --- a/libNeonDomain/include/Neon/domain/interface/FieldBase_imp.h +++ b/libNeonDomain/include/Neon/domain/interface/FieldBase_imp.h @@ -2,6 +2,8 @@ #include "Neon/domain/interface/FieldBase.h" #include "Neon/domain/tools/IOGridVTK.h" +#include "Neon/core/tools/io/ioToNanoVDB.h" +#include "Neon/core/tools/io/ioToHDF5.h" namespace Neon::domain::interface { @@ -318,6 +320,80 @@ auto FieldBase::ioToVtk(const std::string& fileName, return; } +template +template +auto FieldBase::ioToNanoVDB(const std::string& fileName) const -> void +{ + Neon::ioToNanoVDB io(fileName, + this->getDimension(), + [&](Neon::Integer_3d idx, int card) -> NanoVDBExportType { + return (*this)(idx, card); + }, + this->getCardinality(), + 1.0, + Neon::Integer_3d(0, 0, 0)); + + + io.flush(); + return; +} + +template +template +auto FieldBase::ioDomainToNanoVDB(const std::string& fileName) const -> void +{ + Neon::ioToNanoVDB io(fileName, + this->getDimension(), + [&](const Neon::index_3d& idx, int) { + NanoVDBExportType setIdx = NanoVDBExportType(getBaseGridTool().getSetIdx(idx)); + return setIdx; + }, + 1, + 1.0, + Neon::Integer_3d(0, 0, 0)); + + + io.flush(); + return; +} + +template +template +auto FieldBase::ioToHDF5(const std::string& fileName) const -> void +{ + Neon::ioToHDF5 io(fileName, + this->getDimension(), + [&](Neon::Integer_3d idx, int card) -> HDF5ExportType { + return (*this)(idx, card); + }, + this->getCardinality(), + 1.0, + Neon::Integer_3d(0, 0, 0)); + + + io.flush(); + return; +} + +template +template +auto FieldBase::ioDomainToHDF5(const std::string& fileName) const -> void +{ + Neon::ioToHDF5 io(fileName, + this->getDimension(), + [&](const Neon::index_3d& idx, int) { + HDF5ExportType setIdx = HDF5ExportType(getBaseGridTool().getSetIdx(idx)); + return setIdx; + }, + 1, + 1.0, + Neon::Integer_3d(0, 0, 0)); + + + io.flush(); + return; +} + template auto FieldBase::getClassName() const -> const std::string& { diff --git a/libNeonDomain/include/Neon/domain/interface/GridBaseTemplate.h b/libNeonDomain/include/Neon/domain/interface/GridBaseTemplate.h index 235c2a3f..d2cbe2ab 100644 --- a/libNeonDomain/include/Neon/domain/interface/GridBaseTemplate.h +++ b/libNeonDomain/include/Neon/domain/interface/GridBaseTemplate.h @@ -31,6 +31,16 @@ class GridBaseTemplate : public GridBase */ auto ioDomainToVtk(const std::string& fileName, Neon::IoFileType vtiIOe = IoFileType::ASCII) const -> void; + + /** + * Exporting the domain active voxel to nanoVDB + */ + auto ioDomainToNanoVDB(const std::string& fileName) const -> void; + + /** + * Exporting the domain active voxel to HDF5 + */ + auto ioDomainToHDF5(const std::string& fileName) const -> void; }; } // namespace Neon::domain::interface diff --git a/libNeonDomain/include/Neon/domain/interface/GridBaseTemplate_imp.h b/libNeonDomain/include/Neon/domain/interface/GridBaseTemplate_imp.h index 03c3eab6..2d444d12 100644 --- a/libNeonDomain/include/Neon/domain/interface/GridBaseTemplate_imp.h +++ b/libNeonDomain/include/Neon/domain/interface/GridBaseTemplate_imp.h @@ -1,6 +1,8 @@ #pragma once #include "Neon/core/tools/io/ioToVTK.h" +#include "Neon/core/tools/io/ioToNanoVDB.h" +#include "Neon/core/tools/io/ioToHDF5.h" namespace Neon::domain::interface { @@ -14,8 +16,8 @@ auto GridBaseTemplate::ioDomainToVtk(const std::string& fileName, ioFileType); io.addField([&](const Neon::index_3d& idx, int) { - bool isActieVox = isInsideDomain(idx); - return isActieVox; + bool isActiveVox = isInsideDomain(idx); + return isActiveVox; }, 1, "Domain", ioToVTKns::VtiDataType_e::voxel); @@ -32,4 +34,68 @@ auto GridBaseTemplate::ioDomainToVtk(const std::string& fileName, io.flushAndClear(); return; } + +template +auto GridBaseTemplate::ioDomainToNanoVDB(const std::string& fileName) const -> void +{ + ioToNanoVDB io1(fileName + "_domain", + this->getDimension(), + [&](const Neon::index_3d& idx, int) { + bool isActiveVox = isInsideDomain(idx); + return isActiveVox; + }, + 1, + 1.0, + Neon::Integer_3d(0, 0, 0)); + + ioToNanoVDB io2(fileName + "_partition", + this->getDimension(), + [&](const Neon::index_3d& idx, int) { + const auto& cellProperties = this->getProperties(idx); + if (!cellProperties.isInside()) { + return -1; + } + auto setIdx = cellProperties.getSetIdx(); + return setIdx.idx(); + }, + 1, + 1.0, + Neon::Integer_3d(0, 0, 0)); + + io1.flush(); + io2.flush(); + return; +} + +template +auto GridBaseTemplate::ioDomainToHDF5(const std::string& fileName) const -> void +{ + ioToHDF5 io1(fileName + "_domain", + this->getDimension(), + [&](const Neon::index_3d& idx, int) { + bool isActiveVox = isInsideDomain(idx); + return isActiveVox; + }, + 1, + 1.0, + Neon::Integer_3d(0, 0, 0)); + + ioToHDF5 io2(fileName + "_partition", + this->getDimension(), + [&](const Neon::index_3d& idx, int) { + const auto& cellProperties = this->getProperties(idx); + if (!cellProperties.isInside()) { + return -1; + } + auto setIdx = cellProperties.getSetIdx(); + return setIdx.idx(); + }, + 1, + 1.0, + Neon::Integer_3d(0, 0, 0)); + + io1.flush(); + io2.flush(); + return; +} } // namespace Neon::domain::interface