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

Piecewise version of loadExternalData #1494

Merged
merged 7 commits into from
Jan 24, 2025
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
34 changes: 33 additions & 1 deletion src/axom/sidre/core/Group.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2266,7 +2266,7 @@ bool Group::load(const hid_t& h5_id,
*
* Load External Data from an hdf5 file
*
* Note: this ASSUMES uses the "sidre_hdf5" protocol
* Note: this ASSUMES usage of the "sidre_hdf5" protocol
*************************************************************************
*/
bool Group::loadExternalData(const hid_t& h5_id)
Expand All @@ -2281,6 +2281,38 @@ bool Group::loadExternalData(const hid_t& h5_id)
return !(getDataStore()->getConduitErrorOccurred());
}

/*
*************************************************************************
*
* Load External Data from a path within an hdf5 file
*
* Note: this ASSUMES usage of the "sidre_hdf5" protocol
*************************************************************************
*/
bool Group::loadExternalData(const hid_t& h5_id, const std::string& group_path)
{
bool success;
std::string delim(1, getPathDelimiter());
if(group_path.empty() || group_path == delim)
{
// An empty or trivial path means the load should read from the start of
// the file.
success = loadExternalData(h5_id);
}
else
{
Node n;
createExternalLayout(n);
ConduitErrorSuppressor checkConduitCall(getDataStore());

std::string read_path("sidre/external/" + group_path);

checkConduitCall([&] { conduit::relay::io::hdf5_read(h5_id, read_path, n); });
success = !(getDataStore()->getConduitErrorOccurred());
}
return success;
}

#endif /* AXOM_USE_HDF5 */

////////////////////////////////////////////////////////////////////////
Expand Down
18 changes: 17 additions & 1 deletion src/axom/sidre/core/Group.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1650,7 +1650,7 @@ class Group
std::string& name_from_file);

/*!
* \brief Load data into the Group's external views from a hdf5 handle.
* \brief Load data into the Group's external views from an hdf5 handle.
*
* No protocol argument is needed, as this only is used with the sidre_hdf5
* protocol. Returns true (success) if no Conduit I/O error occurred since
Expand All @@ -1662,6 +1662,22 @@ class Group
*/
bool loadExternalData(const hid_t& h5_id);

/*!
* \brief Load data into the Group's external views from a path into
* hdf5 handle
*
* No protocol argument is needed, as this only is used with the sidre_hdf5
* protocol. Returns true (success) if no Conduit I/O error occurred since
* this Group's DataStore was created or had its error flag cleared; false,
* if an error occurred at some point.
*
* \param h5_id hdf5 handle
* \param group_path Path pointing to this Group. This path must be the
* relative path from the top Group that was written
* to a file to this Group.
*/
bool loadExternalData(const hid_t& h5_id, const std::string& group_path);

#endif /* AXOM_USE_HDF5 */

//@}
Expand Down
155 changes: 155 additions & 0 deletions src/axom/sidre/spio/IOManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -465,6 +465,161 @@ void IOManager::loadExternalData(sidre::Group* datagroup,
(void)m_baton->pass();
}

void IOManager::loadExternalData(sidre::Group* parent_group,
sidre::Group* load_group,
const std::string& root_file)
{
int num_files = getNumFilesFromRoot(root_file);
int num_groups = getNumGroupsFromRoot(root_file);
SLIC_ASSERT(num_files > 0);
SLIC_ASSERT(num_groups > 0);

if(m_baton)
{
if(m_baton->getNumFiles() != num_files)
{
delete m_baton;
m_baton = nullptr;
}
}

if(!m_baton)
{
m_baton = new IOBaton(m_mpi_comm, num_files, num_groups);
}

#ifdef AXOM_USE_HDF5
std::string file_pattern = getHDF5FilePattern(root_file);

std::string parent_name(parent_group->getPathName());
std::string load_name(load_group->getPathName());
axom::Path parent_path(parent_name);
axom::Path load_path(load_name);
std::vector<std::string> parent_parts(parent_path.parts());
std::vector<std::string> load_parts(load_path.parts());

size_t parent_size = parent_parts.size();
bool can_load = true;
if(load_parts.size() < parent_size)
{
can_load = false;
}

for(size_t i = 0; i < parent_size; ++i)
{
if(parent_parts[i] != load_parts[i])
{
can_load = false;
}
}

std::string subpath = load_name.substr(parent_name.size());
char delimiter = parent_group->getPathDelimiter();
if(!subpath.empty() && subpath[0] == delimiter)
{
subpath.erase(0, 1);
}

if(!subpath.empty() && !parent_group->hasGroup(subpath))
{
can_load = false;
}

int set_id = m_baton->wait();

if(can_load)
{
if(num_groups <= m_comm_size)
{
if(m_my_rank < num_groups)
{
herr_t errv;
AXOM_UNUSED_VAR(errv);

std::string hdf5_name =
getFileNameForRank(file_pattern, root_file, set_id);

hdf5_name = getSCRPath(hdf5_name);

hid_t h5_file_id = conduit::relay::io::hdf5_open_file_for_read(hdf5_name);
SLIC_ASSERT(h5_file_id >= 0);

std::string group_name = "datagroup";
if(H5Lexists(h5_file_id, group_name.c_str(), 0) <= 0)
{
group_name = fmt::sprintf("datagroup_%07d", m_my_rank);
}

hid_t h5_group_id = H5Gopen(h5_file_id, group_name.c_str(), 0);
SLIC_ASSERT(h5_group_id >= 0);

load_group->loadExternalData(h5_group_id, subpath);

errv = H5Gclose(h5_group_id);
SLIC_ASSERT(errv >= 0);

errv = H5Fclose(h5_file_id);
SLIC_ASSERT(errv >= 0);
}
}
else
{
for(int input_rank = m_my_rank; input_rank < num_groups;
input_rank += m_comm_size)
{
herr_t errv;
AXOM_UNUSED_VAR(errv);

std::string hdf5_name =
getFileNameForRank(file_pattern, root_file, input_rank);

hdf5_name = getSCRPath(hdf5_name);

hid_t h5_file_id = conduit::relay::io::hdf5_open_file_for_read(hdf5_name);
SLIC_ASSERT(h5_file_id >= 0);

std::string group_name = "datagroup";
if(H5Lexists(h5_file_id, group_name.c_str(), 0) <= 0)
{
group_name = fmt::sprintf("datagroup_%07d", input_rank);
}

hid_t h5_group_id = H5Gopen(h5_file_id, group_name.c_str(), 0);
SLIC_ASSERT(h5_group_id >= 0);

std::string input_name = fmt::sprintf("rank_%07d", input_rank);

input_name = input_name + delimiter + "sidre_input" + delimiter + subpath;

Group* one_rank_input = parent_group->getGroup(input_name);

one_rank_input->loadExternalData(h5_group_id, subpath);

errv = H5Gclose(h5_group_id);
SLIC_ASSERT(errv >= 0);

errv = H5Fclose(h5_file_id);
SLIC_ASSERT(errv >= 0);
}
}
}
else
{
SLIC_WARNING("Path from parent group "
<< parent_group->getPathName() << " to group "
<< load_group->getPathName()
<< " was not found. No external data will be loaded.");
}

#else
AXOM_UNUSED_VAR(datagroup);
SLIC_WARNING("Loading external data only only available "
<< "when Axom is configured with hdf5");
#endif /* AXOM_USE_HDF5 */

(void)m_baton->pass();
}

/*
*************************************************************************
*
Expand Down
37 changes: 37 additions & 0 deletions src/axom/sidre/spio/IOManager.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -289,11 +289,48 @@ class IOManager
* This currently only works if the root file was created for protocol
* sidre_hdf5.
*
* The call to this method must follow a call to the IOManager::read with the
* same group and root file.
*
* This is intended as the third step of the three step process to load
* external data. The first step is the call to IOManager::read; the
* second step is to set valid pointers on all external views in the
* hierarchy under the passed-in group; the third step is the call to
* this method.
*
* \param group Group to fill with external data from input
* \param root_file root file containing input data
*/
void loadExternalData(sidre::Group* group, const std::string& root_file);

/*!
* \brief piecewise load of external data into a group
*
* This currently only works if the root file was created for protocol
* sidre_hdf5.
*
* This is intended as an alternative way to load external data, in a
* piecewise manner rather than loading all external data of a Sidre
* hierarchy at once. load_group is a Group somewhere in the hierarchy
* under parent_group, and only the external data for views in the
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is parent group needed here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In order to find the needed data in the HDF file, the method needs the relative path from parent_group to load_group. The logic to figure this out is inside the method. The alternative would be to force the user to construct this relative path and pass it in.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it. Thank you for the explanation.

* subtree under load_group will be loaded. In the second step of the
* three step process, the calling code should set valid pointers on all
* external views in the subtree under load_group. The third step is
* to call this method. parent_group must be the same group that was
* passed to IOManager::read with the same root file.
*
* This method may be called multiple times with different instances of
* load_group representing different subtrees of the hierachy under
* parent_group.
*
* \param parent_group Group that was passed to IOManager::read
* \param load_group Group holding views to be filled with external data
* \param root_file root file containing input data
*/
void loadExternalData(sidre::Group* parent_group,
sidre::Group* load_group,
const std::string& root_file);

/*!
* \brief gets the number of files in the dataset from the specified root file
*/
Expand Down
Loading