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

add support for writing openPMD/ADIOS2 plotfiles #466

Merged
merged 28 commits into from
Dec 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
a317b85
first attempt at openPMD plotfiles
BenWibking Dec 8, 2023
aab5e9e
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Dec 8, 2023
d48b6dd
exclude ghosts
BenWibking Dec 8, 2023
ffc7d86
Merge branch 'BenWibking/openpmd-output' of github.com:quokka-astro/q…
BenWibking Dec 8, 2023
1c993b5
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Dec 8, 2023
0492785
disable openPMD.cpp unless explicitly enabled
BenWibking Dec 8, 2023
2f0cc12
guard include
BenWibking Dec 8, 2023
9de03f1
fix typo
BenWibking Dec 9, 2023
a7985e9
fix warnings
BenWibking Dec 10, 2023
cb3d261
add openPMD submodule
BenWibking Dec 10, 2023
614dfd7
set openPMD options
BenWibking Dec 10, 2023
9563614
fix cmake issue
BenWibking Dec 10, 2023
2fdc737
fix header include
BenWibking Dec 10, 2023
87fd360
update README
BenWibking Dec 10, 2023
4b651a3
fix warning
BenWibking Dec 10, 2023
bc5b37d
fix primordial_chem build
BenWibking Dec 10, 2023
58b822c
fix path case
BenWibking Dec 10, 2023
628070b
Update clang-tidy.yml
BenWibking Dec 11, 2023
c0d6108
Update clang-tidy.yml
BenWibking Dec 11, 2023
36b5ddb
Update clang-tidy.yml
BenWibking Dec 11, 2023
9b00453
Update CMakeLists.txt
BenWibking Dec 11, 2023
4db83eb
Update src/openPMD.cpp
BenWibking Dec 11, 2023
86e7da7
Update src/openPMD.cpp
BenWibking Dec 11, 2023
9c48586
Update clang-tidy.yml
BenWibking Dec 11, 2023
9807a32
Merge branch 'development' into BenWibking/openpmd-output
BenWibking Dec 11, 2023
a87cef0
add compile- and run-time warning about face-centered vars
BenWibking Dec 12, 2023
9f890b9
fix typo
BenWibking Dec 12, 2023
7a9c954
only warn if QUOKKA_USE_OPENPMD is on
BenWibking Dec 12, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/clang-tidy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ jobs:
config_file: src/.clang-tidy
build_dir: build
apt_packages: libopenmpi-dev,libhdf5-mpi-dev,python3-dev,python3-numpy,python3-matplotlib
cmake_command: cmake . -B build -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DQUOKKA_PYTHON=ON
cmake_command: cmake . -B build -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DQUOKKA_PYTHON=ON -DQUOKKA_OPENPMD=ON -DopenPMD_USE_ADIOS2=OFF
split_workflow: true

# Uploads an artefact containing clang_fixes.json
Expand Down
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,6 @@
[submodule "extern/yaml-cpp"]
path = extern/yaml-cpp
url = https://github.com/jbeder/yaml-cpp.git
[submodule "extern/openPMD-api"]
path = extern/openPMD-api
url = https://github.com/openPMD/openPMD-api.git
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ option(QUOKKA_PYTHON "Compile with Python support (on/off)" ON)
option(DISABLE_FMAD "Disable fused multiply-add instructions on GPU (on/off)" ON)
option(ENABLE_ASAN "Enable AddressSanitizer and UndefinedBehaviorSanitizer" OFF)
option(WARNINGS_AS_ERRORS "Treat compiler warnings as errors" OFF)
option(QUOKKA_OPENPMD "Enable OpenPMD output (on/off)" OFF)

if(AMReX_GPU_BACKEND MATCHES "CUDA")
enable_language(CUDA)
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ Quokka also features advanced Adaptive Quokka Refinement:tm: technology:
* ROCm 5.2.0+ (optional, for AMD GPUs)
* Ninja (optional, for faster builds)
* Python 3.7+ (optional)
* ADIOS2 2.9+ with GPU-aware support (optional, for writing terabyte-sized or larger outputs)

## Quickstart

Expand Down
1 change: 1 addition & 0 deletions extern/openPMD-api
Submodule openPMD-api added at e8e2ae
21 changes: 20 additions & 1 deletion src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,25 @@ if(Python_FOUND)
link_libraries(${Python_LIBRARIES})
endif()

if(QUOKKA_OPENPMD)
message(STATUS "Building Quokka with OpenPMD support")
add_compile_definitions(QUOKKA_USE_OPENPMD)
set(openPMD_USE_ADIOS2 ON CACHE BOOL "") # ADIOS2 is required
set(openPMD_USE_HDF5 OFF CACHE BOOL "")
set(openPMD_USE_PYTHON OFF CACHE BOOL "")
set(openPMD_BUILD_TESTING OFF CACHE BOOL "")
set(openPMD_BUILD_EXAMPLES OFF CACHE BOOL "")
set(openPMD_BUILD_CLI_TOOLS OFF CACHE BOOL "")
set(openPMD_INSTALL OFF CACHE BOOL "")
add_subdirectory(${QuokkaCode_SOURCE_DIR}/extern/openPMD-api ${QuokkaCode_BINARY_DIR}/openPMD-api)
include_directories(${OpenPMD_INCLUDE_DIRS_RET})
link_libraries(openPMD::openPMD)
set(openPMDSources "${CMAKE_CURRENT_SOURCE_DIR}/openPMD.cpp")
message(STATUS "WARNING: OpenPMD plotfiles are ENABLED. Face-centered variables will only be available as cell-centered averages in plotfiles!")
else()
set(openPMDSources "")
endif()

# HDF5
find_package(HDF5 REQUIRED)

Expand Down Expand Up @@ -97,7 +116,7 @@ include(CTest)

#create an object library for files that should be compiled with all test problems
set (QuokkaObjSources "${CMAKE_CURRENT_SOURCE_DIR}/main.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/CloudyCooling.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/GrackleDataReader.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/GrackleDataReader.cpp" "${openPMDSources}"
"${gamma_law_sources}")
#we don't use it anymore because it gives issues on Cray systems
#add_library(QuokkaObj OBJECT ${QuokkaObjSources} ${gamma_law_sources})
Expand Down
2 changes: 1 addition & 1 deletion src/PrimordialChem/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ setup_target_for_microphysics_compilation(${microphysics_network_name} "${CMAKE_
#this is critical to ensure the correct Microphysics files are linked to primordial chem target
include_directories(BEFORE ${primordial_chem_dirs} "${CMAKE_CURRENT_BINARY_DIR}/" "includes/extern_parameters.H" "includes/network_properties.H")

add_executable(test_primordial_chem test_primordial_chem.cpp ../main.cpp ../GrackleDataReader.cpp ../CloudyCooling.cpp ../Chemistry.cpp ${primordial_chem_sources})
add_executable(test_primordial_chem test_primordial_chem.cpp ../main.cpp "${openPMDSources}" ../GrackleDataReader.cpp ../CloudyCooling.cpp ../Chemistry.cpp ${primordial_chem_sources})
target_compile_definitions(test_primordial_chem PUBLIC PRIMORDIAL_CHEM) #this will add #define PRIMORDIAL_CHEM

#de-link QuokkaObj from this target to avoid multiple definitions of same variables
Expand Down
144 changes: 144 additions & 0 deletions src/openPMD.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
//==============================================================================
// TwoMomentRad - a radiation transport library for patch-based AMR codes
// Copyright 2020 Benjamin Wibking.
// Released under the MIT license. See LICENSE file included in the GitHub repo.
//==============================================================================
//! \file openPMD.cpp
/// \brief openPMD I/O for snapshots

#include <cstdint>
#include <string>

// AMReX headers
#include "AMReX.H"
#include "AMReX_Geometry.H"
#include "AMReX_IntVect.H"
#include "AMReX_MultiFab.H"

// openPMD headers
#include "openPMD/openPMD.hpp"

namespace quokka::OpenPMDOutput
{

namespace detail
{
/** \brief
* Convert an IntVect to a std::vector<std::uint64_t>
* and reverse the order of the elements
* (used for compatibility with the openPMD API)
*/
auto getReversedVec(const amrex::IntVect &v) -> std::vector<std::uint64_t>
{
// Convert the IntVect v to and std::vector u
std::vector<std::uint64_t> u = {AMREX_D_DECL(static_cast<std::uint64_t>(v[0]), static_cast<std::uint64_t>(v[1]), static_cast<std::uint64_t>(v[2]))};
// Reverse the order of elements, if v corresponds to the indices of a Fortran-order array (like an AMReX FArrayBox)
// but u is intended to be used with a C-order API (like openPMD)
std::reverse(u.begin(), u.end());
return u;
}

/** \brief
* Convert Real* pointer to a std::vector<double>,
* and reverse the order of the elements
* (used for compatibility with the openPMD API)
*/
auto getReversedVec(const amrex::Real *v) -> std::vector<double>
{
// Convert Real* v to and std::vector u
std::vector<double> u = {AMREX_D_DECL(static_cast<double>(v[0]), static_cast<double>(v[1]), static_cast<double>(v[2]))}; // NOLINT
// Reverse the order of elements, if v corresponds to the indices of a Fortran-order array (like an AMReX FArrayBox)
// but u is intended to be used with a C-order API (like openPMD)
std::reverse(u.begin(), u.end());
return u;
}

void SetupMeshComponent(openPMD::Mesh &mesh, amrex::Geometry &full_geom)
{
amrex::Box const &global_box = full_geom.Domain();
auto global_size = getReversedVec(global_box.size());
std::vector<double> const grid_spacing = getReversedVec(full_geom.CellSize());
std::vector<double> const global_offset = getReversedVec(full_geom.ProbLo());

// Prepare the type of dataset that will be written
mesh.setDataOrder(openPMD::Mesh::DataOrder::C);
mesh.setGridSpacing(grid_spacing);
mesh.setGridGlobalOffset(global_offset);
mesh.setAttribute("fieldSmoothing", "none");

auto mesh_comp = mesh[openPMD::MeshRecordComponent::SCALAR];
auto const dataset = openPMD::Dataset(openPMD::determineDatatype<amrex::Real>(), global_size);
mesh_comp.resetDataset(dataset);
std::vector<amrex::Real> const relativePosition{0.5, 0.5, 0.5}; // cell-centered only (for now)
mesh_comp.setPosition(relativePosition);
}

auto GetMeshComponentName(int meshLevel, std::string const &field_name) -> std::string
{
std::string new_field_name = field_name;
if (meshLevel > 0) {
new_field_name += std::string("_lvl").append(std::to_string(meshLevel));
}
return new_field_name;
}
} // namespace detail
//----------------------------------------------------------------------------------------
//! \fn void OpenPMDOutput:::WriteOutputFile(Mesh *pm)
// \brief Write cell-centered MultiFab using openPMD
void WriteFile(const std::vector<std::string> &varnames, int const output_levels, amrex::Vector<const amrex::MultiFab *> &mf,
amrex::Vector<amrex::Geometry> &geom, const std::string &output_basename, amrex::Real const time, int const file_number)
{
// open file
std::string const filename = output_basename + ".bp";
auto series = openPMD::Series(filename, openPMD::Access::CREATE, amrex::ParallelDescriptor::Communicator());
series.setSoftware("Quokka", "1.0");

auto series_iteration = series.iterations[file_number];
series_iteration.open();
series_iteration.setTime(time);
auto meshes = series_iteration.meshes;

// loop over levels up to output_levels
for (int lev = 0; lev < output_levels; lev++) {
amrex::Geometry full_geom = geom[lev];
amrex::Box const &global_box = full_geom.Domain();
int const ncomp = mf[lev]->nComp();

for (int icomp = 0; icomp < ncomp; icomp++) {
const std::string field_name = detail::GetMeshComponentName(lev, varnames[icomp]);
if (!meshes.contains(field_name)) {
auto mesh = meshes[field_name];
detail::SetupMeshComponent(mesh, full_geom);
}
}

// pass data pointers for each box to ADIOS
for (int icomp = 0; icomp < ncomp; icomp++) {
std::string const field_name = detail::GetMeshComponentName(lev, varnames[icomp]);
openPMD::MeshRecordComponent mesh = series_iteration.meshes[field_name][openPMD::MeshRecordComponent::SCALAR];

// Loop through the multifab, and store each box as a chunk in the openPMD file.
for (amrex::MFIter mfi(*mf[lev]); mfi.isValid(); ++mfi) {
amrex::FArrayBox const &fab = (*mf[lev])[mfi];
amrex::Box const &local_box = fab.box();

// Determine the offset and size of this chunk
amrex::IntVect const box_offset = local_box.smallEnd() - global_box.smallEnd();
auto chunk_offset = detail::getReversedVec(box_offset); // this overflows if ghost zones are used (!)
auto chunk_size = detail::getReversedVec(local_box.size());

// pass device pointer directly to ADIOS
amrex::Real const *local_data = fab.dataPtr(icomp);
mesh.storeChunkRaw(local_data, chunk_offset, chunk_size);
}
}

// flush this level to disk
series.flush();
}

// close file
series.close();
}

} // namespace quokka::OpenPMDOutput
40 changes: 40 additions & 0 deletions src/openPMD.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#ifndef OPENPMD_HPP_ // NOLINT
#define OPENPMD_HPP_
//==============================================================================
// TwoMomentRad - a radiation transport library for patch-based AMR codes
// Copyright 2020 Benjamin Wibking.
// Released under the MIT license. See LICENSE file included in the GitHub repo.
//==============================================================================
//! \file openPMD.hpp
/// \brief openPMD I/O for snapshots

#include <string>

// AMReX headers
#include "AMReX_Geometry.H"
#include "AMReX_IntVect.H"
#include "AMReX_MultiFab.H"
#include <AMReX.H>

// openPMD headers
#include "openPMD/openPMD.hpp"

namespace quokka::OpenPMDOutput
{

namespace detail
{

auto getReversedVec(const amrex::IntVect &v) -> std::vector<std::uint64_t>;
auto getReversedVec(const amrex::Real *v) -> std::vector<double>;
void SetupMeshComponent(openPMD::Mesh &mesh, int /*meshLevel*/, const std::string &comp_name, amrex::Geometry &full_geom);
auto GetMeshComponentName(int meshLevel, std::string const &field_name) -> std::string;

} // namespace detail

void WriteFile(const std::vector<std::string> &varnames, int output_levels, amrex::Vector<const amrex::MultiFab *> &mf, amrex::Vector<amrex::Geometry> &geom,
const std::string &output_basename, amrex::Real time, int file_number);

} // namespace quokka::OpenPMDOutput

#endif // OPENPMD_HPP_
Loading