Skip to content

Commit

Permalink
Defining and computing derived variables (#3816)
Browse files Browse the repository at this point in the history
* Grammar related functions to parse derived variables

* Add the option to define derived variables kept in the IO object

* Update BP5 to be able to deal with derived variables

Co-authored-by: Greg Eisenhauer <[email protected]>

* Testing for correctness of derived variables using add and magnitude

Co-authored-by: lizdulac <[email protected]>

---------

Co-authored-by: lizdulac <[email protected]>
Co-authored-by: Greg Eisenhauer <[email protected]>
  • Loading branch information
3 people authored Nov 11, 2023
1 parent 19846e9 commit f691092
Show file tree
Hide file tree
Showing 37 changed files with 6,266 additions and 39 deletions.
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

0 comments on commit f691092

Please sign in to comment.