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

Defining and computing derived variables #3816

Merged
merged 4 commits into from
Nov 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
5 changes: 3 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@ adios_option(Endian_Reverse "Enable support for Little/Big Endian Interoperabili
adios_option(Sodium "Enable support for Sodium for encryption" AUTO)
adios_option(Catalyst "Enable support for in situ visualization plugin using ParaView Catalyst" AUTO)
adios_option(AWSSDK "Enable support for S3 compatible storage using AWS SDK's S3 module" OFF)
adios_option(Derived_Variable "Enable support for derived variables" OFF)
include(${PROJECT_SOURCE_DIR}/cmake/DetectOptions.cmake)

if(ADIOS2_HAVE_CUDA OR ADIOS2_HAVE_Kokkos_CUDA)
Expand Down Expand Up @@ -243,8 +244,8 @@ endif()
set(ADIOS2_CONFIG_OPTS
DataMan DataSpaces HDF5 HDF5_VOL MHS SST Fortran MPI Python Blosc2 BZip2
LIBPRESSIO MGARD PNG SZ ZFP DAOS IME O_DIRECT Sodium Catalyst SysVShMem UCX
ZeroMQ Profiling Endian_Reverse AWSSDK GPU_Support CUDA Kokkos Kokkos_CUDA
Kokkos_HIP Kokkos_SYCL
ZeroMQ Profiling Endian_Reverse Derived_Variable AWSSDK GPU_Support CUDA Kokkos
Kokkos_CUDA Kokkos_HIP Kokkos_SYCL
)

GenerateADIOSHeaderConfig(${ADIOS2_CONFIG_OPTS})
Expand Down
14 changes: 14 additions & 0 deletions bindings/CXX11/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@ target_include_directories(adios2_cxx11

add_library(adios2::cxx11 ALIAS adios2_cxx11)

if (ADIOS2_HAVE_Derived_Variable)
target_sources(adios2_cxx11 PRIVATE
adios2/cxx11/VariableDerived.cpp
)
endif()

if(ADIOS2_HAVE_MPI)
add_library(adios2_cxx11_mpi
adios2/cxx11/ADIOSMPI.cpp
Expand Down Expand Up @@ -79,6 +85,14 @@ install(
COMPONENT adios2_cxx11-development
)

if (ADIOS2_HAVE_Derived_Variable)
install(
FILES adios2/cxx11/VariableDerived.h
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/adios2/cxx11
COMPONENT adios2_cxx11-development
)
endif()

install(
FILES adios2/cxx11/ADIOS.h
adios2/cxx11/ADIOS.inl
Expand Down
10 changes: 10 additions & 0 deletions bindings/CXX11/adios2/cxx11/IO.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,16 @@ VariableNT IO::DefineVariable(const DataType type, const std::string &name, cons
}
}

#ifdef ADIOS2_HAVE_DERIVED_VARIABLE
VariableDerived IO::DefineDerivedVariable(const std::string &name, const std::string &expression,
const DerivedVarType varType)
{
helper::CheckForNullptr(m_IO,
"for variable name " + name + ", in call to IO::DefineDerivedVariable");

return VariableDerived(&m_IO->DefineDerivedVariable(name, expression, varType));
}
#endif
StructDefinition IO::DefineStruct(const std::string &name, const size_t size)
{
helper::CheckForNullptr(m_IO, "for struct name " + name + ", in call to IO::DefineStruct");
Expand Down
9 changes: 8 additions & 1 deletion bindings/CXX11/adios2/cxx11/IO.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@
#include "Group.h"
#include "Operator.h"
#include "Variable.h"
#ifdef ADIOS2_HAVE_DERIVED_VARIABLE
#include "VariableDerived.h"
#endif
#include "VariableNT.h"
#include "adios2/common/ADIOSMacros.h"
#include "adios2/common/ADIOSTypes.h"
Expand Down Expand Up @@ -151,7 +154,11 @@ class IO
Variable<T> DefineVariable(const std::string &name, const Dims &shape = Dims(),
const Dims &start = Dims(), const Dims &count = Dims(),
const bool constantDims = false);

#ifdef ADIOS2_HAVE_DERIVED_VARIABLE
VariableDerived
DefineDerivedVariable(const std::string &name, const std::string &expression,
const DerivedVarType varType = DerivedVarType::MetadataOnly);
#endif
VariableNT DefineVariable(const DataType type, const std::string &name,
const Dims &shape = Dims(), const Dims &start = Dims(),
const Dims &count = Dims(), const bool constantDims = false);
Expand Down
8 changes: 8 additions & 0 deletions bindings/CXX11/adios2/cxx11/VariableDerived.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#include "VariableDerived.h"

#include "adios2/core/VariableDerived.h"

namespace adios2
{
VariableDerived::VariableDerived(core::VariableDerived *variable) : m_VariableDerived(variable) {}
} // end namespace adios2
43 changes: 43 additions & 0 deletions bindings/CXX11/adios2/cxx11/VariableDerived.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#ifndef ADIOS2_BINDINGS_CXX11_VARIABLE_DERIVED_H_
#define ADIOS2_BINDINGS_CXX11_VARIABLE_DERIVED_H_

#include "Operator.h"
#include "adios2/common/ADIOSTypes.h"

namespace adios2
{

/// \cond EXCLUDE_FROM_DOXYGEN
// forward declare
class IO; // friend
namespace core
{

class VariableDerived; // private implementation
}
/// \endcond

class VariableDerived
{
friend class IO;

public:
/**
* Empty (default) constructor, use it as a placeholder for future
* variables from IO:DefineVariableDerived<T> or IO:InquireVariableDerived<T>.
* Can be used with STL containers.
*/
VariableDerived() = default;

/** Default, using RAII STL containers */
~VariableDerived() = default;

private:
core::VariableDerived *m_VariableDerived = nullptr;

VariableDerived(core::VariableDerived *variable);
};

} // end namespace adios2

#endif // ADIOS2_BINDINGS_CXX11_VARIABLE_DERIVED_H_
4 changes: 4 additions & 0 deletions cmake/DetectOptions.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,10 @@ endif()

set(mpi_find_components C)

if(ADIOS2_USE_Derived_Variable)
set(ADIOS2_HAVE_Derived_Variable TRUE)
endif()

if(ADIOS2_USE_Kokkos AND ADIOS2_USE_CUDA)
message(FATAL_ERROR "ADIOS2_USE_Kokkos is incompatible with ADIOS2_USE_CUDA")
endif()
Expand Down
18 changes: 17 additions & 1 deletion source/adios2/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,21 @@ add_library(adios2_core
set_property(TARGET adios2_core PROPERTY EXPORT_NAME core)
set_property(TARGET adios2_core PROPERTY OUTPUT_NAME adios2${ADIOS2_LIBRARY_SUFFIX}_core)

set(maybe_adios2_core_derived)
if (ADIOS2_HAVE_Derived_Variable)
target_sources(adios2_core PRIVATE
core/VariableDerived.cpp
toolkit/derived/Expression.cpp
toolkit/derived/Function.cpp toolkit/derived/Function.tcc
toolkit/derived/ExprHelper.h)
add_library(adios2_core_derived
toolkit/derived/parser/lexer.cpp
toolkit/derived/parser/parser.cpp
toolkit/derived/parser/ASTNode.cpp)
target_link_libraries(adios2_core PRIVATE adios2_core_derived)
set(maybe_adios2_core_derived adios2_core_derived)
endif()

set(maybe_adios2_core_cuda)
if(ADIOS2_HAVE_CUDA)
add_library(adios2_core_cuda helper/adiosCUDA.cu)
Expand Down Expand Up @@ -447,10 +462,11 @@ install(DIRECTORY toolkit/
PATTERN "*/*.inl"
REGEX "sst/util" EXCLUDE
REGEX "sst/dp" EXCLUDE
REGEX "derived/parser" EXCLUDE
)

# Library installation
install(TARGETS adios2_core ${maybe_adios2_core_mpi} ${maybe_adios2_core_cuda} ${maybe_adios2_core_kokkos} ${maybe_adios2_blosc2} EXPORT adios2Exports
install(TARGETS adios2_core ${maybe_adios2_core_mpi} ${maybe_adios2_core_cuda} ${maybe_adios2_core_kokkos} ${maybe_adios2_blosc2} ${maybe_adios2_core_derived} EXPORT adios2Exports
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT adios2_core-runtime
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT adios2_core-libraries NAMELINK_COMPONENT adios2_core-development
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT adios2_core-development
Expand Down
10 changes: 10 additions & 0 deletions source/adios2/common/ADIOSTypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,16 @@
namespace adios2
{

#ifdef ADIOS2_HAVE_DERIVED_VARIABLE
/** Type of derived variables */
enum class DerivedVarType
{
MetadataOnly, ///< Store only the metadata (default)
ExpressionString, ///< Store only the expression string
StoreData ///< Store data and metadata
};
#endif

/** Memory space for the user provided buffers */
enum class MemorySpace
{
Expand Down
89 changes: 89 additions & 0 deletions source/adios2/core/IO.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,9 @@ void IO::SetTransportParameter(const size_t transportIndex, const std::string ke
}

const VarMap &IO::GetVariables() const noexcept { return m_Variables; }
#ifdef ADIOS2_HAVE_DERIVED_VARIABLE
const VarMap &IO::GetDerivedVariables() const noexcept { return m_VariablesDerived; }
#endif

const AttrMap &IO::GetAttributes() const noexcept { return m_Attributes; }

Expand Down Expand Up @@ -808,6 +811,92 @@ void IO::CheckTransportType(const std::string type) const
}
}

#ifdef ADIOS2_HAVE_DERIVED_VARIABLE
VariableDerived &IO::DefineDerivedVariable(const std::string &name, const std::string &exp_string,
const DerivedVarType varType)
{
PERFSTUBS_SCOPED_TIMER("IO::DefineDerivedVariable");

{
auto itVariable = m_VariablesDerived.find(name);
if (itVariable != m_VariablesDerived.end())
{
helper::Throw<std::invalid_argument>("Core", "IO", "DefineDerivedVariable",
"derived variable " + name +
" already defined in IO " + m_Name);
}
else
{
auto itVariable = m_Variables.find(name);
if (itVariable != m_Variables.end())
{
helper::Throw<std::invalid_argument>(
"Core", "IO", "DefineDerivedVariable",
"derived variable " + name +
" trying to use an already defined variable name in IO " + m_Name);
}
}
}

derived::Expression derived_exp(exp_string);
std::vector<std::string> var_list = derived_exp.VariableNameList();
DataType expressionType = DataType::None;
bool isConstant = true;
std::map<std::string, std::tuple<Dims, Dims, Dims>> name_to_dims;
// check correctness for the variable names and types within the expression
for (auto var_name : var_list)
{
auto itVariable = m_Variables.find(var_name);
if (itVariable == m_Variables.end())
helper::Throw<std::invalid_argument>("Core", "IO", "DefineDerivedVariable",
"using undefine variable " + var_name +
" in defining the derived variable " + name);
DataType var_type = InquireVariableType(var_name);
if (expressionType == DataType::None)
expressionType = var_type;
if (expressionType != var_type)
helper::Throw<std::invalid_argument>("Core", "IO", "DefineDerivedVariable",
"all variables within a derived variable "
" must have the same type ");
if ((itVariable->second)->IsConstantDims() == false)
isConstant = false;
name_to_dims.insert({var_name,
{(itVariable->second)->m_Start, (itVariable->second)->m_Count,
(itVariable->second)->m_Shape}});
}
// std::cout << "Derived variable " << name << ": PASS : variables exist and have the same type"
// << std::endl;
// set the initial shape of the expression and check correcness
derived_exp.SetDims(name_to_dims);
// std::cout << "Derived variable " << name << ": PASS : initial variable dimensions are valid"
// << std::endl;

// create derived variable with the expression
auto itVariablePair = m_VariablesDerived.emplace(
name, std::unique_ptr<VariableBase>(
new VariableDerived(name, derived_exp, expressionType, isConstant, varType)));
VariableDerived &variable = static_cast<VariableDerived &>(*itVariablePair.first->second);

// check IO placeholder for variable operations
auto itOperations = m_VarOpsPlaceholder.find(name);
if (itOperations != m_VarOpsPlaceholder.end())
{
// allow to apply an operation only for derived variables that save the data
if (varType != DerivedVarType::StoreData)
helper::Throw<std::invalid_argument>(
"Core", "IO", "DefineDerivedVariable",
"Operators for derived variables can only be applied "
" for DerivedVarType::StoreData types.");
variable.m_Operations.reserve(itOperations->second.size());
for (auto &operation : itOperations->second)
{
variable.AddOperation(operation.first, operation.second);
}
}
return variable;
}
#endif

StructDefinition &IO::DefineStruct(const std::string &name, const size_t size)
{
return m_ADIOS.m_StructDefinitions.emplace(name, StructDefinition(name, size))->second;
Expand Down
15 changes: 14 additions & 1 deletion source/adios2/core/IO.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@
#include "adios2/core/CoreTypes.h"
#include "adios2/core/Group.h"
#include "adios2/core/Variable.h"
#ifdef ADIOS2_HAVE_DERIVED_VARIABLE
#include "adios2/core/VariableDerived.h"
#endif
#include "adios2/core/VariableStruct.h"

namespace adios2
Expand Down Expand Up @@ -179,7 +182,11 @@ class IO
Variable<T> &DefineVariable(const std::string &name, const Dims &shape = Dims(),
const Dims &start = Dims(), const Dims &count = Dims(),
const bool constantDims = false);

#ifdef ADIOS2_HAVE_DERIVED_VARIABLE
VariableDerived &
DefineDerivedVariable(const std::string &name, const std::string &expression,
const DerivedVarType varType = DerivedVarType::MetadataOnly);
#endif
VariableStruct &DefineStructVariable(const std::string &name, StructDefinition &def,
const Dims &shape = Dims(), const Dims &start = Dims(),
const Dims &count = Dims(),
Expand Down Expand Up @@ -304,6 +311,9 @@ class IO
* </pre>
*/
const VarMap &GetVariables() const noexcept;
#ifdef ADIOS2_HAVE_DERIVED_VARIABLE
const VarMap &GetDerivedVariables() const noexcept;
#endif

/**
* Retrieves hash holding internal Attributes identifiers
Expand Down Expand Up @@ -500,6 +510,9 @@ class IO
adios2::IOMode m_IOMode = adios2::IOMode::Independent;

VarMap m_Variables;
#ifdef ADIOS2_HAVE_DERIVED_VARIABLE
VarMap m_VariablesDerived;
#endif

AttrMap m_Attributes;

Expand Down
Loading
Loading