From 36e770183ff2b583c7069ac8246b40b1f7f7569f Mon Sep 17 00:00:00 2001 From: Justin Privitera Date: Thu, 28 Mar 2024 17:06:32 -0700 Subject: [PATCH 01/56] got quadmesh to wrok --- src/libs/relay/conduit_relay_io_silo.cpp | 87 +- src/tests/relay/t_relay_io_silo.cpp | 3645 +++++++++++----------- 2 files changed, 1904 insertions(+), 1828 deletions(-) diff --git a/src/libs/relay/conduit_relay_io_silo.cpp b/src/libs/relay/conduit_relay_io_silo.cpp index 5e33ff685..e71781601 100644 --- a/src/libs/relay/conduit_relay_io_silo.cpp +++ b/src/libs/relay/conduit_relay_io_silo.cpp @@ -2126,7 +2126,7 @@ bool read_multivars(DBtoc *toc, DBfile *dbfile, const std::string &multimesh_name, - const int nblocks, + const int &nblocks, Node &root_node, std::ostringstream &error_oss) { @@ -2493,8 +2493,74 @@ read_root_silo_index(const std::string &root_file_path, // check for multimeshes if (toc->nmultimesh <= 0) { - error_oss << "No multimesh found in file: " << root_file_path; - return false; + std::string meshname; + if (toc->nqmesh > 0) + { + meshname = toc->qmesh_names[0]; + root_node[meshname]["nblocks"] = 1; + root_node[meshname]["nameschemes"] = "no"; + root_node[meshname]["mesh_types"].set(DB_QUADMESH); + Node &mesh_path = root_node[meshname]["mesh_paths"].append(); + mesh_path.set(meshname); + } + + multimesh_name = meshname; + + if (toc->nqvar > 0 && !meshname.empty()) + { + for (int var_id = 0; var_id < toc->nqvar; var_id ++) + { + const std::string varname = toc->qvar_names[var_id]; + Node &var = root_node[meshname]["vars"][varname]; + var["nameschemes"] = "no"; + var["var_types"].set(DB_QUADVAR); + Node &var_path = var["var_paths"].append(); + var_path.set(varname); + } + } + + if (toc->nmat > 0 && !meshname.empty()) + { + const std::string matname = toc->mat_names[0]; + Node &material = root_node[meshname]["matsets"][matname]; + material["nameschemes"] = "no"; + Node &matset_path = material["matset_paths"].append(); + matset_path.set(matname); + } + + read_state(dbfile.getSiloObject(), root_node, meshname); + + // overlink-specific + read_var_attributes(dbfile.getSiloObject(), + meshname, + root_node); + + // Get the selected matset flavor + if (opts.has_child("matset_style") && opts["matset_style"].dtype().is_string()) + { + std::string opts_matset_style = opts["matset_style"].as_string(); + if (opts_matset_style != "default" && + opts_matset_style != "multi_buffer_full" && + opts_matset_style != "sparse_by_element" && + opts_matset_style != "multi_buffer_by_material") + { + CONDUIT_ERROR("read_mesh invalid matset_style option: \"" + << opts_matset_style << "\"\n" + " expected: \"default\", \"multi_buffer_full\", " + "\"sparse_by_element\", or \"multi_buffer_by_material\""); + } + else + { + root_node[meshname]["matset_style"] = opts_matset_style; + } + } + + std::cout << root_node.to_yaml() << std::endl; + + return true; + + // error_oss << "No multimesh found in file: " << root_file_path; + // return false; } // decide what multimesh to extract @@ -2579,9 +2645,10 @@ read_root_silo_index(const std::string &root_file_path, { root_node[multimesh_name]["matset_style"] = opts_matset_style; } - } + // TODO why not have an option to read multiple multimeshes? + // our silo index should look like this: // mesh: @@ -2768,11 +2835,7 @@ read_mesh(const std::string &root_file_path, // If the root file is named OvlTop.silo, then there is a very good chance that // this file is valid overlink. Therefore, we must modify the paths we get from // the root node to reflect this. - bool ovltop_case = false; - if (root_file_name == "OvlTop.silo") - { - ovltop_case = true; - } + bool ovltop_case = root_file_name == "OvlTop.silo"; for (int domain_id = domain_start; domain_id < domain_end; domain_id ++) { @@ -2780,9 +2843,9 @@ read_mesh(const std::string &root_file_path, // Read Mesh // - std::string silo_mesh_path = mesh_index["mesh_paths"][domain_id].as_string(); - int_accessor meshtypes = mesh_index["mesh_types"].value(); - int meshtype = meshtypes[domain_id]; + const std::string silo_mesh_path = mesh_index["mesh_paths"][domain_id].as_string(); + const int_accessor meshtypes = mesh_index["mesh_types"].value(); + const int meshtype = meshtypes[domain_id]; std::string mesh_name, mesh_domain_filename; mesh_path_gen.GeneratePaths(silo_mesh_path, relative_dir, mesh_domain_filename, mesh_name); diff --git a/src/tests/relay/t_relay_io_silo.cpp b/src/tests/relay/t_relay_io_silo.cpp index 994039876..242570849 100644 --- a/src/tests/relay/t_relay_io_silo.cpp +++ b/src/tests/relay/t_relay_io_silo.cpp @@ -132,1834 +132,1847 @@ TEST(conduit_relay_io_silo, load_mesh_geometry) } //----------------------------------------------------------------------------- -TEST(conduit_relay_io_silo, round_trip_basic) +TEST(conduit_relay_io_silo, read_curv2d) { - const std::vector> mesh_types = { - std::make_pair("uniform", "2"), std::make_pair("uniform", "3"), - std::make_pair("rectilinear", "2"), std::make_pair("rectilinear", "3"), - std::make_pair("structured", "2"), std::make_pair("structured", "3"), - std::make_pair("tris", "2"), - std::make_pair("quads", "2"), - std::make_pair("polygons", "2"), - std::make_pair("tets", "3"), - std::make_pair("hexs", "3"), - std::make_pair("wedges", "3"), - std::make_pair("pyramids", "3"), - // std::make_pair("polyhedra", "3") - }; - for (int i = 0; i < mesh_types.size(); ++i) - { - std::string dim = mesh_types[i].second; - index_t nx = 3; - index_t ny = 4; - index_t nz = (dim == "2" ? 0 : 2); - - const std::string mesh_type = mesh_types[i].first; - - Node save_mesh, load_mesh, info; - blueprint::mesh::examples::basic(mesh_type, nx, ny, nz, save_mesh); - - const std::string basename = "silo_basic_" + mesh_type + "_" + dim + "D"; - const std::string filename = basename + ".root"; - - remove_path_if_exists(filename); - io::silo::save_mesh(save_mesh, basename); - io::silo::load_mesh(filename, load_mesh); - EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - - // make changes to save mesh so the diff will pass - if (mesh_type == "uniform") - { - silo_uniform_to_rect_conversion("coords", "mesh", save_mesh); - } - silo_name_changer("mesh", save_mesh); - - // the loaded mesh will be in the multidomain format - // but the saved mesh is in the single domain format - EXPECT_EQ(load_mesh.number_of_children(), 1); - EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); - EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); - } -} - -//----------------------------------------------------------------------------- -// we are testing vector fields in this test -TEST(conduit_relay_io_silo, round_trip_braid) -{ - const std::vector> mesh_types = { - std::make_pair("uniform", "2"), std::make_pair("uniform", "3"), - std::make_pair("rectilinear", "2"), std::make_pair("rectilinear", "3"), - std::make_pair("structured", "2"), std::make_pair("structured", "3"), - std::make_pair("points", "2"), std::make_pair("points", "3"), - std::make_pair("points_implicit", "2"), std::make_pair("points_implicit", "3"), - std::make_pair("lines", "2"), std::make_pair("lines", "3"), - std::make_pair("tris", "2"), - std::make_pair("quads", "2"), - std::make_pair("tets", "3"), - std::make_pair("hexs", "3"), - std::make_pair("wedges", "3"), - std::make_pair("pyramids", "3"), - // std::make_pair("mixed_2d", "2"), - // std::make_pair("mixed", "3"), - }; - for (int i = 0; i < mesh_types.size(); ++i) - { - std::string dim = mesh_types[i].second; - index_t nx = 3; - index_t ny = 4; - index_t nz = (dim == "2" ? 0 : 2); - - std::string mesh_type = mesh_types[i].first; - - Node save_mesh, load_mesh, info; - blueprint::mesh::examples::braid(mesh_type, nx, ny, nz, save_mesh); - - const std::string basename = "silo_braid_" + mesh_type + "_" + dim + "D"; - const std::string filename = basename + ".cycle_000100.root"; - - // remove existing root file, directory and any output files - remove_path_if_exists(filename); - - io::silo::save_mesh(save_mesh, basename); - io::silo::load_mesh(filename, load_mesh); - EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - - // make changes to save mesh so the diff will pass - if (mesh_type == "uniform") - { - silo_uniform_to_rect_conversion("coords", "mesh", save_mesh); - } - if (mesh_type == "points") - { - // this is custom code for braid - // We know it is correct because the unstructured points version of braid - // uses every point in the coordset - save_mesh["topologies"].remove_child("mesh"); - save_mesh["topologies"]["mesh"]["type"] = "points"; - save_mesh["topologies"]["mesh"]["coordset"] = "coords"; - } - if (mesh_type == "points_implicit" || mesh_type == "points") - { - // the association doesn't matter for point meshes - // we choose vertex by convention - save_mesh["fields"]["radial"]["association"].reset(); - save_mesh["fields"]["radial"]["association"] = "vertex"; - } - silo_name_changer("mesh", save_mesh); - - // the loaded mesh will be in the multidomain format - // but the saved mesh is in the single domain format - EXPECT_EQ(load_mesh.number_of_children(), 1); - EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); - EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); - } -} - -//----------------------------------------------------------------------------- -// multidomain test -TEST(conduit_relay_io_silo, round_trip_spiral) -{ - for (int ndomains = 2; ndomains < 6; ndomains ++) - { - Node save_mesh, load_mesh, info; - blueprint::mesh::examples::spiral(ndomains, save_mesh); - - const std::string basename = "silo_spiral_" + std::to_string(ndomains) + "_domains"; - const std::string filename = basename + ".cycle_000000.root"; - - remove_path_if_exists(filename); - io::silo::save_mesh(save_mesh, basename); - io::silo::load_mesh(filename, load_mesh); - EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); - - // make changes to save mesh so the diff will pass - for (index_t child = 0; child < save_mesh.number_of_children(); child ++) - { - silo_name_changer("mesh", save_mesh[child]); - } - - EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); - NodeConstIterator l_itr = load_mesh.children(); - NodeConstIterator s_itr = save_mesh.children(); - while (l_itr.has_next()) - { - const Node &l_curr = l_itr.next(); - const Node &s_curr = s_itr.next(); - - EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); - } - } -} - -//----------------------------------------------------------------------------- -TEST(conduit_relay_io_silo, round_trip_julia) -{ - Node save_mesh, load_mesh, info; - blueprint::mesh::examples::julia(5, // nx - 5, // ny - 0, // x_min - 10, // x_max - 2, // y_min - 7, // y_max - 3, // c_re - 4, // c_im - save_mesh); - - const std::string basename = "silo_julia"; - const std::string filename = basename + ".root"; - - remove_path_if_exists(filename); - io::silo::save_mesh(save_mesh, basename); + Node load_mesh, info; + const std::string filename = "/usr/workspace/justin/visit_builds/3.4RC-w-tpls-03_05_24/visit/build/testdata/silo_hdf5_test_data/curv2d.silo"; io::silo::load_mesh(filename, load_mesh); EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - // make changes to save mesh so the diff will pass - silo_name_changer("mesh", save_mesh); - - // the loaded mesh will be in the multidomain format - // but the saved mesh is in the single domain format - EXPECT_EQ(load_mesh.number_of_children(), 1); - EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); - EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); -} + io::blueprint::save_mesh(load_mesh, "curvmesh2d_blueprint", "hdf5"); -//----------------------------------------------------------------------------- -// test material write and read -TEST(conduit_relay_io_silo, round_trip_venn) -{ - std::string matset_type = "sparse_by_element"; - for (int j = 0; j < 2; j ++) - { - Node save_mesh, sbe, load_mesh, info; - std::string size; - int nx, ny; - const double radius = 0.25; - if (j == 0) - { - size = "small"; - nx = ny = 4; - } - else - { - size = "large"; - nx = ny = 100; - } - blueprint::mesh::examples::venn(matset_type, nx, ny, radius, save_mesh); - - const std::string basename = "silo_venn_" + matset_type + "_" + size; - const std::string filename = basename + ".root"; - - remove_path_if_exists(filename); - io::silo::save_mesh(save_mesh, basename); - io::silo::load_mesh(filename, load_mesh); - EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - - // make changes to save mesh so the diff will pass - - // The field mat_check has values that are one type and matset_values - // that are another type. The silo writer converts both to double arrays - // in this case, so we follow suit. - Node mat_check_new_values, mat_check_new_matset_values; - save_mesh["fields"]["mat_check"]["values"].to_double_array(mat_check_new_values); - save_mesh["fields"]["mat_check"]["matset_values"].to_double_array(mat_check_new_matset_values); - save_mesh["fields"]["mat_check"]["values"].set_external(mat_check_new_values); - save_mesh["fields"]["mat_check"]["matset_values"].set_external(mat_check_new_matset_values); - - silo_name_changer("mesh", save_mesh); - - // the loaded mesh will be in the multidomain format - // but the saved mesh is in the single domain format - EXPECT_EQ(load_mesh.number_of_children(), 1); - EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); - EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); - } + load_mesh.print(); } -//----------------------------------------------------------------------------- -TEST(conduit_relay_io_silo, round_trip_venn_modded_matnos) -{ - const std::string matset_type = "sparse_by_element"; - Node save_mesh, load_mesh, info; - const int nx = 4; - const int ny = 4; - const double radius = 0.25; - blueprint::mesh::examples::venn(matset_type, nx, ny, radius, save_mesh); - - auto replace_matno = [](int matno) - { - return (matno == 1 ? 15 : - (matno == 2 ? 37 : - (matno == 3 ? 4 : - (matno == 0 ? 22 : - -1)))); - }; - - auto matmap_itr = save_mesh["matsets"]["matset"]["material_map"].children(); - while (matmap_itr.has_next()) - { - Node &mat = matmap_itr.next(); - mat.set(replace_matno(mat.as_int())); - } - - int_array matids = save_mesh["matsets"]["matset"]["material_ids"].value(); - for (int i = 0; i < save_mesh["matsets"]["matset"]["material_ids"].dtype().number_of_elements(); i ++) - { - matids[i] = replace_matno(matids[i]); - } - - const std::string silo_basename = "silo_venn_" + matset_type + "_modded_matnos"; - const std::string silo_filename = silo_basename + ".root"; - remove_path_if_exists(silo_filename); - io::silo::save_mesh(save_mesh, silo_basename); - - const std::string bp_basename = "bp_venn_" + matset_type + "_modded_matnos"; - const std::string bp_filename = bp_basename + ".root"; - remove_path_if_exists(bp_filename); - io::blueprint::save_mesh(save_mesh, bp_basename, "hdf5"); +// //----------------------------------------------------------------------------- +// TEST(conduit_relay_io_silo, round_trip_basic) +// { +// const std::vector> mesh_types = { +// std::make_pair("uniform", "2"), std::make_pair("uniform", "3"), +// std::make_pair("rectilinear", "2"), std::make_pair("rectilinear", "3"), +// std::make_pair("structured", "2"), std::make_pair("structured", "3"), +// std::make_pair("tris", "2"), +// std::make_pair("quads", "2"), +// std::make_pair("polygons", "2"), +// std::make_pair("tets", "3"), +// std::make_pair("hexs", "3"), +// std::make_pair("wedges", "3"), +// std::make_pair("pyramids", "3"), +// // std::make_pair("polyhedra", "3") +// }; +// for (int i = 0; i < mesh_types.size(); ++i) +// { +// std::string dim = mesh_types[i].second; +// index_t nx = 3; +// index_t ny = 4; +// index_t nz = (dim == "2" ? 0 : 2); + +// const std::string mesh_type = mesh_types[i].first; + +// Node save_mesh, load_mesh, info; +// blueprint::mesh::examples::basic(mesh_type, nx, ny, nz, save_mesh); + +// const std::string basename = "silo_basic_" + mesh_type + "_" + dim + "D"; +// const std::string filename = basename + ".root"; + +// remove_path_if_exists(filename); +// io::silo::save_mesh(save_mesh, basename); +// io::silo::load_mesh(filename, load_mesh); +// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + +// // make changes to save mesh so the diff will pass +// if (mesh_type == "uniform") +// { +// silo_uniform_to_rect_conversion("coords", "mesh", save_mesh); +// } +// silo_name_changer("mesh", save_mesh); + +// // the loaded mesh will be in the multidomain format +// // but the saved mesh is in the single domain format +// EXPECT_EQ(load_mesh.number_of_children(), 1); +// EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); +// EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); +// } +// } + +// //----------------------------------------------------------------------------- +// // we are testing vector fields in this test +// TEST(conduit_relay_io_silo, round_trip_braid) +// { +// const std::vector> mesh_types = { +// std::make_pair("uniform", "2"), std::make_pair("uniform", "3"), +// std::make_pair("rectilinear", "2"), std::make_pair("rectilinear", "3"), +// std::make_pair("structured", "2"), std::make_pair("structured", "3"), +// std::make_pair("points", "2"), std::make_pair("points", "3"), +// std::make_pair("points_implicit", "2"), std::make_pair("points_implicit", "3"), +// std::make_pair("lines", "2"), std::make_pair("lines", "3"), +// std::make_pair("tris", "2"), +// std::make_pair("quads", "2"), +// std::make_pair("tets", "3"), +// std::make_pair("hexs", "3"), +// std::make_pair("wedges", "3"), +// std::make_pair("pyramids", "3"), +// // std::make_pair("mixed_2d", "2"), +// // std::make_pair("mixed", "3"), +// }; +// for (int i = 0; i < mesh_types.size(); ++i) +// { +// std::string dim = mesh_types[i].second; +// index_t nx = 3; +// index_t ny = 4; +// index_t nz = (dim == "2" ? 0 : 2); + +// std::string mesh_type = mesh_types[i].first; + +// Node save_mesh, load_mesh, info; +// blueprint::mesh::examples::braid(mesh_type, nx, ny, nz, save_mesh); + +// const std::string basename = "silo_braid_" + mesh_type + "_" + dim + "D"; +// const std::string filename = basename + ".cycle_000100.root"; + +// // remove existing root file, directory and any output files +// remove_path_if_exists(filename); + +// io::silo::save_mesh(save_mesh, basename); +// io::silo::load_mesh(filename, load_mesh); +// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + +// // make changes to save mesh so the diff will pass +// if (mesh_type == "uniform") +// { +// silo_uniform_to_rect_conversion("coords", "mesh", save_mesh); +// } +// if (mesh_type == "points") +// { +// // this is custom code for braid +// // We know it is correct because the unstructured points version of braid +// // uses every point in the coordset +// save_mesh["topologies"].remove_child("mesh"); +// save_mesh["topologies"]["mesh"]["type"] = "points"; +// save_mesh["topologies"]["mesh"]["coordset"] = "coords"; +// } +// if (mesh_type == "points_implicit" || mesh_type == "points") +// { +// // the association doesn't matter for point meshes +// // we choose vertex by convention +// save_mesh["fields"]["radial"]["association"].reset(); +// save_mesh["fields"]["radial"]["association"] = "vertex"; +// } +// silo_name_changer("mesh", save_mesh); + +// // the loaded mesh will be in the multidomain format +// // but the saved mesh is in the single domain format +// EXPECT_EQ(load_mesh.number_of_children(), 1); +// EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); +// EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); +// } +// } + +// //----------------------------------------------------------------------------- +// // multidomain test +// TEST(conduit_relay_io_silo, round_trip_spiral) +// { +// for (int ndomains = 2; ndomains < 6; ndomains ++) +// { +// Node save_mesh, load_mesh, info; +// blueprint::mesh::examples::spiral(ndomains, save_mesh); + +// const std::string basename = "silo_spiral_" + std::to_string(ndomains) + "_domains"; +// const std::string filename = basename + ".cycle_000000.root"; + +// remove_path_if_exists(filename); +// io::silo::save_mesh(save_mesh, basename); +// io::silo::load_mesh(filename, load_mesh); +// EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); + +// // make changes to save mesh so the diff will pass +// for (index_t child = 0; child < save_mesh.number_of_children(); child ++) +// { +// silo_name_changer("mesh", save_mesh[child]); +// } + +// EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); +// NodeConstIterator l_itr = load_mesh.children(); +// NodeConstIterator s_itr = save_mesh.children(); +// while (l_itr.has_next()) +// { +// const Node &l_curr = l_itr.next(); +// const Node &s_curr = s_itr.next(); + +// EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); +// } +// } +// } + +// //----------------------------------------------------------------------------- +// TEST(conduit_relay_io_silo, round_trip_julia) +// { +// Node save_mesh, load_mesh, info; +// blueprint::mesh::examples::julia(5, // nx +// 5, // ny +// 0, // x_min +// 10, // x_max +// 2, // y_min +// 7, // y_max +// 3, // c_re +// 4, // c_im +// save_mesh); + +// const std::string basename = "silo_julia"; +// const std::string filename = basename + ".root"; + +// remove_path_if_exists(filename); +// io::silo::save_mesh(save_mesh, basename); +// io::silo::load_mesh(filename, load_mesh); +// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + +// // make changes to save mesh so the diff will pass +// silo_name_changer("mesh", save_mesh); + +// // the loaded mesh will be in the multidomain format +// // but the saved mesh is in the single domain format +// EXPECT_EQ(load_mesh.number_of_children(), 1); +// EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); +// EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); +// } + +// //----------------------------------------------------------------------------- +// // test material write and read +// TEST(conduit_relay_io_silo, round_trip_venn) +// { +// std::string matset_type = "sparse_by_element"; +// for (int j = 0; j < 2; j ++) +// { +// Node save_mesh, sbe, load_mesh, info; +// std::string size; +// int nx, ny; +// const double radius = 0.25; +// if (j == 0) +// { +// size = "small"; +// nx = ny = 4; +// } +// else +// { +// size = "large"; +// nx = ny = 100; +// } +// blueprint::mesh::examples::venn(matset_type, nx, ny, radius, save_mesh); + +// const std::string basename = "silo_venn_" + matset_type + "_" + size; +// const std::string filename = basename + ".root"; + +// remove_path_if_exists(filename); +// io::silo::save_mesh(save_mesh, basename); +// io::silo::load_mesh(filename, load_mesh); +// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + +// // make changes to save mesh so the diff will pass + +// // The field mat_check has values that are one type and matset_values +// // that are another type. The silo writer converts both to double arrays +// // in this case, so we follow suit. +// Node mat_check_new_values, mat_check_new_matset_values; +// save_mesh["fields"]["mat_check"]["values"].to_double_array(mat_check_new_values); +// save_mesh["fields"]["mat_check"]["matset_values"].to_double_array(mat_check_new_matset_values); +// save_mesh["fields"]["mat_check"]["values"].set_external(mat_check_new_values); +// save_mesh["fields"]["mat_check"]["matset_values"].set_external(mat_check_new_matset_values); + +// silo_name_changer("mesh", save_mesh); + +// // the loaded mesh will be in the multidomain format +// // but the saved mesh is in the single domain format +// EXPECT_EQ(load_mesh.number_of_children(), 1); +// EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); +// EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); +// } +// } + +// //----------------------------------------------------------------------------- +// TEST(conduit_relay_io_silo, round_trip_venn_modded_matnos) +// { +// const std::string matset_type = "sparse_by_element"; +// Node save_mesh, load_mesh, info; +// const int nx = 4; +// const int ny = 4; +// const double radius = 0.25; +// blueprint::mesh::examples::venn(matset_type, nx, ny, radius, save_mesh); + +// auto replace_matno = [](int matno) +// { +// return (matno == 1 ? 15 : +// (matno == 2 ? 37 : +// (matno == 3 ? 4 : +// (matno == 0 ? 22 : +// -1)))); +// }; + +// auto matmap_itr = save_mesh["matsets"]["matset"]["material_map"].children(); +// while (matmap_itr.has_next()) +// { +// Node &mat = matmap_itr.next(); +// mat.set(replace_matno(mat.as_int())); +// } + +// int_array matids = save_mesh["matsets"]["matset"]["material_ids"].value(); +// for (int i = 0; i < save_mesh["matsets"]["matset"]["material_ids"].dtype().number_of_elements(); i ++) +// { +// matids[i] = replace_matno(matids[i]); +// } + +// const std::string silo_basename = "silo_venn_" + matset_type + "_modded_matnos"; +// const std::string silo_filename = silo_basename + ".root"; +// remove_path_if_exists(silo_filename); +// io::silo::save_mesh(save_mesh, silo_basename); + +// const std::string bp_basename = "bp_venn_" + matset_type + "_modded_matnos"; +// const std::string bp_filename = bp_basename + ".root"; +// remove_path_if_exists(bp_filename); +// io::blueprint::save_mesh(save_mesh, bp_basename, "hdf5"); - io::silo::load_mesh(silo_filename, load_mesh); - EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - - // make changes to save mesh so the diff will pass - - // The field mat_check has values that are one type and matset_values - // that are another type. The silo writer converts both to double arrays - // in this case, so we follow suit. - Node mat_check_new_values, mat_check_new_matset_values; - save_mesh["fields"]["mat_check"]["values"].to_double_array(mat_check_new_values); - save_mesh["fields"]["mat_check"]["matset_values"].to_double_array(mat_check_new_matset_values); - save_mesh["fields"]["mat_check"]["values"].set_external(mat_check_new_values); - save_mesh["fields"]["mat_check"]["matset_values"].set_external(mat_check_new_matset_values); - - // to_silo is going to reorder mixed materials least to greatest - // so we must do the same - int_array mat_ids = save_mesh["matsets"]["matset"]["material_ids"].value(); - const auto mat_id10 = mat_ids[10]; - const auto mat_id11 = mat_ids[11]; - const auto mat_id12 = mat_ids[12]; - mat_ids[10] = mat_id12; - mat_ids[11] = mat_id10; - mat_ids[12] = mat_id11; - auto field_itr = save_mesh["fields"].children(); - while (field_itr.has_next()) - { - const Node &n_field = field_itr.next(); - if (n_field.has_child("matset")) - { - double_array matset_vals = n_field["matset_values"].value(); - const auto matset_val10 = matset_vals[10]; - const auto matset_val11 = matset_vals[11]; - const auto matset_val12 = matset_vals[12]; - matset_vals[10] = matset_val12; - matset_vals[11] = matset_val10; - matset_vals[12] = matset_val11; - } - } - - silo_name_changer("mesh", save_mesh); - - // the loaded mesh will be in the multidomain format - // but the saved mesh is in the single domain format - EXPECT_EQ(load_mesh.number_of_children(), 1); - EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); - - EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); -} - -//----------------------------------------------------------------------------- -TEST(conduit_relay_io_silo, round_trip_spiral_multi_dom_materials) -{ - Node save_mesh, load_mesh, info; - const int ndomains = 4; - blueprint::mesh::examples::spiral(ndomains, save_mesh); - add_matset_to_spiral(save_mesh, ndomains); - EXPECT_TRUE(blueprint::mesh::verify(save_mesh, info)); +// io::silo::load_mesh(silo_filename, load_mesh); +// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + +// // make changes to save mesh so the diff will pass + +// // The field mat_check has values that are one type and matset_values +// // that are another type. The silo writer converts both to double arrays +// // in this case, so we follow suit. +// Node mat_check_new_values, mat_check_new_matset_values; +// save_mesh["fields"]["mat_check"]["values"].to_double_array(mat_check_new_values); +// save_mesh["fields"]["mat_check"]["matset_values"].to_double_array(mat_check_new_matset_values); +// save_mesh["fields"]["mat_check"]["values"].set_external(mat_check_new_values); +// save_mesh["fields"]["mat_check"]["matset_values"].set_external(mat_check_new_matset_values); + +// // to_silo is going to reorder mixed materials least to greatest +// // so we must do the same +// int_array mat_ids = save_mesh["matsets"]["matset"]["material_ids"].value(); +// const auto mat_id10 = mat_ids[10]; +// const auto mat_id11 = mat_ids[11]; +// const auto mat_id12 = mat_ids[12]; +// mat_ids[10] = mat_id12; +// mat_ids[11] = mat_id10; +// mat_ids[12] = mat_id11; +// auto field_itr = save_mesh["fields"].children(); +// while (field_itr.has_next()) +// { +// const Node &n_field = field_itr.next(); +// if (n_field.has_child("matset")) +// { +// double_array matset_vals = n_field["matset_values"].value(); +// const auto matset_val10 = matset_vals[10]; +// const auto matset_val11 = matset_vals[11]; +// const auto matset_val12 = matset_vals[12]; +// matset_vals[10] = matset_val12; +// matset_vals[11] = matset_val10; +// matset_vals[12] = matset_val11; +// } +// } + +// silo_name_changer("mesh", save_mesh); + +// // the loaded mesh will be in the multidomain format +// // but the saved mesh is in the single domain format +// EXPECT_EQ(load_mesh.number_of_children(), 1); +// EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); + +// EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); +// } + +// //----------------------------------------------------------------------------- +// TEST(conduit_relay_io_silo, round_trip_spiral_multi_dom_materials) +// { +// Node save_mesh, load_mesh, info; +// const int ndomains = 4; +// blueprint::mesh::examples::spiral(ndomains, save_mesh); +// add_matset_to_spiral(save_mesh, ndomains); +// EXPECT_TRUE(blueprint::mesh::verify(save_mesh, info)); - const std::string basename = "silo_multidom_materials_spiral"; - const std::string filename = basename + ".cycle_000000.root"; - - remove_path_if_exists(filename); - io::silo::save_mesh(save_mesh, basename); - io::silo::load_mesh(filename, load_mesh); - EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); - - // make changes to save mesh so the diff will pass - for (index_t child = 0; child < save_mesh.number_of_children(); child ++) - { - // get the matset for this domain - Node &n_matset = save_mesh[child]["matsets"]["matset"]; - - // clean up volume fractions - Node vf_arr; - n_matset["volume_fractions"].to_float64_array(vf_arr); - n_matset["volume_fractions"].reset(); - n_matset["volume_fractions"].set(vf_arr); +// const std::string basename = "silo_multidom_materials_spiral"; +// const std::string filename = basename + ".cycle_000000.root"; + +// remove_path_if_exists(filename); +// io::silo::save_mesh(save_mesh, basename); +// io::silo::load_mesh(filename, load_mesh); +// EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); + +// // make changes to save mesh so the diff will pass +// for (index_t child = 0; child < save_mesh.number_of_children(); child ++) +// { +// // get the matset for this domain +// Node &n_matset = save_mesh[child]["matsets"]["matset"]; + +// // clean up volume fractions +// Node vf_arr; +// n_matset["volume_fractions"].to_float64_array(vf_arr); +// n_matset["volume_fractions"].reset(); +// n_matset["volume_fractions"].set(vf_arr); - // cheat a little bit - we don't have these to start - n_matset["sizes"].set_external(load_mesh[child]["matsets"]["mesh_matset"]["sizes"]); - n_matset["offsets"].set_external(load_mesh[child]["matsets"]["mesh_matset"]["offsets"]); - - silo_name_changer("mesh", save_mesh[child]); - } - - EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); - NodeConstIterator l_itr = load_mesh.children(); - NodeConstIterator s_itr = save_mesh.children(); - while (l_itr.has_next()) - { - const Node &l_curr = l_itr.next(); - const Node &s_curr = s_itr.next(); - - EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); - } -} - -//----------------------------------------------------------------------------- -TEST(conduit_relay_io_silo, round_trip_grid_adjset) -{ - Node save_mesh, load_mesh, info; - blueprint::mesh::examples::grid("structured", 3, 3, 1, 2, 2, 1, save_mesh); - - // we need a material in order for this to be valid overlink - for (index_t child = 0; child < save_mesh.number_of_children(); child ++) - { - add_multi_buffer_full_matset(save_mesh[child], 4, "mesh"); - } - - const std::string basename = "silo_grid_adjset"; - const std::string filename = basename + "/OvlTop.silo"; - - Node write_opts; - write_opts["file_style"] = "overlink"; - write_opts["ovl_topo_name"] = "mesh"; - - Node read_opts; - read_opts["matset_style"] = "multi_buffer_full"; - - remove_path_if_exists(filename); - io::silo::save_mesh(save_mesh, basename, write_opts); - io::blueprint::save_mesh(save_mesh, basename, "hdf5"); - io::silo::load_mesh(filename, read_opts, load_mesh); - EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - - for (index_t child = 0; child < save_mesh.number_of_children(); child ++) - { - // separate out vector fields - Node &field_vel = save_mesh[child]["fields"]["vel"]; - Node &field_vel_u = save_mesh[child]["fields"]["vel_u"]; - Node &field_vel_v = save_mesh[child]["fields"]["vel_v"]; +// // cheat a little bit - we don't have these to start +// n_matset["sizes"].set_external(load_mesh[child]["matsets"]["mesh_matset"]["sizes"]); +// n_matset["offsets"].set_external(load_mesh[child]["matsets"]["mesh_matset"]["offsets"]); + +// silo_name_changer("mesh", save_mesh[child]); +// } + +// EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); +// NodeConstIterator l_itr = load_mesh.children(); +// NodeConstIterator s_itr = save_mesh.children(); +// while (l_itr.has_next()) +// { +// const Node &l_curr = l_itr.next(); +// const Node &s_curr = s_itr.next(); + +// EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); +// } +// } + +// //----------------------------------------------------------------------------- +// TEST(conduit_relay_io_silo, round_trip_grid_adjset) +// { +// Node save_mesh, load_mesh, info; +// blueprint::mesh::examples::grid("structured", 3, 3, 1, 2, 2, 1, save_mesh); + +// // we need a material in order for this to be valid overlink +// for (index_t child = 0; child < save_mesh.number_of_children(); child ++) +// { +// add_multi_buffer_full_matset(save_mesh[child], 4, "mesh"); +// } + +// const std::string basename = "silo_grid_adjset"; +// const std::string filename = basename + "/OvlTop.silo"; + +// Node write_opts; +// write_opts["file_style"] = "overlink"; +// write_opts["ovl_topo_name"] = "mesh"; + +// Node read_opts; +// read_opts["matset_style"] = "multi_buffer_full"; + +// remove_path_if_exists(filename); +// io::silo::save_mesh(save_mesh, basename, write_opts); +// io::blueprint::save_mesh(save_mesh, basename, "hdf5"); +// io::silo::load_mesh(filename, read_opts, load_mesh); +// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + +// for (index_t child = 0; child < save_mesh.number_of_children(); child ++) +// { +// // separate out vector fields +// Node &field_vel = save_mesh[child]["fields"]["vel"]; +// Node &field_vel_u = save_mesh[child]["fields"]["vel_u"]; +// Node &field_vel_v = save_mesh[child]["fields"]["vel_v"]; - field_vel_u["topology"].set(field_vel["topology"]); - field_vel_u["association"].set(field_vel["association"]); - field_vel_u["values"].set(field_vel["values/u"]); - field_vel_v["topology"].set(field_vel["topology"]); - field_vel_v["association"].set(field_vel["association"]); - field_vel_v["values"].set(field_vel["values/v"]); - - save_mesh[child]["fields"].remove_child("vel"); - - // make adjset pairwise - Node &pairwise_adjset = save_mesh[child]["adjsets"]["adjset"]; - conduit::blueprint::mesh::adjset::to_pairwise(save_mesh[child]["adjsets"]["mesh_adj"], pairwise_adjset); - save_mesh[child]["adjsets"].remove_child("mesh_adj"); - - // make changes to save mesh so the diff will pass - overlink_name_changer(save_mesh[child]); - } - - EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); - NodeConstIterator l_itr = load_mesh.children(); - NodeConstIterator s_itr = save_mesh.children(); - while (l_itr.has_next()) - { - const Node &l_curr = l_itr.next(); - const Node &s_curr = s_itr.next(); - - EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); - } -} - -//----------------------------------------------------------------------------- -// -// test read and write semantics -// - -//----------------------------------------------------------------------------- -TEST(conduit_relay_io_silo, read_and_write_semantics) -{ - for (int ndomains = 2; ndomains < 6; ndomains ++) - { - Node save_mesh, load_mesh, info; - blueprint::mesh::examples::spiral(ndomains, save_mesh); - - const std::string basename = "silo_spiral_" + std::to_string(ndomains) + "_domains"; - const std::string filename = basename + ".cycle_000000.root"; - - remove_path_if_exists(filename); - io::silo::write_mesh(save_mesh, basename); - io::silo::read_mesh(filename, load_mesh); - EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); - - // make changes to save mesh so the diff will pass - for (index_t child = 0; child < save_mesh.number_of_children(); child ++) - { - silo_name_changer("mesh", save_mesh[child]); - } - - EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); - NodeConstIterator l_itr = load_mesh.children(); - NodeConstIterator s_itr = save_mesh.children(); - while (l_itr.has_next()) - { - const Node &l_curr = l_itr.next(); - const Node &s_curr = s_itr.next(); - - EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); - } - } -} - -//----------------------------------------------------------------------------- -// -// special case tests -// - -//----------------------------------------------------------------------------- -// var is not defined on a domain -// -// tests the silo "EMPTY" capability -TEST(conduit_relay_io_silo, missing_domain_var) -{ - Node save_mesh, load_mesh, info; - const int ndomains = 4; - blueprint::mesh::examples::spiral(ndomains, save_mesh); - - // remove information for a particular domain - save_mesh[2]["fields"].remove_child("dist"); - - const std::string basename = "silo_missing_domain_var_spiral"; - const std::string filename = basename + ".cycle_000000.root"; - - remove_path_if_exists(filename); - io::silo::save_mesh(save_mesh, basename); - io::silo::load_mesh(filename, load_mesh); - EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); - - // make changes to save mesh so the diff will pass - for (index_t child = 0; child < save_mesh.number_of_children(); child ++) - { - silo_name_changer("mesh", save_mesh[child]); - } - save_mesh[2].remove_child("fields"); - - EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); - NodeConstIterator l_itr = load_mesh.children(); - NodeConstIterator s_itr = save_mesh.children(); - while (l_itr.has_next()) - { - const Node &l_curr = l_itr.next(); - const Node &s_curr = s_itr.next(); - - EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); - } -} - -//----------------------------------------------------------------------------- -// matset is not defined on a domain -// -// tests the silo "EMPTY" capability -TEST(conduit_relay_io_silo, missing_domain_matset) -{ - Node save_mesh, load_mesh, info; - const int ndomains = 4; - blueprint::mesh::examples::spiral(ndomains, save_mesh); - add_matset_to_spiral(save_mesh, ndomains); - EXPECT_TRUE(blueprint::mesh::verify(save_mesh, info)); - - // remove information for a particular domain - save_mesh[2]["matsets"].remove_child("matset"); - - const std::string basename = "silo_missing_domain_matset_spiral"; - const std::string filename = basename + ".cycle_000000.root"; - - remove_path_if_exists(filename); - io::silo::save_mesh(save_mesh, basename); - io::silo::load_mesh(filename, load_mesh); - EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); - - // make changes to save mesh so the diff will pass - for (index_t child = 0; child < save_mesh.number_of_children(); child ++) - { - if (save_mesh[child].has_path("matsets/matset")) - { - // get the matset for this domain - Node &n_matset = save_mesh[child]["matsets"]["matset"]; - - // clean up volume fractions - Node vf_arr; - n_matset["volume_fractions"].to_float64_array(vf_arr); - n_matset["volume_fractions"].reset(); - n_matset["volume_fractions"].set(vf_arr); +// field_vel_u["topology"].set(field_vel["topology"]); +// field_vel_u["association"].set(field_vel["association"]); +// field_vel_u["values"].set(field_vel["values/u"]); +// field_vel_v["topology"].set(field_vel["topology"]); +// field_vel_v["association"].set(field_vel["association"]); +// field_vel_v["values"].set(field_vel["values/v"]); + +// save_mesh[child]["fields"].remove_child("vel"); + +// // make adjset pairwise +// Node &pairwise_adjset = save_mesh[child]["adjsets"]["adjset"]; +// conduit::blueprint::mesh::adjset::to_pairwise(save_mesh[child]["adjsets"]["mesh_adj"], pairwise_adjset); +// save_mesh[child]["adjsets"].remove_child("mesh_adj"); + +// // make changes to save mesh so the diff will pass +// overlink_name_changer(save_mesh[child]); +// } + +// EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); +// NodeConstIterator l_itr = load_mesh.children(); +// NodeConstIterator s_itr = save_mesh.children(); +// while (l_itr.has_next()) +// { +// const Node &l_curr = l_itr.next(); +// const Node &s_curr = s_itr.next(); + +// EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); +// } +// } + +// //----------------------------------------------------------------------------- +// // +// // test read and write semantics +// // + +// //----------------------------------------------------------------------------- +// TEST(conduit_relay_io_silo, read_and_write_semantics) +// { +// for (int ndomains = 2; ndomains < 6; ndomains ++) +// { +// Node save_mesh, load_mesh, info; +// blueprint::mesh::examples::spiral(ndomains, save_mesh); + +// const std::string basename = "silo_spiral_" + std::to_string(ndomains) + "_domains"; +// const std::string filename = basename + ".cycle_000000.root"; + +// remove_path_if_exists(filename); +// io::silo::write_mesh(save_mesh, basename); +// io::silo::read_mesh(filename, load_mesh); +// EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); + +// // make changes to save mesh so the diff will pass +// for (index_t child = 0; child < save_mesh.number_of_children(); child ++) +// { +// silo_name_changer("mesh", save_mesh[child]); +// } + +// EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); +// NodeConstIterator l_itr = load_mesh.children(); +// NodeConstIterator s_itr = save_mesh.children(); +// while (l_itr.has_next()) +// { +// const Node &l_curr = l_itr.next(); +// const Node &s_curr = s_itr.next(); + +// EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); +// } +// } +// } + +// //----------------------------------------------------------------------------- +// // +// // special case tests +// // + +// //----------------------------------------------------------------------------- +// // var is not defined on a domain +// // +// // tests the silo "EMPTY" capability +// TEST(conduit_relay_io_silo, missing_domain_var) +// { +// Node save_mesh, load_mesh, info; +// const int ndomains = 4; +// blueprint::mesh::examples::spiral(ndomains, save_mesh); + +// // remove information for a particular domain +// save_mesh[2]["fields"].remove_child("dist"); + +// const std::string basename = "silo_missing_domain_var_spiral"; +// const std::string filename = basename + ".cycle_000000.root"; + +// remove_path_if_exists(filename); +// io::silo::save_mesh(save_mesh, basename); +// io::silo::load_mesh(filename, load_mesh); +// EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); + +// // make changes to save mesh so the diff will pass +// for (index_t child = 0; child < save_mesh.number_of_children(); child ++) +// { +// silo_name_changer("mesh", save_mesh[child]); +// } +// save_mesh[2].remove_child("fields"); + +// EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); +// NodeConstIterator l_itr = load_mesh.children(); +// NodeConstIterator s_itr = save_mesh.children(); +// while (l_itr.has_next()) +// { +// const Node &l_curr = l_itr.next(); +// const Node &s_curr = s_itr.next(); + +// EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); +// } +// } + +// //----------------------------------------------------------------------------- +// // matset is not defined on a domain +// // +// // tests the silo "EMPTY" capability +// TEST(conduit_relay_io_silo, missing_domain_matset) +// { +// Node save_mesh, load_mesh, info; +// const int ndomains = 4; +// blueprint::mesh::examples::spiral(ndomains, save_mesh); +// add_matset_to_spiral(save_mesh, ndomains); +// EXPECT_TRUE(blueprint::mesh::verify(save_mesh, info)); + +// // remove information for a particular domain +// save_mesh[2]["matsets"].remove_child("matset"); + +// const std::string basename = "silo_missing_domain_matset_spiral"; +// const std::string filename = basename + ".cycle_000000.root"; + +// remove_path_if_exists(filename); +// io::silo::save_mesh(save_mesh, basename); +// io::silo::load_mesh(filename, load_mesh); +// EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); + +// // make changes to save mesh so the diff will pass +// for (index_t child = 0; child < save_mesh.number_of_children(); child ++) +// { +// if (save_mesh[child].has_path("matsets/matset")) +// { +// // get the matset for this domain +// Node &n_matset = save_mesh[child]["matsets"]["matset"]; + +// // clean up volume fractions +// Node vf_arr; +// n_matset["volume_fractions"].to_float64_array(vf_arr); +// n_matset["volume_fractions"].reset(); +// n_matset["volume_fractions"].set(vf_arr); - // cheat a little bit - we don't have these to start - n_matset["sizes"].set_external(load_mesh[child]["matsets"]["mesh_matset"]["sizes"]); - n_matset["offsets"].set_external(load_mesh[child]["matsets"]["mesh_matset"]["offsets"]); - } - - silo_name_changer("mesh", save_mesh[child]); - } - save_mesh[2].remove_child("matsets"); - - EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); - NodeConstIterator l_itr = load_mesh.children(); - NodeConstIterator s_itr = save_mesh.children(); - while (l_itr.has_next()) - { - const Node &l_curr = l_itr.next(); - const Node &s_curr = s_itr.next(); - - EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); - } -} - -//----------------------------------------------------------------------------- -// mesh is not defined on a domain -// -// This case is much less interesting. -// data passes through the clean mesh filter which -// deletes domains that are missing topos. -// They simply are not part of the mesh and so silo -// doesn't have to deal with it. -TEST(conduit_relay_io_silo, missing_domain_mesh_trivial) -{ - Node save_mesh, load_mesh, info; - const int ndomains = 4; - blueprint::mesh::examples::spiral(ndomains, save_mesh); - - // remove information for a particular domain - save_mesh[2]["topologies"].remove_child("topo"); - - const std::string basename = "silo_missing_domain_mesh_trivial_spiral"; - const std::string filename = basename + ".cycle_000000.root"; - - remove_path_if_exists(filename); - io::silo::save_mesh(save_mesh, basename); - io::silo::load_mesh(filename, load_mesh); - - EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); - - // make changes to save mesh so the diff will pass - save_mesh.remove(2); - save_mesh.rename_child("domain_000003", "domain_000002"); - save_mesh[2]["state"]["domain_id"].reset(); - save_mesh[2]["state"]["domain_id"] = 2; - for (index_t child = 0; child < save_mesh.number_of_children(); child ++) - { - silo_name_changer("mesh", save_mesh[child]); - } - - EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); - NodeConstIterator l_itr = load_mesh.children(); - NodeConstIterator s_itr = save_mesh.children(); - while (l_itr.has_next()) - { - const Node &l_curr = l_itr.next(); - const Node &s_curr = s_itr.next(); - - EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); - } -} - -//----------------------------------------------------------------------------- -// mesh is not defined on a domain but there are multiple meshes -TEST(conduit_relay_io_silo, missing_domain_mesh) -{ - Node save_mesh, save_mesh2, load_mesh, load_mesh2, info, opts; - const int ndomains = 4; - blueprint::mesh::examples::spiral(ndomains, save_mesh); - blueprint::mesh::examples::spiral(ndomains, save_mesh2); - - for (index_t child = 0; child < save_mesh.number_of_children(); child ++) - { - save_mesh[child]["coordsets"].rename_child("coords", "coords2"); - save_mesh[child]["topologies"]["topo"]["coordset"].reset(); - save_mesh[child]["topologies"]["topo"]["coordset"] = "coords2"; - save_mesh[child]["topologies"].rename_child("topo", "topo2"); - save_mesh[child]["fields"]["dist"]["topology"].reset(); - save_mesh[child]["fields"]["dist"]["topology"] = "topo2"; - save_mesh[child]["fields"].rename_child("dist", "dist2"); - - save_mesh[child]["coordsets"]["coords"].set_external(save_mesh2[child]["coordsets"]["coords"]); - save_mesh[child]["topologies"]["topo"].set_external(save_mesh2[child]["topologies"]["topo"]); - save_mesh[child]["fields"]["dist"].set_external(save_mesh2[child]["fields"]["dist"]); - } - - // remove information for a particular domain - save_mesh[2]["topologies"].remove_child("topo"); - - const std::string basename = "silo_missing_domain_mesh_spiral"; - const std::string filename = basename + ".cycle_000000.root"; - - remove_path_if_exists(filename); - io::silo::save_mesh(save_mesh, basename); +// // cheat a little bit - we don't have these to start +// n_matset["sizes"].set_external(load_mesh[child]["matsets"]["mesh_matset"]["sizes"]); +// n_matset["offsets"].set_external(load_mesh[child]["matsets"]["mesh_matset"]["offsets"]); +// } + +// silo_name_changer("mesh", save_mesh[child]); +// } +// save_mesh[2].remove_child("matsets"); + +// EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); +// NodeConstIterator l_itr = load_mesh.children(); +// NodeConstIterator s_itr = save_mesh.children(); +// while (l_itr.has_next()) +// { +// const Node &l_curr = l_itr.next(); +// const Node &s_curr = s_itr.next(); + +// EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); +// } +// } + +// //----------------------------------------------------------------------------- +// // mesh is not defined on a domain +// // +// // This case is much less interesting. +// // data passes through the clean mesh filter which +// // deletes domains that are missing topos. +// // They simply are not part of the mesh and so silo +// // doesn't have to deal with it. +// TEST(conduit_relay_io_silo, missing_domain_mesh_trivial) +// { +// Node save_mesh, load_mesh, info; +// const int ndomains = 4; +// blueprint::mesh::examples::spiral(ndomains, save_mesh); + +// // remove information for a particular domain +// save_mesh[2]["topologies"].remove_child("topo"); + +// const std::string basename = "silo_missing_domain_mesh_trivial_spiral"; +// const std::string filename = basename + ".cycle_000000.root"; + +// remove_path_if_exists(filename); +// io::silo::save_mesh(save_mesh, basename); +// io::silo::load_mesh(filename, load_mesh); + +// EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); + +// // make changes to save mesh so the diff will pass +// save_mesh.remove(2); +// save_mesh.rename_child("domain_000003", "domain_000002"); +// save_mesh[2]["state"]["domain_id"].reset(); +// save_mesh[2]["state"]["domain_id"] = 2; +// for (index_t child = 0; child < save_mesh.number_of_children(); child ++) +// { +// silo_name_changer("mesh", save_mesh[child]); +// } + +// EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); +// NodeConstIterator l_itr = load_mesh.children(); +// NodeConstIterator s_itr = save_mesh.children(); +// while (l_itr.has_next()) +// { +// const Node &l_curr = l_itr.next(); +// const Node &s_curr = s_itr.next(); + +// EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); +// } +// } + +// //----------------------------------------------------------------------------- +// // mesh is not defined on a domain but there are multiple meshes +// TEST(conduit_relay_io_silo, missing_domain_mesh) +// { +// Node save_mesh, save_mesh2, load_mesh, load_mesh2, info, opts; +// const int ndomains = 4; +// blueprint::mesh::examples::spiral(ndomains, save_mesh); +// blueprint::mesh::examples::spiral(ndomains, save_mesh2); + +// for (index_t child = 0; child < save_mesh.number_of_children(); child ++) +// { +// save_mesh[child]["coordsets"].rename_child("coords", "coords2"); +// save_mesh[child]["topologies"]["topo"]["coordset"].reset(); +// save_mesh[child]["topologies"]["topo"]["coordset"] = "coords2"; +// save_mesh[child]["topologies"].rename_child("topo", "topo2"); +// save_mesh[child]["fields"]["dist"]["topology"].reset(); +// save_mesh[child]["fields"]["dist"]["topology"] = "topo2"; +// save_mesh[child]["fields"].rename_child("dist", "dist2"); + +// save_mesh[child]["coordsets"]["coords"].set_external(save_mesh2[child]["coordsets"]["coords"]); +// save_mesh[child]["topologies"]["topo"].set_external(save_mesh2[child]["topologies"]["topo"]); +// save_mesh[child]["fields"]["dist"].set_external(save_mesh2[child]["fields"]["dist"]); +// } + +// // remove information for a particular domain +// save_mesh[2]["topologies"].remove_child("topo"); + +// const std::string basename = "silo_missing_domain_mesh_spiral"; +// const std::string filename = basename + ".cycle_000000.root"; + +// remove_path_if_exists(filename); +// io::silo::save_mesh(save_mesh, basename); - opts["mesh_name"] = "mesh_topo2"; - io::silo::load_mesh(filename, opts, load_mesh); - opts["mesh_name"] = "mesh_topo"; - io::silo::load_mesh(filename, opts, load_mesh2); - EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - EXPECT_TRUE(blueprint::mesh::verify(load_mesh2, info)); - - // make changes to save mesh so the diff will pass - save_mesh[2]["coordsets"].remove_child("coords"); - save_mesh[2]["fields"].remove_child("dist"); - for (index_t child = 0; child < save_mesh.number_of_children(); child ++) - { - silo_name_changer("mesh", save_mesh[child]); - } - - // we must merge the two meshes in load mesh - // the indexing is tricky because one is missing a domain - load_mesh[0]["coordsets"]["mesh_topo"].set_external(load_mesh2[0]["coordsets"]["mesh_topo"]); - load_mesh[0]["topologies"]["mesh_topo"].set_external(load_mesh2[0]["topologies"]["mesh_topo"]); - load_mesh[0]["fields"]["mesh_dist"].set_external(load_mesh2[0]["fields"]["mesh_dist"]); - load_mesh[1]["coordsets"]["mesh_topo"].set_external(load_mesh2[1]["coordsets"]["mesh_topo"]); - load_mesh[1]["topologies"]["mesh_topo"].set_external(load_mesh2[1]["topologies"]["mesh_topo"]); - load_mesh[1]["fields"]["mesh_dist"].set_external(load_mesh2[1]["fields"]["mesh_dist"]); - load_mesh[3]["coordsets"]["mesh_topo"].set_external(load_mesh2[2]["coordsets"]["mesh_topo"]); - load_mesh[3]["topologies"]["mesh_topo"].set_external(load_mesh2[2]["topologies"]["mesh_topo"]); - load_mesh[3]["fields"]["mesh_dist"].set_external(load_mesh2[2]["fields"]["mesh_dist"]); - - EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); - NodeConstIterator l_itr = load_mesh.children(); - NodeConstIterator s_itr = save_mesh.children(); - while (l_itr.has_next()) - { - const Node &l_curr = l_itr.next(); - const Node &s_curr = s_itr.next(); - - EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); - } -} - -//----------------------------------------------------------------------------- -// explicit points (unstructured mesh) do not use every coord -TEST(conduit_relay_io_silo, unstructured_points) -{ - Node save_mesh, load_mesh, info; - blueprint::mesh::examples::braid("points", 2, 2, 2, save_mesh); - - std::vector new_conn; - std::vector new_field1; - std::vector new_field2; - std::vector new_xcoords, new_ycoords, new_zcoords; - - int_accessor conn = save_mesh["topologies"]["mesh"]["elements"]["connectivity"].value(); - - float_accessor field1 = save_mesh["fields"]["braid"]["values"].value(); - float_accessor field2 = save_mesh["fields"]["radial"]["values"].value(); - - float_accessor xcoords = save_mesh["coordsets"]["coords"]["values"]["x"].value(); - float_accessor ycoords = save_mesh["coordsets"]["coords"]["values"]["y"].value(); - float_accessor zcoords = save_mesh["coordsets"]["coords"]["values"]["z"].value(); - - for (int i = 1; i < conn.number_of_elements(); i += 2) - { - new_conn.push_back(conn[i]); - new_field1.push_back(field1[i]); - new_field2.push_back(field2[i]); - - new_xcoords.push_back(xcoords[conn[i]]); - new_ycoords.push_back(ycoords[conn[i]]); - new_zcoords.push_back(zcoords[conn[i]]); - } - save_mesh["topologies"]["mesh"]["elements"]["connectivity"].reset(); - save_mesh["topologies"]["mesh"]["elements"]["connectivity"].set(new_conn); - - save_mesh["fields"].remove_child("vel"); - save_mesh["fields"]["braid"]["values"].reset(); - save_mesh["fields"]["braid"]["values"].set(new_field1); - save_mesh["fields"]["radial"]["values"].reset(); - save_mesh["fields"]["radial"]["values"].set(new_field2); - - // we have modified braid such that it only uses half of the points in the coordset - - const std::string basename = "silo_unstructured_points_braid"; - const std::string filename = basename + ".cycle_000100.root"; - - // remove existing root file, directory and any output files - remove_path_if_exists(filename); - - io::silo::save_mesh(save_mesh, basename); - io::silo::load_mesh(filename, load_mesh); - EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - - // now we must remove the unused points and change to an implicit points topo so that the diff passes - save_mesh["coordsets"]["coords"]["values"]["x"].reset(); - save_mesh["coordsets"]["coords"]["values"]["x"].set(new_xcoords); - save_mesh["coordsets"]["coords"]["values"]["y"].reset(); - save_mesh["coordsets"]["coords"]["values"]["y"].set(new_ycoords); - save_mesh["coordsets"]["coords"]["values"]["z"].reset(); - save_mesh["coordsets"]["coords"]["values"]["z"].set(new_zcoords); - - save_mesh["topologies"].remove_child("mesh"); - save_mesh["topologies"]["mesh"]["type"] = "points"; - save_mesh["topologies"]["mesh"]["coordset"] = "coords"; - - // the association doesn't matter for point meshes - // we choose vertex by convention - save_mesh["fields"]["radial"]["association"].reset(); - save_mesh["fields"]["radial"]["association"] = "vertex"; - - silo_name_changer("mesh", save_mesh); - - // the loaded mesh will be in the multidomain format - // but the saved mesh is in the single domain format - EXPECT_EQ(load_mesh.number_of_children(), 1); - EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); - - EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); -} - -//----------------------------------------------------------------------------- - -// -// save and read option tests -// - -// save options: -/// opts: -/// -/// file_style: "default", "root_only", "multi_file", "overlink" -/// when # of domains == 1, "default" ==> "root_only" -/// else, "default" ==> "multi_file" -/// -/// silo_type: "default", "pdb", "hdf5", "unknown" -/// when the file we are writing to exists, "default" ==> "unknown" -/// else, "default" ==> "hdf5" -/// note: these are additional silo_type options that we could add -/// support for in the future: -/// "hdf5_sec2", "hdf5_stdio", "hdf5_mpio", "hdf5_mpiposix", "taurus" -/// -/// suffix: "default", "cycle", "none" -/// when cycle is present, "default" ==> "cycle" -/// else, "default" ==> "none" -/// -/// root_file_ext: "default", "root", "silo" -/// "default" ==> "root" -/// if overlink, this parameter is unused. -/// -/// mesh_name: (used if present, default ==> "mesh") -/// -/// ovl_topo_name: (used if present, default ==> "") -/// -/// number_of_files: {# of files} -/// when "multi_file" or "overlink": -/// <= 0, use # of files == # of domains -/// > 0, # of files == number_of_files - -// read options: -/// opts: -/// mesh_name: "{name}" -/// provide explicit mesh name, for cases where silo data includes -/// more than one mesh. -/// -/// matset_style: "default", "multi_buffer_full", "sparse_by_element", -/// "multi_buffer_by_material" -/// "default" ==> "sparse_by_element" - -//----------------------------------------------------------------------------- -TEST(conduit_relay_io_silo, round_trip_save_option_file_style) -{ - // we will do overlink tests separately - const std::vector file_styles = {"default", "root_only", "multi_file"}; - for (int i = 0; i < file_styles.size(); i ++) - { - Node opts; - opts["file_style"] = file_styles[i]; - - const std::string basename = "silo_save_option_file_style_" + file_styles[i] + "_spiral"; - const std::string filename = basename + ".cycle_000000.root"; - - for (int ndomains = 1; ndomains < 5; ndomains += 3) - { - Node save_mesh, load_mesh, info; - blueprint::mesh::examples::spiral(ndomains, save_mesh); - remove_path_if_exists(filename); - io::silo::save_mesh(save_mesh, basename, opts); - io::silo::load_mesh(filename, load_mesh); - EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); - - // make changes to save mesh so the diff will pass - for (index_t child = 0; child < save_mesh.number_of_children(); child ++) - { - silo_name_changer("mesh", save_mesh[child]); - } - - EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); - NodeConstIterator l_itr = load_mesh.children(); - NodeConstIterator s_itr = save_mesh.children(); - while (l_itr.has_next()) - { - const Node &l_curr = l_itr.next(); - const Node &s_curr = s_itr.next(); - - EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); - } - } - } -} - -//----------------------------------------------------------------------------- -TEST(conduit_relay_io_silo, round_trip_save_option_number_of_files) -{ - const std::vector number_of_files = {-1, 2}; - for (int i = 0; i < number_of_files.size(); i ++) - { - Node opts; - opts["file_style"] = "multi_file"; - opts["number_of_files"] = number_of_files[i]; - - const std::string basename = "silo_save_option_number_of_files_" + - std::to_string(number_of_files[i]) + - "_spiral"; - const std::string filename = basename + ".cycle_000000.root"; - - const int ndomains = 5; - - Node save_mesh, load_mesh, info; - blueprint::mesh::examples::spiral(ndomains, save_mesh); - - remove_path_if_exists(filename); - io::silo::save_mesh(save_mesh, basename, opts); - io::silo::load_mesh(filename, load_mesh); - EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); - - // make changes to save mesh so the diff will pass - for (index_t child = 0; child < save_mesh.number_of_children(); child ++) - { - silo_name_changer("mesh", save_mesh[child]); - } - - EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); - NodeConstIterator l_itr = load_mesh.children(); - NodeConstIterator s_itr = save_mesh.children(); - while (l_itr.has_next()) - { - const Node &l_curr = l_itr.next(); - const Node &s_curr = s_itr.next(); - - EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); - } - } -} - -//----------------------------------------------------------------------------- -TEST(conduit_relay_io_silo, round_trip_save_option_suffix) -{ - const std::vector suffixes = {"default", "default", "cycle", "none"}; - const std::vector file_suffixes = { - "", // cycle is not present - ".cycle_000005", // cycle is present - ".cycle_000005", // cycle is turned on - "", // cycle is turned off - }; - const std::vector include_cycle = {"no", "yes", "yes", "yes"}; - for (int i = 0; i < suffixes.size(); i ++) - { - Node opts; - opts["suffix"] = suffixes[i]; - - const std::string basename = "silo_save_option_suffix_" + suffixes[i] + - "_" + include_cycle[i] + "_basic"; - const std::string filename = basename + file_suffixes[i] + ".root"; - - Node save_mesh, load_mesh, info; - blueprint::mesh::examples::basic("rectilinear", 3, 4, 0, save_mesh); - - if (include_cycle[i] == "yes") - { - save_mesh["state/cycle"] = 5; - } - - remove_path_if_exists(filename); - io::silo::save_mesh(save_mesh, basename, opts); - io::silo::load_mesh(filename, load_mesh); - EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - - silo_name_changer("mesh", save_mesh); - - // the loaded mesh will be in the multidomain format - // but the saved mesh is in the single domain format - EXPECT_EQ(load_mesh.number_of_children(), 1); - EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); - - EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); - } -} - -//----------------------------------------------------------------------------- -TEST(conduit_relay_io_silo, round_trip_save_option_root_file_ext) -{ - const std::vector root_file_exts = {"default", "root", "silo"}; - - for (int i = 0; i < root_file_exts.size(); i ++) - { - Node opts; - opts["root_file_ext"] = root_file_exts[i]; - - std::string actual_file_ext = root_file_exts[i]; - if (actual_file_ext == "default") - { - actual_file_ext = "root"; - } - - const std::string basename = "round_trip_save_option_root_file_ext_" + - root_file_exts[i] + "_basic"; - const std::string filename = basename + "." + actual_file_ext; - - Node save_mesh, load_mesh, info; - blueprint::mesh::examples::basic("rectilinear", 3, 4, 0, save_mesh); - remove_path_if_exists(filename); - io::silo::save_mesh(save_mesh, basename, opts); - io::silo::load_mesh(filename, load_mesh); - EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - - silo_name_changer("mesh", save_mesh); - - // the loaded mesh will be in the multidomain format - // but the saved mesh is in the single domain format - EXPECT_EQ(load_mesh.number_of_children(), 1); - EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); - EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); - } -} - -//----------------------------------------------------------------------------- -TEST(conduit_relay_io_silo, round_trip_save_option_mesh_name) -{ - const std::string basename = "silo_save_option_mesh_name_basic"; - const std::string filename = basename + ".root"; - - Node opts; - opts["mesh_name"] = "mymesh"; - - Node save_mesh, load_mesh, info; - blueprint::mesh::examples::basic("rectilinear", 3, 4, 0, save_mesh); - remove_path_if_exists(filename); - io::silo::save_mesh(save_mesh, basename, opts); - io::silo::load_mesh(filename, load_mesh); - EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - - silo_name_changer("mymesh", save_mesh); - - // the loaded mesh will be in the multidomain format - // but the saved mesh is in the single domain format - EXPECT_EQ(load_mesh.number_of_children(), 1); - EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); - EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); -} - -//----------------------------------------------------------------------------- -TEST(conduit_relay_io_silo, round_trip_read_option_mesh_name) -{ - Node load_mesh, info, opts; - const std::string path = utils::join_file_path("silo", "multi_curv3d.silo"); - const std::string input_file = relay_test_silo_data_path(path); - - opts["mesh_name"] = "mesh1_dup"; - - io::silo::load_mesh(input_file, opts, load_mesh); - EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - - EXPECT_TRUE(load_mesh[0].has_path("topologies/mesh1_dup")); -} - -//----------------------------------------------------------------------------- -TEST(conduit_relay_io_silo, round_trip_read_option_matset_style) -{ - // the matset type and the type we are requesting on read - const std::vector> matset_types = { - std::make_pair("full", "full"), - std::make_pair("sparse_by_material", "sparse_by_material"), - std::make_pair("sparse_by_element", "sparse_by_element"), - std::make_pair("sparse_by_element", "full"), - std::make_pair("sparse_by_material", "sparse_by_element"), - std::make_pair("sparse_by_material", "default"), - }; - - for (int i = 0; i < matset_types.size(); i ++) - { - std::string matset_type = matset_types[i].first; - std::string matset_request = matset_types[i].second; - - for (int j = 0; j < 2; j ++) - { - Node mesh_full, mesh_sbe, mesh_sbm, baseline_mesh, load_mesh, info; - std::string size; - int nx, ny; - const double radius = 0.25; - if (j == 0) - { - size = "small"; - nx = ny = 4; - } - else - { - size = "large"; - nx = ny = 100; - } - - blueprint::mesh::examples::venn("full", nx, ny, radius, mesh_full); - blueprint::mesh::examples::venn("sparse_by_material", nx, ny, radius, mesh_sbm); - blueprint::mesh::examples::venn("sparse_by_element", nx, ny, radius, mesh_sbe); - - if (matset_type == "full") - { - baseline_mesh.set_external(mesh_full); - } - else if (matset_type == "sparse_by_material") - { - baseline_mesh.set_external(mesh_sbm); - } - else // (matset_type == "sparse_by_element") - { - baseline_mesh.set_external(mesh_sbe); - } - - Node opts; - if (matset_request == "full") - { - opts["matset_style"] = "multi_buffer_full"; - } - else if (matset_request == "sparse_by_material") - { - opts["matset_style"] = "multi_buffer_by_material"; - } - else if (matset_request == "sparse_by_element") - { - opts["matset_style"] = "sparse_by_element"; - } - else - { - opts["matset_style"] = "default"; - } - - const std::string basename = "silo_venn2_" + matset_type + "_" + size; - const std::string filename = basename + ".root"; - - remove_path_if_exists(filename); - io::silo::save_mesh(baseline_mesh, basename); - io::silo::load_mesh(filename, opts, load_mesh); - EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - - if (matset_request == "full") - { - baseline_mesh.set_external(mesh_full); - } - else if (matset_request == "sparse_by_material") - { - baseline_mesh.set_external(mesh_sbm); - } - else if (matset_request == "sparse_by_element") - { - baseline_mesh.set_external(mesh_sbe); - } - else - { - baseline_mesh.set_external(mesh_sbe); - } - - // make changes to save mesh so the diff will pass - - // The field mat_check has values that are one type and matset_values - // that are another type. The silo writer converts both to double arrays - // in this case, so we follow suit. - Node mat_check_new_values, mat_check_new_matset_values; - baseline_mesh["fields"]["mat_check"]["values"].to_double_array(mat_check_new_values); - if (baseline_mesh["fields"]["mat_check"]["matset_values"].dtype().is_object()) - { - auto mat_vals_itr = baseline_mesh["fields"]["mat_check"]["matset_values"].children(); - while (mat_vals_itr.has_next()) - { - Node &mat_vals_for_mat = mat_vals_itr.next(); - const std::string mat_name = mat_vals_itr.name(); - mat_vals_for_mat.to_double_array(mat_check_new_matset_values[mat_name]); - } - } - else - { - baseline_mesh["fields"]["mat_check"]["matset_values"].to_double_array(mat_check_new_matset_values); - } - baseline_mesh["fields"]["mat_check"]["values"].set_external(mat_check_new_values); - baseline_mesh["fields"]["mat_check"]["matset_values"].set_external(mat_check_new_matset_values); - - silo_name_changer("mesh", baseline_mesh); - - // the loaded mesh will be in the multidomain format - // but the saved mesh is in the single domain format - EXPECT_EQ(load_mesh.number_of_children(), 1); - EXPECT_EQ(load_mesh[0].number_of_children(), baseline_mesh.number_of_children()); - EXPECT_FALSE(load_mesh[0].diff(baseline_mesh, info, CONDUIT_EPSILON, true)); - } - } -} - -//----------------------------------------------------------------------------- -TEST(conduit_relay_io_silo, round_trip_save_option_silo_type) -{ - const std::vector silo_types = {"default", "pdb", "hdf5", "unknown"}; - for (int i = 3; i < silo_types.size(); i ++) - { - Node opts; - opts["silo_type"] = silo_types[i]; - - Node save_mesh, load_mesh, info; - blueprint::mesh::examples::basic("rectilinear", 3, 4, 0, save_mesh); - - const std::string basename = "silo_save_option_silo_type_" + silo_types[i] + "_basic"; - const std::string filename = basename + ".root"; - - remove_path_if_exists(filename); - io::silo::save_mesh(save_mesh, basename, opts); - io::silo::load_mesh(filename, load_mesh); - EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); +// opts["mesh_name"] = "mesh_topo2"; +// io::silo::load_mesh(filename, opts, load_mesh); +// opts["mesh_name"] = "mesh_topo"; +// io::silo::load_mesh(filename, opts, load_mesh2); +// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); +// EXPECT_TRUE(blueprint::mesh::verify(load_mesh2, info)); + +// // make changes to save mesh so the diff will pass +// save_mesh[2]["coordsets"].remove_child("coords"); +// save_mesh[2]["fields"].remove_child("dist"); +// for (index_t child = 0; child < save_mesh.number_of_children(); child ++) +// { +// silo_name_changer("mesh", save_mesh[child]); +// } + +// // we must merge the two meshes in load mesh +// // the indexing is tricky because one is missing a domain +// load_mesh[0]["coordsets"]["mesh_topo"].set_external(load_mesh2[0]["coordsets"]["mesh_topo"]); +// load_mesh[0]["topologies"]["mesh_topo"].set_external(load_mesh2[0]["topologies"]["mesh_topo"]); +// load_mesh[0]["fields"]["mesh_dist"].set_external(load_mesh2[0]["fields"]["mesh_dist"]); +// load_mesh[1]["coordsets"]["mesh_topo"].set_external(load_mesh2[1]["coordsets"]["mesh_topo"]); +// load_mesh[1]["topologies"]["mesh_topo"].set_external(load_mesh2[1]["topologies"]["mesh_topo"]); +// load_mesh[1]["fields"]["mesh_dist"].set_external(load_mesh2[1]["fields"]["mesh_dist"]); +// load_mesh[3]["coordsets"]["mesh_topo"].set_external(load_mesh2[2]["coordsets"]["mesh_topo"]); +// load_mesh[3]["topologies"]["mesh_topo"].set_external(load_mesh2[2]["topologies"]["mesh_topo"]); +// load_mesh[3]["fields"]["mesh_dist"].set_external(load_mesh2[2]["fields"]["mesh_dist"]); + +// EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); +// NodeConstIterator l_itr = load_mesh.children(); +// NodeConstIterator s_itr = save_mesh.children(); +// while (l_itr.has_next()) +// { +// const Node &l_curr = l_itr.next(); +// const Node &s_curr = s_itr.next(); + +// EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); +// } +// } + +// //----------------------------------------------------------------------------- +// // explicit points (unstructured mesh) do not use every coord +// TEST(conduit_relay_io_silo, unstructured_points) +// { +// Node save_mesh, load_mesh, info; +// blueprint::mesh::examples::braid("points", 2, 2, 2, save_mesh); + +// std::vector new_conn; +// std::vector new_field1; +// std::vector new_field2; +// std::vector new_xcoords, new_ycoords, new_zcoords; + +// int_accessor conn = save_mesh["topologies"]["mesh"]["elements"]["connectivity"].value(); + +// float_accessor field1 = save_mesh["fields"]["braid"]["values"].value(); +// float_accessor field2 = save_mesh["fields"]["radial"]["values"].value(); + +// float_accessor xcoords = save_mesh["coordsets"]["coords"]["values"]["x"].value(); +// float_accessor ycoords = save_mesh["coordsets"]["coords"]["values"]["y"].value(); +// float_accessor zcoords = save_mesh["coordsets"]["coords"]["values"]["z"].value(); + +// for (int i = 1; i < conn.number_of_elements(); i += 2) +// { +// new_conn.push_back(conn[i]); +// new_field1.push_back(field1[i]); +// new_field2.push_back(field2[i]); + +// new_xcoords.push_back(xcoords[conn[i]]); +// new_ycoords.push_back(ycoords[conn[i]]); +// new_zcoords.push_back(zcoords[conn[i]]); +// } +// save_mesh["topologies"]["mesh"]["elements"]["connectivity"].reset(); +// save_mesh["topologies"]["mesh"]["elements"]["connectivity"].set(new_conn); + +// save_mesh["fields"].remove_child("vel"); +// save_mesh["fields"]["braid"]["values"].reset(); +// save_mesh["fields"]["braid"]["values"].set(new_field1); +// save_mesh["fields"]["radial"]["values"].reset(); +// save_mesh["fields"]["radial"]["values"].set(new_field2); + +// // we have modified braid such that it only uses half of the points in the coordset + +// const std::string basename = "silo_unstructured_points_braid"; +// const std::string filename = basename + ".cycle_000100.root"; + +// // remove existing root file, directory and any output files +// remove_path_if_exists(filename); + +// io::silo::save_mesh(save_mesh, basename); +// io::silo::load_mesh(filename, load_mesh); +// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + +// // now we must remove the unused points and change to an implicit points topo so that the diff passes +// save_mesh["coordsets"]["coords"]["values"]["x"].reset(); +// save_mesh["coordsets"]["coords"]["values"]["x"].set(new_xcoords); +// save_mesh["coordsets"]["coords"]["values"]["y"].reset(); +// save_mesh["coordsets"]["coords"]["values"]["y"].set(new_ycoords); +// save_mesh["coordsets"]["coords"]["values"]["z"].reset(); +// save_mesh["coordsets"]["coords"]["values"]["z"].set(new_zcoords); + +// save_mesh["topologies"].remove_child("mesh"); +// save_mesh["topologies"]["mesh"]["type"] = "points"; +// save_mesh["topologies"]["mesh"]["coordset"] = "coords"; + +// // the association doesn't matter for point meshes +// // we choose vertex by convention +// save_mesh["fields"]["radial"]["association"].reset(); +// save_mesh["fields"]["radial"]["association"] = "vertex"; + +// silo_name_changer("mesh", save_mesh); + +// // the loaded mesh will be in the multidomain format +// // but the saved mesh is in the single domain format +// EXPECT_EQ(load_mesh.number_of_children(), 1); +// EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); + +// EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); +// } + +// //----------------------------------------------------------------------------- + +// // +// // save and read option tests +// // + +// // save options: +// /// opts: +// /// +// /// file_style: "default", "root_only", "multi_file", "overlink" +// /// when # of domains == 1, "default" ==> "root_only" +// /// else, "default" ==> "multi_file" +// /// +// /// silo_type: "default", "pdb", "hdf5", "unknown" +// /// when the file we are writing to exists, "default" ==> "unknown" +// /// else, "default" ==> "hdf5" +// /// note: these are additional silo_type options that we could add +// /// support for in the future: +// /// "hdf5_sec2", "hdf5_stdio", "hdf5_mpio", "hdf5_mpiposix", "taurus" +// /// +// /// suffix: "default", "cycle", "none" +// /// when cycle is present, "default" ==> "cycle" +// /// else, "default" ==> "none" +// /// +// /// root_file_ext: "default", "root", "silo" +// /// "default" ==> "root" +// /// if overlink, this parameter is unused. +// /// +// /// mesh_name: (used if present, default ==> "mesh") +// /// +// /// ovl_topo_name: (used if present, default ==> "") +// /// +// /// number_of_files: {# of files} +// /// when "multi_file" or "overlink": +// /// <= 0, use # of files == # of domains +// /// > 0, # of files == number_of_files + +// // read options: +// /// opts: +// /// mesh_name: "{name}" +// /// provide explicit mesh name, for cases where silo data includes +// /// more than one mesh. +// /// +// /// matset_style: "default", "multi_buffer_full", "sparse_by_element", +// /// "multi_buffer_by_material" +// /// "default" ==> "sparse_by_element" + +// //----------------------------------------------------------------------------- +// TEST(conduit_relay_io_silo, round_trip_save_option_file_style) +// { +// // we will do overlink tests separately +// const std::vector file_styles = {"default", "root_only", "multi_file"}; +// for (int i = 0; i < file_styles.size(); i ++) +// { +// Node opts; +// opts["file_style"] = file_styles[i]; + +// const std::string basename = "silo_save_option_file_style_" + file_styles[i] + "_spiral"; +// const std::string filename = basename + ".cycle_000000.root"; + +// for (int ndomains = 1; ndomains < 5; ndomains += 3) +// { +// Node save_mesh, load_mesh, info; +// blueprint::mesh::examples::spiral(ndomains, save_mesh); +// remove_path_if_exists(filename); +// io::silo::save_mesh(save_mesh, basename, opts); +// io::silo::load_mesh(filename, load_mesh); +// EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); + +// // make changes to save mesh so the diff will pass +// for (index_t child = 0; child < save_mesh.number_of_children(); child ++) +// { +// silo_name_changer("mesh", save_mesh[child]); +// } + +// EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); +// NodeConstIterator l_itr = load_mesh.children(); +// NodeConstIterator s_itr = save_mesh.children(); +// while (l_itr.has_next()) +// { +// const Node &l_curr = l_itr.next(); +// const Node &s_curr = s_itr.next(); + +// EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); +// } +// } +// } +// } + +// //----------------------------------------------------------------------------- +// TEST(conduit_relay_io_silo, round_trip_save_option_number_of_files) +// { +// const std::vector number_of_files = {-1, 2}; +// for (int i = 0; i < number_of_files.size(); i ++) +// { +// Node opts; +// opts["file_style"] = "multi_file"; +// opts["number_of_files"] = number_of_files[i]; + +// const std::string basename = "silo_save_option_number_of_files_" + +// std::to_string(number_of_files[i]) + +// "_spiral"; +// const std::string filename = basename + ".cycle_000000.root"; + +// const int ndomains = 5; + +// Node save_mesh, load_mesh, info; +// blueprint::mesh::examples::spiral(ndomains, save_mesh); + +// remove_path_if_exists(filename); +// io::silo::save_mesh(save_mesh, basename, opts); +// io::silo::load_mesh(filename, load_mesh); +// EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); + +// // make changes to save mesh so the diff will pass +// for (index_t child = 0; child < save_mesh.number_of_children(); child ++) +// { +// silo_name_changer("mesh", save_mesh[child]); +// } + +// EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); +// NodeConstIterator l_itr = load_mesh.children(); +// NodeConstIterator s_itr = save_mesh.children(); +// while (l_itr.has_next()) +// { +// const Node &l_curr = l_itr.next(); +// const Node &s_curr = s_itr.next(); + +// EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); +// } +// } +// } + +// //----------------------------------------------------------------------------- +// TEST(conduit_relay_io_silo, round_trip_save_option_suffix) +// { +// const std::vector suffixes = {"default", "default", "cycle", "none"}; +// const std::vector file_suffixes = { +// "", // cycle is not present +// ".cycle_000005", // cycle is present +// ".cycle_000005", // cycle is turned on +// "", // cycle is turned off +// }; +// const std::vector include_cycle = {"no", "yes", "yes", "yes"}; +// for (int i = 0; i < suffixes.size(); i ++) +// { +// Node opts; +// opts["suffix"] = suffixes[i]; + +// const std::string basename = "silo_save_option_suffix_" + suffixes[i] + +// "_" + include_cycle[i] + "_basic"; +// const std::string filename = basename + file_suffixes[i] + ".root"; + +// Node save_mesh, load_mesh, info; +// blueprint::mesh::examples::basic("rectilinear", 3, 4, 0, save_mesh); + +// if (include_cycle[i] == "yes") +// { +// save_mesh["state/cycle"] = 5; +// } + +// remove_path_if_exists(filename); +// io::silo::save_mesh(save_mesh, basename, opts); +// io::silo::load_mesh(filename, load_mesh); +// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + +// silo_name_changer("mesh", save_mesh); + +// // the loaded mesh will be in the multidomain format +// // but the saved mesh is in the single domain format +// EXPECT_EQ(load_mesh.number_of_children(), 1); +// EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); + +// EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); +// } +// } + +// //----------------------------------------------------------------------------- +// TEST(conduit_relay_io_silo, round_trip_save_option_root_file_ext) +// { +// const std::vector root_file_exts = {"default", "root", "silo"}; + +// for (int i = 0; i < root_file_exts.size(); i ++) +// { +// Node opts; +// opts["root_file_ext"] = root_file_exts[i]; + +// std::string actual_file_ext = root_file_exts[i]; +// if (actual_file_ext == "default") +// { +// actual_file_ext = "root"; +// } + +// const std::string basename = "round_trip_save_option_root_file_ext_" + +// root_file_exts[i] + "_basic"; +// const std::string filename = basename + "." + actual_file_ext; + +// Node save_mesh, load_mesh, info; +// blueprint::mesh::examples::basic("rectilinear", 3, 4, 0, save_mesh); +// remove_path_if_exists(filename); +// io::silo::save_mesh(save_mesh, basename, opts); +// io::silo::load_mesh(filename, load_mesh); +// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + +// silo_name_changer("mesh", save_mesh); + +// // the loaded mesh will be in the multidomain format +// // but the saved mesh is in the single domain format +// EXPECT_EQ(load_mesh.number_of_children(), 1); +// EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); +// EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); +// } +// } + +// //----------------------------------------------------------------------------- +// TEST(conduit_relay_io_silo, round_trip_save_option_mesh_name) +// { +// const std::string basename = "silo_save_option_mesh_name_basic"; +// const std::string filename = basename + ".root"; + +// Node opts; +// opts["mesh_name"] = "mymesh"; + +// Node save_mesh, load_mesh, info; +// blueprint::mesh::examples::basic("rectilinear", 3, 4, 0, save_mesh); +// remove_path_if_exists(filename); +// io::silo::save_mesh(save_mesh, basename, opts); +// io::silo::load_mesh(filename, load_mesh); +// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + +// silo_name_changer("mymesh", save_mesh); + +// // the loaded mesh will be in the multidomain format +// // but the saved mesh is in the single domain format +// EXPECT_EQ(load_mesh.number_of_children(), 1); +// EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); +// EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); +// } + +// //----------------------------------------------------------------------------- +// TEST(conduit_relay_io_silo, round_trip_read_option_mesh_name) +// { +// Node load_mesh, info, opts; +// const std::string path = utils::join_file_path("silo", "multi_curv3d.silo"); +// const std::string input_file = relay_test_silo_data_path(path); + +// opts["mesh_name"] = "mesh1_dup"; + +// io::silo::load_mesh(input_file, opts, load_mesh); +// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + +// EXPECT_TRUE(load_mesh[0].has_path("topologies/mesh1_dup")); +// } + +// //----------------------------------------------------------------------------- +// TEST(conduit_relay_io_silo, round_trip_read_option_matset_style) +// { +// // the matset type and the type we are requesting on read +// const std::vector> matset_types = { +// std::make_pair("full", "full"), +// std::make_pair("sparse_by_material", "sparse_by_material"), +// std::make_pair("sparse_by_element", "sparse_by_element"), +// std::make_pair("sparse_by_element", "full"), +// std::make_pair("sparse_by_material", "sparse_by_element"), +// std::make_pair("sparse_by_material", "default"), +// }; + +// for (int i = 0; i < matset_types.size(); i ++) +// { +// std::string matset_type = matset_types[i].first; +// std::string matset_request = matset_types[i].second; + +// for (int j = 0; j < 2; j ++) +// { +// Node mesh_full, mesh_sbe, mesh_sbm, baseline_mesh, load_mesh, info; +// std::string size; +// int nx, ny; +// const double radius = 0.25; +// if (j == 0) +// { +// size = "small"; +// nx = ny = 4; +// } +// else +// { +// size = "large"; +// nx = ny = 100; +// } + +// blueprint::mesh::examples::venn("full", nx, ny, radius, mesh_full); +// blueprint::mesh::examples::venn("sparse_by_material", nx, ny, radius, mesh_sbm); +// blueprint::mesh::examples::venn("sparse_by_element", nx, ny, radius, mesh_sbe); + +// if (matset_type == "full") +// { +// baseline_mesh.set_external(mesh_full); +// } +// else if (matset_type == "sparse_by_material") +// { +// baseline_mesh.set_external(mesh_sbm); +// } +// else // (matset_type == "sparse_by_element") +// { +// baseline_mesh.set_external(mesh_sbe); +// } + +// Node opts; +// if (matset_request == "full") +// { +// opts["matset_style"] = "multi_buffer_full"; +// } +// else if (matset_request == "sparse_by_material") +// { +// opts["matset_style"] = "multi_buffer_by_material"; +// } +// else if (matset_request == "sparse_by_element") +// { +// opts["matset_style"] = "sparse_by_element"; +// } +// else +// { +// opts["matset_style"] = "default"; +// } + +// const std::string basename = "silo_venn2_" + matset_type + "_" + size; +// const std::string filename = basename + ".root"; + +// remove_path_if_exists(filename); +// io::silo::save_mesh(baseline_mesh, basename); +// io::silo::load_mesh(filename, opts, load_mesh); +// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + +// if (matset_request == "full") +// { +// baseline_mesh.set_external(mesh_full); +// } +// else if (matset_request == "sparse_by_material") +// { +// baseline_mesh.set_external(mesh_sbm); +// } +// else if (matset_request == "sparse_by_element") +// { +// baseline_mesh.set_external(mesh_sbe); +// } +// else +// { +// baseline_mesh.set_external(mesh_sbe); +// } + +// // make changes to save mesh so the diff will pass + +// // The field mat_check has values that are one type and matset_values +// // that are another type. The silo writer converts both to double arrays +// // in this case, so we follow suit. +// Node mat_check_new_values, mat_check_new_matset_values; +// baseline_mesh["fields"]["mat_check"]["values"].to_double_array(mat_check_new_values); +// if (baseline_mesh["fields"]["mat_check"]["matset_values"].dtype().is_object()) +// { +// auto mat_vals_itr = baseline_mesh["fields"]["mat_check"]["matset_values"].children(); +// while (mat_vals_itr.has_next()) +// { +// Node &mat_vals_for_mat = mat_vals_itr.next(); +// const std::string mat_name = mat_vals_itr.name(); +// mat_vals_for_mat.to_double_array(mat_check_new_matset_values[mat_name]); +// } +// } +// else +// { +// baseline_mesh["fields"]["mat_check"]["matset_values"].to_double_array(mat_check_new_matset_values); +// } +// baseline_mesh["fields"]["mat_check"]["values"].set_external(mat_check_new_values); +// baseline_mesh["fields"]["mat_check"]["matset_values"].set_external(mat_check_new_matset_values); + +// silo_name_changer("mesh", baseline_mesh); + +// // the loaded mesh will be in the multidomain format +// // but the saved mesh is in the single domain format +// EXPECT_EQ(load_mesh.number_of_children(), 1); +// EXPECT_EQ(load_mesh[0].number_of_children(), baseline_mesh.number_of_children()); +// EXPECT_FALSE(load_mesh[0].diff(baseline_mesh, info, CONDUIT_EPSILON, true)); +// } +// } +// } + +// //----------------------------------------------------------------------------- +// TEST(conduit_relay_io_silo, round_trip_save_option_silo_type) +// { +// const std::vector silo_types = {"default", "pdb", "hdf5", "unknown"}; +// for (int i = 3; i < silo_types.size(); i ++) +// { +// Node opts; +// opts["silo_type"] = silo_types[i]; + +// Node save_mesh, load_mesh, info; +// blueprint::mesh::examples::basic("rectilinear", 3, 4, 0, save_mesh); + +// const std::string basename = "silo_save_option_silo_type_" + silo_types[i] + "_basic"; +// const std::string filename = basename + ".root"; + +// remove_path_if_exists(filename); +// io::silo::save_mesh(save_mesh, basename, opts); +// io::silo::load_mesh(filename, load_mesh); +// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - silo_name_changer("mesh", save_mesh); - - // the loaded mesh will be in the multidomain format - // but the saved mesh is in the single domain format - EXPECT_EQ(load_mesh.number_of_children(), 1); - EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); - - EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); - } -} - -//----------------------------------------------------------------------------- -TEST(conduit_relay_io_silo, round_trip_save_option_overlink1) -{ - const std::vector ovl_topo_names = {"", "topo"}; - for (int i = 0; i < ovl_topo_names.size(); i ++) - { - std::string basename; - if (ovl_topo_names[i].empty()) - { - basename = "silo_save_option_overlink_spiral"; - } - else - { - basename = "silo_save_option_overlink_spiral_" + ovl_topo_names[i]; - } - const std::string filename = basename + "/OvlTop.silo"; - - Node opts; - opts["file_style"] = "overlink"; - opts["ovl_topo_name"] = ovl_topo_names[i]; - - int ndomains = 2; - - Node save_mesh, load_mesh, info; - blueprint::mesh::examples::spiral(ndomains, save_mesh); - add_matset_to_spiral(save_mesh, ndomains); - remove_path_if_exists(filename); - io::silo::save_mesh(save_mesh, basename, opts); - io::silo::load_mesh(filename, load_mesh); - EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); - - // make changes to save mesh so the diff will pass - for (index_t child = 0; child < save_mesh.number_of_children(); child ++) - { - // get the matset for this domain - Node &n_matset = save_mesh[child]["matsets"]["matset"]; - - // clean up volume fractions - Node vf_arr; - n_matset["volume_fractions"].to_float64_array(vf_arr); - n_matset["volume_fractions"].reset(); - n_matset["volume_fractions"].set(vf_arr); +// silo_name_changer("mesh", save_mesh); + +// // the loaded mesh will be in the multidomain format +// // but the saved mesh is in the single domain format +// EXPECT_EQ(load_mesh.number_of_children(), 1); +// EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); + +// EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); +// } +// } + +// //----------------------------------------------------------------------------- +// TEST(conduit_relay_io_silo, round_trip_save_option_overlink1) +// { +// const std::vector ovl_topo_names = {"", "topo"}; +// for (int i = 0; i < ovl_topo_names.size(); i ++) +// { +// std::string basename; +// if (ovl_topo_names[i].empty()) +// { +// basename = "silo_save_option_overlink_spiral"; +// } +// else +// { +// basename = "silo_save_option_overlink_spiral_" + ovl_topo_names[i]; +// } +// const std::string filename = basename + "/OvlTop.silo"; + +// Node opts; +// opts["file_style"] = "overlink"; +// opts["ovl_topo_name"] = ovl_topo_names[i]; + +// int ndomains = 2; + +// Node save_mesh, load_mesh, info; +// blueprint::mesh::examples::spiral(ndomains, save_mesh); +// add_matset_to_spiral(save_mesh, ndomains); +// remove_path_if_exists(filename); +// io::silo::save_mesh(save_mesh, basename, opts); +// io::silo::load_mesh(filename, load_mesh); +// EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); + +// // make changes to save mesh so the diff will pass +// for (index_t child = 0; child < save_mesh.number_of_children(); child ++) +// { +// // get the matset for this domain +// Node &n_matset = save_mesh[child]["matsets"]["matset"]; + +// // clean up volume fractions +// Node vf_arr; +// n_matset["volume_fractions"].to_float64_array(vf_arr); +// n_matset["volume_fractions"].reset(); +// n_matset["volume_fractions"].set(vf_arr); - // cheat a little bit - we don't have these to start - n_matset["sizes"].set_external(load_mesh[child]["matsets"]["MMATERIAL"]["sizes"]); - n_matset["offsets"].set_external(load_mesh[child]["matsets"]["MMATERIAL"]["offsets"]); - - overlink_name_changer(save_mesh[child]); - } - - EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); - NodeConstIterator l_itr = load_mesh.children(); - NodeConstIterator s_itr = save_mesh.children(); - while (l_itr.has_next()) - { - const Node &l_curr = l_itr.next(); - const Node &s_curr = s_itr.next(); - - EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); - } - } -} - -//----------------------------------------------------------------------------- -// this tests var attributes and padding dimensions -TEST(conduit_relay_io_silo, round_trip_save_option_overlink2) -{ - const std::string basename = "silo_save_option_overlink_basic"; - const std::string filename = basename + "/OvlTop.silo"; - - Node write_opts, read_opts; - write_opts["file_style"] = "overlink"; - read_opts["matset_style"] = "multi_buffer_full"; - - Node save_mesh, load_mesh, info; - blueprint::mesh::examples::basic("structured", 3, 3, 1, save_mesh); - - // add another field that is volume dependent - Node &field2 = save_mesh["fields"]["field2"]; - field2["association"] = "element"; - field2["topology"] = "mesh"; - field2["volume_dependent"] = "true"; - field2["values"].set_external(save_mesh["fields"]["field"]["values"]); - - // add a matset to make overlink happy - add_multi_buffer_full_matset(save_mesh, 4, "mesh"); - - remove_path_if_exists(filename); - io::silo::save_mesh(save_mesh, basename, write_opts); - io::silo::load_mesh(filename, read_opts, load_mesh); - EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); - - // make changes to save mesh so the diff will pass - overlink_name_changer(save_mesh); - - // the loaded mesh will be in the multidomain format - // but the saved mesh is in the single domain format - EXPECT_EQ(load_mesh.number_of_children(), 1); - EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); - - EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); - - // open silo files and do some checks - - DBfile *rootfile = DBOpen(filename.c_str(), DB_UNKNOWN, DB_READ); - EXPECT_TRUE(DBInqVarExists(rootfile, "VAR_ATTRIBUTES")); - EXPECT_TRUE(DBInqVarType(rootfile, "VAR_ATTRIBUTES") == DB_ARRAY); - - DBcompoundarray *var_attr = DBGetCompoundarray(rootfile, "VAR_ATTRIBUTES"); - - // fetch pointers to elements inside the compound array - char **elemnames = var_attr->elemnames; - int *elemlengths = var_attr->elemlengths; - int nelems = var_attr->nelems; - int *values = static_cast(var_attr->values); - int nvalues = var_attr->nvalues; - int datatype = var_attr->datatype; - - EXPECT_EQ(std::string(elemnames[0]), "field"); - EXPECT_EQ(std::string(elemnames[1]), "field2"); - EXPECT_EQ(elemlengths[0], 5); - EXPECT_EQ(elemlengths[1], 5); - EXPECT_EQ(nelems, 2); - // for first var - EXPECT_EQ(values[0], 1); - EXPECT_EQ(values[1], 0); - EXPECT_EQ(values[2], 1); - EXPECT_EQ(values[3], 0); - EXPECT_EQ(values[4], 1); - // for second var - EXPECT_EQ(values[5], 1); - EXPECT_EQ(values[6], 1); - EXPECT_EQ(values[7], 1); - EXPECT_EQ(values[8], 0); - EXPECT_EQ(values[9], 1); - EXPECT_EQ(nvalues, 10); - EXPECT_EQ(datatype, DB_INT); - - DBFreeCompoundarray(var_attr); - - EXPECT_TRUE(DBInqVarExists(rootfile, "PAD_DIMS")); - EXPECT_TRUE(DBInqVarType(rootfile, "PAD_DIMS") == DB_ARRAY); - - DBcompoundarray *pad_dims = DBGetCompoundarray(rootfile, "PAD_DIMS"); - - // fetch pointers to elements inside the compound array - elemnames = pad_dims->elemnames; - elemlengths = pad_dims->elemlengths; - nelems = pad_dims->nelems; - values = static_cast(pad_dims->values); - nvalues = pad_dims->nvalues; - datatype = pad_dims->datatype; - - EXPECT_EQ(std::string(elemnames[0]), "paddims"); - EXPECT_EQ(elemlengths[0], 6); - EXPECT_EQ(nelems, 1); - EXPECT_EQ(values[0], 0); - EXPECT_EQ(values[1], 0); - EXPECT_EQ(values[2], 0); - EXPECT_EQ(values[3], 0); - EXPECT_EQ(values[4], 0); - EXPECT_EQ(values[5], 0); - EXPECT_EQ(nvalues, 6); - EXPECT_EQ(datatype, DB_INT); - - DBFreeCompoundarray(pad_dims); - - DBClose(rootfile); - - // now check domain file - - const std::string dom_filename = basename + "/domain0.silo"; - DBfile *domfile = DBOpen(dom_filename.c_str(), DB_UNKNOWN, DB_READ); - - EXPECT_TRUE(DBInqVarExists(domfile, "DOMAIN_NEIGHBOR_NUMS")); - EXPECT_TRUE(DBInqVarType(domfile, "DOMAIN_NEIGHBOR_NUMS") == DB_ARRAY); - - DBcompoundarray *dom_neighbor_nums = DBGetCompoundarray(domfile, "DOMAIN_NEIGHBOR_NUMS"); - - // fetch pointers to elements inside the compound array - elemnames = dom_neighbor_nums->elemnames; - elemlengths = dom_neighbor_nums->elemlengths; - nelems = dom_neighbor_nums->nelems; - values = static_cast(dom_neighbor_nums->values); - nvalues = dom_neighbor_nums->nvalues; - datatype = dom_neighbor_nums->datatype; - - EXPECT_EQ(std::string(elemnames[0]), "num_neighbors"); - EXPECT_EQ(std::string(elemnames[1]), "neighbor_nums"); - EXPECT_EQ(elemlengths[0], 1); - EXPECT_EQ(elemlengths[1], 0); - EXPECT_EQ(nelems, 2); - EXPECT_EQ(values[0], 0); - EXPECT_EQ(nvalues, 1); - EXPECT_EQ(datatype, DB_INT); - - DBFreeCompoundarray(dom_neighbor_nums); - - DBClose(domfile); -} - -//----------------------------------------------------------------------------- -// this tests material i/o -TEST(conduit_relay_io_silo, round_trip_save_option_overlink3) -{ - Node save_mesh, load_mesh, info; - const int nx = 100, ny = 100; - const double radius = 0.25; - blueprint::mesh::examples::venn("sparse_by_element", nx, ny, radius, save_mesh); - - const std::string basename = "silo_save_option_overlink_venn"; - const std::string filename = basename + "/OvlTop.silo"; - - Node opts; - opts["file_style"] = "overlink"; - - remove_path_if_exists(filename); - io::silo::save_mesh(save_mesh, basename, opts); - io::silo::load_mesh(filename, load_mesh); - EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - - // make changes to save mesh so the diff will pass - - // The field mat_check has values that are one type and matset_values - // that are another type. The silo writer converts both to double arrays - // in this case, so we follow suit. - Node mat_check_new_values, mat_check_new_matset_values; - save_mesh["fields"]["mat_check"]["values"].to_double_array(mat_check_new_values); - save_mesh["fields"]["mat_check"]["matset_values"].to_double_array(mat_check_new_matset_values); - save_mesh["fields"]["mat_check"]["values"].set_external(mat_check_new_values); - save_mesh["fields"]["mat_check"]["matset_values"].set_external(mat_check_new_matset_values); - - overlink_name_changer(save_mesh); - - // the loaded mesh will be in the multidomain format - // but the saved mesh is in the single domain format - EXPECT_EQ(load_mesh.number_of_children(), 1); - EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); - - EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); -} - -//----------------------------------------------------------------------------- -// we are testing vector fields get converted to scalars -TEST(conduit_relay_io_silo, round_trip_save_option_overlink4) -{ - const std::vector> mesh_types = { - std::make_pair("rectilinear", "2"), std::make_pair("rectilinear", "3"), - std::make_pair("structured", "2"), std::make_pair("structured", "3"), - std::make_pair("quads", "2"), - std::make_pair("hexs", "3"), - }; - for (int i = 0; i < mesh_types.size(); ++i) - { - std::string dim = mesh_types[i].second; - index_t nx = 3; - index_t ny = 4; - index_t nz = (dim == "2" ? 0 : 2); - - std::string mesh_type = mesh_types[i].first; - - Node save_mesh, load_mesh, info; - blueprint::mesh::examples::braid(mesh_type, nx, ny, nz, save_mesh); - index_t nele_x = nx - 1; - index_t nele_y = ny - 1; - index_t nele_z = (dim == "2" ? 0 : nz - 1); - - // provide a matset for braid - braid_init_example_matset(nele_x, nele_y, nele_z, save_mesh["matsets"]["matset"]); - - const std::string basename = "silo_save_option_overlink_braid_" + mesh_type + "_" + dim + "D"; - const std::string filename = basename + "/OvlTop.silo"; - - Node write_opts, read_opts; - write_opts["file_style"] = "overlink"; - read_opts["matset_style"] = "multi_buffer_full"; - - // remove existing root file, directory and any output files - remove_path_if_exists(filename); - io::silo::save_mesh(save_mesh, basename, write_opts); - io::silo::load_mesh(filename, read_opts, load_mesh); - EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - - Node &field_vel = save_mesh["fields"]["vel"]; - Node &field_vel_u = save_mesh["fields"]["vel_u"]; - Node &field_vel_v = save_mesh["fields"]["vel_v"]; - - field_vel_u["topology"].set(field_vel["topology"]); - field_vel_u["association"].set(field_vel["association"]); - field_vel_u["values"].set(field_vel["values/u"]); - field_vel_v["topology"].set(field_vel["topology"]); - field_vel_v["association"].set(field_vel["association"]); - field_vel_v["values"].set(field_vel["values/v"]); - - if (dim == "3") - { - Node &field_vel_w = save_mesh["fields"]["vel_w"]; - field_vel_w["topology"].set(field_vel["topology"]); - field_vel_w["association"].set(field_vel["association"]); - field_vel_w["values"].set(field_vel["values/w"]); - } - - save_mesh["fields"].remove_child("vel"); - - // make changes to save mesh so the diff will pass - overlink_name_changer(save_mesh); - - // the loaded mesh will be in the multidomain format - // but the saved mesh is in the single domain format - EXPECT_EQ(load_mesh.number_of_children(), 1); - EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); - - EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); - } -} - -//----------------------------------------------------------------------------- -// check that all the shape types work (specifically polytopal ones) -TEST(conduit_relay_io_silo, round_trip_save_option_overlink5) -{ - const std::vector> mesh_types = { - std::make_pair("uniform", "2"), std::make_pair("uniform", "3"), - std::make_pair("rectilinear", "2"), std::make_pair("rectilinear", "3"), - std::make_pair("structured", "2"), std::make_pair("structured", "3"), - std::make_pair("quads", "2"), - std::make_pair("polygons", "2"), - std::make_pair("hexs", "3"), - // std::make_pair("polyhedra", "3") - // Overlink does not support tris, wedges, pyramids, or tets - }; - for (int i = 0; i < mesh_types.size(); ++i) - { - const std::string dim = mesh_types[i].second; - index_t nx = 3; - index_t ny = 4; - index_t nz = (dim == "2" ? 0 : 2); - - const std::string mesh_type = mesh_types[i].first; - - Node save_mesh, load_mesh, info; - blueprint::mesh::examples::basic(mesh_type, nx, ny, nz, save_mesh); - - const std::string basename = "silo_save_option_overlink_basic_" + mesh_type + "_" + dim + "D"; - const std::string filename = basename + "/OvlTop.silo"; - const std::string domfile = basename + "/domain0.silo"; - - Node write_opts, read_opts; - write_opts["file_style"] = "overlink"; - read_opts["matset_style"] = "multi_buffer_full"; - - // add a matset to make overlink happy - int num_elems = (nx - 1) * (ny - 1); - if (mesh_type == "tets") - { - num_elems *= 6; - } - add_multi_buffer_full_matset(save_mesh, num_elems, "mesh"); - - remove_path_if_exists(filename); - remove_path_if_exists(domfile); - io::silo::save_mesh(save_mesh, basename, write_opts); - io::silo::load_mesh(filename, read_opts, load_mesh); - EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); - - // make changes to save mesh so the diff will pass - if (mesh_type == "uniform") - { - silo_uniform_to_rect_conversion("coords", "mesh", save_mesh); - } - overlink_name_changer(save_mesh); - - // the loaded mesh will be in the multidomain format - // but the saved mesh is in the single domain format - EXPECT_EQ(load_mesh.number_of_children(), 1); - EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); - - EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); - } -} - -//----------------------------------------------------------------------------- - -// -// read and write Silo and Overlink tests -// - -//----------------------------------------------------------------------------- -// read normal silo files containing multimeshes, multivars, and multimats -TEST(conduit_relay_io_silo, read_silo) -{ - const std::vector> file_info = { - {".", "multi_curv3d", ".silo", "" }, // test default case - {".", "multi_curv3d", ".silo", "mesh1" }, - // {".", "multi_curv3d", ".silo", "mesh1_back" }, // this multimesh points to paths that do not exist - {".", "multi_curv3d", ".silo", "mesh1_dup" }, - // {".", "multi_curv3d", ".silo", "mesh1_front" }, // same here - {".", "multi_curv3d", ".silo", "mesh1_hidden"}, - {".", "tire", ".silo", "" }, // test default case - {".", "tire", ".silo", "tire" }, - {".", "galaxy0000", ".silo", "" }, // test default case - {".", "galaxy0000", ".silo", "StarMesh" }, - {".", "emptydomains", ".silo", "" }, // test default case - {".", "emptydomains", ".silo", "mesh" }, - {"multidir_test_data", "multidir0000", ".root", "" }, // test default case - {"multidir_test_data", "multidir0000", ".root", "Mesh" }, - }; - - // TODO what to do in the case where a multimesh points to no data? (mesh1_back) - // fail silently, as we do now? - - for (int i = 0; i < file_info.size(); i ++) - { - const std::string dirname = file_info[i][0]; - const std::string basename = file_info[i][1]; - const std::string fileext = file_info[i][2]; - const std::string meshname = file_info[i][3]; - - Node load_mesh, info, read_opts, write_opts; - std::string filepath = utils::join_file_path(dirname, basename) + fileext; - filepath = utils::join_file_path("silo", filepath); - std::string input_file = relay_test_silo_data_path(filepath); - - read_opts["mesh_name"] = meshname; - - io::silo::load_mesh(input_file, read_opts, load_mesh); - EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - - std::string out_name = "read_silo_" + basename; - if (!meshname.empty()) - { - out_name += "_" + meshname; - } - - // TODO are these remove paths doing anything? Don't they need filenames? - remove_path_if_exists(out_name + "_write_blueprint"); - io::blueprint::save_mesh(load_mesh, out_name + "_write_blueprint", "hdf5"); - - remove_path_if_exists(out_name + "_write_silo"); - io::silo::save_mesh(load_mesh, out_name + "_write_silo"); - - // overlink requires matsets and does not support point meshes - if (load_mesh[0].has_child("matsets") && basename != "galaxy0000") - { - remove_path_if_exists(out_name + "_write_overlink"); - write_opts["file_style"] = "overlink"; - write_opts["ovl_topo_name"] = meshname; - io::silo::save_mesh(load_mesh, out_name + "_write_overlink", write_opts); - } - } -} - -//----------------------------------------------------------------------------- -// test that we can read the fake overlink files from the visit test data -TEST(conduit_relay_io_silo, read_fake_overlink) -{ - const std::vector> file_info = { - // {"ev_0_0_100", "OvlTop", ".silo", "" }, // test default case - // {"ev_0_0_100", "OvlTop", ".silo", "MMESH"}, - // uncomment once silo ucdmesh phzones are supported - {"hl18spec", "OvlTop", ".silo", "" }, // test default case - {"hl18spec", "OvlTop", ".silo", "MMESH"}, - // {"regrovl_qh_1000_10001_4", "OvlTop", ".silo", "" }, // test default case - // {"regrovl_qh_1000_10001_4", "OvlTop", ".silo", "MMESH"}, - // uncomment once silo ucdmesh phzones are supported - {"utpyr4", "OvlTop", ".silo", "" }, // test default case - {"utpyr4", "OvlTop", ".silo", "MMESH"}, - }; - - for (int i = 0; i < file_info.size(); i ++) - { - const std::string dirname = file_info[i][0]; - const std::string basename = file_info[i][1]; - const std::string fileext = file_info[i][2]; - const std::string meshname = file_info[i][3]; - - Node load_mesh, info, read_opts, write_opts; - std::string filepath = utils::join_file_path(dirname, basename) + fileext; - filepath = utils::join_file_path("fake_overlink", filepath); - std::string input_file = relay_test_silo_data_path(filepath); - - read_opts["mesh_name"] = meshname; - - io::silo::load_mesh(input_file, read_opts, load_mesh); - EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - - std::string out_name = "read_fake_overlink_" + dirname; - if (!meshname.empty()) - { - out_name += "_" + meshname; - } - - remove_path_if_exists(out_name + "_write_blueprint"); - io::blueprint::save_mesh(load_mesh, out_name + "_write_blueprint", "hdf5"); - - remove_path_if_exists(out_name + "_write_silo"); - io::silo::save_mesh(load_mesh, out_name + "_write_silo"); - - remove_path_if_exists(out_name + "_write_overlink"); - write_opts["file_style"] = "overlink"; - write_opts["ovl_topo_name"] = "MMESH"; - io::silo::save_mesh(load_mesh, out_name + "_write_overlink", write_opts); - } -} - -//----------------------------------------------------------------------------- -// read overlink files in symlink format -// should be similar to reading raw silo -TEST(conduit_relay_io_silo, read_overlink_symlink_format) -{ - const std::vector> file_info = { - {".", "box2d", ".silo", "" }, // test default case - {".", "box2d", ".silo", "MMESH"}, - {".", "box3d", ".silo", "" }, // test default case - {".", "box3d", ".silo", "MMESH"}, - // {".", "diamond", ".silo", "" }, // test default case - // {".", "diamond", ".silo", "MMESH"}, - // fails b/c polytopal not yet supported - {".", "testDisk2D_a", ".silo", "" }, // test default case - {".", "testDisk2D_a", ".silo", "MMESH"}, - // {".", "donordiv.s2_materials2", ".silo", "" }, // test default case - // {".", "donordiv.s2_materials2", ".silo", "MMESH"}, - // fails b/c polytopal not yet supported - {".", "donordiv.s2_materials3", ".silo", "" }, // test default case - {".", "donordiv.s2_materials3", ".silo", "MMESH"}, - }; - - for (int i = 0; i < file_info.size(); i ++) - { - const std::string dirname = file_info[i][0]; - const std::string basename = file_info[i][1]; - const std::string fileext = file_info[i][2]; - const std::string meshname = file_info[i][3]; - - Node load_mesh, info, read_opts, write_opts; - std::string filepath = utils::join_file_path(dirname, basename) + fileext; - filepath = utils::join_file_path("overlink", filepath); - std::string input_file = relay_test_silo_data_path(filepath); - - read_opts["mesh_name"] = meshname; - - io::silo::load_mesh(input_file, read_opts, load_mesh); - EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - - std::string out_name = "read_overlink_symlink_" + basename; - if (!meshname.empty()) - { - out_name += "_" + meshname; - } - - remove_path_if_exists(out_name + "_write_blueprint"); - io::blueprint::save_mesh(load_mesh, out_name + "_write_blueprint", "hdf5"); - - remove_path_if_exists(out_name + "_write_silo"); - io::silo::save_mesh(load_mesh, out_name + "_write_silo"); - - remove_path_if_exists(out_name + "_write_overlink"); - write_opts["file_style"] = "overlink"; - write_opts["ovl_topo_name"] = "MMESH"; - io::silo::save_mesh(load_mesh, out_name + "_write_overlink", write_opts); - } -} - -//----------------------------------------------------------------------------- -// read overlink directly from ovltop.silo -// this case is tricky and involves messing with paths -TEST(conduit_relay_io_silo, read_overlink_directly) -{ - const std::vector> file_info = { - {"box2d", "OvlTop", ".silo", "" }, // test default case - {"box2d", "OvlTop", ".silo", "MMESH"}, - {"box3d", "OvlTop", ".silo", "" }, // test default case - {"box3d", "OvlTop", ".silo", "MMESH"}, - // {"diamond", "OvlTop", ".silo", "" }, // test default case - // {"diamond", "OvlTop", ".silo", "MMESH"}, - {"testDisk2D_a", "OvlTop", ".silo", "" }, // test default case - {"testDisk2D_a", "OvlTop", ".silo", "MMESH"}, - // {"donordiv.s2_materials2", "OvlTop", ".silo", "" }, // test default case - // {"donordiv.s2_materials2", "OvlTop", ".silo", "MMESH"}, - {"donordiv.s2_materials3", "OvlTop", ".silo", "" }, // test default case - {"donordiv.s2_materials3", "OvlTop", ".silo", "MMESH"}, - }; - - for (int i = 0; i < file_info.size(); i ++) - { - const std::string dirname = file_info[i][0]; - const std::string basename = file_info[i][1]; - const std::string fileext = file_info[i][2]; - const std::string meshname = file_info[i][3]; - - Node load_mesh, info, read_opts, write_opts; - - std::string filepath = utils::join_file_path(dirname, basename) + fileext; - filepath = utils::join_file_path("overlink", filepath); - std::string input_file = relay_test_silo_data_path(filepath); - - read_opts["mesh_name"] = meshname; - - io::silo::load_mesh(input_file, read_opts, load_mesh); - EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - - std::string out_name = "read_overlink_direct_" + dirname; - if (!meshname.empty()) - { - out_name += "_" + meshname; - } - - remove_path_if_exists(out_name + "_write_blueprint"); - io::blueprint::save_mesh(load_mesh, out_name + "_write_blueprint", "hdf5"); - - remove_path_if_exists(out_name + "_write_silo"); - io::silo::save_mesh(load_mesh, out_name + "_write_silo"); - - remove_path_if_exists(out_name + "_write_overlink"); - write_opts["file_style"] = "overlink"; - write_opts["ovl_topo_name"] = "MMESH"; - io::silo::save_mesh(load_mesh, out_name + "_write_overlink", write_opts); - } -} - -// TODO add tests for... -// - polytopal meshes once they are supported -// - units once they are supported -// - etc. - -// TODO add tetra8 and c36_m5 to all the overlink i/o tests - -// TODO somewhere I need to error on overlink when there are different var or mesh types across domains +// // cheat a little bit - we don't have these to start +// n_matset["sizes"].set_external(load_mesh[child]["matsets"]["MMATERIAL"]["sizes"]); +// n_matset["offsets"].set_external(load_mesh[child]["matsets"]["MMATERIAL"]["offsets"]); + +// overlink_name_changer(save_mesh[child]); +// } + +// EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); +// NodeConstIterator l_itr = load_mesh.children(); +// NodeConstIterator s_itr = save_mesh.children(); +// while (l_itr.has_next()) +// { +// const Node &l_curr = l_itr.next(); +// const Node &s_curr = s_itr.next(); + +// EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); +// } +// } +// } + +// //----------------------------------------------------------------------------- +// // this tests var attributes and padding dimensions +// TEST(conduit_relay_io_silo, round_trip_save_option_overlink2) +// { +// const std::string basename = "silo_save_option_overlink_basic"; +// const std::string filename = basename + "/OvlTop.silo"; + +// Node write_opts, read_opts; +// write_opts["file_style"] = "overlink"; +// read_opts["matset_style"] = "multi_buffer_full"; + +// Node save_mesh, load_mesh, info; +// blueprint::mesh::examples::basic("structured", 3, 3, 1, save_mesh); + +// // add another field that is volume dependent +// Node &field2 = save_mesh["fields"]["field2"]; +// field2["association"] = "element"; +// field2["topology"] = "mesh"; +// field2["volume_dependent"] = "true"; +// field2["values"].set_external(save_mesh["fields"]["field"]["values"]); + +// // add a matset to make overlink happy +// add_multi_buffer_full_matset(save_mesh, 4, "mesh"); + +// remove_path_if_exists(filename); +// io::silo::save_mesh(save_mesh, basename, write_opts); +// io::silo::load_mesh(filename, read_opts, load_mesh); +// EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); + +// // make changes to save mesh so the diff will pass +// overlink_name_changer(save_mesh); + +// // the loaded mesh will be in the multidomain format +// // but the saved mesh is in the single domain format +// EXPECT_EQ(load_mesh.number_of_children(), 1); +// EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); + +// EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); + +// // open silo files and do some checks + +// DBfile *rootfile = DBOpen(filename.c_str(), DB_UNKNOWN, DB_READ); +// EXPECT_TRUE(DBInqVarExists(rootfile, "VAR_ATTRIBUTES")); +// EXPECT_TRUE(DBInqVarType(rootfile, "VAR_ATTRIBUTES") == DB_ARRAY); + +// DBcompoundarray *var_attr = DBGetCompoundarray(rootfile, "VAR_ATTRIBUTES"); + +// // fetch pointers to elements inside the compound array +// char **elemnames = var_attr->elemnames; +// int *elemlengths = var_attr->elemlengths; +// int nelems = var_attr->nelems; +// int *values = static_cast(var_attr->values); +// int nvalues = var_attr->nvalues; +// int datatype = var_attr->datatype; + +// EXPECT_EQ(std::string(elemnames[0]), "field"); +// EXPECT_EQ(std::string(elemnames[1]), "field2"); +// EXPECT_EQ(elemlengths[0], 5); +// EXPECT_EQ(elemlengths[1], 5); +// EXPECT_EQ(nelems, 2); +// // for first var +// EXPECT_EQ(values[0], 1); +// EXPECT_EQ(values[1], 0); +// EXPECT_EQ(values[2], 1); +// EXPECT_EQ(values[3], 0); +// EXPECT_EQ(values[4], 1); +// // for second var +// EXPECT_EQ(values[5], 1); +// EXPECT_EQ(values[6], 1); +// EXPECT_EQ(values[7], 1); +// EXPECT_EQ(values[8], 0); +// EXPECT_EQ(values[9], 1); +// EXPECT_EQ(nvalues, 10); +// EXPECT_EQ(datatype, DB_INT); + +// DBFreeCompoundarray(var_attr); + +// EXPECT_TRUE(DBInqVarExists(rootfile, "PAD_DIMS")); +// EXPECT_TRUE(DBInqVarType(rootfile, "PAD_DIMS") == DB_ARRAY); + +// DBcompoundarray *pad_dims = DBGetCompoundarray(rootfile, "PAD_DIMS"); + +// // fetch pointers to elements inside the compound array +// elemnames = pad_dims->elemnames; +// elemlengths = pad_dims->elemlengths; +// nelems = pad_dims->nelems; +// values = static_cast(pad_dims->values); +// nvalues = pad_dims->nvalues; +// datatype = pad_dims->datatype; + +// EXPECT_EQ(std::string(elemnames[0]), "paddims"); +// EXPECT_EQ(elemlengths[0], 6); +// EXPECT_EQ(nelems, 1); +// EXPECT_EQ(values[0], 0); +// EXPECT_EQ(values[1], 0); +// EXPECT_EQ(values[2], 0); +// EXPECT_EQ(values[3], 0); +// EXPECT_EQ(values[4], 0); +// EXPECT_EQ(values[5], 0); +// EXPECT_EQ(nvalues, 6); +// EXPECT_EQ(datatype, DB_INT); + +// DBFreeCompoundarray(pad_dims); + +// DBClose(rootfile); + +// // now check domain file + +// const std::string dom_filename = basename + "/domain0.silo"; +// DBfile *domfile = DBOpen(dom_filename.c_str(), DB_UNKNOWN, DB_READ); + +// EXPECT_TRUE(DBInqVarExists(domfile, "DOMAIN_NEIGHBOR_NUMS")); +// EXPECT_TRUE(DBInqVarType(domfile, "DOMAIN_NEIGHBOR_NUMS") == DB_ARRAY); + +// DBcompoundarray *dom_neighbor_nums = DBGetCompoundarray(domfile, "DOMAIN_NEIGHBOR_NUMS"); + +// // fetch pointers to elements inside the compound array +// elemnames = dom_neighbor_nums->elemnames; +// elemlengths = dom_neighbor_nums->elemlengths; +// nelems = dom_neighbor_nums->nelems; +// values = static_cast(dom_neighbor_nums->values); +// nvalues = dom_neighbor_nums->nvalues; +// datatype = dom_neighbor_nums->datatype; + +// EXPECT_EQ(std::string(elemnames[0]), "num_neighbors"); +// EXPECT_EQ(std::string(elemnames[1]), "neighbor_nums"); +// EXPECT_EQ(elemlengths[0], 1); +// EXPECT_EQ(elemlengths[1], 0); +// EXPECT_EQ(nelems, 2); +// EXPECT_EQ(values[0], 0); +// EXPECT_EQ(nvalues, 1); +// EXPECT_EQ(datatype, DB_INT); + +// DBFreeCompoundarray(dom_neighbor_nums); + +// DBClose(domfile); +// } + +// //----------------------------------------------------------------------------- +// // this tests material i/o +// TEST(conduit_relay_io_silo, round_trip_save_option_overlink3) +// { +// Node save_mesh, load_mesh, info; +// const int nx = 100, ny = 100; +// const double radius = 0.25; +// blueprint::mesh::examples::venn("sparse_by_element", nx, ny, radius, save_mesh); + +// const std::string basename = "silo_save_option_overlink_venn"; +// const std::string filename = basename + "/OvlTop.silo"; + +// Node opts; +// opts["file_style"] = "overlink"; + +// remove_path_if_exists(filename); +// io::silo::save_mesh(save_mesh, basename, opts); +// io::silo::load_mesh(filename, load_mesh); +// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + +// // make changes to save mesh so the diff will pass + +// // The field mat_check has values that are one type and matset_values +// // that are another type. The silo writer converts both to double arrays +// // in this case, so we follow suit. +// Node mat_check_new_values, mat_check_new_matset_values; +// save_mesh["fields"]["mat_check"]["values"].to_double_array(mat_check_new_values); +// save_mesh["fields"]["mat_check"]["matset_values"].to_double_array(mat_check_new_matset_values); +// save_mesh["fields"]["mat_check"]["values"].set_external(mat_check_new_values); +// save_mesh["fields"]["mat_check"]["matset_values"].set_external(mat_check_new_matset_values); + +// overlink_name_changer(save_mesh); + +// // the loaded mesh will be in the multidomain format +// // but the saved mesh is in the single domain format +// EXPECT_EQ(load_mesh.number_of_children(), 1); +// EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); + +// EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); +// } + +// //----------------------------------------------------------------------------- +// // we are testing vector fields get converted to scalars +// TEST(conduit_relay_io_silo, round_trip_save_option_overlink4) +// { +// const std::vector> mesh_types = { +// std::make_pair("rectilinear", "2"), std::make_pair("rectilinear", "3"), +// std::make_pair("structured", "2"), std::make_pair("structured", "3"), +// std::make_pair("quads", "2"), +// std::make_pair("hexs", "3"), +// }; +// for (int i = 0; i < mesh_types.size(); ++i) +// { +// std::string dim = mesh_types[i].second; +// index_t nx = 3; +// index_t ny = 4; +// index_t nz = (dim == "2" ? 0 : 2); + +// std::string mesh_type = mesh_types[i].first; + +// Node save_mesh, load_mesh, info; +// blueprint::mesh::examples::braid(mesh_type, nx, ny, nz, save_mesh); +// index_t nele_x = nx - 1; +// index_t nele_y = ny - 1; +// index_t nele_z = (dim == "2" ? 0 : nz - 1); + +// // provide a matset for braid +// braid_init_example_matset(nele_x, nele_y, nele_z, save_mesh["matsets"]["matset"]); + +// const std::string basename = "silo_save_option_overlink_braid_" + mesh_type + "_" + dim + "D"; +// const std::string filename = basename + "/OvlTop.silo"; + +// Node write_opts, read_opts; +// write_opts["file_style"] = "overlink"; +// read_opts["matset_style"] = "multi_buffer_full"; + +// // remove existing root file, directory and any output files +// remove_path_if_exists(filename); +// io::silo::save_mesh(save_mesh, basename, write_opts); +// io::silo::load_mesh(filename, read_opts, load_mesh); +// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + +// Node &field_vel = save_mesh["fields"]["vel"]; +// Node &field_vel_u = save_mesh["fields"]["vel_u"]; +// Node &field_vel_v = save_mesh["fields"]["vel_v"]; + +// field_vel_u["topology"].set(field_vel["topology"]); +// field_vel_u["association"].set(field_vel["association"]); +// field_vel_u["values"].set(field_vel["values/u"]); +// field_vel_v["topology"].set(field_vel["topology"]); +// field_vel_v["association"].set(field_vel["association"]); +// field_vel_v["values"].set(field_vel["values/v"]); + +// if (dim == "3") +// { +// Node &field_vel_w = save_mesh["fields"]["vel_w"]; +// field_vel_w["topology"].set(field_vel["topology"]); +// field_vel_w["association"].set(field_vel["association"]); +// field_vel_w["values"].set(field_vel["values/w"]); +// } + +// save_mesh["fields"].remove_child("vel"); + +// // make changes to save mesh so the diff will pass +// overlink_name_changer(save_mesh); + +// // the loaded mesh will be in the multidomain format +// // but the saved mesh is in the single domain format +// EXPECT_EQ(load_mesh.number_of_children(), 1); +// EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); + +// EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); +// } +// } + +// //----------------------------------------------------------------------------- +// // check that all the shape types work (specifically polytopal ones) +// TEST(conduit_relay_io_silo, round_trip_save_option_overlink5) +// { +// const std::vector> mesh_types = { +// std::make_pair("uniform", "2"), std::make_pair("uniform", "3"), +// std::make_pair("rectilinear", "2"), std::make_pair("rectilinear", "3"), +// std::make_pair("structured", "2"), std::make_pair("structured", "3"), +// std::make_pair("quads", "2"), +// std::make_pair("polygons", "2"), +// std::make_pair("hexs", "3"), +// // std::make_pair("polyhedra", "3") +// // Overlink does not support tris, wedges, pyramids, or tets +// }; +// for (int i = 0; i < mesh_types.size(); ++i) +// { +// const std::string dim = mesh_types[i].second; +// index_t nx = 3; +// index_t ny = 4; +// index_t nz = (dim == "2" ? 0 : 2); + +// const std::string mesh_type = mesh_types[i].first; + +// Node save_mesh, load_mesh, info; +// blueprint::mesh::examples::basic(mesh_type, nx, ny, nz, save_mesh); + +// const std::string basename = "silo_save_option_overlink_basic_" + mesh_type + "_" + dim + "D"; +// const std::string filename = basename + "/OvlTop.silo"; +// const std::string domfile = basename + "/domain0.silo"; + +// Node write_opts, read_opts; +// write_opts["file_style"] = "overlink"; +// read_opts["matset_style"] = "multi_buffer_full"; + +// // add a matset to make overlink happy +// int num_elems = (nx - 1) * (ny - 1); +// if (mesh_type == "tets") +// { +// num_elems *= 6; +// } +// add_multi_buffer_full_matset(save_mesh, num_elems, "mesh"); + +// remove_path_if_exists(filename); +// remove_path_if_exists(domfile); +// io::silo::save_mesh(save_mesh, basename, write_opts); +// io::silo::load_mesh(filename, read_opts, load_mesh); +// EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); + +// // make changes to save mesh so the diff will pass +// if (mesh_type == "uniform") +// { +// silo_uniform_to_rect_conversion("coords", "mesh", save_mesh); +// } +// overlink_name_changer(save_mesh); + +// // the loaded mesh will be in the multidomain format +// // but the saved mesh is in the single domain format +// EXPECT_EQ(load_mesh.number_of_children(), 1); +// EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); + +// EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); +// } +// } + +// //----------------------------------------------------------------------------- + +// // +// // read and write Silo and Overlink tests +// // + +// //----------------------------------------------------------------------------- +// // read normal silo files containing multimeshes, multivars, and multimats +// TEST(conduit_relay_io_silo, read_silo) +// { +// const std::vector> file_info = { +// {".", "multi_curv3d", ".silo", "" }, // test default case +// {".", "multi_curv3d", ".silo", "mesh1" }, +// // {".", "multi_curv3d", ".silo", "mesh1_back" }, // this multimesh points to paths that do not exist +// {".", "multi_curv3d", ".silo", "mesh1_dup" }, +// // {".", "multi_curv3d", ".silo", "mesh1_front" }, // same here +// {".", "multi_curv3d", ".silo", "mesh1_hidden"}, +// {".", "tire", ".silo", "" }, // test default case +// {".", "tire", ".silo", "tire" }, +// {".", "galaxy0000", ".silo", "" }, // test default case +// {".", "galaxy0000", ".silo", "StarMesh" }, +// {".", "emptydomains", ".silo", "" }, // test default case +// {".", "emptydomains", ".silo", "mesh" }, +// {"multidir_test_data", "multidir0000", ".root", "" }, // test default case +// {"multidir_test_data", "multidir0000", ".root", "Mesh" }, +// }; + +// // TODO what to do in the case where a multimesh points to no data? (mesh1_back) +// // fail silently, as we do now? + +// for (int i = 0; i < file_info.size(); i ++) +// { +// const std::string dirname = file_info[i][0]; +// const std::string basename = file_info[i][1]; +// const std::string fileext = file_info[i][2]; +// const std::string meshname = file_info[i][3]; + +// Node load_mesh, info, read_opts, write_opts; +// std::string filepath = utils::join_file_path(dirname, basename) + fileext; +// filepath = utils::join_file_path("silo", filepath); +// std::string input_file = relay_test_silo_data_path(filepath); + +// read_opts["mesh_name"] = meshname; + +// io::silo::load_mesh(input_file, read_opts, load_mesh); +// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + +// std::string out_name = "read_silo_" + basename; +// if (!meshname.empty()) +// { +// out_name += "_" + meshname; +// } + +// // TODO are these remove paths doing anything? Don't they need filenames? +// remove_path_if_exists(out_name + "_write_blueprint"); +// io::blueprint::save_mesh(load_mesh, out_name + "_write_blueprint", "hdf5"); + +// remove_path_if_exists(out_name + "_write_silo"); +// io::silo::save_mesh(load_mesh, out_name + "_write_silo"); + +// // overlink requires matsets and does not support point meshes +// if (load_mesh[0].has_child("matsets") && basename != "galaxy0000") +// { +// remove_path_if_exists(out_name + "_write_overlink"); +// write_opts["file_style"] = "overlink"; +// write_opts["ovl_topo_name"] = meshname; +// io::silo::save_mesh(load_mesh, out_name + "_write_overlink", write_opts); +// } +// } +// } + +// //----------------------------------------------------------------------------- +// // test that we can read the fake overlink files from the visit test data +// TEST(conduit_relay_io_silo, read_fake_overlink) +// { +// const std::vector> file_info = { +// // {"ev_0_0_100", "OvlTop", ".silo", "" }, // test default case +// // {"ev_0_0_100", "OvlTop", ".silo", "MMESH"}, +// // uncomment once silo ucdmesh phzones are supported +// {"hl18spec", "OvlTop", ".silo", "" }, // test default case +// {"hl18spec", "OvlTop", ".silo", "MMESH"}, +// // {"regrovl_qh_1000_10001_4", "OvlTop", ".silo", "" }, // test default case +// // {"regrovl_qh_1000_10001_4", "OvlTop", ".silo", "MMESH"}, +// // uncomment once silo ucdmesh phzones are supported +// {"utpyr4", "OvlTop", ".silo", "" }, // test default case +// {"utpyr4", "OvlTop", ".silo", "MMESH"}, +// }; + +// for (int i = 0; i < file_info.size(); i ++) +// { +// const std::string dirname = file_info[i][0]; +// const std::string basename = file_info[i][1]; +// const std::string fileext = file_info[i][2]; +// const std::string meshname = file_info[i][3]; + +// Node load_mesh, info, read_opts, write_opts; +// std::string filepath = utils::join_file_path(dirname, basename) + fileext; +// filepath = utils::join_file_path("fake_overlink", filepath); +// std::string input_file = relay_test_silo_data_path(filepath); + +// read_opts["mesh_name"] = meshname; + +// io::silo::load_mesh(input_file, read_opts, load_mesh); +// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + +// std::string out_name = "read_fake_overlink_" + dirname; +// if (!meshname.empty()) +// { +// out_name += "_" + meshname; +// } + +// remove_path_if_exists(out_name + "_write_blueprint"); +// io::blueprint::save_mesh(load_mesh, out_name + "_write_blueprint", "hdf5"); + +// remove_path_if_exists(out_name + "_write_silo"); +// io::silo::save_mesh(load_mesh, out_name + "_write_silo"); + +// remove_path_if_exists(out_name + "_write_overlink"); +// write_opts["file_style"] = "overlink"; +// write_opts["ovl_topo_name"] = "MMESH"; +// io::silo::save_mesh(load_mesh, out_name + "_write_overlink", write_opts); +// } +// } + +// //----------------------------------------------------------------------------- +// // read overlink files in symlink format +// // should be similar to reading raw silo +// TEST(conduit_relay_io_silo, read_overlink_symlink_format) +// { +// const std::vector> file_info = { +// {".", "box2d", ".silo", "" }, // test default case +// {".", "box2d", ".silo", "MMESH"}, +// {".", "box3d", ".silo", "" }, // test default case +// {".", "box3d", ".silo", "MMESH"}, +// // {".", "diamond", ".silo", "" }, // test default case +// // {".", "diamond", ".silo", "MMESH"}, +// // fails b/c polytopal not yet supported +// {".", "testDisk2D_a", ".silo", "" }, // test default case +// {".", "testDisk2D_a", ".silo", "MMESH"}, +// // {".", "donordiv.s2_materials2", ".silo", "" }, // test default case +// // {".", "donordiv.s2_materials2", ".silo", "MMESH"}, +// // fails b/c polytopal not yet supported +// {".", "donordiv.s2_materials3", ".silo", "" }, // test default case +// {".", "donordiv.s2_materials3", ".silo", "MMESH"}, +// }; + +// for (int i = 0; i < file_info.size(); i ++) +// { +// const std::string dirname = file_info[i][0]; +// const std::string basename = file_info[i][1]; +// const std::string fileext = file_info[i][2]; +// const std::string meshname = file_info[i][3]; + +// Node load_mesh, info, read_opts, write_opts; +// std::string filepath = utils::join_file_path(dirname, basename) + fileext; +// filepath = utils::join_file_path("overlink", filepath); +// std::string input_file = relay_test_silo_data_path(filepath); + +// read_opts["mesh_name"] = meshname; + +// io::silo::load_mesh(input_file, read_opts, load_mesh); +// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + +// std::string out_name = "read_overlink_symlink_" + basename; +// if (!meshname.empty()) +// { +// out_name += "_" + meshname; +// } + +// remove_path_if_exists(out_name + "_write_blueprint"); +// io::blueprint::save_mesh(load_mesh, out_name + "_write_blueprint", "hdf5"); + +// remove_path_if_exists(out_name + "_write_silo"); +// io::silo::save_mesh(load_mesh, out_name + "_write_silo"); + +// remove_path_if_exists(out_name + "_write_overlink"); +// write_opts["file_style"] = "overlink"; +// write_opts["ovl_topo_name"] = "MMESH"; +// io::silo::save_mesh(load_mesh, out_name + "_write_overlink", write_opts); +// } +// } + +// //----------------------------------------------------------------------------- +// // read overlink directly from ovltop.silo +// // this case is tricky and involves messing with paths +// TEST(conduit_relay_io_silo, read_overlink_directly) +// { +// const std::vector> file_info = { +// {"box2d", "OvlTop", ".silo", "" }, // test default case +// {"box2d", "OvlTop", ".silo", "MMESH"}, +// {"box3d", "OvlTop", ".silo", "" }, // test default case +// {"box3d", "OvlTop", ".silo", "MMESH"}, +// // {"diamond", "OvlTop", ".silo", "" }, // test default case +// // {"diamond", "OvlTop", ".silo", "MMESH"}, +// {"testDisk2D_a", "OvlTop", ".silo", "" }, // test default case +// {"testDisk2D_a", "OvlTop", ".silo", "MMESH"}, +// // {"donordiv.s2_materials2", "OvlTop", ".silo", "" }, // test default case +// // {"donordiv.s2_materials2", "OvlTop", ".silo", "MMESH"}, +// {"donordiv.s2_materials3", "OvlTop", ".silo", "" }, // test default case +// {"donordiv.s2_materials3", "OvlTop", ".silo", "MMESH"}, +// }; + +// for (int i = 0; i < file_info.size(); i ++) +// { +// const std::string dirname = file_info[i][0]; +// const std::string basename = file_info[i][1]; +// const std::string fileext = file_info[i][2]; +// const std::string meshname = file_info[i][3]; + +// Node load_mesh, info, read_opts, write_opts; + +// std::string filepath = utils::join_file_path(dirname, basename) + fileext; +// filepath = utils::join_file_path("overlink", filepath); +// std::string input_file = relay_test_silo_data_path(filepath); + +// read_opts["mesh_name"] = meshname; + +// io::silo::load_mesh(input_file, read_opts, load_mesh); +// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + +// std::string out_name = "read_overlink_direct_" + dirname; +// if (!meshname.empty()) +// { +// out_name += "_" + meshname; +// } + +// remove_path_if_exists(out_name + "_write_blueprint"); +// io::blueprint::save_mesh(load_mesh, out_name + "_write_blueprint", "hdf5"); + +// remove_path_if_exists(out_name + "_write_silo"); +// io::silo::save_mesh(load_mesh, out_name + "_write_silo"); + +// remove_path_if_exists(out_name + "_write_overlink"); +// write_opts["file_style"] = "overlink"; +// write_opts["ovl_topo_name"] = "MMESH"; +// io::silo::save_mesh(load_mesh, out_name + "_write_overlink", write_opts); +// } +// } + +// // TODO add tests for... +// // - polytopal meshes once they are supported +// // - units once they are supported +// // - etc. + +// // TODO add tetra8 and c36_m5 to all the overlink i/o tests + +// // TODO somewhere I need to error on overlink when there are different var or mesh types across domains From 26223de260d19319a709ba237d79d2ad292cc90b Mon Sep 17 00:00:00 2001 From: Justin Privitera Date: Fri, 29 Mar 2024 15:34:22 -0700 Subject: [PATCH 02/56] so it begins --- src/libs/relay/conduit_relay_io_silo.cpp | 117 ++++++++++++++++++++++- 1 file changed, 114 insertions(+), 3 deletions(-) diff --git a/src/libs/relay/conduit_relay_io_silo.cpp b/src/libs/relay/conduit_relay_io_silo.cpp index e71781601..9e157d281 100644 --- a/src/libs/relay/conduit_relay_io_silo.cpp +++ b/src/libs/relay/conduit_relay_io_silo.cpp @@ -261,6 +261,8 @@ class SiloObjectWrapper } }; +//----------------------------------------------------------------------------- + template class SiloObjectWrapperCheckError { @@ -283,6 +285,8 @@ class SiloObjectWrapperCheckError } }; +//----------------------------------------------------------------------------- + class SiloTreePathGenerator { private: @@ -308,6 +312,19 @@ class SiloTreePathGenerator } }; +//----------------------------------------------------------------------------- + +class SiloReadBookkeeping +{ +private: + bool read_all; + bool read_none; + +public: + SiloReadBookkeeping(bool do_read_all, bool do_read_none) : + read_all(do_read_all), read_none(do_read_none) {} +}; + //----------------------------------------------------------------------------- std::string sanitize_silo_varname(const std::string &varname) { @@ -2490,6 +2507,82 @@ read_root_silo_index(const std::string &root_file_path, error_oss << "Table of contents could not be extracted from file: " << root_file_path; return false; } + + std::map reading_info; + + // read all is turned on, and read none is turned off + reading_info["multimesh_names"] = SiloReadBookkeeping(true, false); + reading_info["multivar_names"] = SiloReadBookkeeping(true, false); + reading_info["multimat_names"] = SiloReadBookkeeping(true, false); + reading_info["multimatspecies_names"] = SiloReadBookkeeping(true, false); + reading_info["qmesh_names"] = SiloReadBookkeeping(true, false); + reading_info["qvar_names"] = SiloReadBookkeeping(true, false); + reading_info["ucdmesh_names"] = SiloReadBookkeeping(true, false); + reading_info["ucdvar_names"] = SiloReadBookkeeping(true, false); + reading_info["ptmesh_names"] = SiloReadBookkeeping(true, false); + reading_info["ptvar_names"] = SiloReadBookkeeping(true, false); + reading_info["mat_names"] = SiloReadBookkeeping(true, false); + reading_info["matspecies_names"] = SiloReadBookkeeping(true, false); + + if (opts.has_child("silo_names")) + { + read_everything = false; + auto silo_names_itr = opts["silo_names"].children(); + while (silo_names_itr.has_next()) + { + const Node &silo_object_type = silo_names_itr.next(); + const std::string silo_object_type_name = silo_names_itr.name(); + + if (silo_object_type.number_of_children() > 0) + { + if (silo_object_type.has_child("all")) + { + if (silo_object_type.number_of_children() > 1) + { + error_oss << "TODO this is bad"; + return false; + } + reading_info[silo_object_type_name].read_all = true; + reading_info[silo_object_type_name].read_none = false; + } + else if (silo_object_type.has_child("none")) + { + if (silo_object_type.number_of_children() > 1) + { + error_oss << "TODO this is bad"; + return false; + } + reading_info[silo_object_type_name].read_all = false; + reading_info[silo_object_type_name].read_none = true; + } + else + { + // we must have named some specific items we want to read + reading_info[silo_object_type_name].read_all = false; + reading_info[silo_object_type_name].read_none = false; + } + } + else + { + // no children were specified so we want to read everything of this kind + reading_info[silo_object_type_name].read_all = true; + reading_info[silo_object_type_name].read_none = false; + } + } + } + + // if we are not reading no multimeshes --> we are reading multimeshes + if (! reading_info["multimeshes"].read_none) + { + // check for multimeshes + if (toc->nmultimesh <= 0) + { + error_oss << "No multimesh found in file: " << root_file_path; + return false; + } + } + + // check for multimeshes if (toc->nmultimesh <= 0) { @@ -2694,9 +2787,27 @@ read_root_silo_index(const std::string &root_file_path, //----------------------------------------------------------------------------- /// /// opts: -/// mesh_name: "{name}" -/// provide explicit mesh name, for cases where silo data includes -/// more than one mesh. +/// silo_names: +/// multimesh_names: +/// "{name1}" - multimeshes with this name will be read if they exist +/// "{name2}" +/// ... +/// or +/// "{all}" - all multimeshes will be read. +/// or +/// "{none}" - no multimeshes will be read. +/// multivar_names: similar to multimesh_names. +/// multimat_names: similar to multimesh_names. +/// multimatspecies_names: similar to multimesh_names. TODO +/// qmesh_names: similar to multimesh_names. +/// qvar_names: similar to multimesh_names. +/// ucdmesh_names: similar to multimesh_names. +/// ucdvar_names: similar to multimesh_names. +/// ptmesh_names: similar to multimesh_names. +/// ptvar_names: similar to multimesh_names. +/// mat_names: similar to multimesh_names. +/// matspecies_names: similar to multimesh_names. TODO +/// By default, everything in the file will be read unless manually turned off. /// /// matset_style: "default", "multi_buffer_full", "sparse_by_element", /// "multi_buffer_by_material" From 8387da6b8ee4420d1ecf7e2b9277abe074590e02 Mon Sep 17 00:00:00 2001 From: Justin Privitera Date: Fri, 29 Mar 2024 16:22:44 -0700 Subject: [PATCH 03/56] stopping point --- src/libs/relay/conduit_relay_io_silo.cpp | 80 +++++++++++++++++++++--- 1 file changed, 71 insertions(+), 9 deletions(-) diff --git a/src/libs/relay/conduit_relay_io_silo.cpp b/src/libs/relay/conduit_relay_io_silo.cpp index 9e157d281..528ea41e1 100644 --- a/src/libs/relay/conduit_relay_io_silo.cpp +++ b/src/libs/relay/conduit_relay_io_silo.cpp @@ -319,6 +319,7 @@ class SiloReadBookkeeping private: bool read_all; bool read_none; + std::vector names_to_read; public: SiloReadBookkeeping(bool do_read_all, bool do_read_none) : @@ -2514,7 +2515,7 @@ read_root_silo_index(const std::string &root_file_path, reading_info["multimesh_names"] = SiloReadBookkeeping(true, false); reading_info["multivar_names"] = SiloReadBookkeeping(true, false); reading_info["multimat_names"] = SiloReadBookkeeping(true, false); - reading_info["multimatspecies_names"] = SiloReadBookkeeping(true, false); + // reading_info["multimatspecies_names"] = SiloReadBookkeeping(true, false); reading_info["qmesh_names"] = SiloReadBookkeeping(true, false); reading_info["qvar_names"] = SiloReadBookkeeping(true, false); reading_info["ucdmesh_names"] = SiloReadBookkeeping(true, false); @@ -2522,7 +2523,7 @@ read_root_silo_index(const std::string &root_file_path, reading_info["ptmesh_names"] = SiloReadBookkeeping(true, false); reading_info["ptvar_names"] = SiloReadBookkeeping(true, false); reading_info["mat_names"] = SiloReadBookkeeping(true, false); - reading_info["matspecies_names"] = SiloReadBookkeeping(true, false); + // reading_info["matspecies_names"] = SiloReadBookkeeping(true, false); if (opts.has_child("silo_names")) { @@ -2571,16 +2572,77 @@ read_root_silo_index(const std::string &root_file_path, } } - // if we are not reading no multimeshes --> we are reading multimeshes - if (! reading_info["multimeshes"].read_none) + // names to read get stored in reading_info["multimesh_names"].names_to_read + auto generate_read_list = [&](const std::string silo_obj_name, // e.g. "multimesh_names" + const std::string obj_name, // e.g. "multimesh" - just for errors + const int num_silo_objects_in_toc, + const char** toc_names) { - // check for multimeshes - if (toc->nmultimesh <= 0) + // if we are not reading no multimeshes --> we are reading multimeshes + if (! reading_info[silo_obj_name].read_none) { - error_oss << "No multimesh found in file: " << root_file_path; - return false; + // check for multimeshes + if (num_silo_objects_in_toc <= 0) + { + error_oss << "No " << obj_name << " found in file: " << root_file_path; + return false; + } + + if (reading_info[silo_obj_name].read_all) + { + for (int toc_id = 0; toc_id < num_silo_objects_in_toc; toc_id ++) + { + reading_info["multimesh_names"].names_to_read.push_back(toc_names[toc_id]) + } + } + else + { + reading_info["multimesh_names"].names_to_read = opts["silo_names"][silo_obj_name].child_names(); + for (size_t list_id = 0; list_id < reading_info["multimesh_names"].names_to_read.size(); list_id ++) + { + bool found = false; + for (int toc_id = 0; toc_id < num_silo_objects_in_toc; toc_id ++) + { + if (toc_names[toc_id] == reading_info["multimesh_names"].names_to_read[list_id]) + { + found = true; + break; + } + } + if (!found) + { + error_oss << "No " << obj_name << " found matching " << reading_info["multimesh_names"].names_to_read[list_id]; + return false; + } + } + } } - } + + return true; + }; + + generate_read_list("multimesh_names", "multimesh", toc->nmultimesh, toc->multimesh_names); + generate_read_list("multivar_names", "multivar", toc->nmultivar, toc->multivar_names); + generate_read_list("multimat_names", "multimat", toc->nmultimat, toc->multimat_names); + // generate_read_list("multimatspecies_names", "multimatspecies", toc->nmultimatspecies, toc->multimatspecies_names); + generate_read_list("qmesh_names", "qmesh", toc->nqmesh, toc->qmesh_names); + generate_read_list("qvar_names", "qvar", toc->nqvar, toc->qvar_names); + generate_read_list("ucdmesh_names", "ucdmesh", toc->nucdmesh, toc->ucdmesh_names); + generate_read_list("ucdvar_names", "ucdvar", toc->nucdvar, toc->ucdvar_names); + generate_read_list("ptmesh_names", "ptmesh", toc->nptmesh, toc->ptmesh_names); + generate_read_list("ptvar_names", "ptvar", toc->nptvar, toc->ptvar_names); + generate_read_list("mat_names", "mat", toc->nmat, toc->mat_names); + // generate_read_list("matspecies_names", "matspecies", toc->nmatspecies, toc->matspecies_names); + + // TODO JUSTIN I left off here + +// I now have a list of names for each silo type of things to read from the root file + // next step is to read them + // I know for sure that each one is in the root file + // so should be easy + // for mvars and mmats, assume that they are in all mmeshes if they do not say + // then my logic in main read function should just work + // check for multimeshes From e8b194210b8b489284729f8933ede787e390deb5 Mon Sep 17 00:00:00 2001 From: Justin Privitera Date: Tue, 2 Apr 2024 11:42:52 -0700 Subject: [PATCH 04/56] I think the root index gen function is done, now need to rewrite the main read function --- src/libs/relay/conduit_relay_io_silo.cpp | 322 ++++++++++++----------- 1 file changed, 171 insertions(+), 151 deletions(-) diff --git a/src/libs/relay/conduit_relay_io_silo.cpp b/src/libs/relay/conduit_relay_io_silo.cpp index 528ea41e1..d0b9c83c8 100644 --- a/src/libs/relay/conduit_relay_io_silo.cpp +++ b/src/libs/relay/conduit_relay_io_silo.cpp @@ -2089,6 +2089,42 @@ open_or_reuse_file(const bool ovltop_case, return domain_file_to_use; } +//----------------------------------------------------------------------------- +void +prepare_simple_mesh_metadata(const std::string &mesh_name, + const int mesh_type, + Node &root_node) +{ + root_node[mesh_name]["nblocks"] = 1; + root_node[mesh_name]["nameschemes"] = "no"; + root_node[mesh_name]["mesh_types"].set(mesh_type); + root_node[mesh_name]["mesh_paths"].append().set(mesh_name); +} + +//----------------------------------------------------------------------------- +void +prepare_simple_var_metadata(const std::string &mesh_name, + const std::string &var_name, + const int var_type, + Node &root_node) +{ + Node &var = root_node[mesh_name]["vars"][var_name]; + var["nameschemes"] = "no"; + var["var_types"].set(var_type); + var["var_paths"].append().set(var_name); +} + +//----------------------------------------------------------------------------- +void +prepare_simple_mat_metadata(const std::string &mesh_name, + const std::string &mat_name, + Node &root_node) +{ + Node &material = root_node[mesh_name]["matsets"][mat_name]; + material["nameschemes"] = "no"; + Node &matset_path = material["matset_paths"].append().set(mat_name); +} + //----------------------------------------------------------------------------- bool read_multimesh(DBfile *dbfile, @@ -2527,13 +2563,18 @@ read_root_silo_index(const std::string &root_file_path, if (opts.has_child("silo_names")) { - read_everything = false; auto silo_names_itr = opts["silo_names"].children(); while (silo_names_itr.has_next()) { const Node &silo_object_type = silo_names_itr.next(); const std::string silo_object_type_name = silo_names_itr.name(); + if (reading_info.count(silo_object_type_name) == 0) + { + error_oss << "TODO unknown option"; + return false; + } + if (silo_object_type.number_of_children() > 0) { if (silo_object_type.has_child("all")) @@ -2621,188 +2662,167 @@ read_root_silo_index(const std::string &root_file_path, return true; }; - generate_read_list("multimesh_names", "multimesh", toc->nmultimesh, toc->multimesh_names); - generate_read_list("multivar_names", "multivar", toc->nmultivar, toc->multivar_names); - generate_read_list("multimat_names", "multimat", toc->nmultimat, toc->multimat_names); - // generate_read_list("multimatspecies_names", "multimatspecies", toc->nmultimatspecies, toc->multimatspecies_names); - generate_read_list("qmesh_names", "qmesh", toc->nqmesh, toc->qmesh_names); - generate_read_list("qvar_names", "qvar", toc->nqvar, toc->qvar_names); - generate_read_list("ucdmesh_names", "ucdmesh", toc->nucdmesh, toc->ucdmesh_names); - generate_read_list("ucdvar_names", "ucdvar", toc->nucdvar, toc->ucdvar_names); - generate_read_list("ptmesh_names", "ptmesh", toc->nptmesh, toc->ptmesh_names); - generate_read_list("ptvar_names", "ptvar", toc->nptvar, toc->ptvar_names); - generate_read_list("mat_names", "mat", toc->nmat, toc->mat_names); - // generate_read_list("matspecies_names", "matspecies", toc->nmatspecies, toc->matspecies_names); - - // TODO JUSTIN I left off here - -// I now have a list of names for each silo type of things to read from the root file - // next step is to read them - // I know for sure that each one is in the root file - // so should be easy - // for mvars and mmats, assume that they are in all mmeshes if they do not say - // then my logic in main read function should just work - - + // silo doesn't let us have any names that are the same in the same directory, + // even if they are different types. So we don't have to worry about name collisions. + if (! (generate_read_list("multimesh_names", "multimesh", toc->nmultimesh, toc->multimesh_names) && + generate_read_list("multivar_names", "multivar", toc->nmultivar, toc->multivar_names) && + generate_read_list("multimat_names", "multimat", toc->nmultimat, toc->multimat_names) && + // generate_read_list("multimatspecies_names", "multimatspecies", toc->nmultimatspecies, toc->multimatspecies_names) && + generate_read_list("qmesh_names", "qmesh", toc->nqmesh, toc->qmesh_names) && + generate_read_list("qvar_names", "qvar", toc->nqvar, toc->qvar_names) && + generate_read_list("ucdmesh_names", "ucdmesh", toc->nucdmesh, toc->ucdmesh_names) && + generate_read_list("ucdvar_names", "ucdvar", toc->nucdvar, toc->ucdvar_names) && + generate_read_list("ptmesh_names", "ptmesh", toc->nptmesh, toc->ptmesh_names) && + generate_read_list("ptvar_names", "ptvar", toc->nptvar, toc->ptvar_names) && + generate_read_list("mat_names", "mat", toc->nmat, toc->mat_names))) + // generate_read_list("matspecies_names", "matspecies", toc->nmatspecies, toc->matspecies_names) + { + // error msg should already be populated + return false; + } - // check for multimeshes - if (toc->nmultimesh <= 0) + // Get the selected matset flavor + std::string opts_matset_style = ""; + if (opts.has_child("matset_style") && opts["matset_style"].dtype().is_string()) { - std::string meshname; - if (toc->nqmesh > 0) + opts_matset_style = opts["matset_style"].as_string(); + if (opts_matset_style != "default" && + opts_matset_style != "multi_buffer_full" && + opts_matset_style != "sparse_by_element" && + opts_matset_style != "multi_buffer_by_material") { - meshname = toc->qmesh_names[0]; - root_node[meshname]["nblocks"] = 1; - root_node[meshname]["nameschemes"] = "no"; - root_node[meshname]["mesh_types"].set(DB_QUADMESH); - Node &mesh_path = root_node[meshname]["mesh_paths"].append(); - mesh_path.set(meshname); + error_oss << "read_mesh invalid matset_style option: \"" + << opts_matset_style << "\"\n" + " expected: \"default\", \"multi_buffer_full\", " + "\"sparse_by_element\", or \"multi_buffer_by_material\""; + return false; } + } - multimesh_name = meshname; - - if (toc->nqvar > 0 && !meshname.empty()) + // start with multimeshes, multivars, and multimats (and someday multimatspecies) + for (const std::string &multimesh_name : reading_info["multimesh_names"].names_to_read) + { + int nblocks; + if (! read_multimesh(dbfile.getSiloObject(), + multimesh_name, + nblocks, + root_node, + error_oss)) { - for (int var_id = 0; var_id < toc->nqvar; var_id ++) - { - const std::string varname = toc->qvar_names[var_id]; - Node &var = root_node[meshname]["vars"][varname]; - var["nameschemes"] = "no"; - var["var_types"].set(DB_QUADVAR); - Node &var_path = var["var_paths"].append(); - var_path.set(varname); - } + return false; } - - if (toc->nmat > 0 && !meshname.empty()) + if (! read_multivars(toc, + dbfile.getSiloObject(), + multimesh_name, + nblocks, + root_node, + error_oss)) { - const std::string matname = toc->mat_names[0]; - Node &material = root_node[meshname]["matsets"][matname]; - material["nameschemes"] = "no"; - Node &matset_path = material["matset_paths"].append(); - matset_path.set(matname); + return false; + } + if (! read_multimats(toc, + dbfile.getSiloObject(), + multimesh_name, + nblocks, + root_node, + error_oss)) + { + return false; } - read_state(dbfile.getSiloObject(), root_node, meshname); + read_state(dbfile.getSiloObject(), root_node, multimesh_name); // overlink-specific read_var_attributes(dbfile.getSiloObject(), - meshname, + multimesh_name, root_node); - // Get the selected matset flavor - if (opts.has_child("matset_style") && opts["matset_style"].dtype().is_string()) + if (! opts_matset_style.empty()) { - std::string opts_matset_style = opts["matset_style"].as_string(); - if (opts_matset_style != "default" && - opts_matset_style != "multi_buffer_full" && - opts_matset_style != "sparse_by_element" && - opts_matset_style != "multi_buffer_by_material") - { - CONDUIT_ERROR("read_mesh invalid matset_style option: \"" - << opts_matset_style << "\"\n" - " expected: \"default\", \"multi_buffer_full\", " - "\"sparse_by_element\", or \"multi_buffer_by_material\""); - } - else - { - root_node[meshname]["matset_style"] = opts_matset_style; - } + root_node[multimesh_name]["matset_style"] = opts_matset_style; } - - std::cout << root_node.to_yaml() << std::endl; - - return true; - - // error_oss << "No multimesh found in file: " << root_file_path; - // return false; } - // decide what multimesh to extract - if (opts.has_child("mesh_name") && opts["mesh_name"].dtype().is_string()) + // next quadmeshes and quadvars + for (const std::string &qmesh_name : reading_info["qmesh_names"].names_to_read) { - multimesh_name = opts["mesh_name"].as_string(); - } + prepare_simple_mesh_metadata(qmesh_name, DB_QUADMESH, root_node); - // check multimesh name - if (multimesh_name.empty()) - { - multimesh_name = toc->multimesh_names[0]; - } - else - { - bool found = false; - for (int i = 0; i < toc->nmultimesh; i ++) + // at this stage we assume that all qvars could be associated with this qmesh + // TODO should I do a check here then? + for (const std::string &qvar_name : reading_info["qvar_names"].names_to_read) { - if (toc->multimesh_names[i] == multimesh_name) - { - found = true; - break; - } + prepare_simple_var_metadata(qvar_name, qmesh_name, DB_QUADVAR, root_node); } - if (!found) + // same is true for materials + // TODO ugh I don't like this + for (const std::string &mat_name : reading_info["mat_names"].names_to_read) { - error_oss << "No multimesh found matching " << multimesh_name; - return false; + prepare_simple_mat_metadata(mat_name, qmesh_name, root_node); } - } - int nblocks; - if (! read_multimesh(dbfile.getSiloObject(), - multimesh_name, - nblocks, - root_node, - error_oss)) - { - return false; - } - if (! read_multivars(toc, - dbfile.getSiloObject(), - multimesh_name, - nblocks, - root_node, - error_oss)) - { - return false; + // TODO I love rereading state for every mesh. This is so silly + read_state(dbfile.getSiloObject(), root_node, qmesh_name); + + if (! opts_matset_style.empty()) + { + root_node[qmesh_name]["matset_style"] = opts_matset_style; + } } - if (! read_multimats(toc, - dbfile.getSiloObject(), - multimesh_name, - nblocks, - root_node, - error_oss)) + + // next ucdmeshes and ucdvars + for (const std::string &ucdmesh_name : reading_info["ucdmesh_names"].names_to_read) { - return false; - } + prepare_simple_mesh_metadata(ucdmesh_name, DB_UCDMESH, root_node); - read_state(dbfile.getSiloObject(), root_node, multimesh_name); + // at this stage we assume that all ucdvars could be associated with this ucdmesh + // TODO should I do a check here then? + for (const std::string &ucdvar_name : reading_info["ucdvar_names"].names_to_read) + { + prepare_simple_var_metadata(ucdvar_name, ucdmesh_name, DB_UCDVAR, root_node); + } + // same is true for materials + // TODO ugh I don't like this + for (const std::string &mat_name : reading_info["mat_names"].names_to_read) + { + prepare_simple_mat_metadata(mat_name, ucdmesh_name, root_node); + } - // overlink-specific - read_var_attributes(dbfile.getSiloObject(), - multimesh_name, - root_node); + // TODO I love rereading state for every mesh. This is so silly + read_state(dbfile.getSiloObject(), root_node, ucdmesh_name); - // Get the selected matset flavor - if (opts.has_child("matset_style") && opts["matset_style"].dtype().is_string()) + if (! opts_matset_style.empty()) + { + root_node[ucdmesh_name]["matset_style"] = opts_matset_style; + } + } + + // next ptmeshes and ptvars + for (const std::string &ptmesh_name : reading_info["ptmesh_names"].names_to_read) { - std::string opts_matset_style = opts["matset_style"].as_string(); - if (opts_matset_style != "default" && - opts_matset_style != "multi_buffer_full" && - opts_matset_style != "sparse_by_element" && - opts_matset_style != "multi_buffer_by_material") + prepare_simple_mesh_metadata(ptmesh_name, DB_POINTMESH, root_node); + + // at this stage we assume that all ptvars could be associated with this ptmesh + // TODO should I do a check here then? + for (const std::string &ptvar_name : reading_info["ptvar_names"].names_to_read) { - CONDUIT_ERROR("read_mesh invalid matset_style option: \"" - << opts_matset_style << "\"\n" - " expected: \"default\", \"multi_buffer_full\", " - "\"sparse_by_element\", or \"multi_buffer_by_material\""); + prepare_simple_var_metadata(ptvar_name, ptmesh_name, DB_POINTVAR, root_node); } - else + // same is true for materials + // TODO ugh I don't like this + for (const std::string &mat_name : reading_info["mat_names"].names_to_read) { - root_node[multimesh_name]["matset_style"] = opts_matset_style; + prepare_simple_mat_metadata(mat_name, ptmesh_name, root_node); } - } - // TODO why not have an option to read multiple multimeshes? + // TODO I love rereading state for every mesh. This is so silly + read_state(dbfile.getSiloObject(), root_node, ptmesh_name); + + if (! opts_matset_style.empty()) + { + root_node[ptmesh_name]["matset_style"] = opts_matset_style; + } + } // our silo index should look like this: @@ -2842,6 +2862,9 @@ read_root_silo_index(const std::string &root_file_path, // ... // ... // matset_style: "default", OR "multi_buffer_full", OR "sparse_by_element", OR "multi_buffer_by_material" + // mesh2: + // ... + // ... return true; } @@ -2987,11 +3010,8 @@ read_mesh(const std::string &root_file_path, domain_end = rank_offset + read_size; #endif - std::string opts_matset_style = "default"; - if (mesh_index.has_child("matset_style")) - { - opts_matset_style = mesh_index["matset_style"].as_string(); - } + const std::string opts_matset_style = (mesh_index.has_child("matset_style") ? + mesh_index["matset_style"].as_string() : "default"); bool mesh_nameschemes = false; if (mesh_index.has_child("nameschemes") && From c2cfbb15cf06152c65fb1fafacd5a19c87126fea Mon Sep 17 00:00:00 2001 From: Justin Privitera Date: Tue, 2 Apr 2024 15:01:33 -0700 Subject: [PATCH 05/56] read mesh works in theory --- src/libs/relay/conduit_relay_io_silo.cpp | 448 +++++++++++------------ 1 file changed, 224 insertions(+), 224 deletions(-) diff --git a/src/libs/relay/conduit_relay_io_silo.cpp b/src/libs/relay/conduit_relay_io_silo.cpp index d0b9c83c8..e1be27c68 100644 --- a/src/libs/relay/conduit_relay_io_silo.cpp +++ b/src/libs/relay/conduit_relay_io_silo.cpp @@ -2508,12 +2508,11 @@ bool read_root_silo_index(const std::string &root_file_path, const Node &opts, Node &root_node, // output - std::string &multimesh_name, // output std::ostringstream &error_oss) // output { // clear output vars root_node.reset(); - multimesh_name = ""; + std::string multimesh_name = ""; // TODO preserve legacy functionality somehow error_oss.str(""); // first, make sure we can open the root file @@ -2916,17 +2915,15 @@ read_mesh(const std::string &root_file_path, int error = 0; std::ostringstream error_oss; - std::string multimesh_name; Node root_node; // only read bp index on rank 0 - if(par_rank == 0) + if (par_rank == 0) { - if(!read_root_silo_index(root_file_path, - opts, - root_node, - multimesh_name, - error_oss)) + if (!read_root_silo_index(root_file_path, + opts, + root_node, + error_oss)) { error = 1; } @@ -2954,13 +2951,8 @@ read_mesh(const std::string &root_file_path, } else { - // broadcast the mesh name and the bp index + // broadcast the root node // from rank 0 to all ranks - n_global.set(multimesh_name); - conduit::relay::mpi::broadcast_using_schema(n_global, - 0, - mpi_comm); - multimesh_name = n_global.as_string(); conduit::relay::mpi::broadcast_using_schema(root_node, 0, mpi_comm); @@ -2972,266 +2964,274 @@ read_mesh(const std::string &root_file_path, CONDUIT_ERROR(error_oss.str()); } #endif - const Node &mesh_index = root_node[multimesh_name]; - // read all domains for given mesh - int num_domains = mesh_index["nblocks"].to_int(); + auto root_itr = root_node.children(); + while (root_itr.has_next()) + { + const Node &mesh_index = root_itr.next(); + const std::string mesh_index_name = root_itr.name(); + + // read all domains for given mesh + int num_domains = mesh_index["nblocks"].to_int(); - std::ostringstream oss; - int domain_start = 0; - int domain_end = num_domains; + std::ostringstream oss; + int domain_start = 0; + int domain_end = num_domains; #if CONDUIT_RELAY_IO_MPI_ENABLED - int read_size = num_domains / par_size; - int rem = num_domains % par_size; - if(par_rank < rem) - { - read_size++; - } + int read_size = num_domains / par_size; + int rem = num_domains % par_size; + if(par_rank < rem) + { + read_size++; + } - Node n_read_size; - Node n_doms_per_rank; + Node n_read_size; + Node n_doms_per_rank; - n_read_size.set_int32(read_size); + n_read_size.set_int32(read_size); - relay::mpi::all_gather_using_schema(n_read_size, - n_doms_per_rank, - mpi_comm); - int *counts = (int*)n_doms_per_rank.data_ptr(); + relay::mpi::all_gather_using_schema(n_read_size, + n_doms_per_rank, + mpi_comm); + int *counts = (int*)n_doms_per_rank.data_ptr(); - int rank_offset = 0; - for(int i = 0; i < par_rank; ++i) - { - rank_offset += counts[i]; - } + int rank_offset = 0; + for(int i = 0; i < par_rank; ++i) + { + rank_offset += counts[i]; + } - domain_start = rank_offset; - domain_end = rank_offset + read_size; + domain_start = rank_offset; + domain_end = rank_offset + read_size; #endif - const std::string opts_matset_style = (mesh_index.has_child("matset_style") ? - mesh_index["matset_style"].as_string() : "default"); + // TODO there's no reason for this to live inside each mesh index + // but I don't know where else to stash it + const std::string opts_matset_style = (mesh_index.has_child("matset_style") ? + mesh_index["matset_style"].as_string() : "default"); - bool mesh_nameschemes = false; - if (mesh_index.has_child("nameschemes") && - mesh_index["nameschemes"].as_string() == "yes") - { - mesh_nameschemes = true; - CONDUIT_ERROR("TODO no support for nameschemes yet"); - } - detail::SiloTreePathGenerator mesh_path_gen{mesh_nameschemes}; + bool mesh_nameschemes = false; + if (mesh_index.has_child("nameschemes") && + mesh_index["nameschemes"].as_string() == "yes") + { + mesh_nameschemes = true; + CONDUIT_ERROR("TODO no support for nameschemes yet"); + } + detail::SiloTreePathGenerator mesh_path_gen{mesh_nameschemes}; - std::string root_file_name, relative_dir; - utils::rsplit_file_path(root_file_path, root_file_name, relative_dir); + std::string root_file_name, relative_dir; + utils::rsplit_file_path(root_file_path, root_file_name, relative_dir); - // If the root file is named OvlTop.silo, then there is a very good chance that - // this file is valid overlink. Therefore, we must modify the paths we get from - // the root node to reflect this. - bool ovltop_case = root_file_name == "OvlTop.silo"; + // If the root file is named OvlTop.silo, then there is a very good chance that + // this file is valid overlink. Therefore, we must modify the paths we get from + // the root node to reflect this. + bool ovltop_case = (root_file_name == "OvlTop.silo"); - for (int domain_id = domain_start; domain_id < domain_end; domain_id ++) - { - // - // Read Mesh - // + for (int domain_id = domain_start; domain_id < domain_end; domain_id ++) + { + // + // Read Mesh + // - const std::string silo_mesh_path = mesh_index["mesh_paths"][domain_id].as_string(); - const int_accessor meshtypes = mesh_index["mesh_types"].value(); - const int meshtype = meshtypes[domain_id]; + const std::string silo_mesh_path = mesh_index["mesh_paths"][domain_id].as_string(); + const int_accessor meshtypes = mesh_index["mesh_types"].value(); + const int meshtype = meshtypes[domain_id]; - std::string mesh_name, mesh_domain_filename; - mesh_path_gen.GeneratePaths(silo_mesh_path, relative_dir, mesh_domain_filename, mesh_name); + std::string mesh_name, mesh_domain_filename; + mesh_path_gen.GeneratePaths(silo_mesh_path, relative_dir, mesh_domain_filename, mesh_name); - if (mesh_name == "EMPTY") - { - continue; // skip this domain - } + if (mesh_name == "EMPTY") + { + continue; // skip this domain + } - std::string bottom_level_mesh_name, tmp; - conduit::utils::rsplit_file_path(mesh_name, "/", bottom_level_mesh_name, tmp); + std::string bottom_level_mesh_name, tmp; + conduit::utils::rsplit_file_path(mesh_name, "/", bottom_level_mesh_name, tmp); - // root only case - if (mesh_domain_filename.empty()) - { - mesh_domain_filename = root_file_path; - // we are in the root file only case so overlink is not possible - ovltop_case = false; - } + // root only case + if (mesh_domain_filename.empty()) + { + mesh_domain_filename = root_file_path; + // we are in the root file only case so overlink is not possible + ovltop_case = false; + } - detail::SiloObjectWrapperCheckError mesh_domain_file{ - nullptr, &DBClose}; - DBfile *mesh_domain_file_to_use = open_or_reuse_file(ovltop_case, - mesh_domain_filename, "", nullptr, mesh_domain_file); + detail::SiloObjectWrapperCheckError mesh_domain_file{ + nullptr, &DBClose}; + DBfile *mesh_domain_file_to_use = open_or_reuse_file(ovltop_case, + mesh_domain_filename, "", nullptr, mesh_domain_file); - // this is for the blueprint mesh output - std::string domain_path = conduit_fmt::format("domain_{:06d}", domain_id); + // this is for the blueprint mesh output + std::string domain_path = conduit_fmt::format("domain_{:06d}", domain_id); - if (! read_mesh_domain(meshtype, mesh_domain_file_to_use, mesh_name, - multimesh_name, domain_path, mesh)) - { - continue; // we hit a case where we want to skip this mesh domain - } + if (! read_mesh_domain(meshtype, mesh_domain_file_to_use, mesh_name, + mesh_index_name, domain_path, mesh)) + { + continue; // we hit a case where we want to skip this mesh domain + } - // we know we were for sure successful (we didn't skip ahead to the next domain) - // so we create the mesh_out now for good - Node &mesh_out = mesh[domain_path]; + // we know we were for sure successful (we didn't skip ahead to the next domain) + // so we create the mesh_out now for good + Node &mesh_out = mesh[domain_path]; - // - // Read State - // + // + // Read State + // - mesh_out["state"]["domain_id"] = static_cast(domain_id); - if (mesh_index.has_path("state/time")) - { - mesh_out["state"]["time"] = mesh_index["state"]["time"].as_double(); - } - if (mesh_index.has_path("state/cycle")) - { - mesh_out["state"]["cycle"] = (index_t) mesh_index["state"]["cycle"].as_int(); - } + mesh_out["state"]["domain_id"] = static_cast(domain_id); + if (mesh_index.has_path("state/time")) + { + mesh_out["state"]["time"] = mesh_index["state"]["time"].as_double(); + } + if (mesh_index.has_path("state/cycle")) + { + mesh_out["state"]["cycle"] = (index_t) mesh_index["state"]["cycle"].as_int(); + } - // - // Read Adjset (overlink only) - // + // + // Read Adjset (overlink only) + // - read_adjset(mesh_domain_file_to_use, multimesh_name, domain_id, mesh_out); + read_adjset(mesh_domain_file_to_use, mesh_index_name, domain_id, mesh_out); - // - // Read Materials - // + // + // Read Materials + // - // This node will house the recipe for reconstructing matset_values - // from silo mixvals. - Node matset_field_reconstruction; + // This node will house the recipe for reconstructing matset_values + // from silo mixvals. + Node matset_field_reconstruction; - // for each mesh domain, we would like to iterate through all the materials - // and extract the same domain from them. - if (mesh_index.has_child("matsets")) - { - auto matset_itr = mesh_index["matsets"].children(); - while (matset_itr.has_next()) + // for each mesh domain, we would like to iterate through all the materials + // and extract the same domain from them. + if (mesh_index.has_child("matsets")) { - const Node &n_matset = matset_itr.next(); - std::string multimat_name = matset_itr.name(); - - bool matset_nameschemes = false; - if (n_matset.has_child("nameschemes") && - n_matset["nameschemes"].as_string() == "yes") + auto matset_itr = mesh_index["matsets"].children(); + while (matset_itr.has_next()) { - matset_nameschemes = true; - CONDUIT_ERROR("TODO no support for nameschemes yet"); - } - detail::SiloTreePathGenerator matset_path_gen{matset_nameschemes}; + const Node &n_matset = matset_itr.next(); + std::string multimat_name = matset_itr.name(); - std::string silo_matset_path = n_matset["matset_paths"][domain_id].as_string(); + bool matset_nameschemes = false; + if (n_matset.has_child("nameschemes") && + n_matset["nameschemes"].as_string() == "yes") + { + matset_nameschemes = true; + CONDUIT_ERROR("TODO no support for nameschemes yet"); + } + detail::SiloTreePathGenerator matset_path_gen{matset_nameschemes}; - std::string matset_name, matset_domain_filename; - matset_path_gen.GeneratePaths(silo_matset_path, relative_dir, matset_domain_filename, matset_name); + std::string silo_matset_path = n_matset["matset_paths"][domain_id].as_string(); - if (matset_name == "EMPTY") - { - // we choose not to write anything to blueprint - continue; - } + std::string matset_name, matset_domain_filename; + matset_path_gen.GeneratePaths(silo_matset_path, relative_dir, matset_domain_filename, matset_name); - // root only case - if (matset_domain_filename.empty()) - { - matset_domain_filename = root_file_path; - // we are in the root file only case so overlink is not possible - ovltop_case = false; - } + if (matset_name == "EMPTY") + { + // we choose not to write anything to blueprint + continue; + } - detail::SiloObjectWrapperCheckError matset_domain_file{ - nullptr, &DBClose}; - DBfile *matset_domain_file_to_use = open_or_reuse_file( - ovltop_case, matset_domain_filename, mesh_domain_filename, - mesh_domain_file.getSiloObject(), matset_domain_file); - - // If this completes successfully, it means we have found a matset - // associated with this mesh. Thus we can break iteration here, - // since there can only be one matset. This is the earliest we can - // break iteration because the silo index may have multiple matsets, - // and we have no way of knowing until now which one is associated - // with our mesh. - // In silo, for each mesh, there can only be one matset, because otherwise - // it would be ambiguous. In Blueprint, we can allow multiple matsets per - // topo, because the fields explicitly link to the matset they use. - // Right now, we only ever read one mesh, meaning that there can only - // be one matset in our newly created blueprint mesh. - if (read_matset_domain(matset_domain_file_to_use, n_matset, matset_name, - multimesh_name, multimat_name, bottom_level_mesh_name, - opts_matset_style, matset_field_reconstruction, mesh_out)) - { - break; + // root only case + if (matset_domain_filename.empty()) + { + matset_domain_filename = root_file_path; + // we are in the root file only case so overlink is not possible + ovltop_case = false; + } + + detail::SiloObjectWrapperCheckError matset_domain_file{ + nullptr, &DBClose}; + DBfile *matset_domain_file_to_use = open_or_reuse_file( + ovltop_case, matset_domain_filename, mesh_domain_filename, + mesh_domain_file.getSiloObject(), matset_domain_file); + + // If this completes successfully, it means we have found a matset + // associated with this mesh. Thus we can break iteration here, + // since there can only be one matset. This is the earliest we can + // break iteration because the silo index may have multiple matsets, + // and we have no way of knowing until now which one is associated + // with our mesh. + // In silo, for each mesh, there can only be one matset, because otherwise + // it would be ambiguous. In Blueprint, we can allow multiple matsets per + // topo, because the fields explicitly link to the matset they use. + // Right now, we only ever read one mesh, meaning that there can only + // be one matset in our newly created blueprint mesh. + if (read_matset_domain(matset_domain_file_to_use, n_matset, matset_name, + mesh_index_name, multimat_name, bottom_level_mesh_name, + opts_matset_style, matset_field_reconstruction, mesh_out)) + { + break; + } } } - } - // - // Read Fields - // + // + // Read Fields + // - // for each mesh domain, we would like to iterate through all the variables - // and extract the same domain from them. - if (mesh_index.has_child("vars")) - { - auto var_itr = mesh_index["vars"].children(); - while (var_itr.has_next()) + // for each mesh domain, we would like to iterate through all the variables + // and extract the same domain from them. + if (mesh_index.has_child("vars")) { - const Node &n_var = var_itr.next(); - std::string multivar_name = var_itr.name(); - - bool var_nameschemes = false; - if (n_var.has_child("nameschemes") && - n_var["nameschemes"].as_string() == "yes") + auto var_itr = mesh_index["vars"].children(); + while (var_itr.has_next()) { - var_nameschemes = true; - CONDUIT_ERROR("TODO no support for nameschemes yet"); - } - detail::SiloTreePathGenerator var_path_gen{var_nameschemes}; + const Node &n_var = var_itr.next(); + std::string multivar_name = var_itr.name(); - std::string silo_var_path = n_var["var_paths"][domain_id].as_string(); - int_accessor vartypes = n_var["var_types"].value(); - int vartype = vartypes[domain_id]; + bool var_nameschemes = false; + if (n_var.has_child("nameschemes") && + n_var["nameschemes"].as_string() == "yes") + { + var_nameschemes = true; + CONDUIT_ERROR("TODO no support for nameschemes yet"); + } + detail::SiloTreePathGenerator var_path_gen{var_nameschemes}; - std::string var_name, var_domain_filename; - var_path_gen.GeneratePaths(silo_var_path, relative_dir, var_domain_filename, var_name); + std::string silo_var_path = n_var["var_paths"][domain_id].as_string(); + int_accessor vartypes = n_var["var_types"].value(); + int vartype = vartypes[domain_id]; - if (var_name == "EMPTY") - { - // we choose not to write anything to blueprint - continue; - } + std::string var_name, var_domain_filename; + var_path_gen.GeneratePaths(silo_var_path, relative_dir, var_domain_filename, var_name); - // this info can be tracked with overlink VAR_ATTRIBUTES - std::string volume_dependent = ""; - if (n_var.has_child("volume_dependent")) - { - volume_dependent = n_var["volume_dependent"].as_string(); - } + if (var_name == "EMPTY") + { + // we choose not to write anything to blueprint + continue; + } - // root only case - if (var_domain_filename.empty()) - { - var_domain_filename = root_file_path; - // we are in the root file only case so overlink is not possible - ovltop_case = false; - } + // this info can be tracked with overlink VAR_ATTRIBUTES + std::string volume_dependent = ""; + if (n_var.has_child("volume_dependent")) + { + volume_dependent = n_var["volume_dependent"].as_string(); + } - detail::SiloObjectWrapperCheckError var_domain_file{ - nullptr, &DBClose}; - DBfile *var_domain_file_to_use = open_or_reuse_file( - ovltop_case, var_domain_filename, mesh_domain_filename, - mesh_domain_file.getSiloObject(), var_domain_file); - - // we don't care if this skips the var or not since this is the - // last thing in the loop iteration - read_variable_domain(vartype, var_domain_file_to_use, var_name, - multimesh_name, multivar_name, bottom_level_mesh_name, - volume_dependent, opts_matset_style, - matset_field_reconstruction, mesh_out); + // root only case + if (var_domain_filename.empty()) + { + var_domain_filename = root_file_path; + // we are in the root file only case so overlink is not possible + ovltop_case = false; + } + + detail::SiloObjectWrapperCheckError var_domain_file{ + nullptr, &DBClose}; + DBfile *var_domain_file_to_use = open_or_reuse_file( + ovltop_case, var_domain_filename, mesh_domain_filename, + mesh_domain_file.getSiloObject(), var_domain_file); + + // we don't care if this skips the var or not since this is the + // last thing in the loop iteration + read_variable_domain(vartype, var_domain_file_to_use, var_name, + mesh_index_name, multivar_name, bottom_level_mesh_name, + volume_dependent, opts_matset_style, + matset_field_reconstruction, mesh_out); + } } } } From 01ef5702294aa2d54f06da0ba013186305d0333b Mon Sep 17 00:00:00 2001 From: Justin Privitera Date: Tue, 2 Apr 2024 15:13:26 -0700 Subject: [PATCH 06/56] code compiles, but does it work? --- src/libs/relay/conduit_relay_io_silo.cpp | 48 +++++++++++------------- 1 file changed, 22 insertions(+), 26 deletions(-) diff --git a/src/libs/relay/conduit_relay_io_silo.cpp b/src/libs/relay/conduit_relay_io_silo.cpp index e1be27c68..cb6270585 100644 --- a/src/libs/relay/conduit_relay_io_silo.cpp +++ b/src/libs/relay/conduit_relay_io_silo.cpp @@ -316,14 +316,10 @@ class SiloTreePathGenerator class SiloReadBookkeeping { -private: - bool read_all; - bool read_none; - std::vector names_to_read; - public: - SiloReadBookkeeping(bool do_read_all, bool do_read_none) : - read_all(do_read_all), read_none(do_read_none) {} + bool read_all = true; + bool read_none = false; + std::vector names_to_read; }; //----------------------------------------------------------------------------- @@ -2122,7 +2118,7 @@ prepare_simple_mat_metadata(const std::string &mesh_name, { Node &material = root_node[mesh_name]["matsets"][mat_name]; material["nameschemes"] = "no"; - Node &matset_path = material["matset_paths"].append().set(mat_name); + material["matset_paths"].append().set(mat_name); } //----------------------------------------------------------------------------- @@ -2544,21 +2540,21 @@ read_root_silo_index(const std::string &root_file_path, return false; } - std::map reading_info; + std::map reading_info; // read all is turned on, and read none is turned off - reading_info["multimesh_names"] = SiloReadBookkeeping(true, false); - reading_info["multivar_names"] = SiloReadBookkeeping(true, false); - reading_info["multimat_names"] = SiloReadBookkeeping(true, false); - // reading_info["multimatspecies_names"] = SiloReadBookkeeping(true, false); - reading_info["qmesh_names"] = SiloReadBookkeeping(true, false); - reading_info["qvar_names"] = SiloReadBookkeeping(true, false); - reading_info["ucdmesh_names"] = SiloReadBookkeeping(true, false); - reading_info["ucdvar_names"] = SiloReadBookkeeping(true, false); - reading_info["ptmesh_names"] = SiloReadBookkeeping(true, false); - reading_info["ptvar_names"] = SiloReadBookkeeping(true, false); - reading_info["mat_names"] = SiloReadBookkeeping(true, false); - // reading_info["matspecies_names"] = SiloReadBookkeeping(true, false); + reading_info["multimesh_names"] = detail::SiloReadBookkeeping(); + reading_info["multivar_names"] = detail::SiloReadBookkeeping(); + reading_info["multimat_names"] = detail::SiloReadBookkeeping(); + // reading_info["multimatspecies_names"] = detail::SiloReadBookkeeping(); + reading_info["qmesh_names"] = detail::SiloReadBookkeeping(); + reading_info["qvar_names"] = detail::SiloReadBookkeeping(); + reading_info["ucdmesh_names"] = detail::SiloReadBookkeeping(); + reading_info["ucdvar_names"] = detail::SiloReadBookkeeping(); + reading_info["ptmesh_names"] = detail::SiloReadBookkeeping(); + reading_info["ptvar_names"] = detail::SiloReadBookkeeping(); + reading_info["mat_names"] = detail::SiloReadBookkeeping(); + // reading_info["matspecies_names"] = detail::SiloReadBookkeeping(); if (opts.has_child("silo_names")) { @@ -2616,7 +2612,7 @@ read_root_silo_index(const std::string &root_file_path, auto generate_read_list = [&](const std::string silo_obj_name, // e.g. "multimesh_names" const std::string obj_name, // e.g. "multimesh" - just for errors const int num_silo_objects_in_toc, - const char** toc_names) + char** toc_names) { // if we are not reading no multimeshes --> we are reading multimeshes if (! reading_info[silo_obj_name].read_none) @@ -2632,18 +2628,18 @@ read_root_silo_index(const std::string &root_file_path, { for (int toc_id = 0; toc_id < num_silo_objects_in_toc; toc_id ++) { - reading_info["multimesh_names"].names_to_read.push_back(toc_names[toc_id]) + reading_info[silo_obj_name].names_to_read.push_back(toc_names[toc_id]); } } else { - reading_info["multimesh_names"].names_to_read = opts["silo_names"][silo_obj_name].child_names(); - for (size_t list_id = 0; list_id < reading_info["multimesh_names"].names_to_read.size(); list_id ++) + reading_info[silo_obj_name].names_to_read = opts["silo_names"][silo_obj_name].child_names(); + for (size_t list_id = 0; list_id < reading_info[silo_obj_name].names_to_read.size(); list_id ++) { bool found = false; for (int toc_id = 0; toc_id < num_silo_objects_in_toc; toc_id ++) { - if (toc_names[toc_id] == reading_info["multimesh_names"].names_to_read[list_id]) + if (toc_names[toc_id] == reading_info[silo_obj_name].names_to_read[list_id]) { found = true; break; From f01e4d4a338298de8e7547f8fdb1abe42f88c58c Mon Sep 17 00:00:00 2001 From: Justin Privitera Date: Tue, 2 Apr 2024 15:26:30 -0700 Subject: [PATCH 07/56] amazingly, almost all the tests pass --- src/libs/relay/conduit_relay_io_silo.cpp | 19 +- src/tests/relay/t_relay_io_silo.cpp | 3648 +++++++++++----------- 2 files changed, 1830 insertions(+), 1837 deletions(-) diff --git a/src/libs/relay/conduit_relay_io_silo.cpp b/src/libs/relay/conduit_relay_io_silo.cpp index cb6270585..2f16cc6ea 100644 --- a/src/libs/relay/conduit_relay_io_silo.cpp +++ b/src/libs/relay/conduit_relay_io_silo.cpp @@ -2617,13 +2617,6 @@ read_root_silo_index(const std::string &root_file_path, // if we are not reading no multimeshes --> we are reading multimeshes if (! reading_info[silo_obj_name].read_none) { - // check for multimeshes - if (num_silo_objects_in_toc <= 0) - { - error_oss << "No " << obj_name << " found in file: " << root_file_path; - return false; - } - if (reading_info[silo_obj_name].read_all) { for (int toc_id = 0; toc_id < num_silo_objects_in_toc; toc_id ++) @@ -2747,13 +2740,13 @@ read_root_silo_index(const std::string &root_file_path, // TODO should I do a check here then? for (const std::string &qvar_name : reading_info["qvar_names"].names_to_read) { - prepare_simple_var_metadata(qvar_name, qmesh_name, DB_QUADVAR, root_node); + prepare_simple_var_metadata(qmesh_name, qvar_name, DB_QUADVAR, root_node); } // same is true for materials // TODO ugh I don't like this for (const std::string &mat_name : reading_info["mat_names"].names_to_read) { - prepare_simple_mat_metadata(mat_name, qmesh_name, root_node); + prepare_simple_mat_metadata(qmesh_name, mat_name, root_node); } // TODO I love rereading state for every mesh. This is so silly @@ -2774,13 +2767,13 @@ read_root_silo_index(const std::string &root_file_path, // TODO should I do a check here then? for (const std::string &ucdvar_name : reading_info["ucdvar_names"].names_to_read) { - prepare_simple_var_metadata(ucdvar_name, ucdmesh_name, DB_UCDVAR, root_node); + prepare_simple_var_metadata(ucdmesh_name, ucdvar_name, DB_UCDVAR, root_node); } // same is true for materials // TODO ugh I don't like this for (const std::string &mat_name : reading_info["mat_names"].names_to_read) { - prepare_simple_mat_metadata(mat_name, ucdmesh_name, root_node); + prepare_simple_mat_metadata(ucdmesh_name, mat_name, root_node); } // TODO I love rereading state for every mesh. This is so silly @@ -2801,13 +2794,13 @@ read_root_silo_index(const std::string &root_file_path, // TODO should I do a check here then? for (const std::string &ptvar_name : reading_info["ptvar_names"].names_to_read) { - prepare_simple_var_metadata(ptvar_name, ptmesh_name, DB_POINTVAR, root_node); + prepare_simple_var_metadata(ptmesh_name, ptvar_name, DB_POINTVAR, root_node); } // same is true for materials // TODO ugh I don't like this for (const std::string &mat_name : reading_info["mat_names"].names_to_read) { - prepare_simple_mat_metadata(mat_name, ptmesh_name, root_node); + prepare_simple_mat_metadata(ptmesh_name, mat_name, root_node); } // TODO I love rereading state for every mesh. This is so silly diff --git a/src/tests/relay/t_relay_io_silo.cpp b/src/tests/relay/t_relay_io_silo.cpp index 242570849..2842a483f 100644 --- a/src/tests/relay/t_relay_io_silo.cpp +++ b/src/tests/relay/t_relay_io_silo.cpp @@ -144,1835 +144,1835 @@ TEST(conduit_relay_io_silo, read_curv2d) load_mesh.print(); } -// //----------------------------------------------------------------------------- -// TEST(conduit_relay_io_silo, round_trip_basic) -// { -// const std::vector> mesh_types = { -// std::make_pair("uniform", "2"), std::make_pair("uniform", "3"), -// std::make_pair("rectilinear", "2"), std::make_pair("rectilinear", "3"), -// std::make_pair("structured", "2"), std::make_pair("structured", "3"), -// std::make_pair("tris", "2"), -// std::make_pair("quads", "2"), -// std::make_pair("polygons", "2"), -// std::make_pair("tets", "3"), -// std::make_pair("hexs", "3"), -// std::make_pair("wedges", "3"), -// std::make_pair("pyramids", "3"), -// // std::make_pair("polyhedra", "3") -// }; -// for (int i = 0; i < mesh_types.size(); ++i) -// { -// std::string dim = mesh_types[i].second; -// index_t nx = 3; -// index_t ny = 4; -// index_t nz = (dim == "2" ? 0 : 2); - -// const std::string mesh_type = mesh_types[i].first; - -// Node save_mesh, load_mesh, info; -// blueprint::mesh::examples::basic(mesh_type, nx, ny, nz, save_mesh); - -// const std::string basename = "silo_basic_" + mesh_type + "_" + dim + "D"; -// const std::string filename = basename + ".root"; - -// remove_path_if_exists(filename); -// io::silo::save_mesh(save_mesh, basename); -// io::silo::load_mesh(filename, load_mesh); -// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - -// // make changes to save mesh so the diff will pass -// if (mesh_type == "uniform") -// { -// silo_uniform_to_rect_conversion("coords", "mesh", save_mesh); -// } -// silo_name_changer("mesh", save_mesh); - -// // the loaded mesh will be in the multidomain format -// // but the saved mesh is in the single domain format -// EXPECT_EQ(load_mesh.number_of_children(), 1); -// EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); -// EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); -// } -// } - -// //----------------------------------------------------------------------------- -// // we are testing vector fields in this test -// TEST(conduit_relay_io_silo, round_trip_braid) -// { -// const std::vector> mesh_types = { -// std::make_pair("uniform", "2"), std::make_pair("uniform", "3"), -// std::make_pair("rectilinear", "2"), std::make_pair("rectilinear", "3"), -// std::make_pair("structured", "2"), std::make_pair("structured", "3"), -// std::make_pair("points", "2"), std::make_pair("points", "3"), -// std::make_pair("points_implicit", "2"), std::make_pair("points_implicit", "3"), -// std::make_pair("lines", "2"), std::make_pair("lines", "3"), -// std::make_pair("tris", "2"), -// std::make_pair("quads", "2"), -// std::make_pair("tets", "3"), -// std::make_pair("hexs", "3"), -// std::make_pair("wedges", "3"), -// std::make_pair("pyramids", "3"), -// // std::make_pair("mixed_2d", "2"), -// // std::make_pair("mixed", "3"), -// }; -// for (int i = 0; i < mesh_types.size(); ++i) -// { -// std::string dim = mesh_types[i].second; -// index_t nx = 3; -// index_t ny = 4; -// index_t nz = (dim == "2" ? 0 : 2); - -// std::string mesh_type = mesh_types[i].first; - -// Node save_mesh, load_mesh, info; -// blueprint::mesh::examples::braid(mesh_type, nx, ny, nz, save_mesh); - -// const std::string basename = "silo_braid_" + mesh_type + "_" + dim + "D"; -// const std::string filename = basename + ".cycle_000100.root"; - -// // remove existing root file, directory and any output files -// remove_path_if_exists(filename); - -// io::silo::save_mesh(save_mesh, basename); -// io::silo::load_mesh(filename, load_mesh); -// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - -// // make changes to save mesh so the diff will pass -// if (mesh_type == "uniform") -// { -// silo_uniform_to_rect_conversion("coords", "mesh", save_mesh); -// } -// if (mesh_type == "points") -// { -// // this is custom code for braid -// // We know it is correct because the unstructured points version of braid -// // uses every point in the coordset -// save_mesh["topologies"].remove_child("mesh"); -// save_mesh["topologies"]["mesh"]["type"] = "points"; -// save_mesh["topologies"]["mesh"]["coordset"] = "coords"; -// } -// if (mesh_type == "points_implicit" || mesh_type == "points") -// { -// // the association doesn't matter for point meshes -// // we choose vertex by convention -// save_mesh["fields"]["radial"]["association"].reset(); -// save_mesh["fields"]["radial"]["association"] = "vertex"; -// } -// silo_name_changer("mesh", save_mesh); - -// // the loaded mesh will be in the multidomain format -// // but the saved mesh is in the single domain format -// EXPECT_EQ(load_mesh.number_of_children(), 1); -// EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); -// EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); -// } -// } - -// //----------------------------------------------------------------------------- -// // multidomain test -// TEST(conduit_relay_io_silo, round_trip_spiral) -// { -// for (int ndomains = 2; ndomains < 6; ndomains ++) -// { -// Node save_mesh, load_mesh, info; -// blueprint::mesh::examples::spiral(ndomains, save_mesh); - -// const std::string basename = "silo_spiral_" + std::to_string(ndomains) + "_domains"; -// const std::string filename = basename + ".cycle_000000.root"; - -// remove_path_if_exists(filename); -// io::silo::save_mesh(save_mesh, basename); -// io::silo::load_mesh(filename, load_mesh); -// EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); - -// // make changes to save mesh so the diff will pass -// for (index_t child = 0; child < save_mesh.number_of_children(); child ++) -// { -// silo_name_changer("mesh", save_mesh[child]); -// } - -// EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); -// NodeConstIterator l_itr = load_mesh.children(); -// NodeConstIterator s_itr = save_mesh.children(); -// while (l_itr.has_next()) -// { -// const Node &l_curr = l_itr.next(); -// const Node &s_curr = s_itr.next(); - -// EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); -// } -// } -// } - -// //----------------------------------------------------------------------------- -// TEST(conduit_relay_io_silo, round_trip_julia) -// { -// Node save_mesh, load_mesh, info; -// blueprint::mesh::examples::julia(5, // nx -// 5, // ny -// 0, // x_min -// 10, // x_max -// 2, // y_min -// 7, // y_max -// 3, // c_re -// 4, // c_im -// save_mesh); - -// const std::string basename = "silo_julia"; -// const std::string filename = basename + ".root"; - -// remove_path_if_exists(filename); -// io::silo::save_mesh(save_mesh, basename); -// io::silo::load_mesh(filename, load_mesh); -// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - -// // make changes to save mesh so the diff will pass -// silo_name_changer("mesh", save_mesh); - -// // the loaded mesh will be in the multidomain format -// // but the saved mesh is in the single domain format -// EXPECT_EQ(load_mesh.number_of_children(), 1); -// EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); -// EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); -// } - -// //----------------------------------------------------------------------------- -// // test material write and read -// TEST(conduit_relay_io_silo, round_trip_venn) -// { -// std::string matset_type = "sparse_by_element"; -// for (int j = 0; j < 2; j ++) -// { -// Node save_mesh, sbe, load_mesh, info; -// std::string size; -// int nx, ny; -// const double radius = 0.25; -// if (j == 0) -// { -// size = "small"; -// nx = ny = 4; -// } -// else -// { -// size = "large"; -// nx = ny = 100; -// } -// blueprint::mesh::examples::venn(matset_type, nx, ny, radius, save_mesh); - -// const std::string basename = "silo_venn_" + matset_type + "_" + size; -// const std::string filename = basename + ".root"; - -// remove_path_if_exists(filename); -// io::silo::save_mesh(save_mesh, basename); -// io::silo::load_mesh(filename, load_mesh); -// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - -// // make changes to save mesh so the diff will pass - -// // The field mat_check has values that are one type and matset_values -// // that are another type. The silo writer converts both to double arrays -// // in this case, so we follow suit. -// Node mat_check_new_values, mat_check_new_matset_values; -// save_mesh["fields"]["mat_check"]["values"].to_double_array(mat_check_new_values); -// save_mesh["fields"]["mat_check"]["matset_values"].to_double_array(mat_check_new_matset_values); -// save_mesh["fields"]["mat_check"]["values"].set_external(mat_check_new_values); -// save_mesh["fields"]["mat_check"]["matset_values"].set_external(mat_check_new_matset_values); - -// silo_name_changer("mesh", save_mesh); - -// // the loaded mesh will be in the multidomain format -// // but the saved mesh is in the single domain format -// EXPECT_EQ(load_mesh.number_of_children(), 1); -// EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); -// EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); -// } -// } - -// //----------------------------------------------------------------------------- -// TEST(conduit_relay_io_silo, round_trip_venn_modded_matnos) -// { -// const std::string matset_type = "sparse_by_element"; -// Node save_mesh, load_mesh, info; -// const int nx = 4; -// const int ny = 4; -// const double radius = 0.25; -// blueprint::mesh::examples::venn(matset_type, nx, ny, radius, save_mesh); - -// auto replace_matno = [](int matno) -// { -// return (matno == 1 ? 15 : -// (matno == 2 ? 37 : -// (matno == 3 ? 4 : -// (matno == 0 ? 22 : -// -1)))); -// }; - -// auto matmap_itr = save_mesh["matsets"]["matset"]["material_map"].children(); -// while (matmap_itr.has_next()) -// { -// Node &mat = matmap_itr.next(); -// mat.set(replace_matno(mat.as_int())); -// } - -// int_array matids = save_mesh["matsets"]["matset"]["material_ids"].value(); -// for (int i = 0; i < save_mesh["matsets"]["matset"]["material_ids"].dtype().number_of_elements(); i ++) -// { -// matids[i] = replace_matno(matids[i]); -// } - -// const std::string silo_basename = "silo_venn_" + matset_type + "_modded_matnos"; -// const std::string silo_filename = silo_basename + ".root"; -// remove_path_if_exists(silo_filename); -// io::silo::save_mesh(save_mesh, silo_basename); - -// const std::string bp_basename = "bp_venn_" + matset_type + "_modded_matnos"; -// const std::string bp_filename = bp_basename + ".root"; -// remove_path_if_exists(bp_filename); -// io::blueprint::save_mesh(save_mesh, bp_basename, "hdf5"); +//----------------------------------------------------------------------------- +TEST(conduit_relay_io_silo, round_trip_basic) +{ + const std::vector> mesh_types = { + std::make_pair("uniform", "2"), std::make_pair("uniform", "3"), + std::make_pair("rectilinear", "2"), std::make_pair("rectilinear", "3"), + std::make_pair("structured", "2"), std::make_pair("structured", "3"), + std::make_pair("tris", "2"), + std::make_pair("quads", "2"), + std::make_pair("polygons", "2"), + std::make_pair("tets", "3"), + std::make_pair("hexs", "3"), + std::make_pair("wedges", "3"), + std::make_pair("pyramids", "3"), + // std::make_pair("polyhedra", "3") + }; + for (int i = 0; i < mesh_types.size(); ++i) + { + std::string dim = mesh_types[i].second; + index_t nx = 3; + index_t ny = 4; + index_t nz = (dim == "2" ? 0 : 2); + + const std::string mesh_type = mesh_types[i].first; + + Node save_mesh, load_mesh, info; + blueprint::mesh::examples::basic(mesh_type, nx, ny, nz, save_mesh); + + const std::string basename = "silo_basic_" + mesh_type + "_" + dim + "D"; + const std::string filename = basename + ".root"; + + remove_path_if_exists(filename); + io::silo::save_mesh(save_mesh, basename); + io::silo::load_mesh(filename, load_mesh); + EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + + // make changes to save mesh so the diff will pass + if (mesh_type == "uniform") + { + silo_uniform_to_rect_conversion("coords", "mesh", save_mesh); + } + silo_name_changer("mesh", save_mesh); + + // the loaded mesh will be in the multidomain format + // but the saved mesh is in the single domain format + EXPECT_EQ(load_mesh.number_of_children(), 1); + EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); + EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); + } +} + +//----------------------------------------------------------------------------- +// we are testing vector fields in this test +TEST(conduit_relay_io_silo, round_trip_braid) +{ + const std::vector> mesh_types = { + std::make_pair("uniform", "2"), std::make_pair("uniform", "3"), + std::make_pair("rectilinear", "2"), std::make_pair("rectilinear", "3"), + std::make_pair("structured", "2"), std::make_pair("structured", "3"), + std::make_pair("points", "2"), std::make_pair("points", "3"), + std::make_pair("points_implicit", "2"), std::make_pair("points_implicit", "3"), + std::make_pair("lines", "2"), std::make_pair("lines", "3"), + std::make_pair("tris", "2"), + std::make_pair("quads", "2"), + std::make_pair("tets", "3"), + std::make_pair("hexs", "3"), + std::make_pair("wedges", "3"), + std::make_pair("pyramids", "3"), + // std::make_pair("mixed_2d", "2"), + // std::make_pair("mixed", "3"), + }; + for (int i = 0; i < mesh_types.size(); ++i) + { + std::string dim = mesh_types[i].second; + index_t nx = 3; + index_t ny = 4; + index_t nz = (dim == "2" ? 0 : 2); + + std::string mesh_type = mesh_types[i].first; + + Node save_mesh, load_mesh, info; + blueprint::mesh::examples::braid(mesh_type, nx, ny, nz, save_mesh); + + const std::string basename = "silo_braid_" + mesh_type + "_" + dim + "D"; + const std::string filename = basename + ".cycle_000100.root"; + + // remove existing root file, directory and any output files + remove_path_if_exists(filename); + + io::silo::save_mesh(save_mesh, basename); + io::silo::load_mesh(filename, load_mesh); + EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + + // make changes to save mesh so the diff will pass + if (mesh_type == "uniform") + { + silo_uniform_to_rect_conversion("coords", "mesh", save_mesh); + } + if (mesh_type == "points") + { + // this is custom code for braid + // We know it is correct because the unstructured points version of braid + // uses every point in the coordset + save_mesh["topologies"].remove_child("mesh"); + save_mesh["topologies"]["mesh"]["type"] = "points"; + save_mesh["topologies"]["mesh"]["coordset"] = "coords"; + } + if (mesh_type == "points_implicit" || mesh_type == "points") + { + // the association doesn't matter for point meshes + // we choose vertex by convention + save_mesh["fields"]["radial"]["association"].reset(); + save_mesh["fields"]["radial"]["association"] = "vertex"; + } + silo_name_changer("mesh", save_mesh); + + // the loaded mesh will be in the multidomain format + // but the saved mesh is in the single domain format + EXPECT_EQ(load_mesh.number_of_children(), 1); + EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); + EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); + } +} + +//----------------------------------------------------------------------------- +// multidomain test +TEST(conduit_relay_io_silo, round_trip_spiral) +{ + for (int ndomains = 2; ndomains < 6; ndomains ++) + { + Node save_mesh, load_mesh, info; + blueprint::mesh::examples::spiral(ndomains, save_mesh); + + const std::string basename = "silo_spiral_" + std::to_string(ndomains) + "_domains"; + const std::string filename = basename + ".cycle_000000.root"; + + remove_path_if_exists(filename); + io::silo::save_mesh(save_mesh, basename); + io::silo::load_mesh(filename, load_mesh); + EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); + + // make changes to save mesh so the diff will pass + for (index_t child = 0; child < save_mesh.number_of_children(); child ++) + { + silo_name_changer("mesh", save_mesh[child]); + } + + EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); + NodeConstIterator l_itr = load_mesh.children(); + NodeConstIterator s_itr = save_mesh.children(); + while (l_itr.has_next()) + { + const Node &l_curr = l_itr.next(); + const Node &s_curr = s_itr.next(); + + EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); + } + } +} + +//----------------------------------------------------------------------------- +TEST(conduit_relay_io_silo, round_trip_julia) +{ + Node save_mesh, load_mesh, info; + blueprint::mesh::examples::julia(5, // nx + 5, // ny + 0, // x_min + 10, // x_max + 2, // y_min + 7, // y_max + 3, // c_re + 4, // c_im + save_mesh); + + const std::string basename = "silo_julia"; + const std::string filename = basename + ".root"; + + remove_path_if_exists(filename); + io::silo::save_mesh(save_mesh, basename); + io::silo::load_mesh(filename, load_mesh); + EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + + // make changes to save mesh so the diff will pass + silo_name_changer("mesh", save_mesh); + + // the loaded mesh will be in the multidomain format + // but the saved mesh is in the single domain format + EXPECT_EQ(load_mesh.number_of_children(), 1); + EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); + EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); +} + +//----------------------------------------------------------------------------- +// test material write and read +TEST(conduit_relay_io_silo, round_trip_venn) +{ + std::string matset_type = "sparse_by_element"; + for (int j = 0; j < 2; j ++) + { + Node save_mesh, sbe, load_mesh, info; + std::string size; + int nx, ny; + const double radius = 0.25; + if (j == 0) + { + size = "small"; + nx = ny = 4; + } + else + { + size = "large"; + nx = ny = 100; + } + blueprint::mesh::examples::venn(matset_type, nx, ny, radius, save_mesh); + + const std::string basename = "silo_venn_" + matset_type + "_" + size; + const std::string filename = basename + ".root"; + + remove_path_if_exists(filename); + io::silo::save_mesh(save_mesh, basename); + io::silo::load_mesh(filename, load_mesh); + EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + + // make changes to save mesh so the diff will pass + + // The field mat_check has values that are one type and matset_values + // that are another type. The silo writer converts both to double arrays + // in this case, so we follow suit. + Node mat_check_new_values, mat_check_new_matset_values; + save_mesh["fields"]["mat_check"]["values"].to_double_array(mat_check_new_values); + save_mesh["fields"]["mat_check"]["matset_values"].to_double_array(mat_check_new_matset_values); + save_mesh["fields"]["mat_check"]["values"].set_external(mat_check_new_values); + save_mesh["fields"]["mat_check"]["matset_values"].set_external(mat_check_new_matset_values); + + silo_name_changer("mesh", save_mesh); + + // the loaded mesh will be in the multidomain format + // but the saved mesh is in the single domain format + EXPECT_EQ(load_mesh.number_of_children(), 1); + EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); + EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); + } +} + +//----------------------------------------------------------------------------- +TEST(conduit_relay_io_silo, round_trip_venn_modded_matnos) +{ + const std::string matset_type = "sparse_by_element"; + Node save_mesh, load_mesh, info; + const int nx = 4; + const int ny = 4; + const double radius = 0.25; + blueprint::mesh::examples::venn(matset_type, nx, ny, radius, save_mesh); + + auto replace_matno = [](int matno) + { + return (matno == 1 ? 15 : + (matno == 2 ? 37 : + (matno == 3 ? 4 : + (matno == 0 ? 22 : + -1)))); + }; + + auto matmap_itr = save_mesh["matsets"]["matset"]["material_map"].children(); + while (matmap_itr.has_next()) + { + Node &mat = matmap_itr.next(); + mat.set(replace_matno(mat.as_int())); + } + + int_array matids = save_mesh["matsets"]["matset"]["material_ids"].value(); + for (int i = 0; i < save_mesh["matsets"]["matset"]["material_ids"].dtype().number_of_elements(); i ++) + { + matids[i] = replace_matno(matids[i]); + } + + const std::string silo_basename = "silo_venn_" + matset_type + "_modded_matnos"; + const std::string silo_filename = silo_basename + ".root"; + remove_path_if_exists(silo_filename); + io::silo::save_mesh(save_mesh, silo_basename); + + const std::string bp_basename = "bp_venn_" + matset_type + "_modded_matnos"; + const std::string bp_filename = bp_basename + ".root"; + remove_path_if_exists(bp_filename); + io::blueprint::save_mesh(save_mesh, bp_basename, "hdf5"); -// io::silo::load_mesh(silo_filename, load_mesh); -// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - -// // make changes to save mesh so the diff will pass - -// // The field mat_check has values that are one type and matset_values -// // that are another type. The silo writer converts both to double arrays -// // in this case, so we follow suit. -// Node mat_check_new_values, mat_check_new_matset_values; -// save_mesh["fields"]["mat_check"]["values"].to_double_array(mat_check_new_values); -// save_mesh["fields"]["mat_check"]["matset_values"].to_double_array(mat_check_new_matset_values); -// save_mesh["fields"]["mat_check"]["values"].set_external(mat_check_new_values); -// save_mesh["fields"]["mat_check"]["matset_values"].set_external(mat_check_new_matset_values); - -// // to_silo is going to reorder mixed materials least to greatest -// // so we must do the same -// int_array mat_ids = save_mesh["matsets"]["matset"]["material_ids"].value(); -// const auto mat_id10 = mat_ids[10]; -// const auto mat_id11 = mat_ids[11]; -// const auto mat_id12 = mat_ids[12]; -// mat_ids[10] = mat_id12; -// mat_ids[11] = mat_id10; -// mat_ids[12] = mat_id11; -// auto field_itr = save_mesh["fields"].children(); -// while (field_itr.has_next()) -// { -// const Node &n_field = field_itr.next(); -// if (n_field.has_child("matset")) -// { -// double_array matset_vals = n_field["matset_values"].value(); -// const auto matset_val10 = matset_vals[10]; -// const auto matset_val11 = matset_vals[11]; -// const auto matset_val12 = matset_vals[12]; -// matset_vals[10] = matset_val12; -// matset_vals[11] = matset_val10; -// matset_vals[12] = matset_val11; -// } -// } - -// silo_name_changer("mesh", save_mesh); - -// // the loaded mesh will be in the multidomain format -// // but the saved mesh is in the single domain format -// EXPECT_EQ(load_mesh.number_of_children(), 1); -// EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); - -// EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); -// } - -// //----------------------------------------------------------------------------- -// TEST(conduit_relay_io_silo, round_trip_spiral_multi_dom_materials) -// { -// Node save_mesh, load_mesh, info; -// const int ndomains = 4; -// blueprint::mesh::examples::spiral(ndomains, save_mesh); -// add_matset_to_spiral(save_mesh, ndomains); -// EXPECT_TRUE(blueprint::mesh::verify(save_mesh, info)); + io::silo::load_mesh(silo_filename, load_mesh); + EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + + // make changes to save mesh so the diff will pass + + // The field mat_check has values that are one type and matset_values + // that are another type. The silo writer converts both to double arrays + // in this case, so we follow suit. + Node mat_check_new_values, mat_check_new_matset_values; + save_mesh["fields"]["mat_check"]["values"].to_double_array(mat_check_new_values); + save_mesh["fields"]["mat_check"]["matset_values"].to_double_array(mat_check_new_matset_values); + save_mesh["fields"]["mat_check"]["values"].set_external(mat_check_new_values); + save_mesh["fields"]["mat_check"]["matset_values"].set_external(mat_check_new_matset_values); + + // to_silo is going to reorder mixed materials least to greatest + // so we must do the same + int_array mat_ids = save_mesh["matsets"]["matset"]["material_ids"].value(); + const auto mat_id10 = mat_ids[10]; + const auto mat_id11 = mat_ids[11]; + const auto mat_id12 = mat_ids[12]; + mat_ids[10] = mat_id12; + mat_ids[11] = mat_id10; + mat_ids[12] = mat_id11; + auto field_itr = save_mesh["fields"].children(); + while (field_itr.has_next()) + { + const Node &n_field = field_itr.next(); + if (n_field.has_child("matset")) + { + double_array matset_vals = n_field["matset_values"].value(); + const auto matset_val10 = matset_vals[10]; + const auto matset_val11 = matset_vals[11]; + const auto matset_val12 = matset_vals[12]; + matset_vals[10] = matset_val12; + matset_vals[11] = matset_val10; + matset_vals[12] = matset_val11; + } + } + + silo_name_changer("mesh", save_mesh); + + // the loaded mesh will be in the multidomain format + // but the saved mesh is in the single domain format + EXPECT_EQ(load_mesh.number_of_children(), 1); + EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); + + EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); +} + +//----------------------------------------------------------------------------- +TEST(conduit_relay_io_silo, round_trip_spiral_multi_dom_materials) +{ + Node save_mesh, load_mesh, info; + const int ndomains = 4; + blueprint::mesh::examples::spiral(ndomains, save_mesh); + add_matset_to_spiral(save_mesh, ndomains); + EXPECT_TRUE(blueprint::mesh::verify(save_mesh, info)); -// const std::string basename = "silo_multidom_materials_spiral"; -// const std::string filename = basename + ".cycle_000000.root"; - -// remove_path_if_exists(filename); -// io::silo::save_mesh(save_mesh, basename); -// io::silo::load_mesh(filename, load_mesh); -// EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); - -// // make changes to save mesh so the diff will pass -// for (index_t child = 0; child < save_mesh.number_of_children(); child ++) -// { -// // get the matset for this domain -// Node &n_matset = save_mesh[child]["matsets"]["matset"]; - -// // clean up volume fractions -// Node vf_arr; -// n_matset["volume_fractions"].to_float64_array(vf_arr); -// n_matset["volume_fractions"].reset(); -// n_matset["volume_fractions"].set(vf_arr); + const std::string basename = "silo_multidom_materials_spiral"; + const std::string filename = basename + ".cycle_000000.root"; + + remove_path_if_exists(filename); + io::silo::save_mesh(save_mesh, basename); + io::silo::load_mesh(filename, load_mesh); + EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); + + // make changes to save mesh so the diff will pass + for (index_t child = 0; child < save_mesh.number_of_children(); child ++) + { + // get the matset for this domain + Node &n_matset = save_mesh[child]["matsets"]["matset"]; + + // clean up volume fractions + Node vf_arr; + n_matset["volume_fractions"].to_float64_array(vf_arr); + n_matset["volume_fractions"].reset(); + n_matset["volume_fractions"].set(vf_arr); -// // cheat a little bit - we don't have these to start -// n_matset["sizes"].set_external(load_mesh[child]["matsets"]["mesh_matset"]["sizes"]); -// n_matset["offsets"].set_external(load_mesh[child]["matsets"]["mesh_matset"]["offsets"]); - -// silo_name_changer("mesh", save_mesh[child]); -// } - -// EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); -// NodeConstIterator l_itr = load_mesh.children(); -// NodeConstIterator s_itr = save_mesh.children(); -// while (l_itr.has_next()) -// { -// const Node &l_curr = l_itr.next(); -// const Node &s_curr = s_itr.next(); - -// EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); -// } -// } - -// //----------------------------------------------------------------------------- -// TEST(conduit_relay_io_silo, round_trip_grid_adjset) -// { -// Node save_mesh, load_mesh, info; -// blueprint::mesh::examples::grid("structured", 3, 3, 1, 2, 2, 1, save_mesh); - -// // we need a material in order for this to be valid overlink -// for (index_t child = 0; child < save_mesh.number_of_children(); child ++) -// { -// add_multi_buffer_full_matset(save_mesh[child], 4, "mesh"); -// } - -// const std::string basename = "silo_grid_adjset"; -// const std::string filename = basename + "/OvlTop.silo"; - -// Node write_opts; -// write_opts["file_style"] = "overlink"; -// write_opts["ovl_topo_name"] = "mesh"; - -// Node read_opts; -// read_opts["matset_style"] = "multi_buffer_full"; - -// remove_path_if_exists(filename); -// io::silo::save_mesh(save_mesh, basename, write_opts); -// io::blueprint::save_mesh(save_mesh, basename, "hdf5"); -// io::silo::load_mesh(filename, read_opts, load_mesh); -// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - -// for (index_t child = 0; child < save_mesh.number_of_children(); child ++) -// { -// // separate out vector fields -// Node &field_vel = save_mesh[child]["fields"]["vel"]; -// Node &field_vel_u = save_mesh[child]["fields"]["vel_u"]; -// Node &field_vel_v = save_mesh[child]["fields"]["vel_v"]; + // cheat a little bit - we don't have these to start + n_matset["sizes"].set_external(load_mesh[child]["matsets"]["mesh_matset"]["sizes"]); + n_matset["offsets"].set_external(load_mesh[child]["matsets"]["mesh_matset"]["offsets"]); + + silo_name_changer("mesh", save_mesh[child]); + } + + EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); + NodeConstIterator l_itr = load_mesh.children(); + NodeConstIterator s_itr = save_mesh.children(); + while (l_itr.has_next()) + { + const Node &l_curr = l_itr.next(); + const Node &s_curr = s_itr.next(); + + EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); + } +} + +//----------------------------------------------------------------------------- +TEST(conduit_relay_io_silo, round_trip_grid_adjset) +{ + Node save_mesh, load_mesh, info; + blueprint::mesh::examples::grid("structured", 3, 3, 1, 2, 2, 1, save_mesh); + + // we need a material in order for this to be valid overlink + for (index_t child = 0; child < save_mesh.number_of_children(); child ++) + { + add_multi_buffer_full_matset(save_mesh[child], 4, "mesh"); + } + + const std::string basename = "silo_grid_adjset"; + const std::string filename = basename + "/OvlTop.silo"; + + Node write_opts; + write_opts["file_style"] = "overlink"; + write_opts["ovl_topo_name"] = "mesh"; + + Node read_opts; + read_opts["matset_style"] = "multi_buffer_full"; + + remove_path_if_exists(filename); + io::silo::save_mesh(save_mesh, basename, write_opts); + io::blueprint::save_mesh(save_mesh, basename, "hdf5"); + io::silo::load_mesh(filename, read_opts, load_mesh); + EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + + for (index_t child = 0; child < save_mesh.number_of_children(); child ++) + { + // separate out vector fields + Node &field_vel = save_mesh[child]["fields"]["vel"]; + Node &field_vel_u = save_mesh[child]["fields"]["vel_u"]; + Node &field_vel_v = save_mesh[child]["fields"]["vel_v"]; -// field_vel_u["topology"].set(field_vel["topology"]); -// field_vel_u["association"].set(field_vel["association"]); -// field_vel_u["values"].set(field_vel["values/u"]); -// field_vel_v["topology"].set(field_vel["topology"]); -// field_vel_v["association"].set(field_vel["association"]); -// field_vel_v["values"].set(field_vel["values/v"]); - -// save_mesh[child]["fields"].remove_child("vel"); - -// // make adjset pairwise -// Node &pairwise_adjset = save_mesh[child]["adjsets"]["adjset"]; -// conduit::blueprint::mesh::adjset::to_pairwise(save_mesh[child]["adjsets"]["mesh_adj"], pairwise_adjset); -// save_mesh[child]["adjsets"].remove_child("mesh_adj"); - -// // make changes to save mesh so the diff will pass -// overlink_name_changer(save_mesh[child]); -// } - -// EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); -// NodeConstIterator l_itr = load_mesh.children(); -// NodeConstIterator s_itr = save_mesh.children(); -// while (l_itr.has_next()) -// { -// const Node &l_curr = l_itr.next(); -// const Node &s_curr = s_itr.next(); - -// EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); -// } -// } - -// //----------------------------------------------------------------------------- -// // -// // test read and write semantics -// // - -// //----------------------------------------------------------------------------- -// TEST(conduit_relay_io_silo, read_and_write_semantics) -// { -// for (int ndomains = 2; ndomains < 6; ndomains ++) -// { -// Node save_mesh, load_mesh, info; -// blueprint::mesh::examples::spiral(ndomains, save_mesh); - -// const std::string basename = "silo_spiral_" + std::to_string(ndomains) + "_domains"; -// const std::string filename = basename + ".cycle_000000.root"; - -// remove_path_if_exists(filename); -// io::silo::write_mesh(save_mesh, basename); -// io::silo::read_mesh(filename, load_mesh); -// EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); - -// // make changes to save mesh so the diff will pass -// for (index_t child = 0; child < save_mesh.number_of_children(); child ++) -// { -// silo_name_changer("mesh", save_mesh[child]); -// } - -// EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); -// NodeConstIterator l_itr = load_mesh.children(); -// NodeConstIterator s_itr = save_mesh.children(); -// while (l_itr.has_next()) -// { -// const Node &l_curr = l_itr.next(); -// const Node &s_curr = s_itr.next(); - -// EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); -// } -// } -// } - -// //----------------------------------------------------------------------------- -// // -// // special case tests -// // - -// //----------------------------------------------------------------------------- -// // var is not defined on a domain -// // -// // tests the silo "EMPTY" capability -// TEST(conduit_relay_io_silo, missing_domain_var) -// { -// Node save_mesh, load_mesh, info; -// const int ndomains = 4; -// blueprint::mesh::examples::spiral(ndomains, save_mesh); - -// // remove information for a particular domain -// save_mesh[2]["fields"].remove_child("dist"); - -// const std::string basename = "silo_missing_domain_var_spiral"; -// const std::string filename = basename + ".cycle_000000.root"; - -// remove_path_if_exists(filename); -// io::silo::save_mesh(save_mesh, basename); -// io::silo::load_mesh(filename, load_mesh); -// EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); - -// // make changes to save mesh so the diff will pass -// for (index_t child = 0; child < save_mesh.number_of_children(); child ++) -// { -// silo_name_changer("mesh", save_mesh[child]); -// } -// save_mesh[2].remove_child("fields"); - -// EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); -// NodeConstIterator l_itr = load_mesh.children(); -// NodeConstIterator s_itr = save_mesh.children(); -// while (l_itr.has_next()) -// { -// const Node &l_curr = l_itr.next(); -// const Node &s_curr = s_itr.next(); - -// EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); -// } -// } - -// //----------------------------------------------------------------------------- -// // matset is not defined on a domain -// // -// // tests the silo "EMPTY" capability -// TEST(conduit_relay_io_silo, missing_domain_matset) -// { -// Node save_mesh, load_mesh, info; -// const int ndomains = 4; -// blueprint::mesh::examples::spiral(ndomains, save_mesh); -// add_matset_to_spiral(save_mesh, ndomains); -// EXPECT_TRUE(blueprint::mesh::verify(save_mesh, info)); - -// // remove information for a particular domain -// save_mesh[2]["matsets"].remove_child("matset"); - -// const std::string basename = "silo_missing_domain_matset_spiral"; -// const std::string filename = basename + ".cycle_000000.root"; - -// remove_path_if_exists(filename); -// io::silo::save_mesh(save_mesh, basename); -// io::silo::load_mesh(filename, load_mesh); -// EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); - -// // make changes to save mesh so the diff will pass -// for (index_t child = 0; child < save_mesh.number_of_children(); child ++) -// { -// if (save_mesh[child].has_path("matsets/matset")) -// { -// // get the matset for this domain -// Node &n_matset = save_mesh[child]["matsets"]["matset"]; - -// // clean up volume fractions -// Node vf_arr; -// n_matset["volume_fractions"].to_float64_array(vf_arr); -// n_matset["volume_fractions"].reset(); -// n_matset["volume_fractions"].set(vf_arr); + field_vel_u["topology"].set(field_vel["topology"]); + field_vel_u["association"].set(field_vel["association"]); + field_vel_u["values"].set(field_vel["values/u"]); + field_vel_v["topology"].set(field_vel["topology"]); + field_vel_v["association"].set(field_vel["association"]); + field_vel_v["values"].set(field_vel["values/v"]); + + save_mesh[child]["fields"].remove_child("vel"); + + // make adjset pairwise + Node &pairwise_adjset = save_mesh[child]["adjsets"]["adjset"]; + conduit::blueprint::mesh::adjset::to_pairwise(save_mesh[child]["adjsets"]["mesh_adj"], pairwise_adjset); + save_mesh[child]["adjsets"].remove_child("mesh_adj"); + + // make changes to save mesh so the diff will pass + overlink_name_changer(save_mesh[child]); + } + + EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); + NodeConstIterator l_itr = load_mesh.children(); + NodeConstIterator s_itr = save_mesh.children(); + while (l_itr.has_next()) + { + const Node &l_curr = l_itr.next(); + const Node &s_curr = s_itr.next(); + + EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); + } +} + +//----------------------------------------------------------------------------- +// +// test read and write semantics +// + +//----------------------------------------------------------------------------- +TEST(conduit_relay_io_silo, read_and_write_semantics) +{ + for (int ndomains = 2; ndomains < 6; ndomains ++) + { + Node save_mesh, load_mesh, info; + blueprint::mesh::examples::spiral(ndomains, save_mesh); + + const std::string basename = "silo_spiral_" + std::to_string(ndomains) + "_domains"; + const std::string filename = basename + ".cycle_000000.root"; + + remove_path_if_exists(filename); + io::silo::write_mesh(save_mesh, basename); + io::silo::read_mesh(filename, load_mesh); + EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); + + // make changes to save mesh so the diff will pass + for (index_t child = 0; child < save_mesh.number_of_children(); child ++) + { + silo_name_changer("mesh", save_mesh[child]); + } + + EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); + NodeConstIterator l_itr = load_mesh.children(); + NodeConstIterator s_itr = save_mesh.children(); + while (l_itr.has_next()) + { + const Node &l_curr = l_itr.next(); + const Node &s_curr = s_itr.next(); + + EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); + } + } +} + +//----------------------------------------------------------------------------- +// +// special case tests +// + +//----------------------------------------------------------------------------- +// var is not defined on a domain +// +// tests the silo "EMPTY" capability +TEST(conduit_relay_io_silo, missing_domain_var) +{ + Node save_mesh, load_mesh, info; + const int ndomains = 4; + blueprint::mesh::examples::spiral(ndomains, save_mesh); + + // remove information for a particular domain + save_mesh[2]["fields"].remove_child("dist"); + + const std::string basename = "silo_missing_domain_var_spiral"; + const std::string filename = basename + ".cycle_000000.root"; + + remove_path_if_exists(filename); + io::silo::save_mesh(save_mesh, basename); + io::silo::load_mesh(filename, load_mesh); + EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); + + // make changes to save mesh so the diff will pass + for (index_t child = 0; child < save_mesh.number_of_children(); child ++) + { + silo_name_changer("mesh", save_mesh[child]); + } + save_mesh[2].remove_child("fields"); + + EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); + NodeConstIterator l_itr = load_mesh.children(); + NodeConstIterator s_itr = save_mesh.children(); + while (l_itr.has_next()) + { + const Node &l_curr = l_itr.next(); + const Node &s_curr = s_itr.next(); + + EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); + } +} + +//----------------------------------------------------------------------------- +// matset is not defined on a domain +// +// tests the silo "EMPTY" capability +TEST(conduit_relay_io_silo, missing_domain_matset) +{ + Node save_mesh, load_mesh, info; + const int ndomains = 4; + blueprint::mesh::examples::spiral(ndomains, save_mesh); + add_matset_to_spiral(save_mesh, ndomains); + EXPECT_TRUE(blueprint::mesh::verify(save_mesh, info)); + + // remove information for a particular domain + save_mesh[2]["matsets"].remove_child("matset"); + + const std::string basename = "silo_missing_domain_matset_spiral"; + const std::string filename = basename + ".cycle_000000.root"; + + remove_path_if_exists(filename); + io::silo::save_mesh(save_mesh, basename); + io::silo::load_mesh(filename, load_mesh); + EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); + + // make changes to save mesh so the diff will pass + for (index_t child = 0; child < save_mesh.number_of_children(); child ++) + { + if (save_mesh[child].has_path("matsets/matset")) + { + // get the matset for this domain + Node &n_matset = save_mesh[child]["matsets"]["matset"]; + + // clean up volume fractions + Node vf_arr; + n_matset["volume_fractions"].to_float64_array(vf_arr); + n_matset["volume_fractions"].reset(); + n_matset["volume_fractions"].set(vf_arr); -// // cheat a little bit - we don't have these to start -// n_matset["sizes"].set_external(load_mesh[child]["matsets"]["mesh_matset"]["sizes"]); -// n_matset["offsets"].set_external(load_mesh[child]["matsets"]["mesh_matset"]["offsets"]); -// } - -// silo_name_changer("mesh", save_mesh[child]); -// } -// save_mesh[2].remove_child("matsets"); - -// EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); -// NodeConstIterator l_itr = load_mesh.children(); -// NodeConstIterator s_itr = save_mesh.children(); -// while (l_itr.has_next()) -// { -// const Node &l_curr = l_itr.next(); -// const Node &s_curr = s_itr.next(); - -// EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); -// } -// } - -// //----------------------------------------------------------------------------- -// // mesh is not defined on a domain -// // -// // This case is much less interesting. -// // data passes through the clean mesh filter which -// // deletes domains that are missing topos. -// // They simply are not part of the mesh and so silo -// // doesn't have to deal with it. -// TEST(conduit_relay_io_silo, missing_domain_mesh_trivial) -// { -// Node save_mesh, load_mesh, info; -// const int ndomains = 4; -// blueprint::mesh::examples::spiral(ndomains, save_mesh); - -// // remove information for a particular domain -// save_mesh[2]["topologies"].remove_child("topo"); - -// const std::string basename = "silo_missing_domain_mesh_trivial_spiral"; -// const std::string filename = basename + ".cycle_000000.root"; - -// remove_path_if_exists(filename); -// io::silo::save_mesh(save_mesh, basename); -// io::silo::load_mesh(filename, load_mesh); - -// EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); - -// // make changes to save mesh so the diff will pass -// save_mesh.remove(2); -// save_mesh.rename_child("domain_000003", "domain_000002"); -// save_mesh[2]["state"]["domain_id"].reset(); -// save_mesh[2]["state"]["domain_id"] = 2; -// for (index_t child = 0; child < save_mesh.number_of_children(); child ++) -// { -// silo_name_changer("mesh", save_mesh[child]); -// } - -// EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); -// NodeConstIterator l_itr = load_mesh.children(); -// NodeConstIterator s_itr = save_mesh.children(); -// while (l_itr.has_next()) -// { -// const Node &l_curr = l_itr.next(); -// const Node &s_curr = s_itr.next(); - -// EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); -// } -// } - -// //----------------------------------------------------------------------------- -// // mesh is not defined on a domain but there are multiple meshes -// TEST(conduit_relay_io_silo, missing_domain_mesh) -// { -// Node save_mesh, save_mesh2, load_mesh, load_mesh2, info, opts; -// const int ndomains = 4; -// blueprint::mesh::examples::spiral(ndomains, save_mesh); -// blueprint::mesh::examples::spiral(ndomains, save_mesh2); - -// for (index_t child = 0; child < save_mesh.number_of_children(); child ++) -// { -// save_mesh[child]["coordsets"].rename_child("coords", "coords2"); -// save_mesh[child]["topologies"]["topo"]["coordset"].reset(); -// save_mesh[child]["topologies"]["topo"]["coordset"] = "coords2"; -// save_mesh[child]["topologies"].rename_child("topo", "topo2"); -// save_mesh[child]["fields"]["dist"]["topology"].reset(); -// save_mesh[child]["fields"]["dist"]["topology"] = "topo2"; -// save_mesh[child]["fields"].rename_child("dist", "dist2"); - -// save_mesh[child]["coordsets"]["coords"].set_external(save_mesh2[child]["coordsets"]["coords"]); -// save_mesh[child]["topologies"]["topo"].set_external(save_mesh2[child]["topologies"]["topo"]); -// save_mesh[child]["fields"]["dist"].set_external(save_mesh2[child]["fields"]["dist"]); -// } - -// // remove information for a particular domain -// save_mesh[2]["topologies"].remove_child("topo"); - -// const std::string basename = "silo_missing_domain_mesh_spiral"; -// const std::string filename = basename + ".cycle_000000.root"; - -// remove_path_if_exists(filename); -// io::silo::save_mesh(save_mesh, basename); + // cheat a little bit - we don't have these to start + n_matset["sizes"].set_external(load_mesh[child]["matsets"]["mesh_matset"]["sizes"]); + n_matset["offsets"].set_external(load_mesh[child]["matsets"]["mesh_matset"]["offsets"]); + } + + silo_name_changer("mesh", save_mesh[child]); + } + save_mesh[2].remove_child("matsets"); + + EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); + NodeConstIterator l_itr = load_mesh.children(); + NodeConstIterator s_itr = save_mesh.children(); + while (l_itr.has_next()) + { + const Node &l_curr = l_itr.next(); + const Node &s_curr = s_itr.next(); + + EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); + } +} + +//----------------------------------------------------------------------------- +// mesh is not defined on a domain +// +// This case is much less interesting. +// data passes through the clean mesh filter which +// deletes domains that are missing topos. +// They simply are not part of the mesh and so silo +// doesn't have to deal with it. +TEST(conduit_relay_io_silo, missing_domain_mesh_trivial) +{ + Node save_mesh, load_mesh, info; + const int ndomains = 4; + blueprint::mesh::examples::spiral(ndomains, save_mesh); + + // remove information for a particular domain + save_mesh[2]["topologies"].remove_child("topo"); + + const std::string basename = "silo_missing_domain_mesh_trivial_spiral"; + const std::string filename = basename + ".cycle_000000.root"; + + remove_path_if_exists(filename); + io::silo::save_mesh(save_mesh, basename); + io::silo::load_mesh(filename, load_mesh); + + EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); + + // make changes to save mesh so the diff will pass + save_mesh.remove(2); + save_mesh.rename_child("domain_000003", "domain_000002"); + save_mesh[2]["state"]["domain_id"].reset(); + save_mesh[2]["state"]["domain_id"] = 2; + for (index_t child = 0; child < save_mesh.number_of_children(); child ++) + { + silo_name_changer("mesh", save_mesh[child]); + } + + EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); + NodeConstIterator l_itr = load_mesh.children(); + NodeConstIterator s_itr = save_mesh.children(); + while (l_itr.has_next()) + { + const Node &l_curr = l_itr.next(); + const Node &s_curr = s_itr.next(); + + EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); + } +} + +//----------------------------------------------------------------------------- +// mesh is not defined on a domain but there are multiple meshes +TEST(conduit_relay_io_silo, missing_domain_mesh) +{ + Node save_mesh, save_mesh2, load_mesh, load_mesh2, info, opts; + const int ndomains = 4; + blueprint::mesh::examples::spiral(ndomains, save_mesh); + blueprint::mesh::examples::spiral(ndomains, save_mesh2); + + for (index_t child = 0; child < save_mesh.number_of_children(); child ++) + { + save_mesh[child]["coordsets"].rename_child("coords", "coords2"); + save_mesh[child]["topologies"]["topo"]["coordset"].reset(); + save_mesh[child]["topologies"]["topo"]["coordset"] = "coords2"; + save_mesh[child]["topologies"].rename_child("topo", "topo2"); + save_mesh[child]["fields"]["dist"]["topology"].reset(); + save_mesh[child]["fields"]["dist"]["topology"] = "topo2"; + save_mesh[child]["fields"].rename_child("dist", "dist2"); + + save_mesh[child]["coordsets"]["coords"].set_external(save_mesh2[child]["coordsets"]["coords"]); + save_mesh[child]["topologies"]["topo"].set_external(save_mesh2[child]["topologies"]["topo"]); + save_mesh[child]["fields"]["dist"].set_external(save_mesh2[child]["fields"]["dist"]); + } + + // remove information for a particular domain + save_mesh[2]["topologies"].remove_child("topo"); + + const std::string basename = "silo_missing_domain_mesh_spiral"; + const std::string filename = basename + ".cycle_000000.root"; + + remove_path_if_exists(filename); + io::silo::save_mesh(save_mesh, basename); -// opts["mesh_name"] = "mesh_topo2"; -// io::silo::load_mesh(filename, opts, load_mesh); -// opts["mesh_name"] = "mesh_topo"; -// io::silo::load_mesh(filename, opts, load_mesh2); -// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); -// EXPECT_TRUE(blueprint::mesh::verify(load_mesh2, info)); - -// // make changes to save mesh so the diff will pass -// save_mesh[2]["coordsets"].remove_child("coords"); -// save_mesh[2]["fields"].remove_child("dist"); -// for (index_t child = 0; child < save_mesh.number_of_children(); child ++) -// { -// silo_name_changer("mesh", save_mesh[child]); -// } - -// // we must merge the two meshes in load mesh -// // the indexing is tricky because one is missing a domain -// load_mesh[0]["coordsets"]["mesh_topo"].set_external(load_mesh2[0]["coordsets"]["mesh_topo"]); -// load_mesh[0]["topologies"]["mesh_topo"].set_external(load_mesh2[0]["topologies"]["mesh_topo"]); -// load_mesh[0]["fields"]["mesh_dist"].set_external(load_mesh2[0]["fields"]["mesh_dist"]); -// load_mesh[1]["coordsets"]["mesh_topo"].set_external(load_mesh2[1]["coordsets"]["mesh_topo"]); -// load_mesh[1]["topologies"]["mesh_topo"].set_external(load_mesh2[1]["topologies"]["mesh_topo"]); -// load_mesh[1]["fields"]["mesh_dist"].set_external(load_mesh2[1]["fields"]["mesh_dist"]); -// load_mesh[3]["coordsets"]["mesh_topo"].set_external(load_mesh2[2]["coordsets"]["mesh_topo"]); -// load_mesh[3]["topologies"]["mesh_topo"].set_external(load_mesh2[2]["topologies"]["mesh_topo"]); -// load_mesh[3]["fields"]["mesh_dist"].set_external(load_mesh2[2]["fields"]["mesh_dist"]); - -// EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); -// NodeConstIterator l_itr = load_mesh.children(); -// NodeConstIterator s_itr = save_mesh.children(); -// while (l_itr.has_next()) -// { -// const Node &l_curr = l_itr.next(); -// const Node &s_curr = s_itr.next(); - -// EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); -// } -// } - -// //----------------------------------------------------------------------------- -// // explicit points (unstructured mesh) do not use every coord -// TEST(conduit_relay_io_silo, unstructured_points) -// { -// Node save_mesh, load_mesh, info; -// blueprint::mesh::examples::braid("points", 2, 2, 2, save_mesh); - -// std::vector new_conn; -// std::vector new_field1; -// std::vector new_field2; -// std::vector new_xcoords, new_ycoords, new_zcoords; - -// int_accessor conn = save_mesh["topologies"]["mesh"]["elements"]["connectivity"].value(); - -// float_accessor field1 = save_mesh["fields"]["braid"]["values"].value(); -// float_accessor field2 = save_mesh["fields"]["radial"]["values"].value(); - -// float_accessor xcoords = save_mesh["coordsets"]["coords"]["values"]["x"].value(); -// float_accessor ycoords = save_mesh["coordsets"]["coords"]["values"]["y"].value(); -// float_accessor zcoords = save_mesh["coordsets"]["coords"]["values"]["z"].value(); - -// for (int i = 1; i < conn.number_of_elements(); i += 2) -// { -// new_conn.push_back(conn[i]); -// new_field1.push_back(field1[i]); -// new_field2.push_back(field2[i]); - -// new_xcoords.push_back(xcoords[conn[i]]); -// new_ycoords.push_back(ycoords[conn[i]]); -// new_zcoords.push_back(zcoords[conn[i]]); -// } -// save_mesh["topologies"]["mesh"]["elements"]["connectivity"].reset(); -// save_mesh["topologies"]["mesh"]["elements"]["connectivity"].set(new_conn); - -// save_mesh["fields"].remove_child("vel"); -// save_mesh["fields"]["braid"]["values"].reset(); -// save_mesh["fields"]["braid"]["values"].set(new_field1); -// save_mesh["fields"]["radial"]["values"].reset(); -// save_mesh["fields"]["radial"]["values"].set(new_field2); - -// // we have modified braid such that it only uses half of the points in the coordset - -// const std::string basename = "silo_unstructured_points_braid"; -// const std::string filename = basename + ".cycle_000100.root"; - -// // remove existing root file, directory and any output files -// remove_path_if_exists(filename); - -// io::silo::save_mesh(save_mesh, basename); -// io::silo::load_mesh(filename, load_mesh); -// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - -// // now we must remove the unused points and change to an implicit points topo so that the diff passes -// save_mesh["coordsets"]["coords"]["values"]["x"].reset(); -// save_mesh["coordsets"]["coords"]["values"]["x"].set(new_xcoords); -// save_mesh["coordsets"]["coords"]["values"]["y"].reset(); -// save_mesh["coordsets"]["coords"]["values"]["y"].set(new_ycoords); -// save_mesh["coordsets"]["coords"]["values"]["z"].reset(); -// save_mesh["coordsets"]["coords"]["values"]["z"].set(new_zcoords); - -// save_mesh["topologies"].remove_child("mesh"); -// save_mesh["topologies"]["mesh"]["type"] = "points"; -// save_mesh["topologies"]["mesh"]["coordset"] = "coords"; - -// // the association doesn't matter for point meshes -// // we choose vertex by convention -// save_mesh["fields"]["radial"]["association"].reset(); -// save_mesh["fields"]["radial"]["association"] = "vertex"; - -// silo_name_changer("mesh", save_mesh); - -// // the loaded mesh will be in the multidomain format -// // but the saved mesh is in the single domain format -// EXPECT_EQ(load_mesh.number_of_children(), 1); -// EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); - -// EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); -// } - -// //----------------------------------------------------------------------------- - -// // -// // save and read option tests -// // - -// // save options: -// /// opts: -// /// -// /// file_style: "default", "root_only", "multi_file", "overlink" -// /// when # of domains == 1, "default" ==> "root_only" -// /// else, "default" ==> "multi_file" -// /// -// /// silo_type: "default", "pdb", "hdf5", "unknown" -// /// when the file we are writing to exists, "default" ==> "unknown" -// /// else, "default" ==> "hdf5" -// /// note: these are additional silo_type options that we could add -// /// support for in the future: -// /// "hdf5_sec2", "hdf5_stdio", "hdf5_mpio", "hdf5_mpiposix", "taurus" -// /// -// /// suffix: "default", "cycle", "none" -// /// when cycle is present, "default" ==> "cycle" -// /// else, "default" ==> "none" -// /// -// /// root_file_ext: "default", "root", "silo" -// /// "default" ==> "root" -// /// if overlink, this parameter is unused. -// /// -// /// mesh_name: (used if present, default ==> "mesh") -// /// -// /// ovl_topo_name: (used if present, default ==> "") -// /// -// /// number_of_files: {# of files} -// /// when "multi_file" or "overlink": -// /// <= 0, use # of files == # of domains -// /// > 0, # of files == number_of_files - -// // read options: -// /// opts: -// /// mesh_name: "{name}" -// /// provide explicit mesh name, for cases where silo data includes -// /// more than one mesh. -// /// -// /// matset_style: "default", "multi_buffer_full", "sparse_by_element", -// /// "multi_buffer_by_material" -// /// "default" ==> "sparse_by_element" - -// //----------------------------------------------------------------------------- -// TEST(conduit_relay_io_silo, round_trip_save_option_file_style) -// { -// // we will do overlink tests separately -// const std::vector file_styles = {"default", "root_only", "multi_file"}; -// for (int i = 0; i < file_styles.size(); i ++) -// { -// Node opts; -// opts["file_style"] = file_styles[i]; - -// const std::string basename = "silo_save_option_file_style_" + file_styles[i] + "_spiral"; -// const std::string filename = basename + ".cycle_000000.root"; - -// for (int ndomains = 1; ndomains < 5; ndomains += 3) -// { -// Node save_mesh, load_mesh, info; -// blueprint::mesh::examples::spiral(ndomains, save_mesh); -// remove_path_if_exists(filename); -// io::silo::save_mesh(save_mesh, basename, opts); -// io::silo::load_mesh(filename, load_mesh); -// EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); - -// // make changes to save mesh so the diff will pass -// for (index_t child = 0; child < save_mesh.number_of_children(); child ++) -// { -// silo_name_changer("mesh", save_mesh[child]); -// } - -// EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); -// NodeConstIterator l_itr = load_mesh.children(); -// NodeConstIterator s_itr = save_mesh.children(); -// while (l_itr.has_next()) -// { -// const Node &l_curr = l_itr.next(); -// const Node &s_curr = s_itr.next(); - -// EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); -// } -// } -// } -// } - -// //----------------------------------------------------------------------------- -// TEST(conduit_relay_io_silo, round_trip_save_option_number_of_files) -// { -// const std::vector number_of_files = {-1, 2}; -// for (int i = 0; i < number_of_files.size(); i ++) -// { -// Node opts; -// opts["file_style"] = "multi_file"; -// opts["number_of_files"] = number_of_files[i]; - -// const std::string basename = "silo_save_option_number_of_files_" + -// std::to_string(number_of_files[i]) + -// "_spiral"; -// const std::string filename = basename + ".cycle_000000.root"; - -// const int ndomains = 5; - -// Node save_mesh, load_mesh, info; -// blueprint::mesh::examples::spiral(ndomains, save_mesh); - -// remove_path_if_exists(filename); -// io::silo::save_mesh(save_mesh, basename, opts); -// io::silo::load_mesh(filename, load_mesh); -// EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); - -// // make changes to save mesh so the diff will pass -// for (index_t child = 0; child < save_mesh.number_of_children(); child ++) -// { -// silo_name_changer("mesh", save_mesh[child]); -// } - -// EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); -// NodeConstIterator l_itr = load_mesh.children(); -// NodeConstIterator s_itr = save_mesh.children(); -// while (l_itr.has_next()) -// { -// const Node &l_curr = l_itr.next(); -// const Node &s_curr = s_itr.next(); - -// EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); -// } -// } -// } - -// //----------------------------------------------------------------------------- -// TEST(conduit_relay_io_silo, round_trip_save_option_suffix) -// { -// const std::vector suffixes = {"default", "default", "cycle", "none"}; -// const std::vector file_suffixes = { -// "", // cycle is not present -// ".cycle_000005", // cycle is present -// ".cycle_000005", // cycle is turned on -// "", // cycle is turned off -// }; -// const std::vector include_cycle = {"no", "yes", "yes", "yes"}; -// for (int i = 0; i < suffixes.size(); i ++) -// { -// Node opts; -// opts["suffix"] = suffixes[i]; - -// const std::string basename = "silo_save_option_suffix_" + suffixes[i] + -// "_" + include_cycle[i] + "_basic"; -// const std::string filename = basename + file_suffixes[i] + ".root"; - -// Node save_mesh, load_mesh, info; -// blueprint::mesh::examples::basic("rectilinear", 3, 4, 0, save_mesh); - -// if (include_cycle[i] == "yes") -// { -// save_mesh["state/cycle"] = 5; -// } - -// remove_path_if_exists(filename); -// io::silo::save_mesh(save_mesh, basename, opts); -// io::silo::load_mesh(filename, load_mesh); -// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - -// silo_name_changer("mesh", save_mesh); - -// // the loaded mesh will be in the multidomain format -// // but the saved mesh is in the single domain format -// EXPECT_EQ(load_mesh.number_of_children(), 1); -// EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); - -// EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); -// } -// } - -// //----------------------------------------------------------------------------- -// TEST(conduit_relay_io_silo, round_trip_save_option_root_file_ext) -// { -// const std::vector root_file_exts = {"default", "root", "silo"}; - -// for (int i = 0; i < root_file_exts.size(); i ++) -// { -// Node opts; -// opts["root_file_ext"] = root_file_exts[i]; - -// std::string actual_file_ext = root_file_exts[i]; -// if (actual_file_ext == "default") -// { -// actual_file_ext = "root"; -// } - -// const std::string basename = "round_trip_save_option_root_file_ext_" + -// root_file_exts[i] + "_basic"; -// const std::string filename = basename + "." + actual_file_ext; - -// Node save_mesh, load_mesh, info; -// blueprint::mesh::examples::basic("rectilinear", 3, 4, 0, save_mesh); -// remove_path_if_exists(filename); -// io::silo::save_mesh(save_mesh, basename, opts); -// io::silo::load_mesh(filename, load_mesh); -// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - -// silo_name_changer("mesh", save_mesh); - -// // the loaded mesh will be in the multidomain format -// // but the saved mesh is in the single domain format -// EXPECT_EQ(load_mesh.number_of_children(), 1); -// EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); -// EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); -// } -// } - -// //----------------------------------------------------------------------------- -// TEST(conduit_relay_io_silo, round_trip_save_option_mesh_name) -// { -// const std::string basename = "silo_save_option_mesh_name_basic"; -// const std::string filename = basename + ".root"; - -// Node opts; -// opts["mesh_name"] = "mymesh"; - -// Node save_mesh, load_mesh, info; -// blueprint::mesh::examples::basic("rectilinear", 3, 4, 0, save_mesh); -// remove_path_if_exists(filename); -// io::silo::save_mesh(save_mesh, basename, opts); -// io::silo::load_mesh(filename, load_mesh); -// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - -// silo_name_changer("mymesh", save_mesh); - -// // the loaded mesh will be in the multidomain format -// // but the saved mesh is in the single domain format -// EXPECT_EQ(load_mesh.number_of_children(), 1); -// EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); -// EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); -// } - -// //----------------------------------------------------------------------------- -// TEST(conduit_relay_io_silo, round_trip_read_option_mesh_name) -// { -// Node load_mesh, info, opts; -// const std::string path = utils::join_file_path("silo", "multi_curv3d.silo"); -// const std::string input_file = relay_test_silo_data_path(path); - -// opts["mesh_name"] = "mesh1_dup"; - -// io::silo::load_mesh(input_file, opts, load_mesh); -// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - -// EXPECT_TRUE(load_mesh[0].has_path("topologies/mesh1_dup")); -// } - -// //----------------------------------------------------------------------------- -// TEST(conduit_relay_io_silo, round_trip_read_option_matset_style) -// { -// // the matset type and the type we are requesting on read -// const std::vector> matset_types = { -// std::make_pair("full", "full"), -// std::make_pair("sparse_by_material", "sparse_by_material"), -// std::make_pair("sparse_by_element", "sparse_by_element"), -// std::make_pair("sparse_by_element", "full"), -// std::make_pair("sparse_by_material", "sparse_by_element"), -// std::make_pair("sparse_by_material", "default"), -// }; - -// for (int i = 0; i < matset_types.size(); i ++) -// { -// std::string matset_type = matset_types[i].first; -// std::string matset_request = matset_types[i].second; - -// for (int j = 0; j < 2; j ++) -// { -// Node mesh_full, mesh_sbe, mesh_sbm, baseline_mesh, load_mesh, info; -// std::string size; -// int nx, ny; -// const double radius = 0.25; -// if (j == 0) -// { -// size = "small"; -// nx = ny = 4; -// } -// else -// { -// size = "large"; -// nx = ny = 100; -// } - -// blueprint::mesh::examples::venn("full", nx, ny, radius, mesh_full); -// blueprint::mesh::examples::venn("sparse_by_material", nx, ny, radius, mesh_sbm); -// blueprint::mesh::examples::venn("sparse_by_element", nx, ny, radius, mesh_sbe); - -// if (matset_type == "full") -// { -// baseline_mesh.set_external(mesh_full); -// } -// else if (matset_type == "sparse_by_material") -// { -// baseline_mesh.set_external(mesh_sbm); -// } -// else // (matset_type == "sparse_by_element") -// { -// baseline_mesh.set_external(mesh_sbe); -// } - -// Node opts; -// if (matset_request == "full") -// { -// opts["matset_style"] = "multi_buffer_full"; -// } -// else if (matset_request == "sparse_by_material") -// { -// opts["matset_style"] = "multi_buffer_by_material"; -// } -// else if (matset_request == "sparse_by_element") -// { -// opts["matset_style"] = "sparse_by_element"; -// } -// else -// { -// opts["matset_style"] = "default"; -// } - -// const std::string basename = "silo_venn2_" + matset_type + "_" + size; -// const std::string filename = basename + ".root"; - -// remove_path_if_exists(filename); -// io::silo::save_mesh(baseline_mesh, basename); -// io::silo::load_mesh(filename, opts, load_mesh); -// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - -// if (matset_request == "full") -// { -// baseline_mesh.set_external(mesh_full); -// } -// else if (matset_request == "sparse_by_material") -// { -// baseline_mesh.set_external(mesh_sbm); -// } -// else if (matset_request == "sparse_by_element") -// { -// baseline_mesh.set_external(mesh_sbe); -// } -// else -// { -// baseline_mesh.set_external(mesh_sbe); -// } - -// // make changes to save mesh so the diff will pass - -// // The field mat_check has values that are one type and matset_values -// // that are another type. The silo writer converts both to double arrays -// // in this case, so we follow suit. -// Node mat_check_new_values, mat_check_new_matset_values; -// baseline_mesh["fields"]["mat_check"]["values"].to_double_array(mat_check_new_values); -// if (baseline_mesh["fields"]["mat_check"]["matset_values"].dtype().is_object()) -// { -// auto mat_vals_itr = baseline_mesh["fields"]["mat_check"]["matset_values"].children(); -// while (mat_vals_itr.has_next()) -// { -// Node &mat_vals_for_mat = mat_vals_itr.next(); -// const std::string mat_name = mat_vals_itr.name(); -// mat_vals_for_mat.to_double_array(mat_check_new_matset_values[mat_name]); -// } -// } -// else -// { -// baseline_mesh["fields"]["mat_check"]["matset_values"].to_double_array(mat_check_new_matset_values); -// } -// baseline_mesh["fields"]["mat_check"]["values"].set_external(mat_check_new_values); -// baseline_mesh["fields"]["mat_check"]["matset_values"].set_external(mat_check_new_matset_values); - -// silo_name_changer("mesh", baseline_mesh); - -// // the loaded mesh will be in the multidomain format -// // but the saved mesh is in the single domain format -// EXPECT_EQ(load_mesh.number_of_children(), 1); -// EXPECT_EQ(load_mesh[0].number_of_children(), baseline_mesh.number_of_children()); -// EXPECT_FALSE(load_mesh[0].diff(baseline_mesh, info, CONDUIT_EPSILON, true)); -// } -// } -// } - -// //----------------------------------------------------------------------------- -// TEST(conduit_relay_io_silo, round_trip_save_option_silo_type) -// { -// const std::vector silo_types = {"default", "pdb", "hdf5", "unknown"}; -// for (int i = 3; i < silo_types.size(); i ++) -// { -// Node opts; -// opts["silo_type"] = silo_types[i]; - -// Node save_mesh, load_mesh, info; -// blueprint::mesh::examples::basic("rectilinear", 3, 4, 0, save_mesh); - -// const std::string basename = "silo_save_option_silo_type_" + silo_types[i] + "_basic"; -// const std::string filename = basename + ".root"; - -// remove_path_if_exists(filename); -// io::silo::save_mesh(save_mesh, basename, opts); -// io::silo::load_mesh(filename, load_mesh); -// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + opts["mesh_name"] = "mesh_topo2"; + io::silo::load_mesh(filename, opts, load_mesh); + opts["mesh_name"] = "mesh_topo"; + io::silo::load_mesh(filename, opts, load_mesh2); + EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + EXPECT_TRUE(blueprint::mesh::verify(load_mesh2, info)); + + // make changes to save mesh so the diff will pass + save_mesh[2]["coordsets"].remove_child("coords"); + save_mesh[2]["fields"].remove_child("dist"); + for (index_t child = 0; child < save_mesh.number_of_children(); child ++) + { + silo_name_changer("mesh", save_mesh[child]); + } + + // we must merge the two meshes in load mesh + // the indexing is tricky because one is missing a domain + load_mesh[0]["coordsets"]["mesh_topo"].set_external(load_mesh2[0]["coordsets"]["mesh_topo"]); + load_mesh[0]["topologies"]["mesh_topo"].set_external(load_mesh2[0]["topologies"]["mesh_topo"]); + load_mesh[0]["fields"]["mesh_dist"].set_external(load_mesh2[0]["fields"]["mesh_dist"]); + load_mesh[1]["coordsets"]["mesh_topo"].set_external(load_mesh2[1]["coordsets"]["mesh_topo"]); + load_mesh[1]["topologies"]["mesh_topo"].set_external(load_mesh2[1]["topologies"]["mesh_topo"]); + load_mesh[1]["fields"]["mesh_dist"].set_external(load_mesh2[1]["fields"]["mesh_dist"]); + load_mesh[3]["coordsets"]["mesh_topo"].set_external(load_mesh2[2]["coordsets"]["mesh_topo"]); + load_mesh[3]["topologies"]["mesh_topo"].set_external(load_mesh2[2]["topologies"]["mesh_topo"]); + load_mesh[3]["fields"]["mesh_dist"].set_external(load_mesh2[2]["fields"]["mesh_dist"]); + + EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); + NodeConstIterator l_itr = load_mesh.children(); + NodeConstIterator s_itr = save_mesh.children(); + while (l_itr.has_next()) + { + const Node &l_curr = l_itr.next(); + const Node &s_curr = s_itr.next(); + + EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); + } +} + +//----------------------------------------------------------------------------- +// explicit points (unstructured mesh) do not use every coord +TEST(conduit_relay_io_silo, unstructured_points) +{ + Node save_mesh, load_mesh, info; + blueprint::mesh::examples::braid("points", 2, 2, 2, save_mesh); + + std::vector new_conn; + std::vector new_field1; + std::vector new_field2; + std::vector new_xcoords, new_ycoords, new_zcoords; + + int_accessor conn = save_mesh["topologies"]["mesh"]["elements"]["connectivity"].value(); + + float_accessor field1 = save_mesh["fields"]["braid"]["values"].value(); + float_accessor field2 = save_mesh["fields"]["radial"]["values"].value(); + + float_accessor xcoords = save_mesh["coordsets"]["coords"]["values"]["x"].value(); + float_accessor ycoords = save_mesh["coordsets"]["coords"]["values"]["y"].value(); + float_accessor zcoords = save_mesh["coordsets"]["coords"]["values"]["z"].value(); + + for (int i = 1; i < conn.number_of_elements(); i += 2) + { + new_conn.push_back(conn[i]); + new_field1.push_back(field1[i]); + new_field2.push_back(field2[i]); + + new_xcoords.push_back(xcoords[conn[i]]); + new_ycoords.push_back(ycoords[conn[i]]); + new_zcoords.push_back(zcoords[conn[i]]); + } + save_mesh["topologies"]["mesh"]["elements"]["connectivity"].reset(); + save_mesh["topologies"]["mesh"]["elements"]["connectivity"].set(new_conn); + + save_mesh["fields"].remove_child("vel"); + save_mesh["fields"]["braid"]["values"].reset(); + save_mesh["fields"]["braid"]["values"].set(new_field1); + save_mesh["fields"]["radial"]["values"].reset(); + save_mesh["fields"]["radial"]["values"].set(new_field2); + + // we have modified braid such that it only uses half of the points in the coordset + + const std::string basename = "silo_unstructured_points_braid"; + const std::string filename = basename + ".cycle_000100.root"; + + // remove existing root file, directory and any output files + remove_path_if_exists(filename); + + io::silo::save_mesh(save_mesh, basename); + io::silo::load_mesh(filename, load_mesh); + EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + + // now we must remove the unused points and change to an implicit points topo so that the diff passes + save_mesh["coordsets"]["coords"]["values"]["x"].reset(); + save_mesh["coordsets"]["coords"]["values"]["x"].set(new_xcoords); + save_mesh["coordsets"]["coords"]["values"]["y"].reset(); + save_mesh["coordsets"]["coords"]["values"]["y"].set(new_ycoords); + save_mesh["coordsets"]["coords"]["values"]["z"].reset(); + save_mesh["coordsets"]["coords"]["values"]["z"].set(new_zcoords); + + save_mesh["topologies"].remove_child("mesh"); + save_mesh["topologies"]["mesh"]["type"] = "points"; + save_mesh["topologies"]["mesh"]["coordset"] = "coords"; + + // the association doesn't matter for point meshes + // we choose vertex by convention + save_mesh["fields"]["radial"]["association"].reset(); + save_mesh["fields"]["radial"]["association"] = "vertex"; + + silo_name_changer("mesh", save_mesh); + + // the loaded mesh will be in the multidomain format + // but the saved mesh is in the single domain format + EXPECT_EQ(load_mesh.number_of_children(), 1); + EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); + + EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); +} + +//----------------------------------------------------------------------------- + +// +// save and read option tests +// + +// save options: +/// opts: +/// +/// file_style: "default", "root_only", "multi_file", "overlink" +/// when # of domains == 1, "default" ==> "root_only" +/// else, "default" ==> "multi_file" +/// +/// silo_type: "default", "pdb", "hdf5", "unknown" +/// when the file we are writing to exists, "default" ==> "unknown" +/// else, "default" ==> "hdf5" +/// note: these are additional silo_type options that we could add +/// support for in the future: +/// "hdf5_sec2", "hdf5_stdio", "hdf5_mpio", "hdf5_mpiposix", "taurus" +/// +/// suffix: "default", "cycle", "none" +/// when cycle is present, "default" ==> "cycle" +/// else, "default" ==> "none" +/// +/// root_file_ext: "default", "root", "silo" +/// "default" ==> "root" +/// if overlink, this parameter is unused. +/// +/// mesh_name: (used if present, default ==> "mesh") +/// +/// ovl_topo_name: (used if present, default ==> "") +/// +/// number_of_files: {# of files} +/// when "multi_file" or "overlink": +/// <= 0, use # of files == # of domains +/// > 0, # of files == number_of_files + +// read options: +/// opts: +/// mesh_name: "{name}" +/// provide explicit mesh name, for cases where silo data includes +/// more than one mesh. +/// +/// matset_style: "default", "multi_buffer_full", "sparse_by_element", +/// "multi_buffer_by_material" +/// "default" ==> "sparse_by_element" + +//----------------------------------------------------------------------------- +TEST(conduit_relay_io_silo, round_trip_save_option_file_style) +{ + // we will do overlink tests separately + const std::vector file_styles = {"default", "root_only", "multi_file"}; + for (int i = 0; i < file_styles.size(); i ++) + { + Node opts; + opts["file_style"] = file_styles[i]; + + const std::string basename = "silo_save_option_file_style_" + file_styles[i] + "_spiral"; + const std::string filename = basename + ".cycle_000000.root"; + + for (int ndomains = 1; ndomains < 5; ndomains += 3) + { + Node save_mesh, load_mesh, info; + blueprint::mesh::examples::spiral(ndomains, save_mesh); + remove_path_if_exists(filename); + io::silo::save_mesh(save_mesh, basename, opts); + io::silo::load_mesh(filename, load_mesh); + EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); + + // make changes to save mesh so the diff will pass + for (index_t child = 0; child < save_mesh.number_of_children(); child ++) + { + silo_name_changer("mesh", save_mesh[child]); + } + + EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); + NodeConstIterator l_itr = load_mesh.children(); + NodeConstIterator s_itr = save_mesh.children(); + while (l_itr.has_next()) + { + const Node &l_curr = l_itr.next(); + const Node &s_curr = s_itr.next(); + + EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); + } + } + } +} + +//----------------------------------------------------------------------------- +TEST(conduit_relay_io_silo, round_trip_save_option_number_of_files) +{ + const std::vector number_of_files = {-1, 2}; + for (int i = 0; i < number_of_files.size(); i ++) + { + Node opts; + opts["file_style"] = "multi_file"; + opts["number_of_files"] = number_of_files[i]; + + const std::string basename = "silo_save_option_number_of_files_" + + std::to_string(number_of_files[i]) + + "_spiral"; + const std::string filename = basename + ".cycle_000000.root"; + + const int ndomains = 5; + + Node save_mesh, load_mesh, info; + blueprint::mesh::examples::spiral(ndomains, save_mesh); + + remove_path_if_exists(filename); + io::silo::save_mesh(save_mesh, basename, opts); + io::silo::load_mesh(filename, load_mesh); + EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); + + // make changes to save mesh so the diff will pass + for (index_t child = 0; child < save_mesh.number_of_children(); child ++) + { + silo_name_changer("mesh", save_mesh[child]); + } + + EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); + NodeConstIterator l_itr = load_mesh.children(); + NodeConstIterator s_itr = save_mesh.children(); + while (l_itr.has_next()) + { + const Node &l_curr = l_itr.next(); + const Node &s_curr = s_itr.next(); + + EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); + } + } +} + +//----------------------------------------------------------------------------- +TEST(conduit_relay_io_silo, round_trip_save_option_suffix) +{ + const std::vector suffixes = {"default", "default", "cycle", "none"}; + const std::vector file_suffixes = { + "", // cycle is not present + ".cycle_000005", // cycle is present + ".cycle_000005", // cycle is turned on + "", // cycle is turned off + }; + const std::vector include_cycle = {"no", "yes", "yes", "yes"}; + for (int i = 0; i < suffixes.size(); i ++) + { + Node opts; + opts["suffix"] = suffixes[i]; + + const std::string basename = "silo_save_option_suffix_" + suffixes[i] + + "_" + include_cycle[i] + "_basic"; + const std::string filename = basename + file_suffixes[i] + ".root"; + + Node save_mesh, load_mesh, info; + blueprint::mesh::examples::basic("rectilinear", 3, 4, 0, save_mesh); + + if (include_cycle[i] == "yes") + { + save_mesh["state/cycle"] = 5; + } + + remove_path_if_exists(filename); + io::silo::save_mesh(save_mesh, basename, opts); + io::silo::load_mesh(filename, load_mesh); + EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + + silo_name_changer("mesh", save_mesh); + + // the loaded mesh will be in the multidomain format + // but the saved mesh is in the single domain format + EXPECT_EQ(load_mesh.number_of_children(), 1); + EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); + + EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); + } +} + +//----------------------------------------------------------------------------- +TEST(conduit_relay_io_silo, round_trip_save_option_root_file_ext) +{ + const std::vector root_file_exts = {"default", "root", "silo"}; + + for (int i = 0; i < root_file_exts.size(); i ++) + { + Node opts; + opts["root_file_ext"] = root_file_exts[i]; + + std::string actual_file_ext = root_file_exts[i]; + if (actual_file_ext == "default") + { + actual_file_ext = "root"; + } + + const std::string basename = "round_trip_save_option_root_file_ext_" + + root_file_exts[i] + "_basic"; + const std::string filename = basename + "." + actual_file_ext; + + Node save_mesh, load_mesh, info; + blueprint::mesh::examples::basic("rectilinear", 3, 4, 0, save_mesh); + remove_path_if_exists(filename); + io::silo::save_mesh(save_mesh, basename, opts); + io::silo::load_mesh(filename, load_mesh); + EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + + silo_name_changer("mesh", save_mesh); + + // the loaded mesh will be in the multidomain format + // but the saved mesh is in the single domain format + EXPECT_EQ(load_mesh.number_of_children(), 1); + EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); + EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); + } +} + +//----------------------------------------------------------------------------- +TEST(conduit_relay_io_silo, round_trip_save_option_mesh_name) +{ + const std::string basename = "silo_save_option_mesh_name_basic"; + const std::string filename = basename + ".root"; + + Node opts; + opts["mesh_name"] = "mymesh"; + + Node save_mesh, load_mesh, info; + blueprint::mesh::examples::basic("rectilinear", 3, 4, 0, save_mesh); + remove_path_if_exists(filename); + io::silo::save_mesh(save_mesh, basename, opts); + io::silo::load_mesh(filename, load_mesh); + EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + + silo_name_changer("mymesh", save_mesh); + + // the loaded mesh will be in the multidomain format + // but the saved mesh is in the single domain format + EXPECT_EQ(load_mesh.number_of_children(), 1); + EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); + EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); +} + +//----------------------------------------------------------------------------- +TEST(conduit_relay_io_silo, round_trip_read_option_mesh_name) +{ + Node load_mesh, info, opts; + const std::string path = utils::join_file_path("silo", "multi_curv3d.silo"); + const std::string input_file = relay_test_silo_data_path(path); + + opts["mesh_name"] = "mesh1_dup"; + + io::silo::load_mesh(input_file, opts, load_mesh); + EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + + EXPECT_TRUE(load_mesh[0].has_path("topologies/mesh1_dup")); +} + +//----------------------------------------------------------------------------- +TEST(conduit_relay_io_silo, round_trip_read_option_matset_style) +{ + // the matset type and the type we are requesting on read + const std::vector> matset_types = { + std::make_pair("full", "full"), + std::make_pair("sparse_by_material", "sparse_by_material"), + std::make_pair("sparse_by_element", "sparse_by_element"), + std::make_pair("sparse_by_element", "full"), + std::make_pair("sparse_by_material", "sparse_by_element"), + std::make_pair("sparse_by_material", "default"), + }; + + for (int i = 0; i < matset_types.size(); i ++) + { + std::string matset_type = matset_types[i].first; + std::string matset_request = matset_types[i].second; + + for (int j = 0; j < 2; j ++) + { + Node mesh_full, mesh_sbe, mesh_sbm, baseline_mesh, load_mesh, info; + std::string size; + int nx, ny; + const double radius = 0.25; + if (j == 0) + { + size = "small"; + nx = ny = 4; + } + else + { + size = "large"; + nx = ny = 100; + } + + blueprint::mesh::examples::venn("full", nx, ny, radius, mesh_full); + blueprint::mesh::examples::venn("sparse_by_material", nx, ny, radius, mesh_sbm); + blueprint::mesh::examples::venn("sparse_by_element", nx, ny, radius, mesh_sbe); + + if (matset_type == "full") + { + baseline_mesh.set_external(mesh_full); + } + else if (matset_type == "sparse_by_material") + { + baseline_mesh.set_external(mesh_sbm); + } + else // (matset_type == "sparse_by_element") + { + baseline_mesh.set_external(mesh_sbe); + } + + Node opts; + if (matset_request == "full") + { + opts["matset_style"] = "multi_buffer_full"; + } + else if (matset_request == "sparse_by_material") + { + opts["matset_style"] = "multi_buffer_by_material"; + } + else if (matset_request == "sparse_by_element") + { + opts["matset_style"] = "sparse_by_element"; + } + else + { + opts["matset_style"] = "default"; + } + + const std::string basename = "silo_venn2_" + matset_type + "_" + size; + const std::string filename = basename + ".root"; + + remove_path_if_exists(filename); + io::silo::save_mesh(baseline_mesh, basename); + io::silo::load_mesh(filename, opts, load_mesh); + EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + + if (matset_request == "full") + { + baseline_mesh.set_external(mesh_full); + } + else if (matset_request == "sparse_by_material") + { + baseline_mesh.set_external(mesh_sbm); + } + else if (matset_request == "sparse_by_element") + { + baseline_mesh.set_external(mesh_sbe); + } + else + { + baseline_mesh.set_external(mesh_sbe); + } + + // make changes to save mesh so the diff will pass + + // The field mat_check has values that are one type and matset_values + // that are another type. The silo writer converts both to double arrays + // in this case, so we follow suit. + Node mat_check_new_values, mat_check_new_matset_values; + baseline_mesh["fields"]["mat_check"]["values"].to_double_array(mat_check_new_values); + if (baseline_mesh["fields"]["mat_check"]["matset_values"].dtype().is_object()) + { + auto mat_vals_itr = baseline_mesh["fields"]["mat_check"]["matset_values"].children(); + while (mat_vals_itr.has_next()) + { + Node &mat_vals_for_mat = mat_vals_itr.next(); + const std::string mat_name = mat_vals_itr.name(); + mat_vals_for_mat.to_double_array(mat_check_new_matset_values[mat_name]); + } + } + else + { + baseline_mesh["fields"]["mat_check"]["matset_values"].to_double_array(mat_check_new_matset_values); + } + baseline_mesh["fields"]["mat_check"]["values"].set_external(mat_check_new_values); + baseline_mesh["fields"]["mat_check"]["matset_values"].set_external(mat_check_new_matset_values); + + silo_name_changer("mesh", baseline_mesh); + + // the loaded mesh will be in the multidomain format + // but the saved mesh is in the single domain format + EXPECT_EQ(load_mesh.number_of_children(), 1); + EXPECT_EQ(load_mesh[0].number_of_children(), baseline_mesh.number_of_children()); + EXPECT_FALSE(load_mesh[0].diff(baseline_mesh, info, CONDUIT_EPSILON, true)); + } + } +} + +//----------------------------------------------------------------------------- +TEST(conduit_relay_io_silo, round_trip_save_option_silo_type) +{ + const std::vector silo_types = {"default", "pdb", "hdf5", "unknown"}; + for (int i = 3; i < silo_types.size(); i ++) + { + Node opts; + opts["silo_type"] = silo_types[i]; + + Node save_mesh, load_mesh, info; + blueprint::mesh::examples::basic("rectilinear", 3, 4, 0, save_mesh); + + const std::string basename = "silo_save_option_silo_type_" + silo_types[i] + "_basic"; + const std::string filename = basename + ".root"; + + remove_path_if_exists(filename); + io::silo::save_mesh(save_mesh, basename, opts); + io::silo::load_mesh(filename, load_mesh); + EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); -// silo_name_changer("mesh", save_mesh); - -// // the loaded mesh will be in the multidomain format -// // but the saved mesh is in the single domain format -// EXPECT_EQ(load_mesh.number_of_children(), 1); -// EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); - -// EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); -// } -// } - -// //----------------------------------------------------------------------------- -// TEST(conduit_relay_io_silo, round_trip_save_option_overlink1) -// { -// const std::vector ovl_topo_names = {"", "topo"}; -// for (int i = 0; i < ovl_topo_names.size(); i ++) -// { -// std::string basename; -// if (ovl_topo_names[i].empty()) -// { -// basename = "silo_save_option_overlink_spiral"; -// } -// else -// { -// basename = "silo_save_option_overlink_spiral_" + ovl_topo_names[i]; -// } -// const std::string filename = basename + "/OvlTop.silo"; - -// Node opts; -// opts["file_style"] = "overlink"; -// opts["ovl_topo_name"] = ovl_topo_names[i]; - -// int ndomains = 2; - -// Node save_mesh, load_mesh, info; -// blueprint::mesh::examples::spiral(ndomains, save_mesh); -// add_matset_to_spiral(save_mesh, ndomains); -// remove_path_if_exists(filename); -// io::silo::save_mesh(save_mesh, basename, opts); -// io::silo::load_mesh(filename, load_mesh); -// EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); - -// // make changes to save mesh so the diff will pass -// for (index_t child = 0; child < save_mesh.number_of_children(); child ++) -// { -// // get the matset for this domain -// Node &n_matset = save_mesh[child]["matsets"]["matset"]; - -// // clean up volume fractions -// Node vf_arr; -// n_matset["volume_fractions"].to_float64_array(vf_arr); -// n_matset["volume_fractions"].reset(); -// n_matset["volume_fractions"].set(vf_arr); + silo_name_changer("mesh", save_mesh); + + // the loaded mesh will be in the multidomain format + // but the saved mesh is in the single domain format + EXPECT_EQ(load_mesh.number_of_children(), 1); + EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); + + EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); + } +} + +//----------------------------------------------------------------------------- +TEST(conduit_relay_io_silo, round_trip_save_option_overlink1) +{ + const std::vector ovl_topo_names = {"", "topo"}; + for (int i = 0; i < ovl_topo_names.size(); i ++) + { + std::string basename; + if (ovl_topo_names[i].empty()) + { + basename = "silo_save_option_overlink_spiral"; + } + else + { + basename = "silo_save_option_overlink_spiral_" + ovl_topo_names[i]; + } + const std::string filename = basename + "/OvlTop.silo"; + + Node opts; + opts["file_style"] = "overlink"; + opts["ovl_topo_name"] = ovl_topo_names[i]; + + int ndomains = 2; + + Node save_mesh, load_mesh, info; + blueprint::mesh::examples::spiral(ndomains, save_mesh); + add_matset_to_spiral(save_mesh, ndomains); + remove_path_if_exists(filename); + io::silo::save_mesh(save_mesh, basename, opts); + io::silo::load_mesh(filename, load_mesh); + EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); + + // make changes to save mesh so the diff will pass + for (index_t child = 0; child < save_mesh.number_of_children(); child ++) + { + // get the matset for this domain + Node &n_matset = save_mesh[child]["matsets"]["matset"]; + + // clean up volume fractions + Node vf_arr; + n_matset["volume_fractions"].to_float64_array(vf_arr); + n_matset["volume_fractions"].reset(); + n_matset["volume_fractions"].set(vf_arr); -// // cheat a little bit - we don't have these to start -// n_matset["sizes"].set_external(load_mesh[child]["matsets"]["MMATERIAL"]["sizes"]); -// n_matset["offsets"].set_external(load_mesh[child]["matsets"]["MMATERIAL"]["offsets"]); - -// overlink_name_changer(save_mesh[child]); -// } - -// EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); -// NodeConstIterator l_itr = load_mesh.children(); -// NodeConstIterator s_itr = save_mesh.children(); -// while (l_itr.has_next()) -// { -// const Node &l_curr = l_itr.next(); -// const Node &s_curr = s_itr.next(); - -// EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); -// } -// } -// } - -// //----------------------------------------------------------------------------- -// // this tests var attributes and padding dimensions -// TEST(conduit_relay_io_silo, round_trip_save_option_overlink2) -// { -// const std::string basename = "silo_save_option_overlink_basic"; -// const std::string filename = basename + "/OvlTop.silo"; - -// Node write_opts, read_opts; -// write_opts["file_style"] = "overlink"; -// read_opts["matset_style"] = "multi_buffer_full"; - -// Node save_mesh, load_mesh, info; -// blueprint::mesh::examples::basic("structured", 3, 3, 1, save_mesh); - -// // add another field that is volume dependent -// Node &field2 = save_mesh["fields"]["field2"]; -// field2["association"] = "element"; -// field2["topology"] = "mesh"; -// field2["volume_dependent"] = "true"; -// field2["values"].set_external(save_mesh["fields"]["field"]["values"]); - -// // add a matset to make overlink happy -// add_multi_buffer_full_matset(save_mesh, 4, "mesh"); - -// remove_path_if_exists(filename); -// io::silo::save_mesh(save_mesh, basename, write_opts); -// io::silo::load_mesh(filename, read_opts, load_mesh); -// EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); - -// // make changes to save mesh so the diff will pass -// overlink_name_changer(save_mesh); - -// // the loaded mesh will be in the multidomain format -// // but the saved mesh is in the single domain format -// EXPECT_EQ(load_mesh.number_of_children(), 1); -// EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); - -// EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); - -// // open silo files and do some checks - -// DBfile *rootfile = DBOpen(filename.c_str(), DB_UNKNOWN, DB_READ); -// EXPECT_TRUE(DBInqVarExists(rootfile, "VAR_ATTRIBUTES")); -// EXPECT_TRUE(DBInqVarType(rootfile, "VAR_ATTRIBUTES") == DB_ARRAY); - -// DBcompoundarray *var_attr = DBGetCompoundarray(rootfile, "VAR_ATTRIBUTES"); - -// // fetch pointers to elements inside the compound array -// char **elemnames = var_attr->elemnames; -// int *elemlengths = var_attr->elemlengths; -// int nelems = var_attr->nelems; -// int *values = static_cast(var_attr->values); -// int nvalues = var_attr->nvalues; -// int datatype = var_attr->datatype; - -// EXPECT_EQ(std::string(elemnames[0]), "field"); -// EXPECT_EQ(std::string(elemnames[1]), "field2"); -// EXPECT_EQ(elemlengths[0], 5); -// EXPECT_EQ(elemlengths[1], 5); -// EXPECT_EQ(nelems, 2); -// // for first var -// EXPECT_EQ(values[0], 1); -// EXPECT_EQ(values[1], 0); -// EXPECT_EQ(values[2], 1); -// EXPECT_EQ(values[3], 0); -// EXPECT_EQ(values[4], 1); -// // for second var -// EXPECT_EQ(values[5], 1); -// EXPECT_EQ(values[6], 1); -// EXPECT_EQ(values[7], 1); -// EXPECT_EQ(values[8], 0); -// EXPECT_EQ(values[9], 1); -// EXPECT_EQ(nvalues, 10); -// EXPECT_EQ(datatype, DB_INT); - -// DBFreeCompoundarray(var_attr); - -// EXPECT_TRUE(DBInqVarExists(rootfile, "PAD_DIMS")); -// EXPECT_TRUE(DBInqVarType(rootfile, "PAD_DIMS") == DB_ARRAY); - -// DBcompoundarray *pad_dims = DBGetCompoundarray(rootfile, "PAD_DIMS"); - -// // fetch pointers to elements inside the compound array -// elemnames = pad_dims->elemnames; -// elemlengths = pad_dims->elemlengths; -// nelems = pad_dims->nelems; -// values = static_cast(pad_dims->values); -// nvalues = pad_dims->nvalues; -// datatype = pad_dims->datatype; - -// EXPECT_EQ(std::string(elemnames[0]), "paddims"); -// EXPECT_EQ(elemlengths[0], 6); -// EXPECT_EQ(nelems, 1); -// EXPECT_EQ(values[0], 0); -// EXPECT_EQ(values[1], 0); -// EXPECT_EQ(values[2], 0); -// EXPECT_EQ(values[3], 0); -// EXPECT_EQ(values[4], 0); -// EXPECT_EQ(values[5], 0); -// EXPECT_EQ(nvalues, 6); -// EXPECT_EQ(datatype, DB_INT); - -// DBFreeCompoundarray(pad_dims); - -// DBClose(rootfile); - -// // now check domain file - -// const std::string dom_filename = basename + "/domain0.silo"; -// DBfile *domfile = DBOpen(dom_filename.c_str(), DB_UNKNOWN, DB_READ); - -// EXPECT_TRUE(DBInqVarExists(domfile, "DOMAIN_NEIGHBOR_NUMS")); -// EXPECT_TRUE(DBInqVarType(domfile, "DOMAIN_NEIGHBOR_NUMS") == DB_ARRAY); - -// DBcompoundarray *dom_neighbor_nums = DBGetCompoundarray(domfile, "DOMAIN_NEIGHBOR_NUMS"); - -// // fetch pointers to elements inside the compound array -// elemnames = dom_neighbor_nums->elemnames; -// elemlengths = dom_neighbor_nums->elemlengths; -// nelems = dom_neighbor_nums->nelems; -// values = static_cast(dom_neighbor_nums->values); -// nvalues = dom_neighbor_nums->nvalues; -// datatype = dom_neighbor_nums->datatype; - -// EXPECT_EQ(std::string(elemnames[0]), "num_neighbors"); -// EXPECT_EQ(std::string(elemnames[1]), "neighbor_nums"); -// EXPECT_EQ(elemlengths[0], 1); -// EXPECT_EQ(elemlengths[1], 0); -// EXPECT_EQ(nelems, 2); -// EXPECT_EQ(values[0], 0); -// EXPECT_EQ(nvalues, 1); -// EXPECT_EQ(datatype, DB_INT); - -// DBFreeCompoundarray(dom_neighbor_nums); - -// DBClose(domfile); -// } - -// //----------------------------------------------------------------------------- -// // this tests material i/o -// TEST(conduit_relay_io_silo, round_trip_save_option_overlink3) -// { -// Node save_mesh, load_mesh, info; -// const int nx = 100, ny = 100; -// const double radius = 0.25; -// blueprint::mesh::examples::venn("sparse_by_element", nx, ny, radius, save_mesh); - -// const std::string basename = "silo_save_option_overlink_venn"; -// const std::string filename = basename + "/OvlTop.silo"; - -// Node opts; -// opts["file_style"] = "overlink"; - -// remove_path_if_exists(filename); -// io::silo::save_mesh(save_mesh, basename, opts); -// io::silo::load_mesh(filename, load_mesh); -// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - -// // make changes to save mesh so the diff will pass - -// // The field mat_check has values that are one type and matset_values -// // that are another type. The silo writer converts both to double arrays -// // in this case, so we follow suit. -// Node mat_check_new_values, mat_check_new_matset_values; -// save_mesh["fields"]["mat_check"]["values"].to_double_array(mat_check_new_values); -// save_mesh["fields"]["mat_check"]["matset_values"].to_double_array(mat_check_new_matset_values); -// save_mesh["fields"]["mat_check"]["values"].set_external(mat_check_new_values); -// save_mesh["fields"]["mat_check"]["matset_values"].set_external(mat_check_new_matset_values); - -// overlink_name_changer(save_mesh); - -// // the loaded mesh will be in the multidomain format -// // but the saved mesh is in the single domain format -// EXPECT_EQ(load_mesh.number_of_children(), 1); -// EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); - -// EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); -// } - -// //----------------------------------------------------------------------------- -// // we are testing vector fields get converted to scalars -// TEST(conduit_relay_io_silo, round_trip_save_option_overlink4) -// { -// const std::vector> mesh_types = { -// std::make_pair("rectilinear", "2"), std::make_pair("rectilinear", "3"), -// std::make_pair("structured", "2"), std::make_pair("structured", "3"), -// std::make_pair("quads", "2"), -// std::make_pair("hexs", "3"), -// }; -// for (int i = 0; i < mesh_types.size(); ++i) -// { -// std::string dim = mesh_types[i].second; -// index_t nx = 3; -// index_t ny = 4; -// index_t nz = (dim == "2" ? 0 : 2); - -// std::string mesh_type = mesh_types[i].first; - -// Node save_mesh, load_mesh, info; -// blueprint::mesh::examples::braid(mesh_type, nx, ny, nz, save_mesh); -// index_t nele_x = nx - 1; -// index_t nele_y = ny - 1; -// index_t nele_z = (dim == "2" ? 0 : nz - 1); - -// // provide a matset for braid -// braid_init_example_matset(nele_x, nele_y, nele_z, save_mesh["matsets"]["matset"]); - -// const std::string basename = "silo_save_option_overlink_braid_" + mesh_type + "_" + dim + "D"; -// const std::string filename = basename + "/OvlTop.silo"; - -// Node write_opts, read_opts; -// write_opts["file_style"] = "overlink"; -// read_opts["matset_style"] = "multi_buffer_full"; - -// // remove existing root file, directory and any output files -// remove_path_if_exists(filename); -// io::silo::save_mesh(save_mesh, basename, write_opts); -// io::silo::load_mesh(filename, read_opts, load_mesh); -// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - -// Node &field_vel = save_mesh["fields"]["vel"]; -// Node &field_vel_u = save_mesh["fields"]["vel_u"]; -// Node &field_vel_v = save_mesh["fields"]["vel_v"]; - -// field_vel_u["topology"].set(field_vel["topology"]); -// field_vel_u["association"].set(field_vel["association"]); -// field_vel_u["values"].set(field_vel["values/u"]); -// field_vel_v["topology"].set(field_vel["topology"]); -// field_vel_v["association"].set(field_vel["association"]); -// field_vel_v["values"].set(field_vel["values/v"]); - -// if (dim == "3") -// { -// Node &field_vel_w = save_mesh["fields"]["vel_w"]; -// field_vel_w["topology"].set(field_vel["topology"]); -// field_vel_w["association"].set(field_vel["association"]); -// field_vel_w["values"].set(field_vel["values/w"]); -// } - -// save_mesh["fields"].remove_child("vel"); - -// // make changes to save mesh so the diff will pass -// overlink_name_changer(save_mesh); - -// // the loaded mesh will be in the multidomain format -// // but the saved mesh is in the single domain format -// EXPECT_EQ(load_mesh.number_of_children(), 1); -// EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); - -// EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); -// } -// } - -// //----------------------------------------------------------------------------- -// // check that all the shape types work (specifically polytopal ones) -// TEST(conduit_relay_io_silo, round_trip_save_option_overlink5) -// { -// const std::vector> mesh_types = { -// std::make_pair("uniform", "2"), std::make_pair("uniform", "3"), -// std::make_pair("rectilinear", "2"), std::make_pair("rectilinear", "3"), -// std::make_pair("structured", "2"), std::make_pair("structured", "3"), -// std::make_pair("quads", "2"), -// std::make_pair("polygons", "2"), -// std::make_pair("hexs", "3"), -// // std::make_pair("polyhedra", "3") -// // Overlink does not support tris, wedges, pyramids, or tets -// }; -// for (int i = 0; i < mesh_types.size(); ++i) -// { -// const std::string dim = mesh_types[i].second; -// index_t nx = 3; -// index_t ny = 4; -// index_t nz = (dim == "2" ? 0 : 2); - -// const std::string mesh_type = mesh_types[i].first; - -// Node save_mesh, load_mesh, info; -// blueprint::mesh::examples::basic(mesh_type, nx, ny, nz, save_mesh); - -// const std::string basename = "silo_save_option_overlink_basic_" + mesh_type + "_" + dim + "D"; -// const std::string filename = basename + "/OvlTop.silo"; -// const std::string domfile = basename + "/domain0.silo"; - -// Node write_opts, read_opts; -// write_opts["file_style"] = "overlink"; -// read_opts["matset_style"] = "multi_buffer_full"; - -// // add a matset to make overlink happy -// int num_elems = (nx - 1) * (ny - 1); -// if (mesh_type == "tets") -// { -// num_elems *= 6; -// } -// add_multi_buffer_full_matset(save_mesh, num_elems, "mesh"); - -// remove_path_if_exists(filename); -// remove_path_if_exists(domfile); -// io::silo::save_mesh(save_mesh, basename, write_opts); -// io::silo::load_mesh(filename, read_opts, load_mesh); -// EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); - -// // make changes to save mesh so the diff will pass -// if (mesh_type == "uniform") -// { -// silo_uniform_to_rect_conversion("coords", "mesh", save_mesh); -// } -// overlink_name_changer(save_mesh); - -// // the loaded mesh will be in the multidomain format -// // but the saved mesh is in the single domain format -// EXPECT_EQ(load_mesh.number_of_children(), 1); -// EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); - -// EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); -// } -// } - -// //----------------------------------------------------------------------------- - -// // -// // read and write Silo and Overlink tests -// // - -// //----------------------------------------------------------------------------- -// // read normal silo files containing multimeshes, multivars, and multimats -// TEST(conduit_relay_io_silo, read_silo) -// { -// const std::vector> file_info = { -// {".", "multi_curv3d", ".silo", "" }, // test default case -// {".", "multi_curv3d", ".silo", "mesh1" }, -// // {".", "multi_curv3d", ".silo", "mesh1_back" }, // this multimesh points to paths that do not exist -// {".", "multi_curv3d", ".silo", "mesh1_dup" }, -// // {".", "multi_curv3d", ".silo", "mesh1_front" }, // same here -// {".", "multi_curv3d", ".silo", "mesh1_hidden"}, -// {".", "tire", ".silo", "" }, // test default case -// {".", "tire", ".silo", "tire" }, -// {".", "galaxy0000", ".silo", "" }, // test default case -// {".", "galaxy0000", ".silo", "StarMesh" }, -// {".", "emptydomains", ".silo", "" }, // test default case -// {".", "emptydomains", ".silo", "mesh" }, -// {"multidir_test_data", "multidir0000", ".root", "" }, // test default case -// {"multidir_test_data", "multidir0000", ".root", "Mesh" }, -// }; - -// // TODO what to do in the case where a multimesh points to no data? (mesh1_back) -// // fail silently, as we do now? - -// for (int i = 0; i < file_info.size(); i ++) -// { -// const std::string dirname = file_info[i][0]; -// const std::string basename = file_info[i][1]; -// const std::string fileext = file_info[i][2]; -// const std::string meshname = file_info[i][3]; - -// Node load_mesh, info, read_opts, write_opts; -// std::string filepath = utils::join_file_path(dirname, basename) + fileext; -// filepath = utils::join_file_path("silo", filepath); -// std::string input_file = relay_test_silo_data_path(filepath); - -// read_opts["mesh_name"] = meshname; - -// io::silo::load_mesh(input_file, read_opts, load_mesh); -// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - -// std::string out_name = "read_silo_" + basename; -// if (!meshname.empty()) -// { -// out_name += "_" + meshname; -// } - -// // TODO are these remove paths doing anything? Don't they need filenames? -// remove_path_if_exists(out_name + "_write_blueprint"); -// io::blueprint::save_mesh(load_mesh, out_name + "_write_blueprint", "hdf5"); - -// remove_path_if_exists(out_name + "_write_silo"); -// io::silo::save_mesh(load_mesh, out_name + "_write_silo"); - -// // overlink requires matsets and does not support point meshes -// if (load_mesh[0].has_child("matsets") && basename != "galaxy0000") -// { -// remove_path_if_exists(out_name + "_write_overlink"); -// write_opts["file_style"] = "overlink"; -// write_opts["ovl_topo_name"] = meshname; -// io::silo::save_mesh(load_mesh, out_name + "_write_overlink", write_opts); -// } -// } -// } - -// //----------------------------------------------------------------------------- -// // test that we can read the fake overlink files from the visit test data -// TEST(conduit_relay_io_silo, read_fake_overlink) -// { -// const std::vector> file_info = { -// // {"ev_0_0_100", "OvlTop", ".silo", "" }, // test default case -// // {"ev_0_0_100", "OvlTop", ".silo", "MMESH"}, -// // uncomment once silo ucdmesh phzones are supported -// {"hl18spec", "OvlTop", ".silo", "" }, // test default case -// {"hl18spec", "OvlTop", ".silo", "MMESH"}, -// // {"regrovl_qh_1000_10001_4", "OvlTop", ".silo", "" }, // test default case -// // {"regrovl_qh_1000_10001_4", "OvlTop", ".silo", "MMESH"}, -// // uncomment once silo ucdmesh phzones are supported -// {"utpyr4", "OvlTop", ".silo", "" }, // test default case -// {"utpyr4", "OvlTop", ".silo", "MMESH"}, -// }; - -// for (int i = 0; i < file_info.size(); i ++) -// { -// const std::string dirname = file_info[i][0]; -// const std::string basename = file_info[i][1]; -// const std::string fileext = file_info[i][2]; -// const std::string meshname = file_info[i][3]; - -// Node load_mesh, info, read_opts, write_opts; -// std::string filepath = utils::join_file_path(dirname, basename) + fileext; -// filepath = utils::join_file_path("fake_overlink", filepath); -// std::string input_file = relay_test_silo_data_path(filepath); - -// read_opts["mesh_name"] = meshname; - -// io::silo::load_mesh(input_file, read_opts, load_mesh); -// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - -// std::string out_name = "read_fake_overlink_" + dirname; -// if (!meshname.empty()) -// { -// out_name += "_" + meshname; -// } - -// remove_path_if_exists(out_name + "_write_blueprint"); -// io::blueprint::save_mesh(load_mesh, out_name + "_write_blueprint", "hdf5"); - -// remove_path_if_exists(out_name + "_write_silo"); -// io::silo::save_mesh(load_mesh, out_name + "_write_silo"); - -// remove_path_if_exists(out_name + "_write_overlink"); -// write_opts["file_style"] = "overlink"; -// write_opts["ovl_topo_name"] = "MMESH"; -// io::silo::save_mesh(load_mesh, out_name + "_write_overlink", write_opts); -// } -// } - -// //----------------------------------------------------------------------------- -// // read overlink files in symlink format -// // should be similar to reading raw silo -// TEST(conduit_relay_io_silo, read_overlink_symlink_format) -// { -// const std::vector> file_info = { -// {".", "box2d", ".silo", "" }, // test default case -// {".", "box2d", ".silo", "MMESH"}, -// {".", "box3d", ".silo", "" }, // test default case -// {".", "box3d", ".silo", "MMESH"}, -// // {".", "diamond", ".silo", "" }, // test default case -// // {".", "diamond", ".silo", "MMESH"}, -// // fails b/c polytopal not yet supported -// {".", "testDisk2D_a", ".silo", "" }, // test default case -// {".", "testDisk2D_a", ".silo", "MMESH"}, -// // {".", "donordiv.s2_materials2", ".silo", "" }, // test default case -// // {".", "donordiv.s2_materials2", ".silo", "MMESH"}, -// // fails b/c polytopal not yet supported -// {".", "donordiv.s2_materials3", ".silo", "" }, // test default case -// {".", "donordiv.s2_materials3", ".silo", "MMESH"}, -// }; - -// for (int i = 0; i < file_info.size(); i ++) -// { -// const std::string dirname = file_info[i][0]; -// const std::string basename = file_info[i][1]; -// const std::string fileext = file_info[i][2]; -// const std::string meshname = file_info[i][3]; - -// Node load_mesh, info, read_opts, write_opts; -// std::string filepath = utils::join_file_path(dirname, basename) + fileext; -// filepath = utils::join_file_path("overlink", filepath); -// std::string input_file = relay_test_silo_data_path(filepath); - -// read_opts["mesh_name"] = meshname; - -// io::silo::load_mesh(input_file, read_opts, load_mesh); -// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - -// std::string out_name = "read_overlink_symlink_" + basename; -// if (!meshname.empty()) -// { -// out_name += "_" + meshname; -// } - -// remove_path_if_exists(out_name + "_write_blueprint"); -// io::blueprint::save_mesh(load_mesh, out_name + "_write_blueprint", "hdf5"); - -// remove_path_if_exists(out_name + "_write_silo"); -// io::silo::save_mesh(load_mesh, out_name + "_write_silo"); - -// remove_path_if_exists(out_name + "_write_overlink"); -// write_opts["file_style"] = "overlink"; -// write_opts["ovl_topo_name"] = "MMESH"; -// io::silo::save_mesh(load_mesh, out_name + "_write_overlink", write_opts); -// } -// } - -// //----------------------------------------------------------------------------- -// // read overlink directly from ovltop.silo -// // this case is tricky and involves messing with paths -// TEST(conduit_relay_io_silo, read_overlink_directly) -// { -// const std::vector> file_info = { -// {"box2d", "OvlTop", ".silo", "" }, // test default case -// {"box2d", "OvlTop", ".silo", "MMESH"}, -// {"box3d", "OvlTop", ".silo", "" }, // test default case -// {"box3d", "OvlTop", ".silo", "MMESH"}, -// // {"diamond", "OvlTop", ".silo", "" }, // test default case -// // {"diamond", "OvlTop", ".silo", "MMESH"}, -// {"testDisk2D_a", "OvlTop", ".silo", "" }, // test default case -// {"testDisk2D_a", "OvlTop", ".silo", "MMESH"}, -// // {"donordiv.s2_materials2", "OvlTop", ".silo", "" }, // test default case -// // {"donordiv.s2_materials2", "OvlTop", ".silo", "MMESH"}, -// {"donordiv.s2_materials3", "OvlTop", ".silo", "" }, // test default case -// {"donordiv.s2_materials3", "OvlTop", ".silo", "MMESH"}, -// }; - -// for (int i = 0; i < file_info.size(); i ++) -// { -// const std::string dirname = file_info[i][0]; -// const std::string basename = file_info[i][1]; -// const std::string fileext = file_info[i][2]; -// const std::string meshname = file_info[i][3]; - -// Node load_mesh, info, read_opts, write_opts; - -// std::string filepath = utils::join_file_path(dirname, basename) + fileext; -// filepath = utils::join_file_path("overlink", filepath); -// std::string input_file = relay_test_silo_data_path(filepath); - -// read_opts["mesh_name"] = meshname; - -// io::silo::load_mesh(input_file, read_opts, load_mesh); -// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - -// std::string out_name = "read_overlink_direct_" + dirname; -// if (!meshname.empty()) -// { -// out_name += "_" + meshname; -// } - -// remove_path_if_exists(out_name + "_write_blueprint"); -// io::blueprint::save_mesh(load_mesh, out_name + "_write_blueprint", "hdf5"); - -// remove_path_if_exists(out_name + "_write_silo"); -// io::silo::save_mesh(load_mesh, out_name + "_write_silo"); - -// remove_path_if_exists(out_name + "_write_overlink"); -// write_opts["file_style"] = "overlink"; -// write_opts["ovl_topo_name"] = "MMESH"; -// io::silo::save_mesh(load_mesh, out_name + "_write_overlink", write_opts); -// } -// } - -// // TODO add tests for... -// // - polytopal meshes once they are supported -// // - units once they are supported -// // - etc. - -// // TODO add tetra8 and c36_m5 to all the overlink i/o tests - -// // TODO somewhere I need to error on overlink when there are different var or mesh types across domains + // cheat a little bit - we don't have these to start + n_matset["sizes"].set_external(load_mesh[child]["matsets"]["MMATERIAL"]["sizes"]); + n_matset["offsets"].set_external(load_mesh[child]["matsets"]["MMATERIAL"]["offsets"]); + + overlink_name_changer(save_mesh[child]); + } + + EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); + NodeConstIterator l_itr = load_mesh.children(); + NodeConstIterator s_itr = save_mesh.children(); + while (l_itr.has_next()) + { + const Node &l_curr = l_itr.next(); + const Node &s_curr = s_itr.next(); + + EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); + } + } +} + +//----------------------------------------------------------------------------- +// this tests var attributes and padding dimensions +TEST(conduit_relay_io_silo, round_trip_save_option_overlink2) +{ + const std::string basename = "silo_save_option_overlink_basic"; + const std::string filename = basename + "/OvlTop.silo"; + + Node write_opts, read_opts; + write_opts["file_style"] = "overlink"; + read_opts["matset_style"] = "multi_buffer_full"; + + Node save_mesh, load_mesh, info; + blueprint::mesh::examples::basic("structured", 3, 3, 1, save_mesh); + + // add another field that is volume dependent + Node &field2 = save_mesh["fields"]["field2"]; + field2["association"] = "element"; + field2["topology"] = "mesh"; + field2["volume_dependent"] = "true"; + field2["values"].set_external(save_mesh["fields"]["field"]["values"]); + + // add a matset to make overlink happy + add_multi_buffer_full_matset(save_mesh, 4, "mesh"); + + remove_path_if_exists(filename); + io::silo::save_mesh(save_mesh, basename, write_opts); + io::silo::load_mesh(filename, read_opts, load_mesh); + EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); + + // make changes to save mesh so the diff will pass + overlink_name_changer(save_mesh); + + // the loaded mesh will be in the multidomain format + // but the saved mesh is in the single domain format + EXPECT_EQ(load_mesh.number_of_children(), 1); + EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); + + EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); + + // open silo files and do some checks + + DBfile *rootfile = DBOpen(filename.c_str(), DB_UNKNOWN, DB_READ); + EXPECT_TRUE(DBInqVarExists(rootfile, "VAR_ATTRIBUTES")); + EXPECT_TRUE(DBInqVarType(rootfile, "VAR_ATTRIBUTES") == DB_ARRAY); + + DBcompoundarray *var_attr = DBGetCompoundarray(rootfile, "VAR_ATTRIBUTES"); + + // fetch pointers to elements inside the compound array + char **elemnames = var_attr->elemnames; + int *elemlengths = var_attr->elemlengths; + int nelems = var_attr->nelems; + int *values = static_cast(var_attr->values); + int nvalues = var_attr->nvalues; + int datatype = var_attr->datatype; + + EXPECT_EQ(std::string(elemnames[0]), "field"); + EXPECT_EQ(std::string(elemnames[1]), "field2"); + EXPECT_EQ(elemlengths[0], 5); + EXPECT_EQ(elemlengths[1], 5); + EXPECT_EQ(nelems, 2); + // for first var + EXPECT_EQ(values[0], 1); + EXPECT_EQ(values[1], 0); + EXPECT_EQ(values[2], 1); + EXPECT_EQ(values[3], 0); + EXPECT_EQ(values[4], 1); + // for second var + EXPECT_EQ(values[5], 1); + EXPECT_EQ(values[6], 1); + EXPECT_EQ(values[7], 1); + EXPECT_EQ(values[8], 0); + EXPECT_EQ(values[9], 1); + EXPECT_EQ(nvalues, 10); + EXPECT_EQ(datatype, DB_INT); + + DBFreeCompoundarray(var_attr); + + EXPECT_TRUE(DBInqVarExists(rootfile, "PAD_DIMS")); + EXPECT_TRUE(DBInqVarType(rootfile, "PAD_DIMS") == DB_ARRAY); + + DBcompoundarray *pad_dims = DBGetCompoundarray(rootfile, "PAD_DIMS"); + + // fetch pointers to elements inside the compound array + elemnames = pad_dims->elemnames; + elemlengths = pad_dims->elemlengths; + nelems = pad_dims->nelems; + values = static_cast(pad_dims->values); + nvalues = pad_dims->nvalues; + datatype = pad_dims->datatype; + + EXPECT_EQ(std::string(elemnames[0]), "paddims"); + EXPECT_EQ(elemlengths[0], 6); + EXPECT_EQ(nelems, 1); + EXPECT_EQ(values[0], 0); + EXPECT_EQ(values[1], 0); + EXPECT_EQ(values[2], 0); + EXPECT_EQ(values[3], 0); + EXPECT_EQ(values[4], 0); + EXPECT_EQ(values[5], 0); + EXPECT_EQ(nvalues, 6); + EXPECT_EQ(datatype, DB_INT); + + DBFreeCompoundarray(pad_dims); + + DBClose(rootfile); + + // now check domain file + + const std::string dom_filename = basename + "/domain0.silo"; + DBfile *domfile = DBOpen(dom_filename.c_str(), DB_UNKNOWN, DB_READ); + + EXPECT_TRUE(DBInqVarExists(domfile, "DOMAIN_NEIGHBOR_NUMS")); + EXPECT_TRUE(DBInqVarType(domfile, "DOMAIN_NEIGHBOR_NUMS") == DB_ARRAY); + + DBcompoundarray *dom_neighbor_nums = DBGetCompoundarray(domfile, "DOMAIN_NEIGHBOR_NUMS"); + + // fetch pointers to elements inside the compound array + elemnames = dom_neighbor_nums->elemnames; + elemlengths = dom_neighbor_nums->elemlengths; + nelems = dom_neighbor_nums->nelems; + values = static_cast(dom_neighbor_nums->values); + nvalues = dom_neighbor_nums->nvalues; + datatype = dom_neighbor_nums->datatype; + + EXPECT_EQ(std::string(elemnames[0]), "num_neighbors"); + EXPECT_EQ(std::string(elemnames[1]), "neighbor_nums"); + EXPECT_EQ(elemlengths[0], 1); + EXPECT_EQ(elemlengths[1], 0); + EXPECT_EQ(nelems, 2); + EXPECT_EQ(values[0], 0); + EXPECT_EQ(nvalues, 1); + EXPECT_EQ(datatype, DB_INT); + + DBFreeCompoundarray(dom_neighbor_nums); + + DBClose(domfile); +} + +//----------------------------------------------------------------------------- +// this tests material i/o +TEST(conduit_relay_io_silo, round_trip_save_option_overlink3) +{ + Node save_mesh, load_mesh, info; + const int nx = 100, ny = 100; + const double radius = 0.25; + blueprint::mesh::examples::venn("sparse_by_element", nx, ny, radius, save_mesh); + + const std::string basename = "silo_save_option_overlink_venn"; + const std::string filename = basename + "/OvlTop.silo"; + + Node opts; + opts["file_style"] = "overlink"; + + remove_path_if_exists(filename); + io::silo::save_mesh(save_mesh, basename, opts); + io::silo::load_mesh(filename, load_mesh); + EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + + // make changes to save mesh so the diff will pass + + // The field mat_check has values that are one type and matset_values + // that are another type. The silo writer converts both to double arrays + // in this case, so we follow suit. + Node mat_check_new_values, mat_check_new_matset_values; + save_mesh["fields"]["mat_check"]["values"].to_double_array(mat_check_new_values); + save_mesh["fields"]["mat_check"]["matset_values"].to_double_array(mat_check_new_matset_values); + save_mesh["fields"]["mat_check"]["values"].set_external(mat_check_new_values); + save_mesh["fields"]["mat_check"]["matset_values"].set_external(mat_check_new_matset_values); + + overlink_name_changer(save_mesh); + + // the loaded mesh will be in the multidomain format + // but the saved mesh is in the single domain format + EXPECT_EQ(load_mesh.number_of_children(), 1); + EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); + + EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); +} + +//----------------------------------------------------------------------------- +// we are testing vector fields get converted to scalars +TEST(conduit_relay_io_silo, round_trip_save_option_overlink4) +{ + const std::vector> mesh_types = { + std::make_pair("rectilinear", "2"), std::make_pair("rectilinear", "3"), + std::make_pair("structured", "2"), std::make_pair("structured", "3"), + std::make_pair("quads", "2"), + std::make_pair("hexs", "3"), + }; + for (int i = 0; i < mesh_types.size(); ++i) + { + std::string dim = mesh_types[i].second; + index_t nx = 3; + index_t ny = 4; + index_t nz = (dim == "2" ? 0 : 2); + + std::string mesh_type = mesh_types[i].first; + + Node save_mesh, load_mesh, info; + blueprint::mesh::examples::braid(mesh_type, nx, ny, nz, save_mesh); + index_t nele_x = nx - 1; + index_t nele_y = ny - 1; + index_t nele_z = (dim == "2" ? 0 : nz - 1); + + // provide a matset for braid + braid_init_example_matset(nele_x, nele_y, nele_z, save_mesh["matsets"]["matset"]); + + const std::string basename = "silo_save_option_overlink_braid_" + mesh_type + "_" + dim + "D"; + const std::string filename = basename + "/OvlTop.silo"; + + Node write_opts, read_opts; + write_opts["file_style"] = "overlink"; + read_opts["matset_style"] = "multi_buffer_full"; + + // remove existing root file, directory and any output files + remove_path_if_exists(filename); + io::silo::save_mesh(save_mesh, basename, write_opts); + io::silo::load_mesh(filename, read_opts, load_mesh); + EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + + Node &field_vel = save_mesh["fields"]["vel"]; + Node &field_vel_u = save_mesh["fields"]["vel_u"]; + Node &field_vel_v = save_mesh["fields"]["vel_v"]; + + field_vel_u["topology"].set(field_vel["topology"]); + field_vel_u["association"].set(field_vel["association"]); + field_vel_u["values"].set(field_vel["values/u"]); + field_vel_v["topology"].set(field_vel["topology"]); + field_vel_v["association"].set(field_vel["association"]); + field_vel_v["values"].set(field_vel["values/v"]); + + if (dim == "3") + { + Node &field_vel_w = save_mesh["fields"]["vel_w"]; + field_vel_w["topology"].set(field_vel["topology"]); + field_vel_w["association"].set(field_vel["association"]); + field_vel_w["values"].set(field_vel["values/w"]); + } + + save_mesh["fields"].remove_child("vel"); + + // make changes to save mesh so the diff will pass + overlink_name_changer(save_mesh); + + // the loaded mesh will be in the multidomain format + // but the saved mesh is in the single domain format + EXPECT_EQ(load_mesh.number_of_children(), 1); + EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); + + EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); + } +} + +//----------------------------------------------------------------------------- +// check that all the shape types work (specifically polytopal ones) +TEST(conduit_relay_io_silo, round_trip_save_option_overlink5) +{ + const std::vector> mesh_types = { + std::make_pair("uniform", "2"), std::make_pair("uniform", "3"), + std::make_pair("rectilinear", "2"), std::make_pair("rectilinear", "3"), + std::make_pair("structured", "2"), std::make_pair("structured", "3"), + std::make_pair("quads", "2"), + std::make_pair("polygons", "2"), + std::make_pair("hexs", "3"), + // std::make_pair("polyhedra", "3") + // Overlink does not support tris, wedges, pyramids, or tets + }; + for (int i = 0; i < mesh_types.size(); ++i) + { + const std::string dim = mesh_types[i].second; + index_t nx = 3; + index_t ny = 4; + index_t nz = (dim == "2" ? 0 : 2); + + const std::string mesh_type = mesh_types[i].first; + + Node save_mesh, load_mesh, info; + blueprint::mesh::examples::basic(mesh_type, nx, ny, nz, save_mesh); + + const std::string basename = "silo_save_option_overlink_basic_" + mesh_type + "_" + dim + "D"; + const std::string filename = basename + "/OvlTop.silo"; + const std::string domfile = basename + "/domain0.silo"; + + Node write_opts, read_opts; + write_opts["file_style"] = "overlink"; + read_opts["matset_style"] = "multi_buffer_full"; + + // add a matset to make overlink happy + int num_elems = (nx - 1) * (ny - 1); + if (mesh_type == "tets") + { + num_elems *= 6; + } + add_multi_buffer_full_matset(save_mesh, num_elems, "mesh"); + + remove_path_if_exists(filename); + remove_path_if_exists(domfile); + io::silo::save_mesh(save_mesh, basename, write_opts); + io::silo::load_mesh(filename, read_opts, load_mesh); + EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); + + // make changes to save mesh so the diff will pass + if (mesh_type == "uniform") + { + silo_uniform_to_rect_conversion("coords", "mesh", save_mesh); + } + overlink_name_changer(save_mesh); + + // the loaded mesh will be in the multidomain format + // but the saved mesh is in the single domain format + EXPECT_EQ(load_mesh.number_of_children(), 1); + EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); + + EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); + } +} + +//----------------------------------------------------------------------------- + +// +// read and write Silo and Overlink tests +// + +//----------------------------------------------------------------------------- +// read normal silo files containing multimeshes, multivars, and multimats +TEST(conduit_relay_io_silo, read_silo) +{ + const std::vector> file_info = { + {".", "multi_curv3d", ".silo", "" }, // test default case + {".", "multi_curv3d", ".silo", "mesh1" }, + // {".", "multi_curv3d", ".silo", "mesh1_back" }, // this multimesh points to paths that do not exist + {".", "multi_curv3d", ".silo", "mesh1_dup" }, + // {".", "multi_curv3d", ".silo", "mesh1_front" }, // same here + {".", "multi_curv3d", ".silo", "mesh1_hidden"}, + {".", "tire", ".silo", "" }, // test default case + {".", "tire", ".silo", "tire" }, + {".", "galaxy0000", ".silo", "" }, // test default case + {".", "galaxy0000", ".silo", "StarMesh" }, + {".", "emptydomains", ".silo", "" }, // test default case + {".", "emptydomains", ".silo", "mesh" }, + {"multidir_test_data", "multidir0000", ".root", "" }, // test default case + {"multidir_test_data", "multidir0000", ".root", "Mesh" }, + }; + + // TODO what to do in the case where a multimesh points to no data? (mesh1_back) + // fail silently, as we do now? + + for (int i = 0; i < file_info.size(); i ++) + { + const std::string dirname = file_info[i][0]; + const std::string basename = file_info[i][1]; + const std::string fileext = file_info[i][2]; + const std::string meshname = file_info[i][3]; + + Node load_mesh, info, read_opts, write_opts; + std::string filepath = utils::join_file_path(dirname, basename) + fileext; + filepath = utils::join_file_path("silo", filepath); + std::string input_file = relay_test_silo_data_path(filepath); + + read_opts["mesh_name"] = meshname; + + io::silo::load_mesh(input_file, read_opts, load_mesh); + EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + + std::string out_name = "read_silo_" + basename; + if (!meshname.empty()) + { + out_name += "_" + meshname; + } + + // TODO are these remove paths doing anything? Don't they need filenames? + remove_path_if_exists(out_name + "_write_blueprint"); + io::blueprint::save_mesh(load_mesh, out_name + "_write_blueprint", "hdf5"); + + remove_path_if_exists(out_name + "_write_silo"); + io::silo::save_mesh(load_mesh, out_name + "_write_silo"); + + // overlink requires matsets and does not support point meshes + if (load_mesh[0].has_child("matsets") && basename != "galaxy0000") + { + remove_path_if_exists(out_name + "_write_overlink"); + write_opts["file_style"] = "overlink"; + write_opts["ovl_topo_name"] = meshname; + io::silo::save_mesh(load_mesh, out_name + "_write_overlink", write_opts); + } + } +} + +//----------------------------------------------------------------------------- +// test that we can read the fake overlink files from the visit test data +TEST(conduit_relay_io_silo, read_fake_overlink) +{ + const std::vector> file_info = { + // {"ev_0_0_100", "OvlTop", ".silo", "" }, // test default case + // {"ev_0_0_100", "OvlTop", ".silo", "MMESH"}, + // uncomment once silo ucdmesh phzones are supported + {"hl18spec", "OvlTop", ".silo", "" }, // test default case + {"hl18spec", "OvlTop", ".silo", "MMESH"}, + // {"regrovl_qh_1000_10001_4", "OvlTop", ".silo", "" }, // test default case + // {"regrovl_qh_1000_10001_4", "OvlTop", ".silo", "MMESH"}, + // uncomment once silo ucdmesh phzones are supported + {"utpyr4", "OvlTop", ".silo", "" }, // test default case + {"utpyr4", "OvlTop", ".silo", "MMESH"}, + }; + + for (int i = 0; i < file_info.size(); i ++) + { + const std::string dirname = file_info[i][0]; + const std::string basename = file_info[i][1]; + const std::string fileext = file_info[i][2]; + const std::string meshname = file_info[i][3]; + + Node load_mesh, info, read_opts, write_opts; + std::string filepath = utils::join_file_path(dirname, basename) + fileext; + filepath = utils::join_file_path("fake_overlink", filepath); + std::string input_file = relay_test_silo_data_path(filepath); + + read_opts["mesh_name"] = meshname; + + io::silo::load_mesh(input_file, read_opts, load_mesh); + EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + + std::string out_name = "read_fake_overlink_" + dirname; + if (!meshname.empty()) + { + out_name += "_" + meshname; + } + + remove_path_if_exists(out_name + "_write_blueprint"); + io::blueprint::save_mesh(load_mesh, out_name + "_write_blueprint", "hdf5"); + + remove_path_if_exists(out_name + "_write_silo"); + io::silo::save_mesh(load_mesh, out_name + "_write_silo"); + + remove_path_if_exists(out_name + "_write_overlink"); + write_opts["file_style"] = "overlink"; + write_opts["ovl_topo_name"] = "MMESH"; + io::silo::save_mesh(load_mesh, out_name + "_write_overlink", write_opts); + } +} + +//----------------------------------------------------------------------------- +// read overlink files in symlink format +// should be similar to reading raw silo +TEST(conduit_relay_io_silo, read_overlink_symlink_format) +{ + const std::vector> file_info = { + {".", "box2d", ".silo", "" }, // test default case + {".", "box2d", ".silo", "MMESH"}, + {".", "box3d", ".silo", "" }, // test default case + {".", "box3d", ".silo", "MMESH"}, + // {".", "diamond", ".silo", "" }, // test default case + // {".", "diamond", ".silo", "MMESH"}, + // fails b/c polytopal not yet supported + {".", "testDisk2D_a", ".silo", "" }, // test default case + {".", "testDisk2D_a", ".silo", "MMESH"}, + // {".", "donordiv.s2_materials2", ".silo", "" }, // test default case + // {".", "donordiv.s2_materials2", ".silo", "MMESH"}, + // fails b/c polytopal not yet supported + {".", "donordiv.s2_materials3", ".silo", "" }, // test default case + {".", "donordiv.s2_materials3", ".silo", "MMESH"}, + }; + + for (int i = 0; i < file_info.size(); i ++) + { + const std::string dirname = file_info[i][0]; + const std::string basename = file_info[i][1]; + const std::string fileext = file_info[i][2]; + const std::string meshname = file_info[i][3]; + + Node load_mesh, info, read_opts, write_opts; + std::string filepath = utils::join_file_path(dirname, basename) + fileext; + filepath = utils::join_file_path("overlink", filepath); + std::string input_file = relay_test_silo_data_path(filepath); + + read_opts["mesh_name"] = meshname; + + io::silo::load_mesh(input_file, read_opts, load_mesh); + EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + + std::string out_name = "read_overlink_symlink_" + basename; + if (!meshname.empty()) + { + out_name += "_" + meshname; + } + + remove_path_if_exists(out_name + "_write_blueprint"); + io::blueprint::save_mesh(load_mesh, out_name + "_write_blueprint", "hdf5"); + + remove_path_if_exists(out_name + "_write_silo"); + io::silo::save_mesh(load_mesh, out_name + "_write_silo"); + + remove_path_if_exists(out_name + "_write_overlink"); + write_opts["file_style"] = "overlink"; + write_opts["ovl_topo_name"] = "MMESH"; + io::silo::save_mesh(load_mesh, out_name + "_write_overlink", write_opts); + } +} + +//----------------------------------------------------------------------------- +// read overlink directly from ovltop.silo +// this case is tricky and involves messing with paths +TEST(conduit_relay_io_silo, read_overlink_directly) +{ + const std::vector> file_info = { + {"box2d", "OvlTop", ".silo", "" }, // test default case + {"box2d", "OvlTop", ".silo", "MMESH"}, + {"box3d", "OvlTop", ".silo", "" }, // test default case + {"box3d", "OvlTop", ".silo", "MMESH"}, + // {"diamond", "OvlTop", ".silo", "" }, // test default case + // {"diamond", "OvlTop", ".silo", "MMESH"}, + {"testDisk2D_a", "OvlTop", ".silo", "" }, // test default case + {"testDisk2D_a", "OvlTop", ".silo", "MMESH"}, + // {"donordiv.s2_materials2", "OvlTop", ".silo", "" }, // test default case + // {"donordiv.s2_materials2", "OvlTop", ".silo", "MMESH"}, + {"donordiv.s2_materials3", "OvlTop", ".silo", "" }, // test default case + {"donordiv.s2_materials3", "OvlTop", ".silo", "MMESH"}, + }; + + for (int i = 0; i < file_info.size(); i ++) + { + const std::string dirname = file_info[i][0]; + const std::string basename = file_info[i][1]; + const std::string fileext = file_info[i][2]; + const std::string meshname = file_info[i][3]; + + Node load_mesh, info, read_opts, write_opts; + + std::string filepath = utils::join_file_path(dirname, basename) + fileext; + filepath = utils::join_file_path("overlink", filepath); + std::string input_file = relay_test_silo_data_path(filepath); + + read_opts["mesh_name"] = meshname; + + io::silo::load_mesh(input_file, read_opts, load_mesh); + EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + + std::string out_name = "read_overlink_direct_" + dirname; + if (!meshname.empty()) + { + out_name += "_" + meshname; + } + + remove_path_if_exists(out_name + "_write_blueprint"); + io::blueprint::save_mesh(load_mesh, out_name + "_write_blueprint", "hdf5"); + + remove_path_if_exists(out_name + "_write_silo"); + io::silo::save_mesh(load_mesh, out_name + "_write_silo"); + + remove_path_if_exists(out_name + "_write_overlink"); + write_opts["file_style"] = "overlink"; + write_opts["ovl_topo_name"] = "MMESH"; + io::silo::save_mesh(load_mesh, out_name + "_write_overlink", write_opts); + } +} + +// TODO add tests for... +// - polytopal meshes once they are supported +// - units once they are supported +// - etc. + +// TODO add tetra8 and c36_m5 to all the overlink i/o tests + +// TODO somewhere I need to error on overlink when there are different var or mesh types across domains From 6f377e3f25b7db2e7b5fb132c20222617e865a96 Mon Sep 17 00:00:00 2001 From: Justin Privitera Date: Wed, 3 Apr 2024 12:13:42 -0700 Subject: [PATCH 08/56] fixed failing test but broke a whole bunch of others --- src/libs/relay/conduit_relay_io_silo.cpp | 142 +++++++++++++++++++---- 1 file changed, 120 insertions(+), 22 deletions(-) diff --git a/src/libs/relay/conduit_relay_io_silo.cpp b/src/libs/relay/conduit_relay_io_silo.cpp index 2f16cc6ea..1d7fc623a 100644 --- a/src/libs/relay/conduit_relay_io_silo.cpp +++ b/src/libs/relay/conduit_relay_io_silo.cpp @@ -2540,6 +2540,68 @@ read_root_silo_index(const std::string &root_file_path, return false; } + // handle legacy mesh_name argument + Node real_opts; + if (opts.has_child("mesh_name")) + { + const std::string opts_mesh_name = opts["mesh_name"].as_string(); + // we could manually go through the other opts, but I think this approach + // is cleaner in case we add more down the road + auto opts_itr = opts.children(); + while (opts_itr.has_next()) + { + const Node &opts_item = opts_itr.next(); + const std::string opt_name = opts_itr.name(); + if (opt_name != "silo_names") + { + real_opts[opt_name].set_external(opts_item); + } + else + { + // silo names case + auto silo_names_itr = opts["silo_names"].children(); + while (silo_names_itr.has_next()) + { + const Node &silo_names_entry = silo_names_itr.next(); + const std::string silo_names_name = silo_names_itr.name(); + if (silo_names_name != "multimesh_names") + { + real_opts["silo_names"][silo_names_name].set_external(silo_names_entry); + } + } + } + } + + if (opts.has_path("silo_names/multimesh_names")) + { + if (opts["silo_names"]["multimesh_names"].has_child("all") || + opts["silo_names"]["multimesh_names"].has_child("none") || + opts["silo_names"]["multimesh_names"].has_child(opts_mesh_name)) + { + real_opts["silo_names"]["multimesh_names"].set_external(opts["silo_names"]["multimesh_names"]); + } + else + { + auto mmesh_names_itr = opts["silo_names"]["multimesh_names"].children(); + while (mmesh_names_itr.has_next()) + { + const Node &mmesh_names_entry = mmesh_names_itr.next(); + const std::string mmesh_name = mmesh_names_itr.name(); + real_opts["silo_names"]["multimesh_names"][mmesh_name].set_external(mmesh_names_entry); + } + real_opts["silo_names"]["multimesh_names"][opts_mesh_name]; + } + } + else + { + real_opts["silo_names"]["multimesh_names"].set(opts_mesh_name); + } + } + else + { + real_opts.set_external(opts); + } + std::map reading_info; // read all is turned on, and read none is turned off @@ -2556,9 +2618,9 @@ read_root_silo_index(const std::string &root_file_path, reading_info["mat_names"] = detail::SiloReadBookkeeping(); // reading_info["matspecies_names"] = detail::SiloReadBookkeeping(); - if (opts.has_child("silo_names")) + if (real_opts.has_child("silo_names")) { - auto silo_names_itr = opts["silo_names"].children(); + auto silo_names_itr = real_opts["silo_names"].children(); while (silo_names_itr.has_next()) { const Node &silo_object_type = silo_names_itr.next(); @@ -2570,25 +2632,54 @@ read_root_silo_index(const std::string &root_file_path, return false; } - if (silo_object_type.number_of_children() > 0) + if (silo_object_type.dtype().is_object()) { - if (silo_object_type.has_child("all")) + if (silo_object_type.number_of_children() > 0) { - if (silo_object_type.number_of_children() > 1) + if (silo_object_type.has_child("all")) { - error_oss << "TODO this is bad"; - return false; + if (silo_object_type.number_of_children() > 1) + { + error_oss << "TODO this is bad"; + return false; + } + reading_info[silo_object_type_name].read_all = true; + reading_info[silo_object_type_name].read_none = false; } + else if (silo_object_type.has_child("none")) + { + if (silo_object_type.number_of_children() > 1) + { + error_oss << "TODO this is bad"; + return false; + } + reading_info[silo_object_type_name].read_all = false; + reading_info[silo_object_type_name].read_none = true; + } + else + { + // we must have named some specific items we want to read + reading_info[silo_object_type_name].read_all = false; + reading_info[silo_object_type_name].read_none = false; + } + } + else + { + // no children were specified so we want to read everything of this kind reading_info[silo_object_type_name].read_all = true; reading_info[silo_object_type_name].read_none = false; } - else if (silo_object_type.has_child("none")) + } + else + { + const std::string silo_name_value = silo_object_type.as_string(); + if ("all" == silo_name_value) + { + reading_info[silo_object_type_name].read_all = true; + reading_info[silo_object_type_name].read_none = false; + } + else if ("none" == silo_name_value) { - if (silo_object_type.number_of_children() > 1) - { - error_oss << "TODO this is bad"; - return false; - } reading_info[silo_object_type_name].read_all = false; reading_info[silo_object_type_name].read_none = true; } @@ -2599,12 +2690,6 @@ read_root_silo_index(const std::string &root_file_path, reading_info[silo_object_type_name].read_none = false; } } - else - { - // no children were specified so we want to read everything of this kind - reading_info[silo_object_type_name].read_all = true; - reading_info[silo_object_type_name].read_none = false; - } } } @@ -2626,7 +2711,15 @@ read_root_silo_index(const std::string &root_file_path, } else { - reading_info[silo_obj_name].names_to_read = opts["silo_names"][silo_obj_name].child_names(); + if (real_opts["silo_names"][silo_obj_name].dtype().is_object()) + { + reading_info[silo_obj_name].names_to_read = real_opts["silo_names"][silo_obj_name].child_names(); + } + else + { + reading_info[silo_obj_name].names_to_read.push_back(real_opts["silo_names"][silo_obj_name].as_string()); + } + for (size_t list_id = 0; list_id < reading_info[silo_obj_name].names_to_read.size(); list_id ++) { bool found = false; @@ -2671,9 +2764,9 @@ read_root_silo_index(const std::string &root_file_path, // Get the selected matset flavor std::string opts_matset_style = ""; - if (opts.has_child("matset_style") && opts["matset_style"].dtype().is_string()) + if (real_opts.has_child("matset_style") && real_opts["matset_style"].dtype().is_string()) { - opts_matset_style = opts["matset_style"].as_string(); + opts_matset_style = real_opts["matset_style"].as_string(); if (opts_matset_style != "default" && opts_matset_style != "multi_buffer_full" && opts_matset_style != "sparse_by_element" && @@ -2886,6 +2979,11 @@ read_root_silo_index(const std::string &root_file_path, /// "multi_buffer_by_material" /// "default" ==> "sparse_by_element" /// +/// mesh_name: legacy argument. This is interpreted as a multimesh name. +/// It is added to the list of multimesh names to read, unless the +/// user has specified "all" or "none", which will supersede this. +/// TODO does it make sense to remove this? When? +/// /// note: we have made the choice to read ONLY the multimesh with the name /// mesh_name. We also read all multivariables which are associated with the /// chosen multimesh. From 2160a55fd67103183cf525293c8bad11b74655d8 Mon Sep 17 00:00:00 2001 From: Justin Privitera Date: Wed, 3 Apr 2024 13:46:32 -0700 Subject: [PATCH 09/56] that's a silly bug --- src/libs/relay/conduit_relay_io_silo.cpp | 77 +++++++++++++----------- 1 file changed, 42 insertions(+), 35 deletions(-) diff --git a/src/libs/relay/conduit_relay_io_silo.cpp b/src/libs/relay/conduit_relay_io_silo.cpp index 1d7fc623a..2ad0c0a73 100644 --- a/src/libs/relay/conduit_relay_io_silo.cpp +++ b/src/libs/relay/conduit_relay_io_silo.cpp @@ -2545,56 +2545,63 @@ read_root_silo_index(const std::string &root_file_path, if (opts.has_child("mesh_name")) { const std::string opts_mesh_name = opts["mesh_name"].as_string(); - // we could manually go through the other opts, but I think this approach - // is cleaner in case we add more down the road - auto opts_itr = opts.children(); - while (opts_itr.has_next()) - { - const Node &opts_item = opts_itr.next(); - const std::string opt_name = opts_itr.name(); - if (opt_name != "silo_names") - { - real_opts[opt_name].set_external(opts_item); - } - else + if (! opts_mesh_name.empty()) + { + // we could manually go through the other opts, but I think this approach + // is cleaner in case we add more down the road + auto opts_itr = opts.children(); + while (opts_itr.has_next()) { - // silo names case - auto silo_names_itr = opts["silo_names"].children(); - while (silo_names_itr.has_next()) + const Node &opts_item = opts_itr.next(); + const std::string opt_name = opts_itr.name(); + if (opt_name != "silo_names") + { + real_opts[opt_name].set_external(opts_item); + } + else { - const Node &silo_names_entry = silo_names_itr.next(); - const std::string silo_names_name = silo_names_itr.name(); - if (silo_names_name != "multimesh_names") + // silo names case + auto silo_names_itr = opts["silo_names"].children(); + while (silo_names_itr.has_next()) { - real_opts["silo_names"][silo_names_name].set_external(silo_names_entry); + const Node &silo_names_entry = silo_names_itr.next(); + const std::string silo_names_name = silo_names_itr.name(); + if (silo_names_name != "multimesh_names") + { + real_opts["silo_names"][silo_names_name].set_external(silo_names_entry); + } } } } - } - if (opts.has_path("silo_names/multimesh_names")) - { - if (opts["silo_names"]["multimesh_names"].has_child("all") || - opts["silo_names"]["multimesh_names"].has_child("none") || - opts["silo_names"]["multimesh_names"].has_child(opts_mesh_name)) + if (opts.has_path("silo_names/multimesh_names")) { - real_opts["silo_names"]["multimesh_names"].set_external(opts["silo_names"]["multimesh_names"]); + if (opts["silo_names"]["multimesh_names"].has_child("all") || + opts["silo_names"]["multimesh_names"].has_child("none") || + opts["silo_names"]["multimesh_names"].has_child(opts_mesh_name)) + { + real_opts["silo_names"]["multimesh_names"].set_external(opts["silo_names"]["multimesh_names"]); + } + else + { + auto mmesh_names_itr = opts["silo_names"]["multimesh_names"].children(); + while (mmesh_names_itr.has_next()) + { + const Node &mmesh_names_entry = mmesh_names_itr.next(); + const std::string mmesh_name = mmesh_names_itr.name(); + real_opts["silo_names"]["multimesh_names"][mmesh_name].set_external(mmesh_names_entry); + } + real_opts["silo_names"]["multimesh_names"][opts_mesh_name]; + } } else { - auto mmesh_names_itr = opts["silo_names"]["multimesh_names"].children(); - while (mmesh_names_itr.has_next()) - { - const Node &mmesh_names_entry = mmesh_names_itr.next(); - const std::string mmesh_name = mmesh_names_itr.name(); - real_opts["silo_names"]["multimesh_names"][mmesh_name].set_external(mmesh_names_entry); - } - real_opts["silo_names"]["multimesh_names"][opts_mesh_name]; + real_opts["silo_names"]["multimesh_names"].set(opts_mesh_name); } } else { - real_opts["silo_names"]["multimesh_names"].set(opts_mesh_name); + real_opts.set_external(opts); } } else From e45fd632685db836fd31941280f328bcab1d246f Mon Sep 17 00:00:00 2001 From: Justin Privitera Date: Wed, 3 Apr 2024 14:07:49 -0700 Subject: [PATCH 10/56] goodbye helpers hello lambdas --- src/libs/relay/conduit_relay_io_silo.cpp | 158 +++++++---------------- 1 file changed, 49 insertions(+), 109 deletions(-) diff --git a/src/libs/relay/conduit_relay_io_silo.cpp b/src/libs/relay/conduit_relay_io_silo.cpp index 2ad0c0a73..c9fb7fc89 100644 --- a/src/libs/relay/conduit_relay_io_silo.cpp +++ b/src/libs/relay/conduit_relay_io_silo.cpp @@ -2085,42 +2085,6 @@ open_or_reuse_file(const bool ovltop_case, return domain_file_to_use; } -//----------------------------------------------------------------------------- -void -prepare_simple_mesh_metadata(const std::string &mesh_name, - const int mesh_type, - Node &root_node) -{ - root_node[mesh_name]["nblocks"] = 1; - root_node[mesh_name]["nameschemes"] = "no"; - root_node[mesh_name]["mesh_types"].set(mesh_type); - root_node[mesh_name]["mesh_paths"].append().set(mesh_name); -} - -//----------------------------------------------------------------------------- -void -prepare_simple_var_metadata(const std::string &mesh_name, - const std::string &var_name, - const int var_type, - Node &root_node) -{ - Node &var = root_node[mesh_name]["vars"][var_name]; - var["nameschemes"] = "no"; - var["var_types"].set(var_type); - var["var_paths"].append().set(var_name); -} - -//----------------------------------------------------------------------------- -void -prepare_simple_mat_metadata(const std::string &mesh_name, - const std::string &mat_name, - Node &root_node) -{ - Node &material = root_node[mesh_name]["matsets"][mat_name]; - material["nameschemes"] = "no"; - material["matset_paths"].append().set(mat_name); -} - //----------------------------------------------------------------------------- bool read_multimesh(DBfile *dbfile, @@ -2831,86 +2795,62 @@ read_root_silo_index(const std::string &root_file_path, } } - // next quadmeshes and quadvars - for (const std::string &qmesh_name : reading_info["qmesh_names"].names_to_read) - { - prepare_simple_mesh_metadata(qmesh_name, DB_QUADMESH, root_node); - - // at this stage we assume that all qvars could be associated with this qmesh - // TODO should I do a check here then? - for (const std::string &qvar_name : reading_info["qvar_names"].names_to_read) - { - prepare_simple_var_metadata(qmesh_name, qvar_name, DB_QUADVAR, root_node); - } - // same is true for materials - // TODO ugh I don't like this - for (const std::string &mat_name : reading_info["mat_names"].names_to_read) - { - prepare_simple_mat_metadata(qmesh_name, mat_name, root_node); - } - - // TODO I love rereading state for every mesh. This is so silly - read_state(dbfile.getSiloObject(), root_node, qmesh_name); - - if (! opts_matset_style.empty()) - { - root_node[qmesh_name]["matset_style"] = opts_matset_style; - } - } - - // next ucdmeshes and ucdvars - for (const std::string &ucdmesh_name : reading_info["ucdmesh_names"].names_to_read) + auto prep_simple_silo_obj_metadata = [&](const std::vector &mesh_names, + const std::vector &var_names, + const std::vector &mat_names, + const int mesh_type, + const int var_type) { - prepare_simple_mesh_metadata(ucdmesh_name, DB_UCDMESH, root_node); - - // at this stage we assume that all ucdvars could be associated with this ucdmesh - // TODO should I do a check here then? - for (const std::string &ucdvar_name : reading_info["ucdvar_names"].names_to_read) + for (const std::string &mesh_name : mesh_names) { - prepare_simple_var_metadata(ucdmesh_name, ucdvar_name, DB_UCDVAR, root_node); - } - // same is true for materials - // TODO ugh I don't like this - for (const std::string &mat_name : reading_info["mat_names"].names_to_read) - { - prepare_simple_mat_metadata(ucdmesh_name, mat_name, root_node); - } - - // TODO I love rereading state for every mesh. This is so silly - read_state(dbfile.getSiloObject(), root_node, ucdmesh_name); + root_node[mesh_name]["nblocks"] = 1; + root_node[mesh_name]["nameschemes"] = "no"; + root_node[mesh_name]["mesh_types"].set(mesh_type); + root_node[mesh_name]["mesh_paths"].append().set(mesh_name); - if (! opts_matset_style.empty()) - { - root_node[ucdmesh_name]["matset_style"] = opts_matset_style; - } - } + // at this stage we assume that all vars could be associated with this mesh + // TODO should I do a check here then? + for (const std::string &var_name : var_names) + { + Node &var = root_node[mesh_name]["vars"][var_name]; + var["nameschemes"] = "no"; + var["var_types"].set(var_type); + var["var_paths"].append().set(var_name); + } + // same is true for materials + // TODO ugh I don't like this + for (const std::string &mat_name : mat_names) + { + Node &material = root_node[mesh_name]["matsets"][mat_name]; + material["nameschemes"] = "no"; + material["matset_paths"].append().set(mat_name); + } - // next ptmeshes and ptvars - for (const std::string &ptmesh_name : reading_info["ptmesh_names"].names_to_read) - { - prepare_simple_mesh_metadata(ptmesh_name, DB_POINTMESH, root_node); + // TODO I love rereading state for every mesh. This is so silly + read_state(dbfile.getSiloObject(), root_node, mesh_name); - // at this stage we assume that all ptvars could be associated with this ptmesh - // TODO should I do a check here then? - for (const std::string &ptvar_name : reading_info["ptvar_names"].names_to_read) - { - prepare_simple_var_metadata(ptmesh_name, ptvar_name, DB_POINTVAR, root_node); - } - // same is true for materials - // TODO ugh I don't like this - for (const std::string &mat_name : reading_info["mat_names"].names_to_read) - { - prepare_simple_mat_metadata(ptmesh_name, mat_name, root_node); + if (! opts_matset_style.empty()) + { + root_node[mesh_name]["matset_style"] = opts_matset_style; + } } + }; - // TODO I love rereading state for every mesh. This is so silly - read_state(dbfile.getSiloObject(), root_node, ptmesh_name); - - if (! opts_matset_style.empty()) - { - root_node[ptmesh_name]["matset_style"] = opts_matset_style; - } - } + prep_simple_silo_obj_metadata(reading_info["qmesh_names"].names_to_read, + reading_info["qvar_names"].names_to_read, + reading_info["mat_names"].names_to_read, + DB_QUADMESH, + DB_QUADVAR); + prep_simple_silo_obj_metadata(reading_info["ucdmesh_names"].names_to_read, + reading_info["ucdvar_names"].names_to_read, + reading_info["mat_names"].names_to_read, + DB_UCDMESH, + DB_UCDVAR); + prep_simple_silo_obj_metadata(reading_info["ptmesh_names"].names_to_read, + reading_info["ptvar_names"].names_to_read, + reading_info["mat_names"].names_to_read, + DB_POINTMESH, + DB_POINTVAR); // our silo index should look like this: From 03343652d468a332ea472aa851b6cc1c4e988da9 Mon Sep 17 00:00:00 2001 From: Justin Privitera Date: Wed, 3 Apr 2024 14:30:19 -0700 Subject: [PATCH 11/56] clean up tests --- src/tests/relay/t_relay_io_silo.cpp | 121 ++++++++-------------------- 1 file changed, 34 insertions(+), 87 deletions(-) diff --git a/src/tests/relay/t_relay_io_silo.cpp b/src/tests/relay/t_relay_io_silo.cpp index 2842a483f..297c24497 100644 --- a/src/tests/relay/t_relay_io_silo.cpp +++ b/src/tests/relay/t_relay_io_silo.cpp @@ -1740,20 +1740,11 @@ TEST(conduit_relay_io_silo, round_trip_save_option_overlink5) TEST(conduit_relay_io_silo, read_silo) { const std::vector> file_info = { - {".", "multi_curv3d", ".silo", "" }, // test default case - {".", "multi_curv3d", ".silo", "mesh1" }, - // {".", "multi_curv3d", ".silo", "mesh1_back" }, // this multimesh points to paths that do not exist - {".", "multi_curv3d", ".silo", "mesh1_dup" }, - // {".", "multi_curv3d", ".silo", "mesh1_front" }, // same here - {".", "multi_curv3d", ".silo", "mesh1_hidden"}, - {".", "tire", ".silo", "" }, // test default case - {".", "tire", ".silo", "tire" }, - {".", "galaxy0000", ".silo", "" }, // test default case - {".", "galaxy0000", ".silo", "StarMesh" }, - {".", "emptydomains", ".silo", "" }, // test default case - {".", "emptydomains", ".silo", "mesh" }, - {"multidir_test_data", "multidir0000", ".root", "" }, // test default case - {"multidir_test_data", "multidir0000", ".root", "Mesh" }, + {".", "multi_curv3d", ".silo"}, // test default case + {".", "tire", ".silo"}, // test default case + {".", "galaxy0000", ".silo"}, // test default case + {".", "emptydomains", ".silo"}, // test default case + {"multidir_test_data", "multidir0000", ".root"}, // test default case }; // TODO what to do in the case where a multimesh points to no data? (mesh1_back) @@ -1764,23 +1755,16 @@ TEST(conduit_relay_io_silo, read_silo) const std::string dirname = file_info[i][0]; const std::string basename = file_info[i][1]; const std::string fileext = file_info[i][2]; - const std::string meshname = file_info[i][3]; - Node load_mesh, info, read_opts, write_opts; + Node load_mesh, info, write_opts; std::string filepath = utils::join_file_path(dirname, basename) + fileext; filepath = utils::join_file_path("silo", filepath); std::string input_file = relay_test_silo_data_path(filepath); - read_opts["mesh_name"] = meshname; - - io::silo::load_mesh(input_file, read_opts, load_mesh); + io::silo::load_mesh(input_file, load_mesh); EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - std::string out_name = "read_silo_" + basename; - if (!meshname.empty()) - { - out_name += "_" + meshname; - } + const std::string out_name = "read_silo_" + basename; // TODO are these remove paths doing anything? Don't they need filenames? remove_path_if_exists(out_name + "_write_blueprint"); @@ -1794,7 +1778,7 @@ TEST(conduit_relay_io_silo, read_silo) { remove_path_if_exists(out_name + "_write_overlink"); write_opts["file_style"] = "overlink"; - write_opts["ovl_topo_name"] = meshname; + write_opts["ovl_topo_name"] = "MMESH"; // TODO do I even need this io::silo::save_mesh(load_mesh, out_name + "_write_overlink", write_opts); } } @@ -1805,16 +1789,12 @@ TEST(conduit_relay_io_silo, read_silo) TEST(conduit_relay_io_silo, read_fake_overlink) { const std::vector> file_info = { - // {"ev_0_0_100", "OvlTop", ".silo", "" }, // test default case - // {"ev_0_0_100", "OvlTop", ".silo", "MMESH"}, + // {"ev_0_0_100", "OvlTop", ".silo"}, // uncomment once silo ucdmesh phzones are supported - {"hl18spec", "OvlTop", ".silo", "" }, // test default case - {"hl18spec", "OvlTop", ".silo", "MMESH"}, - // {"regrovl_qh_1000_10001_4", "OvlTop", ".silo", "" }, // test default case - // {"regrovl_qh_1000_10001_4", "OvlTop", ".silo", "MMESH"}, + {"hl18spec", "OvlTop", ".silo"}, + // {"regrovl_qh_1000_10001_4", "OvlTop", ".silo"}, // uncomment once silo ucdmesh phzones are supported - {"utpyr4", "OvlTop", ".silo", "" }, // test default case - {"utpyr4", "OvlTop", ".silo", "MMESH"}, + {"utpyr4", "OvlTop", ".silo"}, }; for (int i = 0; i < file_info.size(); i ++) @@ -1822,23 +1802,16 @@ TEST(conduit_relay_io_silo, read_fake_overlink) const std::string dirname = file_info[i][0]; const std::string basename = file_info[i][1]; const std::string fileext = file_info[i][2]; - const std::string meshname = file_info[i][3]; - Node load_mesh, info, read_opts, write_opts; + Node load_mesh, info, write_opts; std::string filepath = utils::join_file_path(dirname, basename) + fileext; filepath = utils::join_file_path("fake_overlink", filepath); std::string input_file = relay_test_silo_data_path(filepath); - read_opts["mesh_name"] = meshname; - - io::silo::load_mesh(input_file, read_opts, load_mesh); + io::silo::load_mesh(input_file, load_mesh); EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - std::string out_name = "read_fake_overlink_" + dirname; - if (!meshname.empty()) - { - out_name += "_" + meshname; - } + const std::string out_name = "read_fake_overlink_" + dirname; remove_path_if_exists(out_name + "_write_blueprint"); io::blueprint::save_mesh(load_mesh, out_name + "_write_blueprint", "hdf5"); @@ -1859,20 +1832,14 @@ TEST(conduit_relay_io_silo, read_fake_overlink) TEST(conduit_relay_io_silo, read_overlink_symlink_format) { const std::vector> file_info = { - {".", "box2d", ".silo", "" }, // test default case - {".", "box2d", ".silo", "MMESH"}, - {".", "box3d", ".silo", "" }, // test default case - {".", "box3d", ".silo", "MMESH"}, - // {".", "diamond", ".silo", "" }, // test default case - // {".", "diamond", ".silo", "MMESH"}, + {".", "box2d", ".silo"}, + {".", "box3d", ".silo"}, + // {".", "diamond", ".silo"}, // fails b/c polytopal not yet supported - {".", "testDisk2D_a", ".silo", "" }, // test default case - {".", "testDisk2D_a", ".silo", "MMESH"}, - // {".", "donordiv.s2_materials2", ".silo", "" }, // test default case - // {".", "donordiv.s2_materials2", ".silo", "MMESH"}, + {".", "testDisk2D_a", ".silo"}, + // {".", "donordiv.s2_materials2", ".silo"}, // fails b/c polytopal not yet supported - {".", "donordiv.s2_materials3", ".silo", "" }, // test default case - {".", "donordiv.s2_materials3", ".silo", "MMESH"}, + {".", "donordiv.s2_materials3", ".silo"}, }; for (int i = 0; i < file_info.size(); i ++) @@ -1880,23 +1847,16 @@ TEST(conduit_relay_io_silo, read_overlink_symlink_format) const std::string dirname = file_info[i][0]; const std::string basename = file_info[i][1]; const std::string fileext = file_info[i][2]; - const std::string meshname = file_info[i][3]; - Node load_mesh, info, read_opts, write_opts; + Node load_mesh, info, write_opts; std::string filepath = utils::join_file_path(dirname, basename) + fileext; filepath = utils::join_file_path("overlink", filepath); std::string input_file = relay_test_silo_data_path(filepath); - read_opts["mesh_name"] = meshname; - - io::silo::load_mesh(input_file, read_opts, load_mesh); + io::silo::load_mesh(input_file, load_mesh); EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - std::string out_name = "read_overlink_symlink_" + basename; - if (!meshname.empty()) - { - out_name += "_" + meshname; - } + const std::string out_name = "read_overlink_symlink_" + basename; remove_path_if_exists(out_name + "_write_blueprint"); io::blueprint::save_mesh(load_mesh, out_name + "_write_blueprint", "hdf5"); @@ -1917,18 +1877,12 @@ TEST(conduit_relay_io_silo, read_overlink_symlink_format) TEST(conduit_relay_io_silo, read_overlink_directly) { const std::vector> file_info = { - {"box2d", "OvlTop", ".silo", "" }, // test default case - {"box2d", "OvlTop", ".silo", "MMESH"}, - {"box3d", "OvlTop", ".silo", "" }, // test default case - {"box3d", "OvlTop", ".silo", "MMESH"}, - // {"diamond", "OvlTop", ".silo", "" }, // test default case - // {"diamond", "OvlTop", ".silo", "MMESH"}, - {"testDisk2D_a", "OvlTop", ".silo", "" }, // test default case - {"testDisk2D_a", "OvlTop", ".silo", "MMESH"}, - // {"donordiv.s2_materials2", "OvlTop", ".silo", "" }, // test default case - // {"donordiv.s2_materials2", "OvlTop", ".silo", "MMESH"}, - {"donordiv.s2_materials3", "OvlTop", ".silo", "" }, // test default case - {"donordiv.s2_materials3", "OvlTop", ".silo", "MMESH"}, + {"box2d", "OvlTop", ".silo"}, + {"box3d", "OvlTop", ".silo"}, + // {"diamond", "OvlTop", ".silo"}, + {"testDisk2D_a", "OvlTop", ".silo"}, + // {"donordiv.s2_materials2", "OvlTop", ".silo"}, + {"donordiv.s2_materials3", "OvlTop", ".silo"}, }; for (int i = 0; i < file_info.size(); i ++) @@ -1936,24 +1890,17 @@ TEST(conduit_relay_io_silo, read_overlink_directly) const std::string dirname = file_info[i][0]; const std::string basename = file_info[i][1]; const std::string fileext = file_info[i][2]; - const std::string meshname = file_info[i][3]; - Node load_mesh, info, read_opts, write_opts; + Node load_mesh, info, write_opts; std::string filepath = utils::join_file_path(dirname, basename) + fileext; filepath = utils::join_file_path("overlink", filepath); std::string input_file = relay_test_silo_data_path(filepath); - read_opts["mesh_name"] = meshname; - - io::silo::load_mesh(input_file, read_opts, load_mesh); + io::silo::load_mesh(input_file, load_mesh); EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - std::string out_name = "read_overlink_direct_" + dirname; - if (!meshname.empty()) - { - out_name += "_" + meshname; - } + const std::string out_name = "read_overlink_direct_" + dirname; remove_path_if_exists(out_name + "_write_blueprint"); io::blueprint::save_mesh(load_mesh, out_name + "_write_blueprint", "hdf5"); From a4eb4ba02a3846afd83811b356c8f5e3c436eb51 Mon Sep 17 00:00:00 2001 From: Justin Privitera Date: Wed, 3 Apr 2024 14:31:35 -0700 Subject: [PATCH 12/56] todo is done --- src/libs/relay/conduit_relay_io_silo.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libs/relay/conduit_relay_io_silo.cpp b/src/libs/relay/conduit_relay_io_silo.cpp index c9fb7fc89..76e391159 100644 --- a/src/libs/relay/conduit_relay_io_silo.cpp +++ b/src/libs/relay/conduit_relay_io_silo.cpp @@ -2472,7 +2472,6 @@ read_root_silo_index(const std::string &root_file_path, { // clear output vars root_node.reset(); - std::string multimesh_name = ""; // TODO preserve legacy functionality somehow error_oss.str(""); // first, make sure we can open the root file From d8035eb5217dab182aaccff0a6ea31423723d768 Mon Sep 17 00:00:00 2001 From: Justin Privitera Date: Wed, 3 Apr 2024 16:10:08 -0700 Subject: [PATCH 13/56] audit of the tests is complete --- src/tests/relay/t_relay_io_silo.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/tests/relay/t_relay_io_silo.cpp b/src/tests/relay/t_relay_io_silo.cpp index 297c24497..2592214e9 100644 --- a/src/tests/relay/t_relay_io_silo.cpp +++ b/src/tests/relay/t_relay_io_silo.cpp @@ -815,10 +815,9 @@ TEST(conduit_relay_io_silo, missing_domain_mesh) remove_path_if_exists(filename); io::silo::save_mesh(save_mesh, basename); - - opts["mesh_name"] = "mesh_topo2"; + opts["silo_names"]["multimesh_names"] = "mesh_topo2"; io::silo::load_mesh(filename, opts, load_mesh); - opts["mesh_name"] = "mesh_topo"; + opts["silo_names"]["multimesh_names"] = "mesh_topo"; io::silo::load_mesh(filename, opts, load_mesh2); EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); EXPECT_TRUE(blueprint::mesh::verify(load_mesh2, info)); @@ -1174,6 +1173,7 @@ TEST(conduit_relay_io_silo, round_trip_save_option_mesh_name) } //----------------------------------------------------------------------------- +// TODO this is now a legacy feature. Should I remove eventually? TEST(conduit_relay_io_silo, round_trip_read_option_mesh_name) { Node load_mesh, info, opts; @@ -1740,11 +1740,11 @@ TEST(conduit_relay_io_silo, round_trip_save_option_overlink5) TEST(conduit_relay_io_silo, read_silo) { const std::vector> file_info = { - {".", "multi_curv3d", ".silo"}, // test default case - {".", "tire", ".silo"}, // test default case - {".", "galaxy0000", ".silo"}, // test default case - {".", "emptydomains", ".silo"}, // test default case - {"multidir_test_data", "multidir0000", ".root"}, // test default case + {".", "multi_curv3d", ".silo"}, + {".", "tire", ".silo"}, + {".", "galaxy0000", ".silo"}, + {".", "emptydomains", ".silo"}, + {"multidir_test_data", "multidir0000", ".root"}, }; // TODO what to do in the case where a multimesh points to no data? (mesh1_back) From e06fb40203ebc9aba50ed2cd13951afb639ed00f Mon Sep 17 00:00:00 2001 From: Justin Privitera Date: Wed, 3 Apr 2024 16:19:57 -0700 Subject: [PATCH 14/56] I am getting some test data --- src/libs/relay/conduit_relay_io_silo.cpp | 36 ++- src/tests/relay/t_relay_io_silo.cpp | 370 ++++++++++++----------- 2 files changed, 225 insertions(+), 181 deletions(-) diff --git a/src/libs/relay/conduit_relay_io_silo.cpp b/src/libs/relay/conduit_relay_io_silo.cpp index 76e391159..22a43f1e3 100644 --- a/src/libs/relay/conduit_relay_io_silo.cpp +++ b/src/libs/relay/conduit_relay_io_silo.cpp @@ -2929,10 +2929,6 @@ read_root_silo_index(const std::string &root_file_path, /// It is added to the list of multimesh names to read, unless the /// user has specified "all" or "none", which will supersede this. /// TODO does it make sense to remove this? When? -/// -/// note: we have made the choice to read ONLY the multimesh with the name -/// mesh_name. We also read all multivariables which are associated with the -/// chosen multimesh. //----------------------------------------------------------------------------- void CONDUIT_RELAY_API read_mesh(const std::string &root_file_path, @@ -3292,19 +3288,37 @@ void load_mesh(const std::string &root_file_path, } //----------------------------------------------------------------------------- -/// /// opts: -/// mesh_name: "{name}" -/// provide explicit mesh name, for cases where silo data includes -/// more than one mesh. +/// silo_names: +/// multimesh_names: +/// "{name1}" - multimeshes with this name will be read if they exist +/// "{name2}" +/// ... +/// or +/// "{all}" - all multimeshes will be read. +/// or +/// "{none}" - no multimeshes will be read. +/// multivar_names: similar to multimesh_names. +/// multimat_names: similar to multimesh_names. +/// multimatspecies_names: similar to multimesh_names. TODO +/// qmesh_names: similar to multimesh_names. +/// qvar_names: similar to multimesh_names. +/// ucdmesh_names: similar to multimesh_names. +/// ucdvar_names: similar to multimesh_names. +/// ptmesh_names: similar to multimesh_names. +/// ptvar_names: similar to multimesh_names. +/// mat_names: similar to multimesh_names. +/// matspecies_names: similar to multimesh_names. TODO +/// By default, everything in the file will be read unless manually turned off. /// /// matset_style: "default", "multi_buffer_full", "sparse_by_element", /// "multi_buffer_by_material" /// "default" ==> "sparse_by_element" /// -/// note: we have made the choice to read ONLY the multimesh with the name -/// mesh_name. We also read all multivariables which are associated with the -/// chosen multimesh. +/// mesh_name: legacy argument. This is interpreted as a multimesh name. +/// It is added to the list of multimesh names to read, unless the +/// user has specified "all" or "none", which will supersede this. +/// TODO does it make sense to remove this? When? //----------------------------------------------------------------------------- void load_mesh(const std::string &root_file_path, const Node &opts, diff --git a/src/tests/relay/t_relay_io_silo.cpp b/src/tests/relay/t_relay_io_silo.cpp index 2592214e9..eef53cdf8 100644 --- a/src/tests/relay/t_relay_io_silo.cpp +++ b/src/tests/relay/t_relay_io_silo.cpp @@ -131,19 +131,6 @@ TEST(conduit_relay_io_silo, load_mesh_geometry) } } -//----------------------------------------------------------------------------- -TEST(conduit_relay_io_silo, read_curv2d) -{ - Node load_mesh, info; - const std::string filename = "/usr/workspace/justin/visit_builds/3.4RC-w-tpls-03_05_24/visit/build/testdata/silo_hdf5_test_data/curv2d.silo"; - io::silo::load_mesh(filename, load_mesh); - EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - - io::blueprint::save_mesh(load_mesh, "curvmesh2d_blueprint", "hdf5"); - - load_mesh.print(); -} - //----------------------------------------------------------------------------- TEST(conduit_relay_io_silo, round_trip_basic) { @@ -936,7 +923,7 @@ TEST(conduit_relay_io_silo, unstructured_points) //----------------------------------------------------------------------------- // -// save and read option tests +// save option tests // // save options: @@ -970,16 +957,6 @@ TEST(conduit_relay_io_silo, unstructured_points) /// <= 0, use # of files == # of domains /// > 0, # of files == number_of_files -// read options: -/// opts: -/// mesh_name: "{name}" -/// provide explicit mesh name, for cases where silo data includes -/// more than one mesh. -/// -/// matset_style: "default", "multi_buffer_full", "sparse_by_element", -/// "multi_buffer_by_material" -/// "default" ==> "sparse_by_element" - //----------------------------------------------------------------------------- TEST(conduit_relay_io_silo, round_trip_save_option_file_style) { @@ -1172,152 +1149,6 @@ TEST(conduit_relay_io_silo, round_trip_save_option_mesh_name) EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); } -//----------------------------------------------------------------------------- -// TODO this is now a legacy feature. Should I remove eventually? -TEST(conduit_relay_io_silo, round_trip_read_option_mesh_name) -{ - Node load_mesh, info, opts; - const std::string path = utils::join_file_path("silo", "multi_curv3d.silo"); - const std::string input_file = relay_test_silo_data_path(path); - - opts["mesh_name"] = "mesh1_dup"; - - io::silo::load_mesh(input_file, opts, load_mesh); - EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - - EXPECT_TRUE(load_mesh[0].has_path("topologies/mesh1_dup")); -} - -//----------------------------------------------------------------------------- -TEST(conduit_relay_io_silo, round_trip_read_option_matset_style) -{ - // the matset type and the type we are requesting on read - const std::vector> matset_types = { - std::make_pair("full", "full"), - std::make_pair("sparse_by_material", "sparse_by_material"), - std::make_pair("sparse_by_element", "sparse_by_element"), - std::make_pair("sparse_by_element", "full"), - std::make_pair("sparse_by_material", "sparse_by_element"), - std::make_pair("sparse_by_material", "default"), - }; - - for (int i = 0; i < matset_types.size(); i ++) - { - std::string matset_type = matset_types[i].first; - std::string matset_request = matset_types[i].second; - - for (int j = 0; j < 2; j ++) - { - Node mesh_full, mesh_sbe, mesh_sbm, baseline_mesh, load_mesh, info; - std::string size; - int nx, ny; - const double radius = 0.25; - if (j == 0) - { - size = "small"; - nx = ny = 4; - } - else - { - size = "large"; - nx = ny = 100; - } - - blueprint::mesh::examples::venn("full", nx, ny, radius, mesh_full); - blueprint::mesh::examples::venn("sparse_by_material", nx, ny, radius, mesh_sbm); - blueprint::mesh::examples::venn("sparse_by_element", nx, ny, radius, mesh_sbe); - - if (matset_type == "full") - { - baseline_mesh.set_external(mesh_full); - } - else if (matset_type == "sparse_by_material") - { - baseline_mesh.set_external(mesh_sbm); - } - else // (matset_type == "sparse_by_element") - { - baseline_mesh.set_external(mesh_sbe); - } - - Node opts; - if (matset_request == "full") - { - opts["matset_style"] = "multi_buffer_full"; - } - else if (matset_request == "sparse_by_material") - { - opts["matset_style"] = "multi_buffer_by_material"; - } - else if (matset_request == "sparse_by_element") - { - opts["matset_style"] = "sparse_by_element"; - } - else - { - opts["matset_style"] = "default"; - } - - const std::string basename = "silo_venn2_" + matset_type + "_" + size; - const std::string filename = basename + ".root"; - - remove_path_if_exists(filename); - io::silo::save_mesh(baseline_mesh, basename); - io::silo::load_mesh(filename, opts, load_mesh); - EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - - if (matset_request == "full") - { - baseline_mesh.set_external(mesh_full); - } - else if (matset_request == "sparse_by_material") - { - baseline_mesh.set_external(mesh_sbm); - } - else if (matset_request == "sparse_by_element") - { - baseline_mesh.set_external(mesh_sbe); - } - else - { - baseline_mesh.set_external(mesh_sbe); - } - - // make changes to save mesh so the diff will pass - - // The field mat_check has values that are one type and matset_values - // that are another type. The silo writer converts both to double arrays - // in this case, so we follow suit. - Node mat_check_new_values, mat_check_new_matset_values; - baseline_mesh["fields"]["mat_check"]["values"].to_double_array(mat_check_new_values); - if (baseline_mesh["fields"]["mat_check"]["matset_values"].dtype().is_object()) - { - auto mat_vals_itr = baseline_mesh["fields"]["mat_check"]["matset_values"].children(); - while (mat_vals_itr.has_next()) - { - Node &mat_vals_for_mat = mat_vals_itr.next(); - const std::string mat_name = mat_vals_itr.name(); - mat_vals_for_mat.to_double_array(mat_check_new_matset_values[mat_name]); - } - } - else - { - baseline_mesh["fields"]["mat_check"]["matset_values"].to_double_array(mat_check_new_matset_values); - } - baseline_mesh["fields"]["mat_check"]["values"].set_external(mat_check_new_values); - baseline_mesh["fields"]["mat_check"]["matset_values"].set_external(mat_check_new_matset_values); - - silo_name_changer("mesh", baseline_mesh); - - // the loaded mesh will be in the multidomain format - // but the saved mesh is in the single domain format - EXPECT_EQ(load_mesh.number_of_children(), 1); - EXPECT_EQ(load_mesh[0].number_of_children(), baseline_mesh.number_of_children()); - EXPECT_FALSE(load_mesh[0].diff(baseline_mesh, info, CONDUIT_EPSILON, true)); - } - } -} - //----------------------------------------------------------------------------- TEST(conduit_relay_io_silo, round_trip_save_option_silo_type) { @@ -1731,6 +1562,191 @@ TEST(conduit_relay_io_silo, round_trip_save_option_overlink5) //----------------------------------------------------------------------------- +// +// read option tests +// + +// read options: +/// opts: +/// silo_names: +/// multimesh_names: +/// "{name1}" - multimeshes with this name will be read if they exist +/// "{name2}" +/// ... +/// or +/// "{all}" - all multimeshes will be read. +/// or +/// "{none}" - no multimeshes will be read. +/// multivar_names: similar to multimesh_names. +/// multimat_names: similar to multimesh_names. +/// multimatspecies_names: similar to multimesh_names. TODO +/// qmesh_names: similar to multimesh_names. +/// qvar_names: similar to multimesh_names. +/// ucdmesh_names: similar to multimesh_names. +/// ucdvar_names: similar to multimesh_names. +/// ptmesh_names: similar to multimesh_names. +/// ptvar_names: similar to multimesh_names. +/// mat_names: similar to multimesh_names. +/// matspecies_names: similar to multimesh_names. TODO +/// By default, everything in the file will be read unless manually turned off. +/// +/// matset_style: "default", "multi_buffer_full", "sparse_by_element", +/// "multi_buffer_by_material" +/// "default" ==> "sparse_by_element" +/// +/// mesh_name: legacy argument. This is interpreted as a multimesh name. +/// It is added to the list of multimesh names to read, unless the +/// user has specified "all" or "none", which will supersede this. +/// TODO does it make sense to remove this? When? + +//----------------------------------------------------------------------------- +// TODO this is now a legacy feature. Should I remove eventually? +TEST(conduit_relay_io_silo, round_trip_read_option_mesh_name) +{ + Node load_mesh, info, opts; + const std::string path = utils::join_file_path("silo", "multi_curv3d.silo"); + const std::string input_file = relay_test_silo_data_path(path); + + opts["mesh_name"] = "mesh1_dup"; + + io::silo::load_mesh(input_file, opts, load_mesh); + EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + + EXPECT_TRUE(load_mesh[0].has_path("topologies/mesh1_dup")); +} + +//----------------------------------------------------------------------------- +TEST(conduit_relay_io_silo, round_trip_read_option_matset_style) +{ + // the matset type and the type we are requesting on read + const std::vector> matset_types = { + std::make_pair("full", "full"), + std::make_pair("sparse_by_material", "sparse_by_material"), + std::make_pair("sparse_by_element", "sparse_by_element"), + std::make_pair("sparse_by_element", "full"), + std::make_pair("sparse_by_material", "sparse_by_element"), + std::make_pair("sparse_by_material", "default"), + }; + + for (int i = 0; i < matset_types.size(); i ++) + { + std::string matset_type = matset_types[i].first; + std::string matset_request = matset_types[i].second; + + for (int j = 0; j < 2; j ++) + { + Node mesh_full, mesh_sbe, mesh_sbm, baseline_mesh, load_mesh, info; + std::string size; + int nx, ny; + const double radius = 0.25; + if (j == 0) + { + size = "small"; + nx = ny = 4; + } + else + { + size = "large"; + nx = ny = 100; + } + + blueprint::mesh::examples::venn("full", nx, ny, radius, mesh_full); + blueprint::mesh::examples::venn("sparse_by_material", nx, ny, radius, mesh_sbm); + blueprint::mesh::examples::venn("sparse_by_element", nx, ny, radius, mesh_sbe); + + if (matset_type == "full") + { + baseline_mesh.set_external(mesh_full); + } + else if (matset_type == "sparse_by_material") + { + baseline_mesh.set_external(mesh_sbm); + } + else // (matset_type == "sparse_by_element") + { + baseline_mesh.set_external(mesh_sbe); + } + + Node opts; + if (matset_request == "full") + { + opts["matset_style"] = "multi_buffer_full"; + } + else if (matset_request == "sparse_by_material") + { + opts["matset_style"] = "multi_buffer_by_material"; + } + else if (matset_request == "sparse_by_element") + { + opts["matset_style"] = "sparse_by_element"; + } + else + { + opts["matset_style"] = "default"; + } + + const std::string basename = "silo_venn2_" + matset_type + "_" + size; + const std::string filename = basename + ".root"; + + remove_path_if_exists(filename); + io::silo::save_mesh(baseline_mesh, basename); + io::silo::load_mesh(filename, opts, load_mesh); + EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + + if (matset_request == "full") + { + baseline_mesh.set_external(mesh_full); + } + else if (matset_request == "sparse_by_material") + { + baseline_mesh.set_external(mesh_sbm); + } + else if (matset_request == "sparse_by_element") + { + baseline_mesh.set_external(mesh_sbe); + } + else + { + baseline_mesh.set_external(mesh_sbe); + } + + // make changes to save mesh so the diff will pass + + // The field mat_check has values that are one type and matset_values + // that are another type. The silo writer converts both to double arrays + // in this case, so we follow suit. + Node mat_check_new_values, mat_check_new_matset_values; + baseline_mesh["fields"]["mat_check"]["values"].to_double_array(mat_check_new_values); + if (baseline_mesh["fields"]["mat_check"]["matset_values"].dtype().is_object()) + { + auto mat_vals_itr = baseline_mesh["fields"]["mat_check"]["matset_values"].children(); + while (mat_vals_itr.has_next()) + { + Node &mat_vals_for_mat = mat_vals_itr.next(); + const std::string mat_name = mat_vals_itr.name(); + mat_vals_for_mat.to_double_array(mat_check_new_matset_values[mat_name]); + } + } + else + { + baseline_mesh["fields"]["mat_check"]["matset_values"].to_double_array(mat_check_new_matset_values); + } + baseline_mesh["fields"]["mat_check"]["values"].set_external(mat_check_new_values); + baseline_mesh["fields"]["mat_check"]["matset_values"].set_external(mat_check_new_matset_values); + + silo_name_changer("mesh", baseline_mesh); + + // the loaded mesh will be in the multidomain format + // but the saved mesh is in the single domain format + EXPECT_EQ(load_mesh.number_of_children(), 1); + EXPECT_EQ(load_mesh[0].number_of_children(), baseline_mesh.number_of_children()); + EXPECT_FALSE(load_mesh[0].diff(baseline_mesh, info, CONDUIT_EPSILON, true)); + } + } +} + +//----------------------------------------------------------------------------- + // // read and write Silo and Overlink tests // @@ -1784,6 +1800,20 @@ TEST(conduit_relay_io_silo, read_silo) } } +//----------------------------------------------------------------------------- +// test that we can read silo without multimeshes, multivars, and multimats +TEST(conduit_relay_io_silo, read_simple_silo) +{ + Node load_mesh, info; + const std::string filename = "/usr/workspace/justin/visit_builds/3.4RC-w-tpls-03_05_24/visit/build/testdata/silo_hdf5_test_data/curv2d.silo"; + io::silo::load_mesh(filename, load_mesh); + EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + + io::blueprint::save_mesh(load_mesh, "curvmesh2d_blueprint", "hdf5"); + + load_mesh.print(); +} + //----------------------------------------------------------------------------- // test that we can read the fake overlink files from the visit test data TEST(conduit_relay_io_silo, read_fake_overlink) From d08cbd12228c48e08009b3c077a33982647b10fc Mon Sep 17 00:00:00 2001 From: Justin Privitera Date: Wed, 3 Apr 2024 16:45:24 -0700 Subject: [PATCH 15/56] update path --- src/tests/relay/t_relay_io_silo.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/tests/relay/t_relay_io_silo.cpp b/src/tests/relay/t_relay_io_silo.cpp index eef53cdf8..f0fea7d1f 100644 --- a/src/tests/relay/t_relay_io_silo.cpp +++ b/src/tests/relay/t_relay_io_silo.cpp @@ -1805,11 +1805,11 @@ TEST(conduit_relay_io_silo, read_silo) TEST(conduit_relay_io_silo, read_simple_silo) { Node load_mesh, info; - const std::string filename = "/usr/workspace/justin/visit_builds/3.4RC-w-tpls-03_05_24/visit/build/testdata/silo_hdf5_test_data/curv2d.silo"; - io::silo::load_mesh(filename, load_mesh); + const std::string input_file = relay_test_silo_data_path(utils::join_file_path("silo", "curv2d.silo")); + io::silo::load_mesh(input_file, load_mesh); EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - io::blueprint::save_mesh(load_mesh, "curvmesh2d_blueprint", "hdf5"); + io::blueprint::save_mesh(load_mesh, "curv2d_blueprint", "hdf5"); load_mesh.print(); } From 8dbaddd5f1c1ab99230a58e46a5acd75b04141b8 Mon Sep 17 00:00:00 2001 From: Justin Privitera Date: Thu, 4 Apr 2024 10:55:46 -0700 Subject: [PATCH 16/56] new test fails --- src/tests/relay/t_relay_io_silo.cpp | 42 ++++++++++++++++++++++++----- 1 file changed, 36 insertions(+), 6 deletions(-) diff --git a/src/tests/relay/t_relay_io_silo.cpp b/src/tests/relay/t_relay_io_silo.cpp index f0fea7d1f..1714b4857 100644 --- a/src/tests/relay/t_relay_io_silo.cpp +++ b/src/tests/relay/t_relay_io_silo.cpp @@ -1804,14 +1804,44 @@ TEST(conduit_relay_io_silo, read_silo) // test that we can read silo without multimeshes, multivars, and multimats TEST(conduit_relay_io_silo, read_simple_silo) { - Node load_mesh, info; - const std::string input_file = relay_test_silo_data_path(utils::join_file_path("silo", "curv2d.silo")); - io::silo::load_mesh(input_file, load_mesh); - EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + const std::vector> file_info = { + {"curv2d", ".silo"}, + {"curv2d_colmajor", ".silo"}, + {"curv3d", ".silo"}, + {"curv3d_colmajor", ".silo"}, + {"globe", ".silo"}, + }; + for (int i = 0; i < file_info.size(); i ++) + { + const std::string basename = file_info[i].first; + const std::string fileext = file_info[i].second; + + Node load_mesh, info, write_opts; + std::string filepath = basename + fileext; + filepath = utils::join_file_path("silo", filepath); + std::string input_file = relay_test_silo_data_path(filepath); + + io::silo::load_mesh(input_file, load_mesh); + EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + + const std::string out_name = "read_silo_" + basename; - io::blueprint::save_mesh(load_mesh, "curv2d_blueprint", "hdf5"); + // TODO are these remove paths doing anything? Don't they need filenames? + remove_path_if_exists(out_name + "_write_blueprint"); + io::blueprint::save_mesh(load_mesh, out_name + "_write_blueprint", "hdf5"); - load_mesh.print(); + remove_path_if_exists(out_name + "_write_silo"); + io::silo::save_mesh(load_mesh, out_name + "_write_silo"); + + // overlink requires matsets + if (load_mesh[0].has_child("matsets")) + { + remove_path_if_exists(out_name + "_write_overlink"); + write_opts["file_style"] = "overlink"; + write_opts["ovl_topo_name"] = "MMESH"; // TODO do I even need this + io::silo::save_mesh(load_mesh, out_name + "_write_overlink", write_opts); + } + } } //----------------------------------------------------------------------------- From 8e74f83f5c8e0ccc0ec3cd7e693ee9b572b7192a Mon Sep 17 00:00:00 2001 From: Justin Privitera Date: Thu, 4 Apr 2024 11:02:21 -0700 Subject: [PATCH 17/56] test pass --- src/tests/relay/t_relay_io_silo.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/tests/relay/t_relay_io_silo.cpp b/src/tests/relay/t_relay_io_silo.cpp index 1714b4857..5247ead4a 100644 --- a/src/tests/relay/t_relay_io_silo.cpp +++ b/src/tests/relay/t_relay_io_silo.cpp @@ -1809,13 +1809,15 @@ TEST(conduit_relay_io_silo, read_simple_silo) {"curv2d_colmajor", ".silo"}, {"curv3d", ".silo"}, {"curv3d_colmajor", ".silo"}, - {"globe", ".silo"}, + // {"globe", ".silo"}, // need to fix a known bug for this one to work }; for (int i = 0; i < file_info.size(); i ++) { const std::string basename = file_info[i].first; const std::string fileext = file_info[i].second; + std::cout << basename << std::endl; + Node load_mesh, info, write_opts; std::string filepath = basename + fileext; filepath = utils::join_file_path("silo", filepath); From 90040f62c4cf9398ec490e02961c652cfaefe4c7 Mon Sep 17 00:00:00 2001 From: Justin Privitera Date: Thu, 4 Apr 2024 16:08:37 -0700 Subject: [PATCH 18/56] I broke so many things --- src/libs/relay/conduit_relay_io_silo.cpp | 44 +++- src/tests/relay/t_relay_io_silo.cpp | 290 +++++++++++------------ 2 files changed, 185 insertions(+), 149 deletions(-) diff --git a/src/libs/relay/conduit_relay_io_silo.cpp b/src/libs/relay/conduit_relay_io_silo.cpp index 22a43f1e3..d6b0422af 100644 --- a/src/libs/relay/conduit_relay_io_silo.cpp +++ b/src/libs/relay/conduit_relay_io_silo.cpp @@ -1112,15 +1112,51 @@ read_quadmesh_domain(DBquadmesh *quadmesh_ptr, mesh_domain["coordsets"][multimesh_name]["type"] = "explicit"; mesh_domain["topologies"][multimesh_name]["type"] = "structured"; - // We subtract 1 from each of these because in silo these dims are node dims, not element dims - mesh_domain["topologies"][multimesh_name]["elements/dims/i"] = quadmesh_ptr->dims[0] - 1; + const int *dims = quadmesh_ptr->dims; + const int *min_index = quadmesh_ptr->min_index; + const int *max_index = quadmesh_ptr->max_index; + + // if min and max are in line with dims + if (min_index[0] == 0 && max_index[0] == dims[0] - 1) + { + // We subtract 1 from each of these because in silo these dims are node dims, not element dims + mesh_domain["topologies"][multimesh_name]["elements/dims/i"] = quadmesh_ptr->dims[0] - 1; + mesh_domain["topologies"][multimesh_name]["elements/origin/i"] = 0; + } + else + { + mesh_domain["topologies"][multimesh_name]["elements/dims/i"] = max_index[0] - min_index[0]; + mesh_domain["topologies"][multimesh_name]["elements/origin/i"] = min_index[0]; + } if (ndims > 1) { - mesh_domain["topologies"][multimesh_name]["elements/dims/j"] = quadmesh_ptr->dims[1] - 1; + // if min and max are in line with dims + if (min_index[1] == 0 && max_index[1] == dims[1] - 1) + { + // We subtract 1 from each of these because in silo these dims are node dims, not element dims + mesh_domain["topologies"][multimesh_name]["elements/dims/j"] = quadmesh_ptr->dims[1] - 1; + mesh_domain["topologies"][multimesh_name]["elements/origin/j"] = 0; + } + else + { + mesh_domain["topologies"][multimesh_name]["elements/dims/j"] = max_index[1] - min_index[1]; + mesh_domain["topologies"][multimesh_name]["elements/origin/j"] = min_index[1]; + } } if (ndims > 2) { - mesh_domain["topologies"][multimesh_name]["elements/dims/k"] = quadmesh_ptr->dims[2] - 1; + // if min and max are in line with dims + if (min_index[2] == 0 && max_index[2] == dims[2] - 1) + { + // We subtract 1 from each of these because in silo these dims are node dims, not element dims + mesh_domain["topologies"][multimesh_name]["elements/dims/k"] = quadmesh_ptr->dims[2] - 1; + mesh_domain["topologies"][multimesh_name]["elements/origin/k"] = 0; + } + else + { + mesh_domain["topologies"][multimesh_name]["elements/dims/k"] = max_index[2] - min_index[2]; + mesh_domain["topologies"][multimesh_name]["elements/origin/k"] = min_index[2]; + } } } else diff --git a/src/tests/relay/t_relay_io_silo.cpp b/src/tests/relay/t_relay_io_silo.cpp index 5247ead4a..3aa98905c 100644 --- a/src/tests/relay/t_relay_io_silo.cpp +++ b/src/tests/relay/t_relay_io_silo.cpp @@ -1806,18 +1806,16 @@ TEST(conduit_relay_io_silo, read_simple_silo) { const std::vector> file_info = { {"curv2d", ".silo"}, - {"curv2d_colmajor", ".silo"}, - {"curv3d", ".silo"}, - {"curv3d_colmajor", ".silo"}, - // {"globe", ".silo"}, // need to fix a known bug for this one to work + // {"curv2d_colmajor", ".silo"}, + // {"curv3d", ".silo"}, + // {"curv3d_colmajor", ".silo"}, + // {"globe", ".silo"}, // TODO need to fix a known bug for this one to work }; for (int i = 0; i < file_info.size(); i ++) { const std::string basename = file_info[i].first; const std::string fileext = file_info[i].second; - std::cout << basename << std::endl; - Node load_mesh, info, write_opts; std::string filepath = basename + fileext; filepath = utils::join_file_path("silo", filepath); @@ -1826,6 +1824,8 @@ TEST(conduit_relay_io_silo, read_simple_silo) io::silo::load_mesh(input_file, load_mesh); EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + std::cout << load_mesh.to_yaml() << std::endl; + const std::string out_name = "read_silo_" + basename; // TODO are these remove paths doing anything? Don't they need filenames? @@ -1846,142 +1846,142 @@ TEST(conduit_relay_io_silo, read_simple_silo) } } -//----------------------------------------------------------------------------- -// test that we can read the fake overlink files from the visit test data -TEST(conduit_relay_io_silo, read_fake_overlink) -{ - const std::vector> file_info = { - // {"ev_0_0_100", "OvlTop", ".silo"}, - // uncomment once silo ucdmesh phzones are supported - {"hl18spec", "OvlTop", ".silo"}, - // {"regrovl_qh_1000_10001_4", "OvlTop", ".silo"}, - // uncomment once silo ucdmesh phzones are supported - {"utpyr4", "OvlTop", ".silo"}, - }; - - for (int i = 0; i < file_info.size(); i ++) - { - const std::string dirname = file_info[i][0]; - const std::string basename = file_info[i][1]; - const std::string fileext = file_info[i][2]; - - Node load_mesh, info, write_opts; - std::string filepath = utils::join_file_path(dirname, basename) + fileext; - filepath = utils::join_file_path("fake_overlink", filepath); - std::string input_file = relay_test_silo_data_path(filepath); - - io::silo::load_mesh(input_file, load_mesh); - EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - - const std::string out_name = "read_fake_overlink_" + dirname; - - remove_path_if_exists(out_name + "_write_blueprint"); - io::blueprint::save_mesh(load_mesh, out_name + "_write_blueprint", "hdf5"); - - remove_path_if_exists(out_name + "_write_silo"); - io::silo::save_mesh(load_mesh, out_name + "_write_silo"); - - remove_path_if_exists(out_name + "_write_overlink"); - write_opts["file_style"] = "overlink"; - write_opts["ovl_topo_name"] = "MMESH"; - io::silo::save_mesh(load_mesh, out_name + "_write_overlink", write_opts); - } -} - -//----------------------------------------------------------------------------- -// read overlink files in symlink format -// should be similar to reading raw silo -TEST(conduit_relay_io_silo, read_overlink_symlink_format) -{ - const std::vector> file_info = { - {".", "box2d", ".silo"}, - {".", "box3d", ".silo"}, - // {".", "diamond", ".silo"}, - // fails b/c polytopal not yet supported - {".", "testDisk2D_a", ".silo"}, - // {".", "donordiv.s2_materials2", ".silo"}, - // fails b/c polytopal not yet supported - {".", "donordiv.s2_materials3", ".silo"}, - }; - - for (int i = 0; i < file_info.size(); i ++) - { - const std::string dirname = file_info[i][0]; - const std::string basename = file_info[i][1]; - const std::string fileext = file_info[i][2]; - - Node load_mesh, info, write_opts; - std::string filepath = utils::join_file_path(dirname, basename) + fileext; - filepath = utils::join_file_path("overlink", filepath); - std::string input_file = relay_test_silo_data_path(filepath); - - io::silo::load_mesh(input_file, load_mesh); - EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - - const std::string out_name = "read_overlink_symlink_" + basename; - - remove_path_if_exists(out_name + "_write_blueprint"); - io::blueprint::save_mesh(load_mesh, out_name + "_write_blueprint", "hdf5"); - - remove_path_if_exists(out_name + "_write_silo"); - io::silo::save_mesh(load_mesh, out_name + "_write_silo"); - - remove_path_if_exists(out_name + "_write_overlink"); - write_opts["file_style"] = "overlink"; - write_opts["ovl_topo_name"] = "MMESH"; - io::silo::save_mesh(load_mesh, out_name + "_write_overlink", write_opts); - } -} - -//----------------------------------------------------------------------------- -// read overlink directly from ovltop.silo -// this case is tricky and involves messing with paths -TEST(conduit_relay_io_silo, read_overlink_directly) -{ - const std::vector> file_info = { - {"box2d", "OvlTop", ".silo"}, - {"box3d", "OvlTop", ".silo"}, - // {"diamond", "OvlTop", ".silo"}, - {"testDisk2D_a", "OvlTop", ".silo"}, - // {"donordiv.s2_materials2", "OvlTop", ".silo"}, - {"donordiv.s2_materials3", "OvlTop", ".silo"}, - }; - - for (int i = 0; i < file_info.size(); i ++) - { - const std::string dirname = file_info[i][0]; - const std::string basename = file_info[i][1]; - const std::string fileext = file_info[i][2]; - - Node load_mesh, info, write_opts; - - std::string filepath = utils::join_file_path(dirname, basename) + fileext; - filepath = utils::join_file_path("overlink", filepath); - std::string input_file = relay_test_silo_data_path(filepath); - - io::silo::load_mesh(input_file, load_mesh); - EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - - const std::string out_name = "read_overlink_direct_" + dirname; - - remove_path_if_exists(out_name + "_write_blueprint"); - io::blueprint::save_mesh(load_mesh, out_name + "_write_blueprint", "hdf5"); - - remove_path_if_exists(out_name + "_write_silo"); - io::silo::save_mesh(load_mesh, out_name + "_write_silo"); - - remove_path_if_exists(out_name + "_write_overlink"); - write_opts["file_style"] = "overlink"; - write_opts["ovl_topo_name"] = "MMESH"; - io::silo::save_mesh(load_mesh, out_name + "_write_overlink", write_opts); - } -} - -// TODO add tests for... -// - polytopal meshes once they are supported -// - units once they are supported -// - etc. - -// TODO add tetra8 and c36_m5 to all the overlink i/o tests - -// TODO somewhere I need to error on overlink when there are different var or mesh types across domains +// //----------------------------------------------------------------------------- +// // test that we can read the fake overlink files from the visit test data +// TEST(conduit_relay_io_silo, read_fake_overlink) +// { +// const std::vector> file_info = { +// // {"ev_0_0_100", "OvlTop", ".silo"}, +// // uncomment once silo ucdmesh phzones are supported +// {"hl18spec", "OvlTop", ".silo"}, +// // {"regrovl_qh_1000_10001_4", "OvlTop", ".silo"}, +// // uncomment once silo ucdmesh phzones are supported +// {"utpyr4", "OvlTop", ".silo"}, +// }; + +// for (int i = 0; i < file_info.size(); i ++) +// { +// const std::string dirname = file_info[i][0]; +// const std::string basename = file_info[i][1]; +// const std::string fileext = file_info[i][2]; + +// Node load_mesh, info, write_opts; +// std::string filepath = utils::join_file_path(dirname, basename) + fileext; +// filepath = utils::join_file_path("fake_overlink", filepath); +// std::string input_file = relay_test_silo_data_path(filepath); + +// io::silo::load_mesh(input_file, load_mesh); +// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + +// const std::string out_name = "read_fake_overlink_" + dirname; + +// remove_path_if_exists(out_name + "_write_blueprint"); +// io::blueprint::save_mesh(load_mesh, out_name + "_write_blueprint", "hdf5"); + +// remove_path_if_exists(out_name + "_write_silo"); +// io::silo::save_mesh(load_mesh, out_name + "_write_silo"); + +// remove_path_if_exists(out_name + "_write_overlink"); +// write_opts["file_style"] = "overlink"; +// write_opts["ovl_topo_name"] = "MMESH"; +// io::silo::save_mesh(load_mesh, out_name + "_write_overlink", write_opts); +// } +// } + +// //----------------------------------------------------------------------------- +// // read overlink files in symlink format +// // should be similar to reading raw silo +// TEST(conduit_relay_io_silo, read_overlink_symlink_format) +// { +// const std::vector> file_info = { +// {".", "box2d", ".silo"}, +// {".", "box3d", ".silo"}, +// // {".", "diamond", ".silo"}, +// // fails b/c polytopal not yet supported +// {".", "testDisk2D_a", ".silo"}, +// // {".", "donordiv.s2_materials2", ".silo"}, +// // fails b/c polytopal not yet supported +// {".", "donordiv.s2_materials3", ".silo"}, +// }; + +// for (int i = 0; i < file_info.size(); i ++) +// { +// const std::string dirname = file_info[i][0]; +// const std::string basename = file_info[i][1]; +// const std::string fileext = file_info[i][2]; + +// Node load_mesh, info, write_opts; +// std::string filepath = utils::join_file_path(dirname, basename) + fileext; +// filepath = utils::join_file_path("overlink", filepath); +// std::string input_file = relay_test_silo_data_path(filepath); + +// io::silo::load_mesh(input_file, load_mesh); +// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + +// const std::string out_name = "read_overlink_symlink_" + basename; + +// remove_path_if_exists(out_name + "_write_blueprint"); +// io::blueprint::save_mesh(load_mesh, out_name + "_write_blueprint", "hdf5"); + +// remove_path_if_exists(out_name + "_write_silo"); +// io::silo::save_mesh(load_mesh, out_name + "_write_silo"); + +// remove_path_if_exists(out_name + "_write_overlink"); +// write_opts["file_style"] = "overlink"; +// write_opts["ovl_topo_name"] = "MMESH"; +// io::silo::save_mesh(load_mesh, out_name + "_write_overlink", write_opts); +// } +// } + +// //----------------------------------------------------------------------------- +// // read overlink directly from ovltop.silo +// // this case is tricky and involves messing with paths +// TEST(conduit_relay_io_silo, read_overlink_directly) +// { +// const std::vector> file_info = { +// {"box2d", "OvlTop", ".silo"}, +// {"box3d", "OvlTop", ".silo"}, +// // {"diamond", "OvlTop", ".silo"}, +// {"testDisk2D_a", "OvlTop", ".silo"}, +// // {"donordiv.s2_materials2", "OvlTop", ".silo"}, +// {"donordiv.s2_materials3", "OvlTop", ".silo"}, +// }; + +// for (int i = 0; i < file_info.size(); i ++) +// { +// const std::string dirname = file_info[i][0]; +// const std::string basename = file_info[i][1]; +// const std::string fileext = file_info[i][2]; + +// Node load_mesh, info, write_opts; + +// std::string filepath = utils::join_file_path(dirname, basename) + fileext; +// filepath = utils::join_file_path("overlink", filepath); +// std::string input_file = relay_test_silo_data_path(filepath); + +// io::silo::load_mesh(input_file, load_mesh); +// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + +// const std::string out_name = "read_overlink_direct_" + dirname; + +// remove_path_if_exists(out_name + "_write_blueprint"); +// io::blueprint::save_mesh(load_mesh, out_name + "_write_blueprint", "hdf5"); + +// remove_path_if_exists(out_name + "_write_silo"); +// io::silo::save_mesh(load_mesh, out_name + "_write_silo"); + +// remove_path_if_exists(out_name + "_write_overlink"); +// write_opts["file_style"] = "overlink"; +// write_opts["ovl_topo_name"] = "MMESH"; +// io::silo::save_mesh(load_mesh, out_name + "_write_overlink", write_opts); +// } +// } + +// // TODO add tests for... +// // - polytopal meshes once they are supported +// // - units once they are supported +// // - etc. + +// // TODO add tetra8 and c36_m5 to all the overlink i/o tests + +// // TODO somewhere I need to error on overlink when there are different var or mesh types across domains From f74a5078c02d89122795cce43d0567313ad171e3 Mon Sep 17 00:00:00 2001 From: Justin Privitera Date: Thu, 4 Apr 2024 16:18:39 -0700 Subject: [PATCH 19/56] I'm not convinced that what I am trying to do is possible in blueprint --- src/libs/relay/conduit_relay_io_silo.cpp | 26 ++- src/tests/relay/t_relay_io_silo.cpp | 280 +++++++++++------------ 2 files changed, 161 insertions(+), 145 deletions(-) diff --git a/src/libs/relay/conduit_relay_io_silo.cpp b/src/libs/relay/conduit_relay_io_silo.cpp index d6b0422af..9a8be5abb 100644 --- a/src/libs/relay/conduit_relay_io_silo.cpp +++ b/src/libs/relay/conduit_relay_io_silo.cpp @@ -1115,18 +1115,19 @@ read_quadmesh_domain(DBquadmesh *quadmesh_ptr, const int *dims = quadmesh_ptr->dims; const int *min_index = quadmesh_ptr->min_index; const int *max_index = quadmesh_ptr->max_index; + int origin[3] = {0,0,0}; // if min and max are in line with dims if (min_index[0] == 0 && max_index[0] == dims[0] - 1) { // We subtract 1 from each of these because in silo these dims are node dims, not element dims mesh_domain["topologies"][multimesh_name]["elements/dims/i"] = quadmesh_ptr->dims[0] - 1; - mesh_domain["topologies"][multimesh_name]["elements/origin/i"] = 0; + origin[0] = 0; } else { mesh_domain["topologies"][multimesh_name]["elements/dims/i"] = max_index[0] - min_index[0]; - mesh_domain["topologies"][multimesh_name]["elements/origin/i"] = min_index[0]; + origin[0] = min_index[0]; } if (ndims > 1) { @@ -1135,12 +1136,13 @@ read_quadmesh_domain(DBquadmesh *quadmesh_ptr, { // We subtract 1 from each of these because in silo these dims are node dims, not element dims mesh_domain["topologies"][multimesh_name]["elements/dims/j"] = quadmesh_ptr->dims[1] - 1; - mesh_domain["topologies"][multimesh_name]["elements/origin/j"] = 0; + origin[1] = 0; } else { mesh_domain["topologies"][multimesh_name]["elements/dims/j"] = max_index[1] - min_index[1]; mesh_domain["topologies"][multimesh_name]["elements/origin/j"] = min_index[1]; + origin[1] = min_index[1]; } } if (ndims > 2) @@ -1150,12 +1152,26 @@ read_quadmesh_domain(DBquadmesh *quadmesh_ptr, { // We subtract 1 from each of these because in silo these dims are node dims, not element dims mesh_domain["topologies"][multimesh_name]["elements/dims/k"] = quadmesh_ptr->dims[2] - 1; - mesh_domain["topologies"][multimesh_name]["elements/origin/k"] = 0; + origin[2] = 0; } else { mesh_domain["topologies"][multimesh_name]["elements/dims/k"] = max_index[2] - min_index[2]; - mesh_domain["topologies"][multimesh_name]["elements/origin/k"] = min_index[2]; + origin[2] = min_index[2]; + } + } + + // if we need to specify an origin + if (origin[0] != 0 || origin[1] != 0 || origin[2] != 0) + { + mesh_domain["topologies"][multimesh_name]["elements/origin/i"] = origin[0]; + if (ndims > 1) + { + mesh_domain["topologies"][multimesh_name]["elements/origin/j"] = origin[1]; + } + if (ndims > 2) + { + mesh_domain["topologies"][multimesh_name]["elements/origin/k"] = origin[2]; } } } diff --git a/src/tests/relay/t_relay_io_silo.cpp b/src/tests/relay/t_relay_io_silo.cpp index 3aa98905c..2f55b31c7 100644 --- a/src/tests/relay/t_relay_io_silo.cpp +++ b/src/tests/relay/t_relay_io_silo.cpp @@ -1824,7 +1824,7 @@ TEST(conduit_relay_io_silo, read_simple_silo) io::silo::load_mesh(input_file, load_mesh); EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - std::cout << load_mesh.to_yaml() << std::endl; + // std::cout << load_mesh.to_yaml() << std::endl; const std::string out_name = "read_silo_" + basename; @@ -1846,142 +1846,142 @@ TEST(conduit_relay_io_silo, read_simple_silo) } } -// //----------------------------------------------------------------------------- -// // test that we can read the fake overlink files from the visit test data -// TEST(conduit_relay_io_silo, read_fake_overlink) -// { -// const std::vector> file_info = { -// // {"ev_0_0_100", "OvlTop", ".silo"}, -// // uncomment once silo ucdmesh phzones are supported -// {"hl18spec", "OvlTop", ".silo"}, -// // {"regrovl_qh_1000_10001_4", "OvlTop", ".silo"}, -// // uncomment once silo ucdmesh phzones are supported -// {"utpyr4", "OvlTop", ".silo"}, -// }; - -// for (int i = 0; i < file_info.size(); i ++) -// { -// const std::string dirname = file_info[i][0]; -// const std::string basename = file_info[i][1]; -// const std::string fileext = file_info[i][2]; - -// Node load_mesh, info, write_opts; -// std::string filepath = utils::join_file_path(dirname, basename) + fileext; -// filepath = utils::join_file_path("fake_overlink", filepath); -// std::string input_file = relay_test_silo_data_path(filepath); - -// io::silo::load_mesh(input_file, load_mesh); -// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - -// const std::string out_name = "read_fake_overlink_" + dirname; - -// remove_path_if_exists(out_name + "_write_blueprint"); -// io::blueprint::save_mesh(load_mesh, out_name + "_write_blueprint", "hdf5"); - -// remove_path_if_exists(out_name + "_write_silo"); -// io::silo::save_mesh(load_mesh, out_name + "_write_silo"); - -// remove_path_if_exists(out_name + "_write_overlink"); -// write_opts["file_style"] = "overlink"; -// write_opts["ovl_topo_name"] = "MMESH"; -// io::silo::save_mesh(load_mesh, out_name + "_write_overlink", write_opts); -// } -// } - -// //----------------------------------------------------------------------------- -// // read overlink files in symlink format -// // should be similar to reading raw silo -// TEST(conduit_relay_io_silo, read_overlink_symlink_format) -// { -// const std::vector> file_info = { -// {".", "box2d", ".silo"}, -// {".", "box3d", ".silo"}, -// // {".", "diamond", ".silo"}, -// // fails b/c polytopal not yet supported -// {".", "testDisk2D_a", ".silo"}, -// // {".", "donordiv.s2_materials2", ".silo"}, -// // fails b/c polytopal not yet supported -// {".", "donordiv.s2_materials3", ".silo"}, -// }; - -// for (int i = 0; i < file_info.size(); i ++) -// { -// const std::string dirname = file_info[i][0]; -// const std::string basename = file_info[i][1]; -// const std::string fileext = file_info[i][2]; - -// Node load_mesh, info, write_opts; -// std::string filepath = utils::join_file_path(dirname, basename) + fileext; -// filepath = utils::join_file_path("overlink", filepath); -// std::string input_file = relay_test_silo_data_path(filepath); - -// io::silo::load_mesh(input_file, load_mesh); -// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - -// const std::string out_name = "read_overlink_symlink_" + basename; - -// remove_path_if_exists(out_name + "_write_blueprint"); -// io::blueprint::save_mesh(load_mesh, out_name + "_write_blueprint", "hdf5"); - -// remove_path_if_exists(out_name + "_write_silo"); -// io::silo::save_mesh(load_mesh, out_name + "_write_silo"); - -// remove_path_if_exists(out_name + "_write_overlink"); -// write_opts["file_style"] = "overlink"; -// write_opts["ovl_topo_name"] = "MMESH"; -// io::silo::save_mesh(load_mesh, out_name + "_write_overlink", write_opts); -// } -// } - -// //----------------------------------------------------------------------------- -// // read overlink directly from ovltop.silo -// // this case is tricky and involves messing with paths -// TEST(conduit_relay_io_silo, read_overlink_directly) -// { -// const std::vector> file_info = { -// {"box2d", "OvlTop", ".silo"}, -// {"box3d", "OvlTop", ".silo"}, -// // {"diamond", "OvlTop", ".silo"}, -// {"testDisk2D_a", "OvlTop", ".silo"}, -// // {"donordiv.s2_materials2", "OvlTop", ".silo"}, -// {"donordiv.s2_materials3", "OvlTop", ".silo"}, -// }; - -// for (int i = 0; i < file_info.size(); i ++) -// { -// const std::string dirname = file_info[i][0]; -// const std::string basename = file_info[i][1]; -// const std::string fileext = file_info[i][2]; - -// Node load_mesh, info, write_opts; - -// std::string filepath = utils::join_file_path(dirname, basename) + fileext; -// filepath = utils::join_file_path("overlink", filepath); -// std::string input_file = relay_test_silo_data_path(filepath); - -// io::silo::load_mesh(input_file, load_mesh); -// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - -// const std::string out_name = "read_overlink_direct_" + dirname; - -// remove_path_if_exists(out_name + "_write_blueprint"); -// io::blueprint::save_mesh(load_mesh, out_name + "_write_blueprint", "hdf5"); - -// remove_path_if_exists(out_name + "_write_silo"); -// io::silo::save_mesh(load_mesh, out_name + "_write_silo"); - -// remove_path_if_exists(out_name + "_write_overlink"); -// write_opts["file_style"] = "overlink"; -// write_opts["ovl_topo_name"] = "MMESH"; -// io::silo::save_mesh(load_mesh, out_name + "_write_overlink", write_opts); -// } -// } - -// // TODO add tests for... -// // - polytopal meshes once they are supported -// // - units once they are supported -// // - etc. - -// // TODO add tetra8 and c36_m5 to all the overlink i/o tests - -// // TODO somewhere I need to error on overlink when there are different var or mesh types across domains +//----------------------------------------------------------------------------- +// test that we can read the fake overlink files from the visit test data +TEST(conduit_relay_io_silo, read_fake_overlink) +{ + const std::vector> file_info = { + // {"ev_0_0_100", "OvlTop", ".silo"}, + // uncomment once silo ucdmesh phzones are supported + {"hl18spec", "OvlTop", ".silo"}, + // {"regrovl_qh_1000_10001_4", "OvlTop", ".silo"}, + // uncomment once silo ucdmesh phzones are supported + {"utpyr4", "OvlTop", ".silo"}, + }; + + for (int i = 0; i < file_info.size(); i ++) + { + const std::string dirname = file_info[i][0]; + const std::string basename = file_info[i][1]; + const std::string fileext = file_info[i][2]; + + Node load_mesh, info, write_opts; + std::string filepath = utils::join_file_path(dirname, basename) + fileext; + filepath = utils::join_file_path("fake_overlink", filepath); + std::string input_file = relay_test_silo_data_path(filepath); + + io::silo::load_mesh(input_file, load_mesh); + EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + + const std::string out_name = "read_fake_overlink_" + dirname; + + remove_path_if_exists(out_name + "_write_blueprint"); + io::blueprint::save_mesh(load_mesh, out_name + "_write_blueprint", "hdf5"); + + remove_path_if_exists(out_name + "_write_silo"); + io::silo::save_mesh(load_mesh, out_name + "_write_silo"); + + remove_path_if_exists(out_name + "_write_overlink"); + write_opts["file_style"] = "overlink"; + write_opts["ovl_topo_name"] = "MMESH"; + io::silo::save_mesh(load_mesh, out_name + "_write_overlink", write_opts); + } +} + +//----------------------------------------------------------------------------- +// read overlink files in symlink format +// should be similar to reading raw silo +TEST(conduit_relay_io_silo, read_overlink_symlink_format) +{ + const std::vector> file_info = { + {".", "box2d", ".silo"}, + {".", "box3d", ".silo"}, + // {".", "diamond", ".silo"}, + // fails b/c polytopal not yet supported + {".", "testDisk2D_a", ".silo"}, + // {".", "donordiv.s2_materials2", ".silo"}, + // fails b/c polytopal not yet supported + {".", "donordiv.s2_materials3", ".silo"}, + }; + + for (int i = 0; i < file_info.size(); i ++) + { + const std::string dirname = file_info[i][0]; + const std::string basename = file_info[i][1]; + const std::string fileext = file_info[i][2]; + + Node load_mesh, info, write_opts; + std::string filepath = utils::join_file_path(dirname, basename) + fileext; + filepath = utils::join_file_path("overlink", filepath); + std::string input_file = relay_test_silo_data_path(filepath); + + io::silo::load_mesh(input_file, load_mesh); + EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + + const std::string out_name = "read_overlink_symlink_" + basename; + + remove_path_if_exists(out_name + "_write_blueprint"); + io::blueprint::save_mesh(load_mesh, out_name + "_write_blueprint", "hdf5"); + + remove_path_if_exists(out_name + "_write_silo"); + io::silo::save_mesh(load_mesh, out_name + "_write_silo"); + + remove_path_if_exists(out_name + "_write_overlink"); + write_opts["file_style"] = "overlink"; + write_opts["ovl_topo_name"] = "MMESH"; + io::silo::save_mesh(load_mesh, out_name + "_write_overlink", write_opts); + } +} + +//----------------------------------------------------------------------------- +// read overlink directly from ovltop.silo +// this case is tricky and involves messing with paths +TEST(conduit_relay_io_silo, read_overlink_directly) +{ + const std::vector> file_info = { + {"box2d", "OvlTop", ".silo"}, + {"box3d", "OvlTop", ".silo"}, + // {"diamond", "OvlTop", ".silo"}, + {"testDisk2D_a", "OvlTop", ".silo"}, + // {"donordiv.s2_materials2", "OvlTop", ".silo"}, + {"donordiv.s2_materials3", "OvlTop", ".silo"}, + }; + + for (int i = 0; i < file_info.size(); i ++) + { + const std::string dirname = file_info[i][0]; + const std::string basename = file_info[i][1]; + const std::string fileext = file_info[i][2]; + + Node load_mesh, info, write_opts; + + std::string filepath = utils::join_file_path(dirname, basename) + fileext; + filepath = utils::join_file_path("overlink", filepath); + std::string input_file = relay_test_silo_data_path(filepath); + + io::silo::load_mesh(input_file, load_mesh); + EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + + const std::string out_name = "read_overlink_direct_" + dirname; + + remove_path_if_exists(out_name + "_write_blueprint"); + io::blueprint::save_mesh(load_mesh, out_name + "_write_blueprint", "hdf5"); + + remove_path_if_exists(out_name + "_write_silo"); + io::silo::save_mesh(load_mesh, out_name + "_write_silo"); + + remove_path_if_exists(out_name + "_write_overlink"); + write_opts["file_style"] = "overlink"; + write_opts["ovl_topo_name"] = "MMESH"; + io::silo::save_mesh(load_mesh, out_name + "_write_overlink", write_opts); + } +} + +// TODO add tests for... +// - polytopal meshes once they are supported +// - units once they are supported +// - etc. + +// TODO add tetra8 and c36_m5 to all the overlink i/o tests + +// TODO somewhere I need to error on overlink when there are different var or mesh types across domains From 0a4076525cc80fb74a82669ce814322587fe645a Mon Sep 17 00:00:00 2001 From: Justin Privitera Date: Thu, 4 Apr 2024 16:20:32 -0700 Subject: [PATCH 20/56] restore old status quo --- src/libs/relay/conduit_relay_io_silo.cpp | 60 ++---------------------- src/tests/relay/t_relay_io_silo.cpp | 8 ++-- 2 files changed, 7 insertions(+), 61 deletions(-) diff --git a/src/libs/relay/conduit_relay_io_silo.cpp b/src/libs/relay/conduit_relay_io_silo.cpp index 9a8be5abb..22a43f1e3 100644 --- a/src/libs/relay/conduit_relay_io_silo.cpp +++ b/src/libs/relay/conduit_relay_io_silo.cpp @@ -1112,67 +1112,15 @@ read_quadmesh_domain(DBquadmesh *quadmesh_ptr, mesh_domain["coordsets"][multimesh_name]["type"] = "explicit"; mesh_domain["topologies"][multimesh_name]["type"] = "structured"; - const int *dims = quadmesh_ptr->dims; - const int *min_index = quadmesh_ptr->min_index; - const int *max_index = quadmesh_ptr->max_index; - int origin[3] = {0,0,0}; - - // if min and max are in line with dims - if (min_index[0] == 0 && max_index[0] == dims[0] - 1) - { - // We subtract 1 from each of these because in silo these dims are node dims, not element dims - mesh_domain["topologies"][multimesh_name]["elements/dims/i"] = quadmesh_ptr->dims[0] - 1; - origin[0] = 0; - } - else - { - mesh_domain["topologies"][multimesh_name]["elements/dims/i"] = max_index[0] - min_index[0]; - origin[0] = min_index[0]; - } + // We subtract 1 from each of these because in silo these dims are node dims, not element dims + mesh_domain["topologies"][multimesh_name]["elements/dims/i"] = quadmesh_ptr->dims[0] - 1; if (ndims > 1) { - // if min and max are in line with dims - if (min_index[1] == 0 && max_index[1] == dims[1] - 1) - { - // We subtract 1 from each of these because in silo these dims are node dims, not element dims - mesh_domain["topologies"][multimesh_name]["elements/dims/j"] = quadmesh_ptr->dims[1] - 1; - origin[1] = 0; - } - else - { - mesh_domain["topologies"][multimesh_name]["elements/dims/j"] = max_index[1] - min_index[1]; - mesh_domain["topologies"][multimesh_name]["elements/origin/j"] = min_index[1]; - origin[1] = min_index[1]; - } + mesh_domain["topologies"][multimesh_name]["elements/dims/j"] = quadmesh_ptr->dims[1] - 1; } if (ndims > 2) { - // if min and max are in line with dims - if (min_index[2] == 0 && max_index[2] == dims[2] - 1) - { - // We subtract 1 from each of these because in silo these dims are node dims, not element dims - mesh_domain["topologies"][multimesh_name]["elements/dims/k"] = quadmesh_ptr->dims[2] - 1; - origin[2] = 0; - } - else - { - mesh_domain["topologies"][multimesh_name]["elements/dims/k"] = max_index[2] - min_index[2]; - origin[2] = min_index[2]; - } - } - - // if we need to specify an origin - if (origin[0] != 0 || origin[1] != 0 || origin[2] != 0) - { - mesh_domain["topologies"][multimesh_name]["elements/origin/i"] = origin[0]; - if (ndims > 1) - { - mesh_domain["topologies"][multimesh_name]["elements/origin/j"] = origin[1]; - } - if (ndims > 2) - { - mesh_domain["topologies"][multimesh_name]["elements/origin/k"] = origin[2]; - } + mesh_domain["topologies"][multimesh_name]["elements/dims/k"] = quadmesh_ptr->dims[2] - 1; } } else diff --git a/src/tests/relay/t_relay_io_silo.cpp b/src/tests/relay/t_relay_io_silo.cpp index 2f55b31c7..bb83b3655 100644 --- a/src/tests/relay/t_relay_io_silo.cpp +++ b/src/tests/relay/t_relay_io_silo.cpp @@ -1806,9 +1806,9 @@ TEST(conduit_relay_io_silo, read_simple_silo) { const std::vector> file_info = { {"curv2d", ".silo"}, - // {"curv2d_colmajor", ".silo"}, - // {"curv3d", ".silo"}, - // {"curv3d_colmajor", ".silo"}, + {"curv2d_colmajor", ".silo"}, + {"curv3d", ".silo"}, + {"curv3d_colmajor", ".silo"}, // {"globe", ".silo"}, // TODO need to fix a known bug for this one to work }; for (int i = 0; i < file_info.size(); i ++) @@ -1824,8 +1824,6 @@ TEST(conduit_relay_io_silo, read_simple_silo) io::silo::load_mesh(input_file, load_mesh); EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - // std::cout << load_mesh.to_yaml() << std::endl; - const std::string out_name = "read_silo_" + basename; // TODO are these remove paths doing anything? Don't they need filenames? From 655f63c5c97a0d8b86482e7c5937b732d75b9e83 Mon Sep 17 00:00:00 2001 From: Justin Privitera Date: Fri, 5 Apr 2024 14:45:16 -0700 Subject: [PATCH 21/56] HAH I got it --- src/libs/relay/conduit_relay_io_silo.cpp | 41 +++- src/tests/relay/t_relay_io_silo.cpp | 291 ++++++++++++----------- 2 files changed, 188 insertions(+), 144 deletions(-) diff --git a/src/libs/relay/conduit_relay_io_silo.cpp b/src/libs/relay/conduit_relay_io_silo.cpp index 22a43f1e3..840935082 100644 --- a/src/libs/relay/conduit_relay_io_silo.cpp +++ b/src/libs/relay/conduit_relay_io_silo.cpp @@ -1094,18 +1094,35 @@ read_quadmesh_domain(DBquadmesh *quadmesh_ptr, const std::string &multimesh_name, Node &mesh_domain) { - int coordtype{quadmesh_ptr->coordtype}; + const int coordtype{quadmesh_ptr->coordtype}; int ndims{quadmesh_ptr->ndims}; int dims[] = {quadmesh_ptr->nnodes, quadmesh_ptr->nnodes, quadmesh_ptr->nnodes}; int *real_dims = dims; + auto check_using_whole_coordset = [](const DBquadmesh *quadmesh_ptr, + const int ndims) + { + const int *dims = quadmesh_ptr->dims; + const int *min_index = quadmesh_ptr->min_index; + const int *max_index = quadmesh_ptr->max_index; + const bool dim0_ok = (min_index[0] == 0 && max_index[0] == dims[0] - 1); + const bool dim1_ok = (ndims > 1 ? (min_index[1] == 0 && max_index[1] == dims[1] - 1) : true); + const bool dim2_ok = (ndims > 2 ? (min_index[2] == 0 && max_index[2] == dims[2] - 1) : true); + return dim0_ok && dim1_ok && dim2_ok; + }; + if (coordtype == DB_COLLINEAR) { mesh_domain["coordsets"][multimesh_name]["type"] = "rectilinear"; mesh_domain["topologies"][multimesh_name]["type"] = "rectilinear"; real_dims = quadmesh_ptr->dims; + + if (! check_using_whole_coordset(quadmesh_ptr, ndims)) + { + CONDUIT_ERROR("TODO what do I do in this case"); + } } else if (coordtype == DB_NONCOLLINEAR) { @@ -1122,12 +1139,34 @@ read_quadmesh_domain(DBquadmesh *quadmesh_ptr, { mesh_domain["topologies"][multimesh_name]["elements/dims/k"] = quadmesh_ptr->dims[2] - 1; } + + if (! check_using_whole_coordset(quadmesh_ptr, ndims)) + { + std::cout << "hello i am here" << std::endl; + const int *offsets = quadmesh_ptr->min_index; + // strided structured case + mesh_domain["topologies"][multimesh_name]["elements/dims/offsets"].set(offsets, ndims); + mesh_domain["topologies"][multimesh_name]["elements/dims/strides"].set(quadmesh_ptr->stride, ndims); + + + mesh_domain["topologies"][multimesh_name]["elements/dims/i"] = quadmesh_ptr->max_index[0] - quadmesh_ptr->min_index[0]; + if (ndims > 1) + { + mesh_domain["topologies"][multimesh_name]["elements/dims/j"] = quadmesh_ptr->max_index[1] - quadmesh_ptr->min_index[1]; + } + if (ndims > 2) + { + mesh_domain["topologies"][multimesh_name]["elements/dims/k"] = quadmesh_ptr->max_index[2] - quadmesh_ptr->min_index[2]; + } + } } else { CONDUIT_ERROR("Undefined coordtype in " << coordtype); } + const int major_order = quadmesh_ptr->major_order; + mesh_domain["topologies"][multimesh_name]["coordset"] = multimesh_name; // If the origin is not the default value, then we need to specify it diff --git a/src/tests/relay/t_relay_io_silo.cpp b/src/tests/relay/t_relay_io_silo.cpp index bb83b3655..d92018caa 100644 --- a/src/tests/relay/t_relay_io_silo.cpp +++ b/src/tests/relay/t_relay_io_silo.cpp @@ -1806,10 +1806,10 @@ TEST(conduit_relay_io_silo, read_simple_silo) { const std::vector> file_info = { {"curv2d", ".silo"}, - {"curv2d_colmajor", ".silo"}, - {"curv3d", ".silo"}, - {"curv3d_colmajor", ".silo"}, - // {"globe", ".silo"}, // TODO need to fix a known bug for this one to work + // {"curv2d_colmajor", ".silo"}, // TODO look at strided structured to handle this case + // {"curv3d", ".silo"}, + // {"curv3d_colmajor", ".silo"}, + // // {"globe", ".silo"}, // TODO need to fix a known bug for this one to work }; for (int i = 0; i < file_info.size(); i ++) { @@ -1824,6 +1824,11 @@ TEST(conduit_relay_io_silo, read_simple_silo) io::silo::load_mesh(input_file, load_mesh); EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + // load_mesh[0]["topologies"]["curvmesh2d"]["elements"]["dims"]["i"] = 3; + // load_mesh[0]["topologies"]["curvmesh2d"]["elements"]["dims"]["j"] = 3; + + load_mesh.print(); + const std::string out_name = "read_silo_" + basename; // TODO are these remove paths doing anything? Don't they need filenames? @@ -1844,142 +1849,142 @@ TEST(conduit_relay_io_silo, read_simple_silo) } } -//----------------------------------------------------------------------------- -// test that we can read the fake overlink files from the visit test data -TEST(conduit_relay_io_silo, read_fake_overlink) -{ - const std::vector> file_info = { - // {"ev_0_0_100", "OvlTop", ".silo"}, - // uncomment once silo ucdmesh phzones are supported - {"hl18spec", "OvlTop", ".silo"}, - // {"regrovl_qh_1000_10001_4", "OvlTop", ".silo"}, - // uncomment once silo ucdmesh phzones are supported - {"utpyr4", "OvlTop", ".silo"}, - }; - - for (int i = 0; i < file_info.size(); i ++) - { - const std::string dirname = file_info[i][0]; - const std::string basename = file_info[i][1]; - const std::string fileext = file_info[i][2]; - - Node load_mesh, info, write_opts; - std::string filepath = utils::join_file_path(dirname, basename) + fileext; - filepath = utils::join_file_path("fake_overlink", filepath); - std::string input_file = relay_test_silo_data_path(filepath); - - io::silo::load_mesh(input_file, load_mesh); - EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - - const std::string out_name = "read_fake_overlink_" + dirname; - - remove_path_if_exists(out_name + "_write_blueprint"); - io::blueprint::save_mesh(load_mesh, out_name + "_write_blueprint", "hdf5"); - - remove_path_if_exists(out_name + "_write_silo"); - io::silo::save_mesh(load_mesh, out_name + "_write_silo"); - - remove_path_if_exists(out_name + "_write_overlink"); - write_opts["file_style"] = "overlink"; - write_opts["ovl_topo_name"] = "MMESH"; - io::silo::save_mesh(load_mesh, out_name + "_write_overlink", write_opts); - } -} - -//----------------------------------------------------------------------------- -// read overlink files in symlink format -// should be similar to reading raw silo -TEST(conduit_relay_io_silo, read_overlink_symlink_format) -{ - const std::vector> file_info = { - {".", "box2d", ".silo"}, - {".", "box3d", ".silo"}, - // {".", "diamond", ".silo"}, - // fails b/c polytopal not yet supported - {".", "testDisk2D_a", ".silo"}, - // {".", "donordiv.s2_materials2", ".silo"}, - // fails b/c polytopal not yet supported - {".", "donordiv.s2_materials3", ".silo"}, - }; - - for (int i = 0; i < file_info.size(); i ++) - { - const std::string dirname = file_info[i][0]; - const std::string basename = file_info[i][1]; - const std::string fileext = file_info[i][2]; - - Node load_mesh, info, write_opts; - std::string filepath = utils::join_file_path(dirname, basename) + fileext; - filepath = utils::join_file_path("overlink", filepath); - std::string input_file = relay_test_silo_data_path(filepath); - - io::silo::load_mesh(input_file, load_mesh); - EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - - const std::string out_name = "read_overlink_symlink_" + basename; - - remove_path_if_exists(out_name + "_write_blueprint"); - io::blueprint::save_mesh(load_mesh, out_name + "_write_blueprint", "hdf5"); - - remove_path_if_exists(out_name + "_write_silo"); - io::silo::save_mesh(load_mesh, out_name + "_write_silo"); - - remove_path_if_exists(out_name + "_write_overlink"); - write_opts["file_style"] = "overlink"; - write_opts["ovl_topo_name"] = "MMESH"; - io::silo::save_mesh(load_mesh, out_name + "_write_overlink", write_opts); - } -} - -//----------------------------------------------------------------------------- -// read overlink directly from ovltop.silo -// this case is tricky and involves messing with paths -TEST(conduit_relay_io_silo, read_overlink_directly) -{ - const std::vector> file_info = { - {"box2d", "OvlTop", ".silo"}, - {"box3d", "OvlTop", ".silo"}, - // {"diamond", "OvlTop", ".silo"}, - {"testDisk2D_a", "OvlTop", ".silo"}, - // {"donordiv.s2_materials2", "OvlTop", ".silo"}, - {"donordiv.s2_materials3", "OvlTop", ".silo"}, - }; - - for (int i = 0; i < file_info.size(); i ++) - { - const std::string dirname = file_info[i][0]; - const std::string basename = file_info[i][1]; - const std::string fileext = file_info[i][2]; - - Node load_mesh, info, write_opts; - - std::string filepath = utils::join_file_path(dirname, basename) + fileext; - filepath = utils::join_file_path("overlink", filepath); - std::string input_file = relay_test_silo_data_path(filepath); - - io::silo::load_mesh(input_file, load_mesh); - EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - - const std::string out_name = "read_overlink_direct_" + dirname; - - remove_path_if_exists(out_name + "_write_blueprint"); - io::blueprint::save_mesh(load_mesh, out_name + "_write_blueprint", "hdf5"); - - remove_path_if_exists(out_name + "_write_silo"); - io::silo::save_mesh(load_mesh, out_name + "_write_silo"); - - remove_path_if_exists(out_name + "_write_overlink"); - write_opts["file_style"] = "overlink"; - write_opts["ovl_topo_name"] = "MMESH"; - io::silo::save_mesh(load_mesh, out_name + "_write_overlink", write_opts); - } -} - -// TODO add tests for... -// - polytopal meshes once they are supported -// - units once they are supported -// - etc. - -// TODO add tetra8 and c36_m5 to all the overlink i/o tests - -// TODO somewhere I need to error on overlink when there are different var or mesh types across domains +// //----------------------------------------------------------------------------- +// // test that we can read the fake overlink files from the visit test data +// TEST(conduit_relay_io_silo, read_fake_overlink) +// { +// const std::vector> file_info = { +// // {"ev_0_0_100", "OvlTop", ".silo"}, +// // uncomment once silo ucdmesh phzones are supported +// {"hl18spec", "OvlTop", ".silo"}, +// // {"regrovl_qh_1000_10001_4", "OvlTop", ".silo"}, +// // uncomment once silo ucdmesh phzones are supported +// {"utpyr4", "OvlTop", ".silo"}, +// }; + +// for (int i = 0; i < file_info.size(); i ++) +// { +// const std::string dirname = file_info[i][0]; +// const std::string basename = file_info[i][1]; +// const std::string fileext = file_info[i][2]; + +// Node load_mesh, info, write_opts; +// std::string filepath = utils::join_file_path(dirname, basename) + fileext; +// filepath = utils::join_file_path("fake_overlink", filepath); +// std::string input_file = relay_test_silo_data_path(filepath); + +// io::silo::load_mesh(input_file, load_mesh); +// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + +// const std::string out_name = "read_fake_overlink_" + dirname; + +// remove_path_if_exists(out_name + "_write_blueprint"); +// io::blueprint::save_mesh(load_mesh, out_name + "_write_blueprint", "hdf5"); + +// remove_path_if_exists(out_name + "_write_silo"); +// io::silo::save_mesh(load_mesh, out_name + "_write_silo"); + +// remove_path_if_exists(out_name + "_write_overlink"); +// write_opts["file_style"] = "overlink"; +// write_opts["ovl_topo_name"] = "MMESH"; +// io::silo::save_mesh(load_mesh, out_name + "_write_overlink", write_opts); +// } +// } + +// //----------------------------------------------------------------------------- +// // read overlink files in symlink format +// // should be similar to reading raw silo +// TEST(conduit_relay_io_silo, read_overlink_symlink_format) +// { +// const std::vector> file_info = { +// {".", "box2d", ".silo"}, +// {".", "box3d", ".silo"}, +// // {".", "diamond", ".silo"}, +// // fails b/c polytopal not yet supported +// {".", "testDisk2D_a", ".silo"}, +// // {".", "donordiv.s2_materials2", ".silo"}, +// // fails b/c polytopal not yet supported +// {".", "donordiv.s2_materials3", ".silo"}, +// }; + +// for (int i = 0; i < file_info.size(); i ++) +// { +// const std::string dirname = file_info[i][0]; +// const std::string basename = file_info[i][1]; +// const std::string fileext = file_info[i][2]; + +// Node load_mesh, info, write_opts; +// std::string filepath = utils::join_file_path(dirname, basename) + fileext; +// filepath = utils::join_file_path("overlink", filepath); +// std::string input_file = relay_test_silo_data_path(filepath); + +// io::silo::load_mesh(input_file, load_mesh); +// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + +// const std::string out_name = "read_overlink_symlink_" + basename; + +// remove_path_if_exists(out_name + "_write_blueprint"); +// io::blueprint::save_mesh(load_mesh, out_name + "_write_blueprint", "hdf5"); + +// remove_path_if_exists(out_name + "_write_silo"); +// io::silo::save_mesh(load_mesh, out_name + "_write_silo"); + +// remove_path_if_exists(out_name + "_write_overlink"); +// write_opts["file_style"] = "overlink"; +// write_opts["ovl_topo_name"] = "MMESH"; +// io::silo::save_mesh(load_mesh, out_name + "_write_overlink", write_opts); +// } +// } + +// //----------------------------------------------------------------------------- +// // read overlink directly from ovltop.silo +// // this case is tricky and involves messing with paths +// TEST(conduit_relay_io_silo, read_overlink_directly) +// { +// const std::vector> file_info = { +// {"box2d", "OvlTop", ".silo"}, +// {"box3d", "OvlTop", ".silo"}, +// // {"diamond", "OvlTop", ".silo"}, +// {"testDisk2D_a", "OvlTop", ".silo"}, +// // {"donordiv.s2_materials2", "OvlTop", ".silo"}, +// {"donordiv.s2_materials3", "OvlTop", ".silo"}, +// }; + +// for (int i = 0; i < file_info.size(); i ++) +// { +// const std::string dirname = file_info[i][0]; +// const std::string basename = file_info[i][1]; +// const std::string fileext = file_info[i][2]; + +// Node load_mesh, info, write_opts; + +// std::string filepath = utils::join_file_path(dirname, basename) + fileext; +// filepath = utils::join_file_path("overlink", filepath); +// std::string input_file = relay_test_silo_data_path(filepath); + +// io::silo::load_mesh(input_file, load_mesh); +// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + +// const std::string out_name = "read_overlink_direct_" + dirname; + +// remove_path_if_exists(out_name + "_write_blueprint"); +// io::blueprint::save_mesh(load_mesh, out_name + "_write_blueprint", "hdf5"); + +// remove_path_if_exists(out_name + "_write_silo"); +// io::silo::save_mesh(load_mesh, out_name + "_write_silo"); + +// remove_path_if_exists(out_name + "_write_overlink"); +// write_opts["file_style"] = "overlink"; +// write_opts["ovl_topo_name"] = "MMESH"; +// io::silo::save_mesh(load_mesh, out_name + "_write_overlink", write_opts); +// } +// } + +// // TODO add tests for... +// // - polytopal meshes once they are supported +// // - units once they are supported +// // - etc. + +// // TODO add tetra8 and c36_m5 to all the overlink i/o tests + +// // TODO somewhere I need to error on overlink when there are different var or mesh types across domains From a5e971a1c02aa1aa1ad7ec4cd36c59fb8043dd29 Mon Sep 17 00:00:00 2001 From: Justin Privitera Date: Fri, 5 Apr 2024 14:52:25 -0700 Subject: [PATCH 22/56] cleanup --- src/libs/relay/conduit_relay_io_silo.cpp | 54 ++++++++++++------------ src/tests/relay/t_relay_io_silo.cpp | 3 -- 2 files changed, 28 insertions(+), 29 deletions(-) diff --git a/src/libs/relay/conduit_relay_io_silo.cpp b/src/libs/relay/conduit_relay_io_silo.cpp index 840935082..a65145585 100644 --- a/src/libs/relay/conduit_relay_io_silo.cpp +++ b/src/libs/relay/conduit_relay_io_silo.cpp @@ -1113,10 +1113,13 @@ read_quadmesh_domain(DBquadmesh *quadmesh_ptr, return dim0_ok && dim1_ok && dim2_ok; }; + Node &topo_out = mesh_domain["topologies"][multimesh_name]; + Node &coords_out = mesh_domain["coordsets"][multimesh_name]; + if (coordtype == DB_COLLINEAR) { - mesh_domain["coordsets"][multimesh_name]["type"] = "rectilinear"; - mesh_domain["topologies"][multimesh_name]["type"] = "rectilinear"; + coords_out["type"] = "rectilinear"; + topo_out["type"] = "rectilinear"; real_dims = quadmesh_ptr->dims; if (! check_using_whole_coordset(quadmesh_ptr, ndims)) @@ -1126,37 +1129,36 @@ read_quadmesh_domain(DBquadmesh *quadmesh_ptr, } else if (coordtype == DB_NONCOLLINEAR) { - mesh_domain["coordsets"][multimesh_name]["type"] = "explicit"; - mesh_domain["topologies"][multimesh_name]["type"] = "structured"; - - // We subtract 1 from each of these because in silo these dims are node dims, not element dims - mesh_domain["topologies"][multimesh_name]["elements/dims/i"] = quadmesh_ptr->dims[0] - 1; - if (ndims > 1) - { - mesh_domain["topologies"][multimesh_name]["elements/dims/j"] = quadmesh_ptr->dims[1] - 1; - } - if (ndims > 2) - { - mesh_domain["topologies"][multimesh_name]["elements/dims/k"] = quadmesh_ptr->dims[2] - 1; - } + coords_out["type"] = "explicit"; + topo_out["type"] = "structured"; if (! check_using_whole_coordset(quadmesh_ptr, ndims)) { - std::cout << "hello i am here" << std::endl; - const int *offsets = quadmesh_ptr->min_index; // strided structured case - mesh_domain["topologies"][multimesh_name]["elements/dims/offsets"].set(offsets, ndims); - mesh_domain["topologies"][multimesh_name]["elements/dims/strides"].set(quadmesh_ptr->stride, ndims); - + topo_out["elements/dims/i"] = quadmesh_ptr->max_index[0] - quadmesh_ptr->min_index[0]; + if (ndims > 1) + { + topo_out["elements/dims/j"] = quadmesh_ptr->max_index[1] - quadmesh_ptr->min_index[1]; + } + if (ndims > 2) + { + topo_out["elements/dims/k"] = quadmesh_ptr->max_index[2] - quadmesh_ptr->min_index[2]; + } - mesh_domain["topologies"][multimesh_name]["elements/dims/i"] = quadmesh_ptr->max_index[0] - quadmesh_ptr->min_index[0]; + topo_out["elements/dims/offsets"].set(quadmesh_ptr->min_index, ndims); + topo_out["elements/dims/strides"].set(quadmesh_ptr->stride, ndims); + } + else + { + // We subtract 1 from each of these because in silo these dims are node dims, not element dims + topo_out["elements/dims/i"] = quadmesh_ptr->dims[0] - 1; if (ndims > 1) { - mesh_domain["topologies"][multimesh_name]["elements/dims/j"] = quadmesh_ptr->max_index[1] - quadmesh_ptr->min_index[1]; + topo_out["elements/dims/j"] = quadmesh_ptr->dims[1] - 1; } if (ndims > 2) { - mesh_domain["topologies"][multimesh_name]["elements/dims/k"] = quadmesh_ptr->max_index[2] - quadmesh_ptr->min_index[2]; + topo_out["elements/dims/k"] = quadmesh_ptr->dims[2] - 1; } } } @@ -1167,14 +1169,14 @@ read_quadmesh_domain(DBquadmesh *quadmesh_ptr, const int major_order = quadmesh_ptr->major_order; - mesh_domain["topologies"][multimesh_name]["coordset"] = multimesh_name; + topo_out["coordset"] = multimesh_name; // If the origin is not the default value, then we need to specify it if (quadmesh_ptr->base_index[0] != 0 && quadmesh_ptr->base_index[1] != 0 && quadmesh_ptr->base_index[2] != 0) { - Node &origin = mesh_domain["topologies"][multimesh_name]["elements"]["origin"]; + Node &origin = topo_out["elements"]["origin"]; origin["i"] = quadmesh_ptr->base_index[0]; if (ndims > 1) { @@ -1191,7 +1193,7 @@ read_quadmesh_domain(DBquadmesh *quadmesh_ptr, ndims, real_dims, quadmesh_ptr->coord_sys, - mesh_domain["coordsets"][multimesh_name]["values"]); + coords_out["values"]); } //----------------------------------------------------------------------------- diff --git a/src/tests/relay/t_relay_io_silo.cpp b/src/tests/relay/t_relay_io_silo.cpp index d92018caa..5242f29f2 100644 --- a/src/tests/relay/t_relay_io_silo.cpp +++ b/src/tests/relay/t_relay_io_silo.cpp @@ -1824,9 +1824,6 @@ TEST(conduit_relay_io_silo, read_simple_silo) io::silo::load_mesh(input_file, load_mesh); EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - // load_mesh[0]["topologies"]["curvmesh2d"]["elements"]["dims"]["i"] = 3; - // load_mesh[0]["topologies"]["curvmesh2d"]["elements"]["dims"]["j"] = 3; - load_mesh.print(); const std::string out_name = "read_silo_" + basename; From 9ad64c5b63cffba099a4b7a98d164a16446828d4 Mon Sep 17 00:00:00 2001 From: Justin Privitera Date: Fri, 5 Apr 2024 15:37:39 -0700 Subject: [PATCH 23/56] now supports strided structured case --- src/libs/relay/conduit_relay_io_silo.cpp | 50 +++++++++++++++++------- 1 file changed, 36 insertions(+), 14 deletions(-) diff --git a/src/libs/relay/conduit_relay_io_silo.cpp b/src/libs/relay/conduit_relay_io_silo.cpp index a65145585..e25dad503 100644 --- a/src/libs/relay/conduit_relay_io_silo.cpp +++ b/src/libs/relay/conduit_relay_io_silo.cpp @@ -606,6 +606,19 @@ void convert_to_double_array(const Node &n_src, } } +//----------------------------------------------------------------------------- +bool +check_using_whole_coordset(const int *dims, + const int *min_index, + const int *max_index, + const int ndims) +{ + const bool dim0_ok = (min_index[0] == 0 && max_index[0] == dims[0] - 1); + const bool dim1_ok = (ndims > 1 ? (min_index[1] == 0 && max_index[1] == dims[1] - 1) : true); + const bool dim2_ok = (ndims > 2 ? (min_index[2] == 0 && max_index[2] == dims[2] - 1) : true); + return dim0_ok && dim1_ok && dim2_ok; +}; + //----------------------------------------------------------------------------- void copy_point_coords(const int datatype, @@ -1101,18 +1114,6 @@ read_quadmesh_domain(DBquadmesh *quadmesh_ptr, quadmesh_ptr->nnodes}; int *real_dims = dims; - auto check_using_whole_coordset = [](const DBquadmesh *quadmesh_ptr, - const int ndims) - { - const int *dims = quadmesh_ptr->dims; - const int *min_index = quadmesh_ptr->min_index; - const int *max_index = quadmesh_ptr->max_index; - const bool dim0_ok = (min_index[0] == 0 && max_index[0] == dims[0] - 1); - const bool dim1_ok = (ndims > 1 ? (min_index[1] == 0 && max_index[1] == dims[1] - 1) : true); - const bool dim2_ok = (ndims > 2 ? (min_index[2] == 0 && max_index[2] == dims[2] - 1) : true); - return dim0_ok && dim1_ok && dim2_ok; - }; - Node &topo_out = mesh_domain["topologies"][multimesh_name]; Node &coords_out = mesh_domain["coordsets"][multimesh_name]; @@ -1122,7 +1123,10 @@ read_quadmesh_domain(DBquadmesh *quadmesh_ptr, topo_out["type"] = "rectilinear"; real_dims = quadmesh_ptr->dims; - if (! check_using_whole_coordset(quadmesh_ptr, ndims)) + if (! detail::check_using_whole_coordset(quadmesh_ptr->dims, + quadmesh_ptr->min_index, + quadmesh_ptr->max_index, + ndims)) { CONDUIT_ERROR("TODO what do I do in this case"); } @@ -1132,7 +1136,10 @@ read_quadmesh_domain(DBquadmesh *quadmesh_ptr, coords_out["type"] = "explicit"; topo_out["type"] = "structured"; - if (! check_using_whole_coordset(quadmesh_ptr, ndims)) + if (! detail::check_using_whole_coordset(quadmesh_ptr->dims, + quadmesh_ptr->min_index, + quadmesh_ptr->max_index, + ndims)) { // strided structured case topo_out["elements/dims/i"] = quadmesh_ptr->max_index[0] - quadmesh_ptr->min_index[0]; @@ -1496,10 +1503,25 @@ read_variable_domain_helper(const T *var_ptr, else if (vartype == DB_QUADVAR) { intermediate_field["association"] = var_ptr->centering == DB_NODECENT ? "vertex" : "element"; + + const DBquadvar* quadvar_ptr = reinterpret_cast(var_ptr); + + // TODO what do we do if we aren't structured? Same q for dealing with meshes + // handle strided structured fields, if appropriate + if (! detail::check_using_whole_coordset(quadvar_ptr->dims, + quadvar_ptr->min_index, + quadvar_ptr->max_index, + quadvar_ptr->ndims)) + { + intermediate_field["offsets"].set(quadvar_ptr->min_index, quadvar_ptr->ndims); + intermediate_field["strides"].set(quadvar_ptr->stride, quadvar_ptr->ndims); + } } else // if (vartype == DB_POINTVAR) { intermediate_field["association"] = "vertex"; + + // TODO what to do about check_using_whole_coordset case? } // if we have volume dependence we can track it From a2cd55f2c5e2acb5577a6b05d0d832f1108d0805 Mon Sep 17 00:00:00 2001 From: Justin Privitera Date: Mon, 8 Apr 2024 15:03:31 -0700 Subject: [PATCH 24/56] sloppily write strided structured case --- src/libs/relay/conduit_relay_io_silo.cpp | 88 ++++++++++++++++++------ src/tests/relay/t_relay_io_silo.cpp | 3 + 2 files changed, 71 insertions(+), 20 deletions(-) diff --git a/src/libs/relay/conduit_relay_io_silo.cpp b/src/libs/relay/conduit_relay_io_silo.cpp index e25dad503..20929bbf4 100644 --- a/src/libs/relay/conduit_relay_io_silo.cpp +++ b/src/libs/relay/conduit_relay_io_silo.cpp @@ -4373,33 +4373,81 @@ void silo_write_structured_mesh(DBfile *dbfile, const bool write_overlink, Node &n_mesh_info) { + int pts_dims[3]; int ele_dims[3]; - ele_dims[0] = n_topo["elements/dims/i"].to_value(); - ele_dims[1] = n_topo["elements/dims/j"].to_value(); - ele_dims[2] = 0; - - index_t num_elems = ele_dims[0] * ele_dims[1]; + index_t num_elems; - if (n_topo["elements/dims"].has_path("k")) + // check for strided structured case + if (n_topo.has_path("elements/dims/offsets") && + n_topo.has_path("elements/dims/strides")) { - ele_dims[2] = n_topo["elements/dims/k"].to_value(); - num_elems *= ele_dims[2]; - } + // we must reverse engineer the dims from the strides and the num coords + const int num_pts = n_mesh_info[topo_name]["num_pts"].as_int(); - // silo needs the node dims to define a structured grid - int pts_dims[3]; - pts_dims[0] = ele_dims[0] + 1; - pts_dims[1] = ele_dims[1] + 1; - pts_dims[2] = 1; + // TODO this can't be right, what if strides[0] != 1? + + const int_accessor strides = n_topo["elements"]["dims"]["strides"].value(); - n_mesh_info[topo_name]["num_elems"].set(num_elems); - n_mesh_info[topo_name]["elements/i"] = ele_dims[0]; - n_mesh_info[topo_name]["elements/j"] = ele_dims[1]; + if (2 == ndims) + { + pts_dims[0] = strides[1]; + pts_dims[1] = num_pts / strides[1]; + + ele_dims[0] = pts_dims[0] - 1; + ele_dims[1] = pts_dims[1] - 1; + + num_elems = ele_dims[0] * ele_dims[1]; + } + else // 3 == ndims + { + pts_dims[0] = strides[1]; + pts_dims[1] = strides[2] / strides[1]; + pts_dims[2] = num_pts / strides[2]; + + ele_dims[0] = pts_dims[0] - 1; + ele_dims[1] = pts_dims[1] - 1; + ele_dims[2] = pts_dims[2] - 1; + + num_elems = ele_dims[0] * ele_dims[1] * ele_dims[2]; + } - if (ndims == 3) + n_mesh_info[topo_name]["num_elems"].set(num_elems); + n_mesh_info[topo_name]["elements/i"] = ele_dims[0]; + n_mesh_info[topo_name]["elements/j"] = ele_dims[1]; + + if (ndims == 3) + { + n_mesh_info[topo_name]["elements/k"] = ele_dims[2]; + } + } + else { - n_mesh_info[topo_name]["elements/k"] = ele_dims[2]; - pts_dims[2] = ele_dims[2] + 1; + ele_dims[0] = n_topo["elements/dims/i"].to_value(); + ele_dims[1] = n_topo["elements/dims/j"].to_value(); + ele_dims[2] = 0; + + num_elems = ele_dims[0] * ele_dims[1]; + + if (ndims == 3) + { + ele_dims[2] = n_topo["elements/dims/k"].to_value(); + num_elems *= ele_dims[2]; + } + + // silo needs the node dims to define a structured grid + pts_dims[0] = ele_dims[0] + 1; + pts_dims[1] = ele_dims[1] + 1; + pts_dims[2] = 1; + + n_mesh_info[topo_name]["num_elems"].set(num_elems); + n_mesh_info[topo_name]["elements/i"] = ele_dims[0]; + n_mesh_info[topo_name]["elements/j"] = ele_dims[1]; + + if (ndims == 3) + { + n_mesh_info[topo_name]["elements/k"] = ele_dims[2]; + pts_dims[2] = ele_dims[2] + 1; + } } int base_index[] = {0,0,0}; diff --git a/src/tests/relay/t_relay_io_silo.cpp b/src/tests/relay/t_relay_io_silo.cpp index 5242f29f2..150f8e1d1 100644 --- a/src/tests/relay/t_relay_io_silo.cpp +++ b/src/tests/relay/t_relay_io_silo.cpp @@ -1826,6 +1826,9 @@ TEST(conduit_relay_io_silo, read_simple_silo) load_mesh.print(); + std::cout << load_mesh[0]["coordsets/curvmesh2d/values/z"].dtype().number_of_elements() << std::endl; + std::cout << load_mesh[0]["coordsets/curvmesh2d/values/r"].dtype().number_of_elements() << std::endl; + const std::string out_name = "read_silo_" + basename; // TODO are these remove paths doing anything? Don't they need filenames? From c91dfc7bdb5c39fa6a205c030250068670d764c8 Mon Sep 17 00:00:00 2001 From: Justin Privitera Date: Mon, 8 Apr 2024 15:52:18 -0700 Subject: [PATCH 25/56] so close on strided structured to silo --- src/libs/relay/conduit_relay_io_silo.cpp | 36 ++++++++++++++++++++---- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/src/libs/relay/conduit_relay_io_silo.cpp b/src/libs/relay/conduit_relay_io_silo.cpp index 20929bbf4..17f3029da 100644 --- a/src/libs/relay/conduit_relay_io_silo.cpp +++ b/src/libs/relay/conduit_relay_io_silo.cpp @@ -4387,6 +4387,7 @@ void silo_write_structured_mesh(DBfile *dbfile, // TODO this can't be right, what if strides[0] != 1? const int_accessor strides = n_topo["elements"]["dims"]["strides"].value(); + const int_accessor offsets = n_topo["elements"]["dims"]["offsets"].value(); if (2 == ndims) { @@ -4415,10 +4416,35 @@ void silo_write_structured_mesh(DBfile *dbfile, n_mesh_info[topo_name]["elements/i"] = ele_dims[0]; n_mesh_info[topo_name]["elements/j"] = ele_dims[1]; - if (ndims == 3) + if (3 == ndims) { n_mesh_info[topo_name]["elements/k"] = ele_dims[2]; } + + int min_index[3]; + int max_index[3]; + + // TODO these numbers are not getting to silo correctly + // it is truly a mystery + min_index[0] = offsets[0]; + max_index[0] = offsets[0] + n_topo["elements"]["dims"]["i"].to_int(); + min_index[1] = offsets[1]; + max_index[1] = offsets[1] + n_topo["elements"]["dims"]["j"].to_int(); + if (3 == ndims) + { + min_index[2] = offsets[2]; + max_index[2] = offsets[2] + n_topo["elements"]["dims"]["k"].to_int(); + } + + CONDUIT_CHECK_SILO_ERROR(DBAddOption(optlist, + DBOPT_LO_OFFSET, + min_index), + "Error adding option"); + + CONDUIT_CHECK_SILO_ERROR(DBAddOption(optlist, + DBOPT_HI_OFFSET, + max_index), + "Error adding option"); } else { @@ -4461,10 +4487,10 @@ void silo_write_structured_mesh(DBfile *dbfile, } - CONDUIT_CHECK_SILO_ERROR( DBAddOption(optlist, - DBOPT_BASEINDEX, - base_index), - "Error adding option"); + CONDUIT_CHECK_SILO_ERROR(DBAddOption(optlist, + DBOPT_BASEINDEX, + base_index), + "Error adding option"); } const std::string safe_meshname = (write_overlink ? "MESH" : detail::sanitize_silo_varname(topo_name)); From b56fa811becc488227ce95f39c9539287ca48472 Mon Sep 17 00:00:00 2001 From: Justin Privitera Date: Tue, 9 Apr 2024 11:16:23 -0700 Subject: [PATCH 26/56] this isn't fair --- src/libs/relay/conduit_relay_io_silo.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/libs/relay/conduit_relay_io_silo.cpp b/src/libs/relay/conduit_relay_io_silo.cpp index 17f3029da..587d6d207 100644 --- a/src/libs/relay/conduit_relay_io_silo.cpp +++ b/src/libs/relay/conduit_relay_io_silo.cpp @@ -4373,9 +4373,12 @@ void silo_write_structured_mesh(DBfile *dbfile, const bool write_overlink, Node &n_mesh_info) { + // declare pointers out here to prevent losing them to scope int pts_dims[3]; int ele_dims[3]; index_t num_elems; + int min_index[3]; + int max_index[3]; // check for strided structured case if (n_topo.has_path("elements/dims/offsets") && @@ -4421,11 +4424,7 @@ void silo_write_structured_mesh(DBfile *dbfile, n_mesh_info[topo_name]["elements/k"] = ele_dims[2]; } - int min_index[3]; - int max_index[3]; - - // TODO these numbers are not getting to silo correctly - // it is truly a mystery + // TODO why aren't these values getting to silo correctly? min_index[0] = offsets[0]; max_index[0] = offsets[0] + n_topo["elements"]["dims"]["i"].to_int(); min_index[1] = offsets[1]; From 008810e4a21d534e74b003b0b917ef8426279779 Mon Sep 17 00:00:00 2001 From: Justin Privitera Date: Tue, 9 Apr 2024 11:31:21 -0700 Subject: [PATCH 27/56] I'm in jail --- src/libs/relay/conduit_relay_io_silo.cpp | 77 +++++++++++++++++++++--- src/tests/relay/t_relay_io_silo.cpp | 26 ++++---- 2 files changed, 80 insertions(+), 23 deletions(-) diff --git a/src/libs/relay/conduit_relay_io_silo.cpp b/src/libs/relay/conduit_relay_io_silo.cpp index 587d6d207..415dfe90c 100644 --- a/src/libs/relay/conduit_relay_io_silo.cpp +++ b/src/libs/relay/conduit_relay_io_silo.cpp @@ -1142,18 +1142,71 @@ read_quadmesh_domain(DBquadmesh *quadmesh_ptr, ndims)) { // strided structured case - topo_out["elements/dims/i"] = quadmesh_ptr->max_index[0] - quadmesh_ptr->min_index[0]; - if (ndims > 1) + + const int major_order = quadmesh_ptr->major_order; + if (major_order == DB_COLMAJOR) { - topo_out["elements/dims/j"] = quadmesh_ptr->max_index[1] - quadmesh_ptr->min_index[1]; + int flipped_strides[] = {0,0,0}; + int flipped_offsets[] = {0,0,0}; + int flipped_dims[] = {0,0,0}; + if (1 == ndims) + { + flipped_strides[0] = quadmesh_ptr->stride[0]; + + flipped_offsets[0] = quadmesh_ptr->min_index[0]; + + flipped_dims[0] = quadmesh_ptr->max_index[0] - quadmesh_ptr->min_index[0]; + topo_out["elements/dims/i"] = flipped_dims[0]; + } + else if (2 == ndims) + { + flipped_strides[0] = quadmesh_ptr->stride[1]; + flipped_strides[1] = quadmesh_ptr->stride[0]; + + flipped_offsets[0] = quadmesh_ptr->min_index[1]; + flipped_offsets[1] = quadmesh_ptr->min_index[0]; + + flipped_dims[0] = quadmesh_ptr->max_index[1] - quadmesh_ptr->min_index[1]; + flipped_dims[1] = quadmesh_ptr->max_index[0] - quadmesh_ptr->min_index[0]; + topo_out["elements/dims/i"] = flipped_dims[0]; + topo_out["elements/dims/j"] = flipped_dims[1]; + } + else // 3 == ndims + { + flipped_strides[0] = quadmesh_ptr->stride[2]; + flipped_strides[1] = quadmesh_ptr->stride[1]; + flipped_strides[2] = quadmesh_ptr->stride[0]; + + flipped_offsets[0] = quadmesh_ptr->min_index[2]; + flipped_offsets[1] = quadmesh_ptr->min_index[1]; + flipped_offsets[2] = quadmesh_ptr->min_index[0]; + + flipped_dims[0] = quadmesh_ptr->max_index[2] - quadmesh_ptr->min_index[2]; + flipped_dims[1] = quadmesh_ptr->max_index[1] - quadmesh_ptr->min_index[1]; + flipped_dims[2] = quadmesh_ptr->max_index[0] - quadmesh_ptr->min_index[0]; + + topo_out["elements/dims/i"] = flipped_dims[0]; + topo_out["elements/dims/j"] = flipped_dims[1]; + topo_out["elements/dims/k"] = flipped_dims[2]; + } + topo_out["elements/dims/offsets"].set(flipped_offsets, ndims); + topo_out["elements/dims/strides"].set(flipped_strides, ndims); } - if (ndims > 2) + else { - topo_out["elements/dims/k"] = quadmesh_ptr->max_index[2] - quadmesh_ptr->min_index[2]; - } + topo_out["elements/dims/i"] = quadmesh_ptr->max_index[0] - quadmesh_ptr->min_index[0]; + if (ndims > 1) + { + topo_out["elements/dims/j"] = quadmesh_ptr->max_index[1] - quadmesh_ptr->min_index[1]; + } + if (ndims > 2) + { + topo_out["elements/dims/k"] = quadmesh_ptr->max_index[2] - quadmesh_ptr->min_index[2]; + } - topo_out["elements/dims/offsets"].set(quadmesh_ptr->min_index, ndims); - topo_out["elements/dims/strides"].set(quadmesh_ptr->stride, ndims); + topo_out["elements/dims/offsets"].set(quadmesh_ptr->min_index, ndims); + topo_out["elements/dims/strides"].set(quadmesh_ptr->stride, ndims); + } } else { @@ -1167,6 +1220,12 @@ read_quadmesh_domain(DBquadmesh *quadmesh_ptr, { topo_out["elements/dims/k"] = quadmesh_ptr->dims[2] - 1; } + + const int major_order = quadmesh_ptr->major_order; + if (major_order == DB_COLMAJOR) + { + CONDUIT_ERROR("TODO what to do in this case"); + } } } else @@ -1174,8 +1233,6 @@ read_quadmesh_domain(DBquadmesh *quadmesh_ptr, CONDUIT_ERROR("Undefined coordtype in " << coordtype); } - const int major_order = quadmesh_ptr->major_order; - topo_out["coordset"] = multimesh_name; // If the origin is not the default value, then we need to specify it diff --git a/src/tests/relay/t_relay_io_silo.cpp b/src/tests/relay/t_relay_io_silo.cpp index 150f8e1d1..42319c385 100644 --- a/src/tests/relay/t_relay_io_silo.cpp +++ b/src/tests/relay/t_relay_io_silo.cpp @@ -1805,8 +1805,8 @@ TEST(conduit_relay_io_silo, read_silo) TEST(conduit_relay_io_silo, read_simple_silo) { const std::vector> file_info = { - {"curv2d", ".silo"}, - // {"curv2d_colmajor", ".silo"}, // TODO look at strided structured to handle this case + // {"curv2d", ".silo"}, + {"curv2d_colmajor", ".silo"}, // TODO look at strided structured to handle this case // {"curv3d", ".silo"}, // {"curv3d_colmajor", ".silo"}, // // {"globe", ".silo"}, // TODO need to fix a known bug for this one to work @@ -1835,17 +1835,17 @@ TEST(conduit_relay_io_silo, read_simple_silo) remove_path_if_exists(out_name + "_write_blueprint"); io::blueprint::save_mesh(load_mesh, out_name + "_write_blueprint", "hdf5"); - remove_path_if_exists(out_name + "_write_silo"); - io::silo::save_mesh(load_mesh, out_name + "_write_silo"); - - // overlink requires matsets - if (load_mesh[0].has_child("matsets")) - { - remove_path_if_exists(out_name + "_write_overlink"); - write_opts["file_style"] = "overlink"; - write_opts["ovl_topo_name"] = "MMESH"; // TODO do I even need this - io::silo::save_mesh(load_mesh, out_name + "_write_overlink", write_opts); - } + // remove_path_if_exists(out_name + "_write_silo"); + // io::silo::save_mesh(load_mesh, out_name + "_write_silo"); + + // // overlink requires matsets + // if (load_mesh[0].has_child("matsets")) + // { + // remove_path_if_exists(out_name + "_write_overlink"); + // write_opts["file_style"] = "overlink"; + // write_opts["ovl_topo_name"] = "MMESH"; // TODO do I even need this + // io::silo::save_mesh(load_mesh, out_name + "_write_overlink", write_opts); + // } } } From 0dad18e7f38d444b9ad441e73427e9710f6c1488 Mon Sep 17 00:00:00 2001 From: Justin Privitera Date: Wed, 10 Apr 2024 10:19:44 -0700 Subject: [PATCH 28/56] cyrus brings clarity to life --- src/libs/relay/conduit_relay_io_silo.cpp | 78 ++++--- src/tests/relay/t_relay_io_silo.cpp | 282 +++++++++++------------ 2 files changed, 191 insertions(+), 169 deletions(-) diff --git a/src/libs/relay/conduit_relay_io_silo.cpp b/src/libs/relay/conduit_relay_io_silo.cpp index 415dfe90c..dee04665d 100644 --- a/src/libs/relay/conduit_relay_io_silo.cpp +++ b/src/libs/relay/conduit_relay_io_silo.cpp @@ -1123,13 +1123,17 @@ read_quadmesh_domain(DBquadmesh *quadmesh_ptr, topo_out["type"] = "rectilinear"; real_dims = quadmesh_ptr->dims; - if (! detail::check_using_whole_coordset(quadmesh_ptr->dims, - quadmesh_ptr->min_index, - quadmesh_ptr->max_index, - ndims)) - { - CONDUIT_ERROR("TODO what do I do in this case"); - } + CONDUIT_ASSERT(detail::check_using_whole_coordset(quadmesh_ptr->dims, + quadmesh_ptr->min_index, + quadmesh_ptr->max_index, + ndims), + "Rectilinear grid (collinear quadmesh) " << multimesh_name << + " is using a subset of the provided coordinates. We do not " + "support this case."); + + CONDUIT_ASSERT(quadmesh_ptr->major_order == DB_ROWMAJOR, + "Rectilinear grid (collinear quadmesh) " << multimesh_name << + " is column major in silo. We do not support this case."); } else if (coordtype == DB_NONCOLLINEAR) { @@ -1146,30 +1150,42 @@ read_quadmesh_domain(DBquadmesh *quadmesh_ptr, const int major_order = quadmesh_ptr->major_order; if (major_order == DB_COLMAJOR) { + // TODO I don't know what I'm doing + topo_out["elements/dims/i"] = quadmesh_ptr->max_index[0] - quadmesh_ptr->min_index[0]; + if (ndims > 1) + { + topo_out["elements/dims/j"] = quadmesh_ptr->max_index[1] - quadmesh_ptr->min_index[1]; + } + if (ndims > 2) + { + topo_out["elements/dims/k"] = quadmesh_ptr->max_index[2] - quadmesh_ptr->min_index[2]; + } + + topo_out["elements/dims/offsets"].set(quadmesh_ptr->min_index, ndims); int flipped_strides[] = {0,0,0}; - int flipped_offsets[] = {0,0,0}; - int flipped_dims[] = {0,0,0}; + // int flipped_offsets[] = {0,0,0}; + // int flipped_dims[] = {0,0,0}; if (1 == ndims) { flipped_strides[0] = quadmesh_ptr->stride[0]; - flipped_offsets[0] = quadmesh_ptr->min_index[0]; + // flipped_offsets[0] = quadmesh_ptr->min_index[0]; - flipped_dims[0] = quadmesh_ptr->max_index[0] - quadmesh_ptr->min_index[0]; - topo_out["elements/dims/i"] = flipped_dims[0]; + // flipped_dims[0] = quadmesh_ptr->max_index[0] - quadmesh_ptr->min_index[0]; + // topo_out["elements/dims/i"] = flipped_dims[0]; } else if (2 == ndims) { flipped_strides[0] = quadmesh_ptr->stride[1]; flipped_strides[1] = quadmesh_ptr->stride[0]; - flipped_offsets[0] = quadmesh_ptr->min_index[1]; - flipped_offsets[1] = quadmesh_ptr->min_index[0]; + // flipped_offsets[0] = quadmesh_ptr->min_index[1]; + // flipped_offsets[1] = quadmesh_ptr->min_index[0]; - flipped_dims[0] = quadmesh_ptr->max_index[1] - quadmesh_ptr->min_index[1]; - flipped_dims[1] = quadmesh_ptr->max_index[0] - quadmesh_ptr->min_index[0]; - topo_out["elements/dims/i"] = flipped_dims[0]; - topo_out["elements/dims/j"] = flipped_dims[1]; + // flipped_dims[0] = quadmesh_ptr->max_index[1] - quadmesh_ptr->min_index[1]; + // flipped_dims[1] = quadmesh_ptr->max_index[0] - quadmesh_ptr->min_index[0]; + // topo_out["elements/dims/i"] = flipped_dims[0]; + // topo_out["elements/dims/j"] = flipped_dims[1]; } else // 3 == ndims { @@ -1177,19 +1193,19 @@ read_quadmesh_domain(DBquadmesh *quadmesh_ptr, flipped_strides[1] = quadmesh_ptr->stride[1]; flipped_strides[2] = quadmesh_ptr->stride[0]; - flipped_offsets[0] = quadmesh_ptr->min_index[2]; - flipped_offsets[1] = quadmesh_ptr->min_index[1]; - flipped_offsets[2] = quadmesh_ptr->min_index[0]; + // flipped_offsets[0] = quadmesh_ptr->min_index[2]; + // flipped_offsets[1] = quadmesh_ptr->min_index[1]; + // flipped_offsets[2] = quadmesh_ptr->min_index[0]; - flipped_dims[0] = quadmesh_ptr->max_index[2] - quadmesh_ptr->min_index[2]; - flipped_dims[1] = quadmesh_ptr->max_index[1] - quadmesh_ptr->min_index[1]; - flipped_dims[2] = quadmesh_ptr->max_index[0] - quadmesh_ptr->min_index[0]; + // flipped_dims[0] = quadmesh_ptr->max_index[2] - quadmesh_ptr->min_index[2]; + // flipped_dims[1] = quadmesh_ptr->max_index[1] - quadmesh_ptr->min_index[1]; + // flipped_dims[2] = quadmesh_ptr->max_index[0] - quadmesh_ptr->min_index[0]; - topo_out["elements/dims/i"] = flipped_dims[0]; - topo_out["elements/dims/j"] = flipped_dims[1]; - topo_out["elements/dims/k"] = flipped_dims[2]; + // topo_out["elements/dims/i"] = flipped_dims[0]; + // topo_out["elements/dims/j"] = flipped_dims[1]; + // topo_out["elements/dims/k"] = flipped_dims[2]; } - topo_out["elements/dims/offsets"].set(flipped_offsets, ndims); + // topo_out["elements/dims/offsets"].set(flipped_offsets, ndims); topo_out["elements/dims/strides"].set(flipped_strides, ndims); } else @@ -2623,6 +2639,9 @@ read_root_silo_index(const std::string &root_file_path, return false; } + // TODO unify lists of meshes and vars + // then be smart about reading + // handle legacy mesh_name argument Node real_opts; if (opts.has_child("mesh_name")) @@ -4441,6 +4460,9 @@ void silo_write_structured_mesh(DBfile *dbfile, if (n_topo.has_path("elements/dims/offsets") && n_topo.has_path("elements/dims/strides")) { + // TODO this case is BUSTED - cyrus says punt on this + + // we must reverse engineer the dims from the strides and the num coords const int num_pts = n_mesh_info[topo_name]["num_pts"].as_int(); diff --git a/src/tests/relay/t_relay_io_silo.cpp b/src/tests/relay/t_relay_io_silo.cpp index 42319c385..a76168597 100644 --- a/src/tests/relay/t_relay_io_silo.cpp +++ b/src/tests/relay/t_relay_io_silo.cpp @@ -1805,8 +1805,8 @@ TEST(conduit_relay_io_silo, read_silo) TEST(conduit_relay_io_silo, read_simple_silo) { const std::vector> file_info = { - // {"curv2d", ".silo"}, - {"curv2d_colmajor", ".silo"}, // TODO look at strided structured to handle this case + {"curv2d", ".silo"}, + // {"curv2d_colmajor", ".silo"}, // TODO look at strided structured to handle this case // {"curv3d", ".silo"}, // {"curv3d_colmajor", ".silo"}, // // {"globe", ".silo"}, // TODO need to fix a known bug for this one to work @@ -1849,142 +1849,142 @@ TEST(conduit_relay_io_silo, read_simple_silo) } } -// //----------------------------------------------------------------------------- -// // test that we can read the fake overlink files from the visit test data -// TEST(conduit_relay_io_silo, read_fake_overlink) -// { -// const std::vector> file_info = { -// // {"ev_0_0_100", "OvlTop", ".silo"}, -// // uncomment once silo ucdmesh phzones are supported -// {"hl18spec", "OvlTop", ".silo"}, -// // {"regrovl_qh_1000_10001_4", "OvlTop", ".silo"}, -// // uncomment once silo ucdmesh phzones are supported -// {"utpyr4", "OvlTop", ".silo"}, -// }; - -// for (int i = 0; i < file_info.size(); i ++) -// { -// const std::string dirname = file_info[i][0]; -// const std::string basename = file_info[i][1]; -// const std::string fileext = file_info[i][2]; - -// Node load_mesh, info, write_opts; -// std::string filepath = utils::join_file_path(dirname, basename) + fileext; -// filepath = utils::join_file_path("fake_overlink", filepath); -// std::string input_file = relay_test_silo_data_path(filepath); - -// io::silo::load_mesh(input_file, load_mesh); -// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - -// const std::string out_name = "read_fake_overlink_" + dirname; - -// remove_path_if_exists(out_name + "_write_blueprint"); -// io::blueprint::save_mesh(load_mesh, out_name + "_write_blueprint", "hdf5"); - -// remove_path_if_exists(out_name + "_write_silo"); -// io::silo::save_mesh(load_mesh, out_name + "_write_silo"); - -// remove_path_if_exists(out_name + "_write_overlink"); -// write_opts["file_style"] = "overlink"; -// write_opts["ovl_topo_name"] = "MMESH"; -// io::silo::save_mesh(load_mesh, out_name + "_write_overlink", write_opts); -// } -// } - -// //----------------------------------------------------------------------------- -// // read overlink files in symlink format -// // should be similar to reading raw silo -// TEST(conduit_relay_io_silo, read_overlink_symlink_format) -// { -// const std::vector> file_info = { -// {".", "box2d", ".silo"}, -// {".", "box3d", ".silo"}, -// // {".", "diamond", ".silo"}, -// // fails b/c polytopal not yet supported -// {".", "testDisk2D_a", ".silo"}, -// // {".", "donordiv.s2_materials2", ".silo"}, -// // fails b/c polytopal not yet supported -// {".", "donordiv.s2_materials3", ".silo"}, -// }; - -// for (int i = 0; i < file_info.size(); i ++) -// { -// const std::string dirname = file_info[i][0]; -// const std::string basename = file_info[i][1]; -// const std::string fileext = file_info[i][2]; - -// Node load_mesh, info, write_opts; -// std::string filepath = utils::join_file_path(dirname, basename) + fileext; -// filepath = utils::join_file_path("overlink", filepath); -// std::string input_file = relay_test_silo_data_path(filepath); - -// io::silo::load_mesh(input_file, load_mesh); -// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - -// const std::string out_name = "read_overlink_symlink_" + basename; - -// remove_path_if_exists(out_name + "_write_blueprint"); -// io::blueprint::save_mesh(load_mesh, out_name + "_write_blueprint", "hdf5"); - -// remove_path_if_exists(out_name + "_write_silo"); -// io::silo::save_mesh(load_mesh, out_name + "_write_silo"); - -// remove_path_if_exists(out_name + "_write_overlink"); -// write_opts["file_style"] = "overlink"; -// write_opts["ovl_topo_name"] = "MMESH"; -// io::silo::save_mesh(load_mesh, out_name + "_write_overlink", write_opts); -// } -// } - -// //----------------------------------------------------------------------------- -// // read overlink directly from ovltop.silo -// // this case is tricky and involves messing with paths -// TEST(conduit_relay_io_silo, read_overlink_directly) -// { -// const std::vector> file_info = { -// {"box2d", "OvlTop", ".silo"}, -// {"box3d", "OvlTop", ".silo"}, -// // {"diamond", "OvlTop", ".silo"}, -// {"testDisk2D_a", "OvlTop", ".silo"}, -// // {"donordiv.s2_materials2", "OvlTop", ".silo"}, -// {"donordiv.s2_materials3", "OvlTop", ".silo"}, -// }; - -// for (int i = 0; i < file_info.size(); i ++) -// { -// const std::string dirname = file_info[i][0]; -// const std::string basename = file_info[i][1]; -// const std::string fileext = file_info[i][2]; - -// Node load_mesh, info, write_opts; - -// std::string filepath = utils::join_file_path(dirname, basename) + fileext; -// filepath = utils::join_file_path("overlink", filepath); -// std::string input_file = relay_test_silo_data_path(filepath); - -// io::silo::load_mesh(input_file, load_mesh); -// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - -// const std::string out_name = "read_overlink_direct_" + dirname; - -// remove_path_if_exists(out_name + "_write_blueprint"); -// io::blueprint::save_mesh(load_mesh, out_name + "_write_blueprint", "hdf5"); - -// remove_path_if_exists(out_name + "_write_silo"); -// io::silo::save_mesh(load_mesh, out_name + "_write_silo"); - -// remove_path_if_exists(out_name + "_write_overlink"); -// write_opts["file_style"] = "overlink"; -// write_opts["ovl_topo_name"] = "MMESH"; -// io::silo::save_mesh(load_mesh, out_name + "_write_overlink", write_opts); -// } -// } - -// // TODO add tests for... -// // - polytopal meshes once they are supported -// // - units once they are supported -// // - etc. - -// // TODO add tetra8 and c36_m5 to all the overlink i/o tests - -// // TODO somewhere I need to error on overlink when there are different var or mesh types across domains +//----------------------------------------------------------------------------- +// test that we can read the fake overlink files from the visit test data +TEST(conduit_relay_io_silo, read_fake_overlink) +{ + const std::vector> file_info = { + // {"ev_0_0_100", "OvlTop", ".silo"}, + // uncomment once silo ucdmesh phzones are supported + {"hl18spec", "OvlTop", ".silo"}, + // {"regrovl_qh_1000_10001_4", "OvlTop", ".silo"}, + // uncomment once silo ucdmesh phzones are supported + {"utpyr4", "OvlTop", ".silo"}, + }; + + for (int i = 0; i < file_info.size(); i ++) + { + const std::string dirname = file_info[i][0]; + const std::string basename = file_info[i][1]; + const std::string fileext = file_info[i][2]; + + Node load_mesh, info, write_opts; + std::string filepath = utils::join_file_path(dirname, basename) + fileext; + filepath = utils::join_file_path("fake_overlink", filepath); + std::string input_file = relay_test_silo_data_path(filepath); + + io::silo::load_mesh(input_file, load_mesh); + EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + + const std::string out_name = "read_fake_overlink_" + dirname; + + remove_path_if_exists(out_name + "_write_blueprint"); + io::blueprint::save_mesh(load_mesh, out_name + "_write_blueprint", "hdf5"); + + remove_path_if_exists(out_name + "_write_silo"); + io::silo::save_mesh(load_mesh, out_name + "_write_silo"); + + remove_path_if_exists(out_name + "_write_overlink"); + write_opts["file_style"] = "overlink"; + write_opts["ovl_topo_name"] = "MMESH"; + io::silo::save_mesh(load_mesh, out_name + "_write_overlink", write_opts); + } +} + +//----------------------------------------------------------------------------- +// read overlink files in symlink format +// should be similar to reading raw silo +TEST(conduit_relay_io_silo, read_overlink_symlink_format) +{ + const std::vector> file_info = { + {".", "box2d", ".silo"}, + {".", "box3d", ".silo"}, + // {".", "diamond", ".silo"}, + // fails b/c polytopal not yet supported + {".", "testDisk2D_a", ".silo"}, + // {".", "donordiv.s2_materials2", ".silo"}, + // fails b/c polytopal not yet supported + {".", "donordiv.s2_materials3", ".silo"}, + }; + + for (int i = 0; i < file_info.size(); i ++) + { + const std::string dirname = file_info[i][0]; + const std::string basename = file_info[i][1]; + const std::string fileext = file_info[i][2]; + + Node load_mesh, info, write_opts; + std::string filepath = utils::join_file_path(dirname, basename) + fileext; + filepath = utils::join_file_path("overlink", filepath); + std::string input_file = relay_test_silo_data_path(filepath); + + io::silo::load_mesh(input_file, load_mesh); + EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + + const std::string out_name = "read_overlink_symlink_" + basename; + + remove_path_if_exists(out_name + "_write_blueprint"); + io::blueprint::save_mesh(load_mesh, out_name + "_write_blueprint", "hdf5"); + + remove_path_if_exists(out_name + "_write_silo"); + io::silo::save_mesh(load_mesh, out_name + "_write_silo"); + + remove_path_if_exists(out_name + "_write_overlink"); + write_opts["file_style"] = "overlink"; + write_opts["ovl_topo_name"] = "MMESH"; + io::silo::save_mesh(load_mesh, out_name + "_write_overlink", write_opts); + } +} + +//----------------------------------------------------------------------------- +// read overlink directly from ovltop.silo +// this case is tricky and involves messing with paths +TEST(conduit_relay_io_silo, read_overlink_directly) +{ + const std::vector> file_info = { + {"box2d", "OvlTop", ".silo"}, + {"box3d", "OvlTop", ".silo"}, + // {"diamond", "OvlTop", ".silo"}, + {"testDisk2D_a", "OvlTop", ".silo"}, + // {"donordiv.s2_materials2", "OvlTop", ".silo"}, + {"donordiv.s2_materials3", "OvlTop", ".silo"}, + }; + + for (int i = 0; i < file_info.size(); i ++) + { + const std::string dirname = file_info[i][0]; + const std::string basename = file_info[i][1]; + const std::string fileext = file_info[i][2]; + + Node load_mesh, info, write_opts; + + std::string filepath = utils::join_file_path(dirname, basename) + fileext; + filepath = utils::join_file_path("overlink", filepath); + std::string input_file = relay_test_silo_data_path(filepath); + + io::silo::load_mesh(input_file, load_mesh); + EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + + const std::string out_name = "read_overlink_direct_" + dirname; + + remove_path_if_exists(out_name + "_write_blueprint"); + io::blueprint::save_mesh(load_mesh, out_name + "_write_blueprint", "hdf5"); + + remove_path_if_exists(out_name + "_write_silo"); + io::silo::save_mesh(load_mesh, out_name + "_write_silo"); + + remove_path_if_exists(out_name + "_write_overlink"); + write_opts["file_style"] = "overlink"; + write_opts["ovl_topo_name"] = "MMESH"; + io::silo::save_mesh(load_mesh, out_name + "_write_overlink", write_opts); + } +} + +// TODO add tests for... +// - polytopal meshes once they are supported +// - units once they are supported +// - etc. + +// TODO add tetra8 and c36_m5 to all the overlink i/o tests + +// TODO somewhere I need to error on overlink when there are different var or mesh types across domains From e5f352a35f1f81cfcd8c869bd06fbe1e6987cc43 Mon Sep 17 00:00:00 2001 From: Justin Privitera Date: Wed, 10 Apr 2024 11:08:03 -0700 Subject: [PATCH 29/56] failure --- src/libs/relay/conduit_relay_io_silo.cpp | 109 +- src/tests/relay/t_relay_io_silo.cpp | 3826 +++++++++++----------- 2 files changed, 1954 insertions(+), 1981 deletions(-) diff --git a/src/libs/relay/conduit_relay_io_silo.cpp b/src/libs/relay/conduit_relay_io_silo.cpp index dee04665d..2c59de2b5 100644 --- a/src/libs/relay/conduit_relay_io_silo.cpp +++ b/src/libs/relay/conduit_relay_io_silo.cpp @@ -1140,17 +1140,52 @@ read_quadmesh_domain(DBquadmesh *quadmesh_ptr, coords_out["type"] = "explicit"; topo_out["type"] = "structured"; - if (! detail::check_using_whole_coordset(quadmesh_ptr->dims, - quadmesh_ptr->min_index, - quadmesh_ptr->max_index, - ndims)) + if (detail::check_using_whole_coordset(quadmesh_ptr->dims, + quadmesh_ptr->min_index, + quadmesh_ptr->max_index, + ndims)) + { + // We subtract 1 from each of these because in silo these dims are node dims, not element dims + topo_out["elements/dims/i"] = quadmesh_ptr->dims[0] - 1; + if (ndims > 1) + { + topo_out["elements/dims/j"] = quadmesh_ptr->dims[1] - 1; + } + if (ndims > 2) + { + topo_out["elements/dims/k"] = quadmesh_ptr->dims[2] - 1; + } + + if (quadmesh_ptr->major_order == DB_COLMAJOR) + { + CONDUIT_ERROR("TODO what to do in this case"); + // resort to strided structured? + } + } + else { // strided structured case + if (quadmesh_ptr->major_order == DB_ROWMAJOR) + { + topo_out["elements/dims/i"] = quadmesh_ptr->max_index[0] - quadmesh_ptr->min_index[0]; + if (ndims > 1) + { + topo_out["elements/dims/j"] = quadmesh_ptr->max_index[1] - quadmesh_ptr->min_index[1]; + } + if (ndims > 2) + { + topo_out["elements/dims/k"] = quadmesh_ptr->max_index[2] - quadmesh_ptr->min_index[2]; + } - const int major_order = quadmesh_ptr->major_order; - if (major_order == DB_COLMAJOR) + topo_out["elements/dims/offsets"].set(quadmesh_ptr->min_index, ndims); + topo_out["elements/dims/strides"].set(quadmesh_ptr->stride, ndims); + } + else { // TODO I don't know what I'm doing + // this doesn't work + // I'm not convinced I'm the one who's wrong + // could be docs, could be bp reader, or could be me topo_out["elements/dims/i"] = quadmesh_ptr->max_index[0] - quadmesh_ptr->min_index[0]; if (ndims > 1) { @@ -1163,85 +1198,23 @@ read_quadmesh_domain(DBquadmesh *quadmesh_ptr, topo_out["elements/dims/offsets"].set(quadmesh_ptr->min_index, ndims); int flipped_strides[] = {0,0,0}; - // int flipped_offsets[] = {0,0,0}; - // int flipped_dims[] = {0,0,0}; if (1 == ndims) { flipped_strides[0] = quadmesh_ptr->stride[0]; - - // flipped_offsets[0] = quadmesh_ptr->min_index[0]; - - // flipped_dims[0] = quadmesh_ptr->max_index[0] - quadmesh_ptr->min_index[0]; - // topo_out["elements/dims/i"] = flipped_dims[0]; } else if (2 == ndims) { flipped_strides[0] = quadmesh_ptr->stride[1]; flipped_strides[1] = quadmesh_ptr->stride[0]; - - // flipped_offsets[0] = quadmesh_ptr->min_index[1]; - // flipped_offsets[1] = quadmesh_ptr->min_index[0]; - - // flipped_dims[0] = quadmesh_ptr->max_index[1] - quadmesh_ptr->min_index[1]; - // flipped_dims[1] = quadmesh_ptr->max_index[0] - quadmesh_ptr->min_index[0]; - // topo_out["elements/dims/i"] = flipped_dims[0]; - // topo_out["elements/dims/j"] = flipped_dims[1]; } else // 3 == ndims { flipped_strides[0] = quadmesh_ptr->stride[2]; flipped_strides[1] = quadmesh_ptr->stride[1]; flipped_strides[2] = quadmesh_ptr->stride[0]; - - // flipped_offsets[0] = quadmesh_ptr->min_index[2]; - // flipped_offsets[1] = quadmesh_ptr->min_index[1]; - // flipped_offsets[2] = quadmesh_ptr->min_index[0]; - - // flipped_dims[0] = quadmesh_ptr->max_index[2] - quadmesh_ptr->min_index[2]; - // flipped_dims[1] = quadmesh_ptr->max_index[1] - quadmesh_ptr->min_index[1]; - // flipped_dims[2] = quadmesh_ptr->max_index[0] - quadmesh_ptr->min_index[0]; - - // topo_out["elements/dims/i"] = flipped_dims[0]; - // topo_out["elements/dims/j"] = flipped_dims[1]; - // topo_out["elements/dims/k"] = flipped_dims[2]; } - // topo_out["elements/dims/offsets"].set(flipped_offsets, ndims); topo_out["elements/dims/strides"].set(flipped_strides, ndims); } - else - { - topo_out["elements/dims/i"] = quadmesh_ptr->max_index[0] - quadmesh_ptr->min_index[0]; - if (ndims > 1) - { - topo_out["elements/dims/j"] = quadmesh_ptr->max_index[1] - quadmesh_ptr->min_index[1]; - } - if (ndims > 2) - { - topo_out["elements/dims/k"] = quadmesh_ptr->max_index[2] - quadmesh_ptr->min_index[2]; - } - - topo_out["elements/dims/offsets"].set(quadmesh_ptr->min_index, ndims); - topo_out["elements/dims/strides"].set(quadmesh_ptr->stride, ndims); - } - } - else - { - // We subtract 1 from each of these because in silo these dims are node dims, not element dims - topo_out["elements/dims/i"] = quadmesh_ptr->dims[0] - 1; - if (ndims > 1) - { - topo_out["elements/dims/j"] = quadmesh_ptr->dims[1] - 1; - } - if (ndims > 2) - { - topo_out["elements/dims/k"] = quadmesh_ptr->dims[2] - 1; - } - - const int major_order = quadmesh_ptr->major_order; - if (major_order == DB_COLMAJOR) - { - CONDUIT_ERROR("TODO what to do in this case"); - } } } else diff --git a/src/tests/relay/t_relay_io_silo.cpp b/src/tests/relay/t_relay_io_silo.cpp index a76168597..9a093a146 100644 --- a/src/tests/relay/t_relay_io_silo.cpp +++ b/src/tests/relay/t_relay_io_silo.cpp @@ -20,1793 +20,1793 @@ using namespace conduit; using namespace conduit::utils; using namespace conduit::relay; -//----------------------------------------------------------------------------- -TEST(conduit_relay_io_silo, conduit_silo_cold_storage) -{ - uint32 a_val = 20; - uint32 b_val = 8; - uint32 c_val = 13; - - Node n; - n["a"] = a_val; - n["b"] = b_val; - n["c"] = c_val; - - EXPECT_EQ(n["a"].as_uint32(), a_val); - EXPECT_EQ(n["b"].as_uint32(), b_val); - EXPECT_EQ(n["c"].as_uint32(), c_val); - - n.print(); - - io::silo_write(n,"tout_cold_storage_test.silo:myobj"); - - Node n_load; - io::silo_read("tout_cold_storage_test.silo:myobj",n_load); - - std::cout << "round trip" << std::endl; - n_load.print(); - - EXPECT_EQ(n_load["a"].as_uint32(), a_val); - EXPECT_EQ(n_load["b"].as_uint32(), b_val); - EXPECT_EQ(n_load["c"].as_uint32(), c_val); -} - -//----------------------------------------------------------------------------- -TEST(conduit_relay_io_silo, conduit_silo_cold_storage_generic_iface) -{ - uint32 a_val = 20; - uint32 b_val = 8; - uint32 c_val = 13; - - Node n; - n["a"] = a_val; - n["b"] = b_val; - n["c"] = c_val; - - EXPECT_EQ(n["a"].as_uint32(), a_val); - EXPECT_EQ(n["b"].as_uint32(), b_val); - EXPECT_EQ(n["c"].as_uint32(), c_val); - - io::save(n, "tout_cold_storage_test_generic_iface.silo:myobj"); - - Node n_load; - io::load("tout_cold_storage_test_generic_iface.silo:myobj",n_load); - - EXPECT_EQ(n_load["a"].as_uint32(), a_val); - EXPECT_EQ(n_load["b"].as_uint32(), b_val); - EXPECT_EQ(n_load["c"].as_uint32(), c_val); -} - -//----------------------------------------------------------------------------- -// test reading in a handful of different overlink files -TEST(conduit_relay_io_silo, load_mesh_geometry) -{ - // TODO: all these files are in overlink symlink format. - // Symlinks may break on Windows (?) - // Could make them overlink format without the symlink. - // But would require modifying the files. - std::vector filename_vec = { - "box2d.silo", - "box3d.silo", - // "diamond.silo", <--- TODO this one fails because polytopal is not yet supported - // TODO: rename these files to be more descriptive. - // would also require modifying the paths stored within the files, - // and re-symlinking - "testDisk2D_a.silo", - // "donordiv.s2_materials2.silo", <--- TODO this one fails because polytopal is not yet supported - "donordiv.s2_materials3.silo" - }; - std::vector dims_vec = {2, 3, /*2,*/ 2, /*2,*/ 2}; - std::vector coordset_length_vec = {4, 8, /*36,*/ 1994, /*16,*/ 961}; - std::vector topology_length_vec = {1, 1, /*33,*/ 1920, /*9,*/ 900}; - for (int i = 0; i < filename_vec.size(); ++i) - { - Node mesh, info; - std::string path = utils::join_file_path("overlink", filename_vec.at(i)); - std::string input_file = relay_test_silo_data_path(path); - io::silo::load_mesh(input_file, mesh); - - EXPECT_TRUE(blueprint::mesh::verify(mesh, info)); - EXPECT_EQ(blueprint::mesh::number_of_domains(mesh), 1); - - const Node &domain = *blueprint::mesh::domains(mesh).front(); - EXPECT_TRUE(domain.has_child("coordsets")); - EXPECT_EQ(domain["coordsets"].number_of_children(), 1); - EXPECT_TRUE(domain.has_child("topologies")); - EXPECT_EQ(domain["topologies"].number_of_children(), 1); - - { // Coordset Validation // - const Node &cset = domain["coordsets"].child(0); - EXPECT_EQ(blueprint::mesh::coordset::dims(cset), dims_vec.at(i)); - EXPECT_EQ(blueprint::mesh::coordset::length(cset), coordset_length_vec.at(i)); - EXPECT_TRUE(blueprint::mesh::coordset::_explicit::verify(cset, info)); - } - - { // Topology Validation // - const Node &topo = domain["topologies"].child(0); - EXPECT_EQ(blueprint::mesh::topology::dims(topo), dims_vec.at(i)); - EXPECT_EQ(blueprint::mesh::topology::length(topo), topology_length_vec.at(i)); - EXPECT_TRUE(blueprint::mesh::topology::unstructured::verify(topo, info)); - } - } -} - -//----------------------------------------------------------------------------- -TEST(conduit_relay_io_silo, round_trip_basic) -{ - const std::vector> mesh_types = { - std::make_pair("uniform", "2"), std::make_pair("uniform", "3"), - std::make_pair("rectilinear", "2"), std::make_pair("rectilinear", "3"), - std::make_pair("structured", "2"), std::make_pair("structured", "3"), - std::make_pair("tris", "2"), - std::make_pair("quads", "2"), - std::make_pair("polygons", "2"), - std::make_pair("tets", "3"), - std::make_pair("hexs", "3"), - std::make_pair("wedges", "3"), - std::make_pair("pyramids", "3"), - // std::make_pair("polyhedra", "3") - }; - for (int i = 0; i < mesh_types.size(); ++i) - { - std::string dim = mesh_types[i].second; - index_t nx = 3; - index_t ny = 4; - index_t nz = (dim == "2" ? 0 : 2); - - const std::string mesh_type = mesh_types[i].first; - - Node save_mesh, load_mesh, info; - blueprint::mesh::examples::basic(mesh_type, nx, ny, nz, save_mesh); - - const std::string basename = "silo_basic_" + mesh_type + "_" + dim + "D"; - const std::string filename = basename + ".root"; - - remove_path_if_exists(filename); - io::silo::save_mesh(save_mesh, basename); - io::silo::load_mesh(filename, load_mesh); - EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - - // make changes to save mesh so the diff will pass - if (mesh_type == "uniform") - { - silo_uniform_to_rect_conversion("coords", "mesh", save_mesh); - } - silo_name_changer("mesh", save_mesh); - - // the loaded mesh will be in the multidomain format - // but the saved mesh is in the single domain format - EXPECT_EQ(load_mesh.number_of_children(), 1); - EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); - EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); - } -} - -//----------------------------------------------------------------------------- -// we are testing vector fields in this test -TEST(conduit_relay_io_silo, round_trip_braid) -{ - const std::vector> mesh_types = { - std::make_pair("uniform", "2"), std::make_pair("uniform", "3"), - std::make_pair("rectilinear", "2"), std::make_pair("rectilinear", "3"), - std::make_pair("structured", "2"), std::make_pair("structured", "3"), - std::make_pair("points", "2"), std::make_pair("points", "3"), - std::make_pair("points_implicit", "2"), std::make_pair("points_implicit", "3"), - std::make_pair("lines", "2"), std::make_pair("lines", "3"), - std::make_pair("tris", "2"), - std::make_pair("quads", "2"), - std::make_pair("tets", "3"), - std::make_pair("hexs", "3"), - std::make_pair("wedges", "3"), - std::make_pair("pyramids", "3"), - // std::make_pair("mixed_2d", "2"), - // std::make_pair("mixed", "3"), - }; - for (int i = 0; i < mesh_types.size(); ++i) - { - std::string dim = mesh_types[i].second; - index_t nx = 3; - index_t ny = 4; - index_t nz = (dim == "2" ? 0 : 2); - - std::string mesh_type = mesh_types[i].first; - - Node save_mesh, load_mesh, info; - blueprint::mesh::examples::braid(mesh_type, nx, ny, nz, save_mesh); - - const std::string basename = "silo_braid_" + mesh_type + "_" + dim + "D"; - const std::string filename = basename + ".cycle_000100.root"; - - // remove existing root file, directory and any output files - remove_path_if_exists(filename); - - io::silo::save_mesh(save_mesh, basename); - io::silo::load_mesh(filename, load_mesh); - EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - - // make changes to save mesh so the diff will pass - if (mesh_type == "uniform") - { - silo_uniform_to_rect_conversion("coords", "mesh", save_mesh); - } - if (mesh_type == "points") - { - // this is custom code for braid - // We know it is correct because the unstructured points version of braid - // uses every point in the coordset - save_mesh["topologies"].remove_child("mesh"); - save_mesh["topologies"]["mesh"]["type"] = "points"; - save_mesh["topologies"]["mesh"]["coordset"] = "coords"; - } - if (mesh_type == "points_implicit" || mesh_type == "points") - { - // the association doesn't matter for point meshes - // we choose vertex by convention - save_mesh["fields"]["radial"]["association"].reset(); - save_mesh["fields"]["radial"]["association"] = "vertex"; - } - silo_name_changer("mesh", save_mesh); - - // the loaded mesh will be in the multidomain format - // but the saved mesh is in the single domain format - EXPECT_EQ(load_mesh.number_of_children(), 1); - EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); - EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); - } -} - -//----------------------------------------------------------------------------- -// multidomain test -TEST(conduit_relay_io_silo, round_trip_spiral) -{ - for (int ndomains = 2; ndomains < 6; ndomains ++) - { - Node save_mesh, load_mesh, info; - blueprint::mesh::examples::spiral(ndomains, save_mesh); - - const std::string basename = "silo_spiral_" + std::to_string(ndomains) + "_domains"; - const std::string filename = basename + ".cycle_000000.root"; - - remove_path_if_exists(filename); - io::silo::save_mesh(save_mesh, basename); - io::silo::load_mesh(filename, load_mesh); - EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); - - // make changes to save mesh so the diff will pass - for (index_t child = 0; child < save_mesh.number_of_children(); child ++) - { - silo_name_changer("mesh", save_mesh[child]); - } - - EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); - NodeConstIterator l_itr = load_mesh.children(); - NodeConstIterator s_itr = save_mesh.children(); - while (l_itr.has_next()) - { - const Node &l_curr = l_itr.next(); - const Node &s_curr = s_itr.next(); - - EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); - } - } -} - -//----------------------------------------------------------------------------- -TEST(conduit_relay_io_silo, round_trip_julia) -{ - Node save_mesh, load_mesh, info; - blueprint::mesh::examples::julia(5, // nx - 5, // ny - 0, // x_min - 10, // x_max - 2, // y_min - 7, // y_max - 3, // c_re - 4, // c_im - save_mesh); - - const std::string basename = "silo_julia"; - const std::string filename = basename + ".root"; - - remove_path_if_exists(filename); - io::silo::save_mesh(save_mesh, basename); - io::silo::load_mesh(filename, load_mesh); - EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - - // make changes to save mesh so the diff will pass - silo_name_changer("mesh", save_mesh); - - // the loaded mesh will be in the multidomain format - // but the saved mesh is in the single domain format - EXPECT_EQ(load_mesh.number_of_children(), 1); - EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); - EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); -} - -//----------------------------------------------------------------------------- -// test material write and read -TEST(conduit_relay_io_silo, round_trip_venn) -{ - std::string matset_type = "sparse_by_element"; - for (int j = 0; j < 2; j ++) - { - Node save_mesh, sbe, load_mesh, info; - std::string size; - int nx, ny; - const double radius = 0.25; - if (j == 0) - { - size = "small"; - nx = ny = 4; - } - else - { - size = "large"; - nx = ny = 100; - } - blueprint::mesh::examples::venn(matset_type, nx, ny, radius, save_mesh); - - const std::string basename = "silo_venn_" + matset_type + "_" + size; - const std::string filename = basename + ".root"; - - remove_path_if_exists(filename); - io::silo::save_mesh(save_mesh, basename); - io::silo::load_mesh(filename, load_mesh); - EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - - // make changes to save mesh so the diff will pass - - // The field mat_check has values that are one type and matset_values - // that are another type. The silo writer converts both to double arrays - // in this case, so we follow suit. - Node mat_check_new_values, mat_check_new_matset_values; - save_mesh["fields"]["mat_check"]["values"].to_double_array(mat_check_new_values); - save_mesh["fields"]["mat_check"]["matset_values"].to_double_array(mat_check_new_matset_values); - save_mesh["fields"]["mat_check"]["values"].set_external(mat_check_new_values); - save_mesh["fields"]["mat_check"]["matset_values"].set_external(mat_check_new_matset_values); - - silo_name_changer("mesh", save_mesh); - - // the loaded mesh will be in the multidomain format - // but the saved mesh is in the single domain format - EXPECT_EQ(load_mesh.number_of_children(), 1); - EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); - EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); - } -} - -//----------------------------------------------------------------------------- -TEST(conduit_relay_io_silo, round_trip_venn_modded_matnos) -{ - const std::string matset_type = "sparse_by_element"; - Node save_mesh, load_mesh, info; - const int nx = 4; - const int ny = 4; - const double radius = 0.25; - blueprint::mesh::examples::venn(matset_type, nx, ny, radius, save_mesh); - - auto replace_matno = [](int matno) - { - return (matno == 1 ? 15 : - (matno == 2 ? 37 : - (matno == 3 ? 4 : - (matno == 0 ? 22 : - -1)))); - }; - - auto matmap_itr = save_mesh["matsets"]["matset"]["material_map"].children(); - while (matmap_itr.has_next()) - { - Node &mat = matmap_itr.next(); - mat.set(replace_matno(mat.as_int())); - } - - int_array matids = save_mesh["matsets"]["matset"]["material_ids"].value(); - for (int i = 0; i < save_mesh["matsets"]["matset"]["material_ids"].dtype().number_of_elements(); i ++) - { - matids[i] = replace_matno(matids[i]); - } - - const std::string silo_basename = "silo_venn_" + matset_type + "_modded_matnos"; - const std::string silo_filename = silo_basename + ".root"; - remove_path_if_exists(silo_filename); - io::silo::save_mesh(save_mesh, silo_basename); - - const std::string bp_basename = "bp_venn_" + matset_type + "_modded_matnos"; - const std::string bp_filename = bp_basename + ".root"; - remove_path_if_exists(bp_filename); - io::blueprint::save_mesh(save_mesh, bp_basename, "hdf5"); +// //----------------------------------------------------------------------------- +// TEST(conduit_relay_io_silo, conduit_silo_cold_storage) +// { +// uint32 a_val = 20; +// uint32 b_val = 8; +// uint32 c_val = 13; + +// Node n; +// n["a"] = a_val; +// n["b"] = b_val; +// n["c"] = c_val; + +// EXPECT_EQ(n["a"].as_uint32(), a_val); +// EXPECT_EQ(n["b"].as_uint32(), b_val); +// EXPECT_EQ(n["c"].as_uint32(), c_val); + +// n.print(); + +// io::silo_write(n,"tout_cold_storage_test.silo:myobj"); + +// Node n_load; +// io::silo_read("tout_cold_storage_test.silo:myobj",n_load); + +// std::cout << "round trip" << std::endl; +// n_load.print(); + +// EXPECT_EQ(n_load["a"].as_uint32(), a_val); +// EXPECT_EQ(n_load["b"].as_uint32(), b_val); +// EXPECT_EQ(n_load["c"].as_uint32(), c_val); +// } + +// //----------------------------------------------------------------------------- +// TEST(conduit_relay_io_silo, conduit_silo_cold_storage_generic_iface) +// { +// uint32 a_val = 20; +// uint32 b_val = 8; +// uint32 c_val = 13; + +// Node n; +// n["a"] = a_val; +// n["b"] = b_val; +// n["c"] = c_val; + +// EXPECT_EQ(n["a"].as_uint32(), a_val); +// EXPECT_EQ(n["b"].as_uint32(), b_val); +// EXPECT_EQ(n["c"].as_uint32(), c_val); + +// io::save(n, "tout_cold_storage_test_generic_iface.silo:myobj"); + +// Node n_load; +// io::load("tout_cold_storage_test_generic_iface.silo:myobj",n_load); + +// EXPECT_EQ(n_load["a"].as_uint32(), a_val); +// EXPECT_EQ(n_load["b"].as_uint32(), b_val); +// EXPECT_EQ(n_load["c"].as_uint32(), c_val); +// } + +// //----------------------------------------------------------------------------- +// // test reading in a handful of different overlink files +// TEST(conduit_relay_io_silo, load_mesh_geometry) +// { +// // TODO: all these files are in overlink symlink format. +// // Symlinks may break on Windows (?) +// // Could make them overlink format without the symlink. +// // But would require modifying the files. +// std::vector filename_vec = { +// "box2d.silo", +// "box3d.silo", +// // "diamond.silo", <--- TODO this one fails because polytopal is not yet supported +// // TODO: rename these files to be more descriptive. +// // would also require modifying the paths stored within the files, +// // and re-symlinking +// "testDisk2D_a.silo", +// // "donordiv.s2_materials2.silo", <--- TODO this one fails because polytopal is not yet supported +// "donordiv.s2_materials3.silo" +// }; +// std::vector dims_vec = {2, 3, /*2,*/ 2, /*2,*/ 2}; +// std::vector coordset_length_vec = {4, 8, /*36,*/ 1994, /*16,*/ 961}; +// std::vector topology_length_vec = {1, 1, /*33,*/ 1920, /*9,*/ 900}; +// for (int i = 0; i < filename_vec.size(); ++i) +// { +// Node mesh, info; +// std::string path = utils::join_file_path("overlink", filename_vec.at(i)); +// std::string input_file = relay_test_silo_data_path(path); +// io::silo::load_mesh(input_file, mesh); + +// EXPECT_TRUE(blueprint::mesh::verify(mesh, info)); +// EXPECT_EQ(blueprint::mesh::number_of_domains(mesh), 1); + +// const Node &domain = *blueprint::mesh::domains(mesh).front(); +// EXPECT_TRUE(domain.has_child("coordsets")); +// EXPECT_EQ(domain["coordsets"].number_of_children(), 1); +// EXPECT_TRUE(domain.has_child("topologies")); +// EXPECT_EQ(domain["topologies"].number_of_children(), 1); + +// { // Coordset Validation // +// const Node &cset = domain["coordsets"].child(0); +// EXPECT_EQ(blueprint::mesh::coordset::dims(cset), dims_vec.at(i)); +// EXPECT_EQ(blueprint::mesh::coordset::length(cset), coordset_length_vec.at(i)); +// EXPECT_TRUE(blueprint::mesh::coordset::_explicit::verify(cset, info)); +// } + +// { // Topology Validation // +// const Node &topo = domain["topologies"].child(0); +// EXPECT_EQ(blueprint::mesh::topology::dims(topo), dims_vec.at(i)); +// EXPECT_EQ(blueprint::mesh::topology::length(topo), topology_length_vec.at(i)); +// EXPECT_TRUE(blueprint::mesh::topology::unstructured::verify(topo, info)); +// } +// } +// } + +// //----------------------------------------------------------------------------- +// TEST(conduit_relay_io_silo, round_trip_basic) +// { +// const std::vector> mesh_types = { +// std::make_pair("uniform", "2"), std::make_pair("uniform", "3"), +// std::make_pair("rectilinear", "2"), std::make_pair("rectilinear", "3"), +// std::make_pair("structured", "2"), std::make_pair("structured", "3"), +// std::make_pair("tris", "2"), +// std::make_pair("quads", "2"), +// std::make_pair("polygons", "2"), +// std::make_pair("tets", "3"), +// std::make_pair("hexs", "3"), +// std::make_pair("wedges", "3"), +// std::make_pair("pyramids", "3"), +// // std::make_pair("polyhedra", "3") +// }; +// for (int i = 0; i < mesh_types.size(); ++i) +// { +// std::string dim = mesh_types[i].second; +// index_t nx = 3; +// index_t ny = 4; +// index_t nz = (dim == "2" ? 0 : 2); + +// const std::string mesh_type = mesh_types[i].first; + +// Node save_mesh, load_mesh, info; +// blueprint::mesh::examples::basic(mesh_type, nx, ny, nz, save_mesh); + +// const std::string basename = "silo_basic_" + mesh_type + "_" + dim + "D"; +// const std::string filename = basename + ".root"; + +// remove_path_if_exists(filename); +// io::silo::save_mesh(save_mesh, basename); +// io::silo::load_mesh(filename, load_mesh); +// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + +// // make changes to save mesh so the diff will pass +// if (mesh_type == "uniform") +// { +// silo_uniform_to_rect_conversion("coords", "mesh", save_mesh); +// } +// silo_name_changer("mesh", save_mesh); + +// // the loaded mesh will be in the multidomain format +// // but the saved mesh is in the single domain format +// EXPECT_EQ(load_mesh.number_of_children(), 1); +// EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); +// EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); +// } +// } + +// //----------------------------------------------------------------------------- +// // we are testing vector fields in this test +// TEST(conduit_relay_io_silo, round_trip_braid) +// { +// const std::vector> mesh_types = { +// std::make_pair("uniform", "2"), std::make_pair("uniform", "3"), +// std::make_pair("rectilinear", "2"), std::make_pair("rectilinear", "3"), +// std::make_pair("structured", "2"), std::make_pair("structured", "3"), +// std::make_pair("points", "2"), std::make_pair("points", "3"), +// std::make_pair("points_implicit", "2"), std::make_pair("points_implicit", "3"), +// std::make_pair("lines", "2"), std::make_pair("lines", "3"), +// std::make_pair("tris", "2"), +// std::make_pair("quads", "2"), +// std::make_pair("tets", "3"), +// std::make_pair("hexs", "3"), +// std::make_pair("wedges", "3"), +// std::make_pair("pyramids", "3"), +// // std::make_pair("mixed_2d", "2"), +// // std::make_pair("mixed", "3"), +// }; +// for (int i = 0; i < mesh_types.size(); ++i) +// { +// std::string dim = mesh_types[i].second; +// index_t nx = 3; +// index_t ny = 4; +// index_t nz = (dim == "2" ? 0 : 2); + +// std::string mesh_type = mesh_types[i].first; + +// Node save_mesh, load_mesh, info; +// blueprint::mesh::examples::braid(mesh_type, nx, ny, nz, save_mesh); + +// const std::string basename = "silo_braid_" + mesh_type + "_" + dim + "D"; +// const std::string filename = basename + ".cycle_000100.root"; + +// // remove existing root file, directory and any output files +// remove_path_if_exists(filename); + +// io::silo::save_mesh(save_mesh, basename); +// io::silo::load_mesh(filename, load_mesh); +// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + +// // make changes to save mesh so the diff will pass +// if (mesh_type == "uniform") +// { +// silo_uniform_to_rect_conversion("coords", "mesh", save_mesh); +// } +// if (mesh_type == "points") +// { +// // this is custom code for braid +// // We know it is correct because the unstructured points version of braid +// // uses every point in the coordset +// save_mesh["topologies"].remove_child("mesh"); +// save_mesh["topologies"]["mesh"]["type"] = "points"; +// save_mesh["topologies"]["mesh"]["coordset"] = "coords"; +// } +// if (mesh_type == "points_implicit" || mesh_type == "points") +// { +// // the association doesn't matter for point meshes +// // we choose vertex by convention +// save_mesh["fields"]["radial"]["association"].reset(); +// save_mesh["fields"]["radial"]["association"] = "vertex"; +// } +// silo_name_changer("mesh", save_mesh); + +// // the loaded mesh will be in the multidomain format +// // but the saved mesh is in the single domain format +// EXPECT_EQ(load_mesh.number_of_children(), 1); +// EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); +// EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); +// } +// } + +// //----------------------------------------------------------------------------- +// // multidomain test +// TEST(conduit_relay_io_silo, round_trip_spiral) +// { +// for (int ndomains = 2; ndomains < 6; ndomains ++) +// { +// Node save_mesh, load_mesh, info; +// blueprint::mesh::examples::spiral(ndomains, save_mesh); + +// const std::string basename = "silo_spiral_" + std::to_string(ndomains) + "_domains"; +// const std::string filename = basename + ".cycle_000000.root"; + +// remove_path_if_exists(filename); +// io::silo::save_mesh(save_mesh, basename); +// io::silo::load_mesh(filename, load_mesh); +// EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); + +// // make changes to save mesh so the diff will pass +// for (index_t child = 0; child < save_mesh.number_of_children(); child ++) +// { +// silo_name_changer("mesh", save_mesh[child]); +// } + +// EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); +// NodeConstIterator l_itr = load_mesh.children(); +// NodeConstIterator s_itr = save_mesh.children(); +// while (l_itr.has_next()) +// { +// const Node &l_curr = l_itr.next(); +// const Node &s_curr = s_itr.next(); + +// EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); +// } +// } +// } + +// //----------------------------------------------------------------------------- +// TEST(conduit_relay_io_silo, round_trip_julia) +// { +// Node save_mesh, load_mesh, info; +// blueprint::mesh::examples::julia(5, // nx +// 5, // ny +// 0, // x_min +// 10, // x_max +// 2, // y_min +// 7, // y_max +// 3, // c_re +// 4, // c_im +// save_mesh); + +// const std::string basename = "silo_julia"; +// const std::string filename = basename + ".root"; + +// remove_path_if_exists(filename); +// io::silo::save_mesh(save_mesh, basename); +// io::silo::load_mesh(filename, load_mesh); +// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + +// // make changes to save mesh so the diff will pass +// silo_name_changer("mesh", save_mesh); + +// // the loaded mesh will be in the multidomain format +// // but the saved mesh is in the single domain format +// EXPECT_EQ(load_mesh.number_of_children(), 1); +// EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); +// EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); +// } + +// //----------------------------------------------------------------------------- +// // test material write and read +// TEST(conduit_relay_io_silo, round_trip_venn) +// { +// std::string matset_type = "sparse_by_element"; +// for (int j = 0; j < 2; j ++) +// { +// Node save_mesh, sbe, load_mesh, info; +// std::string size; +// int nx, ny; +// const double radius = 0.25; +// if (j == 0) +// { +// size = "small"; +// nx = ny = 4; +// } +// else +// { +// size = "large"; +// nx = ny = 100; +// } +// blueprint::mesh::examples::venn(matset_type, nx, ny, radius, save_mesh); + +// const std::string basename = "silo_venn_" + matset_type + "_" + size; +// const std::string filename = basename + ".root"; + +// remove_path_if_exists(filename); +// io::silo::save_mesh(save_mesh, basename); +// io::silo::load_mesh(filename, load_mesh); +// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + +// // make changes to save mesh so the diff will pass + +// // The field mat_check has values that are one type and matset_values +// // that are another type. The silo writer converts both to double arrays +// // in this case, so we follow suit. +// Node mat_check_new_values, mat_check_new_matset_values; +// save_mesh["fields"]["mat_check"]["values"].to_double_array(mat_check_new_values); +// save_mesh["fields"]["mat_check"]["matset_values"].to_double_array(mat_check_new_matset_values); +// save_mesh["fields"]["mat_check"]["values"].set_external(mat_check_new_values); +// save_mesh["fields"]["mat_check"]["matset_values"].set_external(mat_check_new_matset_values); + +// silo_name_changer("mesh", save_mesh); + +// // the loaded mesh will be in the multidomain format +// // but the saved mesh is in the single domain format +// EXPECT_EQ(load_mesh.number_of_children(), 1); +// EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); +// EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); +// } +// } + +// //----------------------------------------------------------------------------- +// TEST(conduit_relay_io_silo, round_trip_venn_modded_matnos) +// { +// const std::string matset_type = "sparse_by_element"; +// Node save_mesh, load_mesh, info; +// const int nx = 4; +// const int ny = 4; +// const double radius = 0.25; +// blueprint::mesh::examples::venn(matset_type, nx, ny, radius, save_mesh); + +// auto replace_matno = [](int matno) +// { +// return (matno == 1 ? 15 : +// (matno == 2 ? 37 : +// (matno == 3 ? 4 : +// (matno == 0 ? 22 : +// -1)))); +// }; + +// auto matmap_itr = save_mesh["matsets"]["matset"]["material_map"].children(); +// while (matmap_itr.has_next()) +// { +// Node &mat = matmap_itr.next(); +// mat.set(replace_matno(mat.as_int())); +// } + +// int_array matids = save_mesh["matsets"]["matset"]["material_ids"].value(); +// for (int i = 0; i < save_mesh["matsets"]["matset"]["material_ids"].dtype().number_of_elements(); i ++) +// { +// matids[i] = replace_matno(matids[i]); +// } + +// const std::string silo_basename = "silo_venn_" + matset_type + "_modded_matnos"; +// const std::string silo_filename = silo_basename + ".root"; +// remove_path_if_exists(silo_filename); +// io::silo::save_mesh(save_mesh, silo_basename); + +// const std::string bp_basename = "bp_venn_" + matset_type + "_modded_matnos"; +// const std::string bp_filename = bp_basename + ".root"; +// remove_path_if_exists(bp_filename); +// io::blueprint::save_mesh(save_mesh, bp_basename, "hdf5"); - io::silo::load_mesh(silo_filename, load_mesh); - EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - - // make changes to save mesh so the diff will pass - - // The field mat_check has values that are one type and matset_values - // that are another type. The silo writer converts both to double arrays - // in this case, so we follow suit. - Node mat_check_new_values, mat_check_new_matset_values; - save_mesh["fields"]["mat_check"]["values"].to_double_array(mat_check_new_values); - save_mesh["fields"]["mat_check"]["matset_values"].to_double_array(mat_check_new_matset_values); - save_mesh["fields"]["mat_check"]["values"].set_external(mat_check_new_values); - save_mesh["fields"]["mat_check"]["matset_values"].set_external(mat_check_new_matset_values); - - // to_silo is going to reorder mixed materials least to greatest - // so we must do the same - int_array mat_ids = save_mesh["matsets"]["matset"]["material_ids"].value(); - const auto mat_id10 = mat_ids[10]; - const auto mat_id11 = mat_ids[11]; - const auto mat_id12 = mat_ids[12]; - mat_ids[10] = mat_id12; - mat_ids[11] = mat_id10; - mat_ids[12] = mat_id11; - auto field_itr = save_mesh["fields"].children(); - while (field_itr.has_next()) - { - const Node &n_field = field_itr.next(); - if (n_field.has_child("matset")) - { - double_array matset_vals = n_field["matset_values"].value(); - const auto matset_val10 = matset_vals[10]; - const auto matset_val11 = matset_vals[11]; - const auto matset_val12 = matset_vals[12]; - matset_vals[10] = matset_val12; - matset_vals[11] = matset_val10; - matset_vals[12] = matset_val11; - } - } - - silo_name_changer("mesh", save_mesh); - - // the loaded mesh will be in the multidomain format - // but the saved mesh is in the single domain format - EXPECT_EQ(load_mesh.number_of_children(), 1); - EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); - - EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); -} - -//----------------------------------------------------------------------------- -TEST(conduit_relay_io_silo, round_trip_spiral_multi_dom_materials) -{ - Node save_mesh, load_mesh, info; - const int ndomains = 4; - blueprint::mesh::examples::spiral(ndomains, save_mesh); - add_matset_to_spiral(save_mesh, ndomains); - EXPECT_TRUE(blueprint::mesh::verify(save_mesh, info)); +// io::silo::load_mesh(silo_filename, load_mesh); +// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + +// // make changes to save mesh so the diff will pass + +// // The field mat_check has values that are one type and matset_values +// // that are another type. The silo writer converts both to double arrays +// // in this case, so we follow suit. +// Node mat_check_new_values, mat_check_new_matset_values; +// save_mesh["fields"]["mat_check"]["values"].to_double_array(mat_check_new_values); +// save_mesh["fields"]["mat_check"]["matset_values"].to_double_array(mat_check_new_matset_values); +// save_mesh["fields"]["mat_check"]["values"].set_external(mat_check_new_values); +// save_mesh["fields"]["mat_check"]["matset_values"].set_external(mat_check_new_matset_values); + +// // to_silo is going to reorder mixed materials least to greatest +// // so we must do the same +// int_array mat_ids = save_mesh["matsets"]["matset"]["material_ids"].value(); +// const auto mat_id10 = mat_ids[10]; +// const auto mat_id11 = mat_ids[11]; +// const auto mat_id12 = mat_ids[12]; +// mat_ids[10] = mat_id12; +// mat_ids[11] = mat_id10; +// mat_ids[12] = mat_id11; +// auto field_itr = save_mesh["fields"].children(); +// while (field_itr.has_next()) +// { +// const Node &n_field = field_itr.next(); +// if (n_field.has_child("matset")) +// { +// double_array matset_vals = n_field["matset_values"].value(); +// const auto matset_val10 = matset_vals[10]; +// const auto matset_val11 = matset_vals[11]; +// const auto matset_val12 = matset_vals[12]; +// matset_vals[10] = matset_val12; +// matset_vals[11] = matset_val10; +// matset_vals[12] = matset_val11; +// } +// } + +// silo_name_changer("mesh", save_mesh); + +// // the loaded mesh will be in the multidomain format +// // but the saved mesh is in the single domain format +// EXPECT_EQ(load_mesh.number_of_children(), 1); +// EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); + +// EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); +// } + +// //----------------------------------------------------------------------------- +// TEST(conduit_relay_io_silo, round_trip_spiral_multi_dom_materials) +// { +// Node save_mesh, load_mesh, info; +// const int ndomains = 4; +// blueprint::mesh::examples::spiral(ndomains, save_mesh); +// add_matset_to_spiral(save_mesh, ndomains); +// EXPECT_TRUE(blueprint::mesh::verify(save_mesh, info)); - const std::string basename = "silo_multidom_materials_spiral"; - const std::string filename = basename + ".cycle_000000.root"; - - remove_path_if_exists(filename); - io::silo::save_mesh(save_mesh, basename); - io::silo::load_mesh(filename, load_mesh); - EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); - - // make changes to save mesh so the diff will pass - for (index_t child = 0; child < save_mesh.number_of_children(); child ++) - { - // get the matset for this domain - Node &n_matset = save_mesh[child]["matsets"]["matset"]; - - // clean up volume fractions - Node vf_arr; - n_matset["volume_fractions"].to_float64_array(vf_arr); - n_matset["volume_fractions"].reset(); - n_matset["volume_fractions"].set(vf_arr); +// const std::string basename = "silo_multidom_materials_spiral"; +// const std::string filename = basename + ".cycle_000000.root"; + +// remove_path_if_exists(filename); +// io::silo::save_mesh(save_mesh, basename); +// io::silo::load_mesh(filename, load_mesh); +// EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); + +// // make changes to save mesh so the diff will pass +// for (index_t child = 0; child < save_mesh.number_of_children(); child ++) +// { +// // get the matset for this domain +// Node &n_matset = save_mesh[child]["matsets"]["matset"]; + +// // clean up volume fractions +// Node vf_arr; +// n_matset["volume_fractions"].to_float64_array(vf_arr); +// n_matset["volume_fractions"].reset(); +// n_matset["volume_fractions"].set(vf_arr); - // cheat a little bit - we don't have these to start - n_matset["sizes"].set_external(load_mesh[child]["matsets"]["mesh_matset"]["sizes"]); - n_matset["offsets"].set_external(load_mesh[child]["matsets"]["mesh_matset"]["offsets"]); - - silo_name_changer("mesh", save_mesh[child]); - } - - EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); - NodeConstIterator l_itr = load_mesh.children(); - NodeConstIterator s_itr = save_mesh.children(); - while (l_itr.has_next()) - { - const Node &l_curr = l_itr.next(); - const Node &s_curr = s_itr.next(); - - EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); - } -} - -//----------------------------------------------------------------------------- -TEST(conduit_relay_io_silo, round_trip_grid_adjset) -{ - Node save_mesh, load_mesh, info; - blueprint::mesh::examples::grid("structured", 3, 3, 1, 2, 2, 1, save_mesh); - - // we need a material in order for this to be valid overlink - for (index_t child = 0; child < save_mesh.number_of_children(); child ++) - { - add_multi_buffer_full_matset(save_mesh[child], 4, "mesh"); - } - - const std::string basename = "silo_grid_adjset"; - const std::string filename = basename + "/OvlTop.silo"; - - Node write_opts; - write_opts["file_style"] = "overlink"; - write_opts["ovl_topo_name"] = "mesh"; - - Node read_opts; - read_opts["matset_style"] = "multi_buffer_full"; - - remove_path_if_exists(filename); - io::silo::save_mesh(save_mesh, basename, write_opts); - io::blueprint::save_mesh(save_mesh, basename, "hdf5"); - io::silo::load_mesh(filename, read_opts, load_mesh); - EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - - for (index_t child = 0; child < save_mesh.number_of_children(); child ++) - { - // separate out vector fields - Node &field_vel = save_mesh[child]["fields"]["vel"]; - Node &field_vel_u = save_mesh[child]["fields"]["vel_u"]; - Node &field_vel_v = save_mesh[child]["fields"]["vel_v"]; +// // cheat a little bit - we don't have these to start +// n_matset["sizes"].set_external(load_mesh[child]["matsets"]["mesh_matset"]["sizes"]); +// n_matset["offsets"].set_external(load_mesh[child]["matsets"]["mesh_matset"]["offsets"]); + +// silo_name_changer("mesh", save_mesh[child]); +// } + +// EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); +// NodeConstIterator l_itr = load_mesh.children(); +// NodeConstIterator s_itr = save_mesh.children(); +// while (l_itr.has_next()) +// { +// const Node &l_curr = l_itr.next(); +// const Node &s_curr = s_itr.next(); + +// EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); +// } +// } + +// //----------------------------------------------------------------------------- +// TEST(conduit_relay_io_silo, round_trip_grid_adjset) +// { +// Node save_mesh, load_mesh, info; +// blueprint::mesh::examples::grid("structured", 3, 3, 1, 2, 2, 1, save_mesh); + +// // we need a material in order for this to be valid overlink +// for (index_t child = 0; child < save_mesh.number_of_children(); child ++) +// { +// add_multi_buffer_full_matset(save_mesh[child], 4, "mesh"); +// } + +// const std::string basename = "silo_grid_adjset"; +// const std::string filename = basename + "/OvlTop.silo"; + +// Node write_opts; +// write_opts["file_style"] = "overlink"; +// write_opts["ovl_topo_name"] = "mesh"; + +// Node read_opts; +// read_opts["matset_style"] = "multi_buffer_full"; + +// remove_path_if_exists(filename); +// io::silo::save_mesh(save_mesh, basename, write_opts); +// io::blueprint::save_mesh(save_mesh, basename, "hdf5"); +// io::silo::load_mesh(filename, read_opts, load_mesh); +// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + +// for (index_t child = 0; child < save_mesh.number_of_children(); child ++) +// { +// // separate out vector fields +// Node &field_vel = save_mesh[child]["fields"]["vel"]; +// Node &field_vel_u = save_mesh[child]["fields"]["vel_u"]; +// Node &field_vel_v = save_mesh[child]["fields"]["vel_v"]; - field_vel_u["topology"].set(field_vel["topology"]); - field_vel_u["association"].set(field_vel["association"]); - field_vel_u["values"].set(field_vel["values/u"]); - field_vel_v["topology"].set(field_vel["topology"]); - field_vel_v["association"].set(field_vel["association"]); - field_vel_v["values"].set(field_vel["values/v"]); - - save_mesh[child]["fields"].remove_child("vel"); - - // make adjset pairwise - Node &pairwise_adjset = save_mesh[child]["adjsets"]["adjset"]; - conduit::blueprint::mesh::adjset::to_pairwise(save_mesh[child]["adjsets"]["mesh_adj"], pairwise_adjset); - save_mesh[child]["adjsets"].remove_child("mesh_adj"); - - // make changes to save mesh so the diff will pass - overlink_name_changer(save_mesh[child]); - } - - EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); - NodeConstIterator l_itr = load_mesh.children(); - NodeConstIterator s_itr = save_mesh.children(); - while (l_itr.has_next()) - { - const Node &l_curr = l_itr.next(); - const Node &s_curr = s_itr.next(); - - EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); - } -} - -//----------------------------------------------------------------------------- -// -// test read and write semantics -// - -//----------------------------------------------------------------------------- -TEST(conduit_relay_io_silo, read_and_write_semantics) -{ - for (int ndomains = 2; ndomains < 6; ndomains ++) - { - Node save_mesh, load_mesh, info; - blueprint::mesh::examples::spiral(ndomains, save_mesh); - - const std::string basename = "silo_spiral_" + std::to_string(ndomains) + "_domains"; - const std::string filename = basename + ".cycle_000000.root"; - - remove_path_if_exists(filename); - io::silo::write_mesh(save_mesh, basename); - io::silo::read_mesh(filename, load_mesh); - EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); - - // make changes to save mesh so the diff will pass - for (index_t child = 0; child < save_mesh.number_of_children(); child ++) - { - silo_name_changer("mesh", save_mesh[child]); - } - - EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); - NodeConstIterator l_itr = load_mesh.children(); - NodeConstIterator s_itr = save_mesh.children(); - while (l_itr.has_next()) - { - const Node &l_curr = l_itr.next(); - const Node &s_curr = s_itr.next(); - - EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); - } - } -} - -//----------------------------------------------------------------------------- -// -// special case tests -// - -//----------------------------------------------------------------------------- -// var is not defined on a domain -// -// tests the silo "EMPTY" capability -TEST(conduit_relay_io_silo, missing_domain_var) -{ - Node save_mesh, load_mesh, info; - const int ndomains = 4; - blueprint::mesh::examples::spiral(ndomains, save_mesh); - - // remove information for a particular domain - save_mesh[2]["fields"].remove_child("dist"); - - const std::string basename = "silo_missing_domain_var_spiral"; - const std::string filename = basename + ".cycle_000000.root"; - - remove_path_if_exists(filename); - io::silo::save_mesh(save_mesh, basename); - io::silo::load_mesh(filename, load_mesh); - EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); - - // make changes to save mesh so the diff will pass - for (index_t child = 0; child < save_mesh.number_of_children(); child ++) - { - silo_name_changer("mesh", save_mesh[child]); - } - save_mesh[2].remove_child("fields"); - - EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); - NodeConstIterator l_itr = load_mesh.children(); - NodeConstIterator s_itr = save_mesh.children(); - while (l_itr.has_next()) - { - const Node &l_curr = l_itr.next(); - const Node &s_curr = s_itr.next(); - - EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); - } -} - -//----------------------------------------------------------------------------- -// matset is not defined on a domain -// -// tests the silo "EMPTY" capability -TEST(conduit_relay_io_silo, missing_domain_matset) -{ - Node save_mesh, load_mesh, info; - const int ndomains = 4; - blueprint::mesh::examples::spiral(ndomains, save_mesh); - add_matset_to_spiral(save_mesh, ndomains); - EXPECT_TRUE(blueprint::mesh::verify(save_mesh, info)); - - // remove information for a particular domain - save_mesh[2]["matsets"].remove_child("matset"); - - const std::string basename = "silo_missing_domain_matset_spiral"; - const std::string filename = basename + ".cycle_000000.root"; - - remove_path_if_exists(filename); - io::silo::save_mesh(save_mesh, basename); - io::silo::load_mesh(filename, load_mesh); - EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); - - // make changes to save mesh so the diff will pass - for (index_t child = 0; child < save_mesh.number_of_children(); child ++) - { - if (save_mesh[child].has_path("matsets/matset")) - { - // get the matset for this domain - Node &n_matset = save_mesh[child]["matsets"]["matset"]; - - // clean up volume fractions - Node vf_arr; - n_matset["volume_fractions"].to_float64_array(vf_arr); - n_matset["volume_fractions"].reset(); - n_matset["volume_fractions"].set(vf_arr); +// field_vel_u["topology"].set(field_vel["topology"]); +// field_vel_u["association"].set(field_vel["association"]); +// field_vel_u["values"].set(field_vel["values/u"]); +// field_vel_v["topology"].set(field_vel["topology"]); +// field_vel_v["association"].set(field_vel["association"]); +// field_vel_v["values"].set(field_vel["values/v"]); + +// save_mesh[child]["fields"].remove_child("vel"); + +// // make adjset pairwise +// Node &pairwise_adjset = save_mesh[child]["adjsets"]["adjset"]; +// conduit::blueprint::mesh::adjset::to_pairwise(save_mesh[child]["adjsets"]["mesh_adj"], pairwise_adjset); +// save_mesh[child]["adjsets"].remove_child("mesh_adj"); + +// // make changes to save mesh so the diff will pass +// overlink_name_changer(save_mesh[child]); +// } + +// EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); +// NodeConstIterator l_itr = load_mesh.children(); +// NodeConstIterator s_itr = save_mesh.children(); +// while (l_itr.has_next()) +// { +// const Node &l_curr = l_itr.next(); +// const Node &s_curr = s_itr.next(); + +// EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); +// } +// } + +// //----------------------------------------------------------------------------- +// // +// // test read and write semantics +// // + +// //----------------------------------------------------------------------------- +// TEST(conduit_relay_io_silo, read_and_write_semantics) +// { +// for (int ndomains = 2; ndomains < 6; ndomains ++) +// { +// Node save_mesh, load_mesh, info; +// blueprint::mesh::examples::spiral(ndomains, save_mesh); + +// const std::string basename = "silo_spiral_" + std::to_string(ndomains) + "_domains"; +// const std::string filename = basename + ".cycle_000000.root"; + +// remove_path_if_exists(filename); +// io::silo::write_mesh(save_mesh, basename); +// io::silo::read_mesh(filename, load_mesh); +// EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); + +// // make changes to save mesh so the diff will pass +// for (index_t child = 0; child < save_mesh.number_of_children(); child ++) +// { +// silo_name_changer("mesh", save_mesh[child]); +// } + +// EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); +// NodeConstIterator l_itr = load_mesh.children(); +// NodeConstIterator s_itr = save_mesh.children(); +// while (l_itr.has_next()) +// { +// const Node &l_curr = l_itr.next(); +// const Node &s_curr = s_itr.next(); + +// EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); +// } +// } +// } + +// //----------------------------------------------------------------------------- +// // +// // special case tests +// // + +// //----------------------------------------------------------------------------- +// // var is not defined on a domain +// // +// // tests the silo "EMPTY" capability +// TEST(conduit_relay_io_silo, missing_domain_var) +// { +// Node save_mesh, load_mesh, info; +// const int ndomains = 4; +// blueprint::mesh::examples::spiral(ndomains, save_mesh); + +// // remove information for a particular domain +// save_mesh[2]["fields"].remove_child("dist"); + +// const std::string basename = "silo_missing_domain_var_spiral"; +// const std::string filename = basename + ".cycle_000000.root"; + +// remove_path_if_exists(filename); +// io::silo::save_mesh(save_mesh, basename); +// io::silo::load_mesh(filename, load_mesh); +// EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); + +// // make changes to save mesh so the diff will pass +// for (index_t child = 0; child < save_mesh.number_of_children(); child ++) +// { +// silo_name_changer("mesh", save_mesh[child]); +// } +// save_mesh[2].remove_child("fields"); + +// EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); +// NodeConstIterator l_itr = load_mesh.children(); +// NodeConstIterator s_itr = save_mesh.children(); +// while (l_itr.has_next()) +// { +// const Node &l_curr = l_itr.next(); +// const Node &s_curr = s_itr.next(); + +// EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); +// } +// } + +// //----------------------------------------------------------------------------- +// // matset is not defined on a domain +// // +// // tests the silo "EMPTY" capability +// TEST(conduit_relay_io_silo, missing_domain_matset) +// { +// Node save_mesh, load_mesh, info; +// const int ndomains = 4; +// blueprint::mesh::examples::spiral(ndomains, save_mesh); +// add_matset_to_spiral(save_mesh, ndomains); +// EXPECT_TRUE(blueprint::mesh::verify(save_mesh, info)); + +// // remove information for a particular domain +// save_mesh[2]["matsets"].remove_child("matset"); + +// const std::string basename = "silo_missing_domain_matset_spiral"; +// const std::string filename = basename + ".cycle_000000.root"; + +// remove_path_if_exists(filename); +// io::silo::save_mesh(save_mesh, basename); +// io::silo::load_mesh(filename, load_mesh); +// EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); + +// // make changes to save mesh so the diff will pass +// for (index_t child = 0; child < save_mesh.number_of_children(); child ++) +// { +// if (save_mesh[child].has_path("matsets/matset")) +// { +// // get the matset for this domain +// Node &n_matset = save_mesh[child]["matsets"]["matset"]; + +// // clean up volume fractions +// Node vf_arr; +// n_matset["volume_fractions"].to_float64_array(vf_arr); +// n_matset["volume_fractions"].reset(); +// n_matset["volume_fractions"].set(vf_arr); - // cheat a little bit - we don't have these to start - n_matset["sizes"].set_external(load_mesh[child]["matsets"]["mesh_matset"]["sizes"]); - n_matset["offsets"].set_external(load_mesh[child]["matsets"]["mesh_matset"]["offsets"]); - } - - silo_name_changer("mesh", save_mesh[child]); - } - save_mesh[2].remove_child("matsets"); - - EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); - NodeConstIterator l_itr = load_mesh.children(); - NodeConstIterator s_itr = save_mesh.children(); - while (l_itr.has_next()) - { - const Node &l_curr = l_itr.next(); - const Node &s_curr = s_itr.next(); - - EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); - } -} - -//----------------------------------------------------------------------------- -// mesh is not defined on a domain -// -// This case is much less interesting. -// data passes through the clean mesh filter which -// deletes domains that are missing topos. -// They simply are not part of the mesh and so silo -// doesn't have to deal with it. -TEST(conduit_relay_io_silo, missing_domain_mesh_trivial) -{ - Node save_mesh, load_mesh, info; - const int ndomains = 4; - blueprint::mesh::examples::spiral(ndomains, save_mesh); - - // remove information for a particular domain - save_mesh[2]["topologies"].remove_child("topo"); - - const std::string basename = "silo_missing_domain_mesh_trivial_spiral"; - const std::string filename = basename + ".cycle_000000.root"; - - remove_path_if_exists(filename); - io::silo::save_mesh(save_mesh, basename); - io::silo::load_mesh(filename, load_mesh); - - EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); - - // make changes to save mesh so the diff will pass - save_mesh.remove(2); - save_mesh.rename_child("domain_000003", "domain_000002"); - save_mesh[2]["state"]["domain_id"].reset(); - save_mesh[2]["state"]["domain_id"] = 2; - for (index_t child = 0; child < save_mesh.number_of_children(); child ++) - { - silo_name_changer("mesh", save_mesh[child]); - } - - EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); - NodeConstIterator l_itr = load_mesh.children(); - NodeConstIterator s_itr = save_mesh.children(); - while (l_itr.has_next()) - { - const Node &l_curr = l_itr.next(); - const Node &s_curr = s_itr.next(); - - EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); - } -} - -//----------------------------------------------------------------------------- -// mesh is not defined on a domain but there are multiple meshes -TEST(conduit_relay_io_silo, missing_domain_mesh) -{ - Node save_mesh, save_mesh2, load_mesh, load_mesh2, info, opts; - const int ndomains = 4; - blueprint::mesh::examples::spiral(ndomains, save_mesh); - blueprint::mesh::examples::spiral(ndomains, save_mesh2); - - for (index_t child = 0; child < save_mesh.number_of_children(); child ++) - { - save_mesh[child]["coordsets"].rename_child("coords", "coords2"); - save_mesh[child]["topologies"]["topo"]["coordset"].reset(); - save_mesh[child]["topologies"]["topo"]["coordset"] = "coords2"; - save_mesh[child]["topologies"].rename_child("topo", "topo2"); - save_mesh[child]["fields"]["dist"]["topology"].reset(); - save_mesh[child]["fields"]["dist"]["topology"] = "topo2"; - save_mesh[child]["fields"].rename_child("dist", "dist2"); - - save_mesh[child]["coordsets"]["coords"].set_external(save_mesh2[child]["coordsets"]["coords"]); - save_mesh[child]["topologies"]["topo"].set_external(save_mesh2[child]["topologies"]["topo"]); - save_mesh[child]["fields"]["dist"].set_external(save_mesh2[child]["fields"]["dist"]); - } - - // remove information for a particular domain - save_mesh[2]["topologies"].remove_child("topo"); - - const std::string basename = "silo_missing_domain_mesh_spiral"; - const std::string filename = basename + ".cycle_000000.root"; - - remove_path_if_exists(filename); - io::silo::save_mesh(save_mesh, basename); - opts["silo_names"]["multimesh_names"] = "mesh_topo2"; - io::silo::load_mesh(filename, opts, load_mesh); - opts["silo_names"]["multimesh_names"] = "mesh_topo"; - io::silo::load_mesh(filename, opts, load_mesh2); - EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - EXPECT_TRUE(blueprint::mesh::verify(load_mesh2, info)); - - // make changes to save mesh so the diff will pass - save_mesh[2]["coordsets"].remove_child("coords"); - save_mesh[2]["fields"].remove_child("dist"); - for (index_t child = 0; child < save_mesh.number_of_children(); child ++) - { - silo_name_changer("mesh", save_mesh[child]); - } - - // we must merge the two meshes in load mesh - // the indexing is tricky because one is missing a domain - load_mesh[0]["coordsets"]["mesh_topo"].set_external(load_mesh2[0]["coordsets"]["mesh_topo"]); - load_mesh[0]["topologies"]["mesh_topo"].set_external(load_mesh2[0]["topologies"]["mesh_topo"]); - load_mesh[0]["fields"]["mesh_dist"].set_external(load_mesh2[0]["fields"]["mesh_dist"]); - load_mesh[1]["coordsets"]["mesh_topo"].set_external(load_mesh2[1]["coordsets"]["mesh_topo"]); - load_mesh[1]["topologies"]["mesh_topo"].set_external(load_mesh2[1]["topologies"]["mesh_topo"]); - load_mesh[1]["fields"]["mesh_dist"].set_external(load_mesh2[1]["fields"]["mesh_dist"]); - load_mesh[3]["coordsets"]["mesh_topo"].set_external(load_mesh2[2]["coordsets"]["mesh_topo"]); - load_mesh[3]["topologies"]["mesh_topo"].set_external(load_mesh2[2]["topologies"]["mesh_topo"]); - load_mesh[3]["fields"]["mesh_dist"].set_external(load_mesh2[2]["fields"]["mesh_dist"]); - - EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); - NodeConstIterator l_itr = load_mesh.children(); - NodeConstIterator s_itr = save_mesh.children(); - while (l_itr.has_next()) - { - const Node &l_curr = l_itr.next(); - const Node &s_curr = s_itr.next(); - - EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); - } -} - -//----------------------------------------------------------------------------- -// explicit points (unstructured mesh) do not use every coord -TEST(conduit_relay_io_silo, unstructured_points) -{ - Node save_mesh, load_mesh, info; - blueprint::mesh::examples::braid("points", 2, 2, 2, save_mesh); - - std::vector new_conn; - std::vector new_field1; - std::vector new_field2; - std::vector new_xcoords, new_ycoords, new_zcoords; - - int_accessor conn = save_mesh["topologies"]["mesh"]["elements"]["connectivity"].value(); - - float_accessor field1 = save_mesh["fields"]["braid"]["values"].value(); - float_accessor field2 = save_mesh["fields"]["radial"]["values"].value(); - - float_accessor xcoords = save_mesh["coordsets"]["coords"]["values"]["x"].value(); - float_accessor ycoords = save_mesh["coordsets"]["coords"]["values"]["y"].value(); - float_accessor zcoords = save_mesh["coordsets"]["coords"]["values"]["z"].value(); - - for (int i = 1; i < conn.number_of_elements(); i += 2) - { - new_conn.push_back(conn[i]); - new_field1.push_back(field1[i]); - new_field2.push_back(field2[i]); - - new_xcoords.push_back(xcoords[conn[i]]); - new_ycoords.push_back(ycoords[conn[i]]); - new_zcoords.push_back(zcoords[conn[i]]); - } - save_mesh["topologies"]["mesh"]["elements"]["connectivity"].reset(); - save_mesh["topologies"]["mesh"]["elements"]["connectivity"].set(new_conn); - - save_mesh["fields"].remove_child("vel"); - save_mesh["fields"]["braid"]["values"].reset(); - save_mesh["fields"]["braid"]["values"].set(new_field1); - save_mesh["fields"]["radial"]["values"].reset(); - save_mesh["fields"]["radial"]["values"].set(new_field2); - - // we have modified braid such that it only uses half of the points in the coordset - - const std::string basename = "silo_unstructured_points_braid"; - const std::string filename = basename + ".cycle_000100.root"; - - // remove existing root file, directory and any output files - remove_path_if_exists(filename); - - io::silo::save_mesh(save_mesh, basename); - io::silo::load_mesh(filename, load_mesh); - EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - - // now we must remove the unused points and change to an implicit points topo so that the diff passes - save_mesh["coordsets"]["coords"]["values"]["x"].reset(); - save_mesh["coordsets"]["coords"]["values"]["x"].set(new_xcoords); - save_mesh["coordsets"]["coords"]["values"]["y"].reset(); - save_mesh["coordsets"]["coords"]["values"]["y"].set(new_ycoords); - save_mesh["coordsets"]["coords"]["values"]["z"].reset(); - save_mesh["coordsets"]["coords"]["values"]["z"].set(new_zcoords); - - save_mesh["topologies"].remove_child("mesh"); - save_mesh["topologies"]["mesh"]["type"] = "points"; - save_mesh["topologies"]["mesh"]["coordset"] = "coords"; - - // the association doesn't matter for point meshes - // we choose vertex by convention - save_mesh["fields"]["radial"]["association"].reset(); - save_mesh["fields"]["radial"]["association"] = "vertex"; - - silo_name_changer("mesh", save_mesh); - - // the loaded mesh will be in the multidomain format - // but the saved mesh is in the single domain format - EXPECT_EQ(load_mesh.number_of_children(), 1); - EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); - - EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); -} - -//----------------------------------------------------------------------------- - -// -// save option tests -// - -// save options: -/// opts: -/// -/// file_style: "default", "root_only", "multi_file", "overlink" -/// when # of domains == 1, "default" ==> "root_only" -/// else, "default" ==> "multi_file" -/// -/// silo_type: "default", "pdb", "hdf5", "unknown" -/// when the file we are writing to exists, "default" ==> "unknown" -/// else, "default" ==> "hdf5" -/// note: these are additional silo_type options that we could add -/// support for in the future: -/// "hdf5_sec2", "hdf5_stdio", "hdf5_mpio", "hdf5_mpiposix", "taurus" -/// -/// suffix: "default", "cycle", "none" -/// when cycle is present, "default" ==> "cycle" -/// else, "default" ==> "none" -/// -/// root_file_ext: "default", "root", "silo" -/// "default" ==> "root" -/// if overlink, this parameter is unused. -/// -/// mesh_name: (used if present, default ==> "mesh") -/// -/// ovl_topo_name: (used if present, default ==> "") -/// -/// number_of_files: {# of files} -/// when "multi_file" or "overlink": -/// <= 0, use # of files == # of domains -/// > 0, # of files == number_of_files - -//----------------------------------------------------------------------------- -TEST(conduit_relay_io_silo, round_trip_save_option_file_style) -{ - // we will do overlink tests separately - const std::vector file_styles = {"default", "root_only", "multi_file"}; - for (int i = 0; i < file_styles.size(); i ++) - { - Node opts; - opts["file_style"] = file_styles[i]; - - const std::string basename = "silo_save_option_file_style_" + file_styles[i] + "_spiral"; - const std::string filename = basename + ".cycle_000000.root"; - - for (int ndomains = 1; ndomains < 5; ndomains += 3) - { - Node save_mesh, load_mesh, info; - blueprint::mesh::examples::spiral(ndomains, save_mesh); - remove_path_if_exists(filename); - io::silo::save_mesh(save_mesh, basename, opts); - io::silo::load_mesh(filename, load_mesh); - EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); - - // make changes to save mesh so the diff will pass - for (index_t child = 0; child < save_mesh.number_of_children(); child ++) - { - silo_name_changer("mesh", save_mesh[child]); - } - - EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); - NodeConstIterator l_itr = load_mesh.children(); - NodeConstIterator s_itr = save_mesh.children(); - while (l_itr.has_next()) - { - const Node &l_curr = l_itr.next(); - const Node &s_curr = s_itr.next(); - - EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); - } - } - } -} - -//----------------------------------------------------------------------------- -TEST(conduit_relay_io_silo, round_trip_save_option_number_of_files) -{ - const std::vector number_of_files = {-1, 2}; - for (int i = 0; i < number_of_files.size(); i ++) - { - Node opts; - opts["file_style"] = "multi_file"; - opts["number_of_files"] = number_of_files[i]; - - const std::string basename = "silo_save_option_number_of_files_" + - std::to_string(number_of_files[i]) + - "_spiral"; - const std::string filename = basename + ".cycle_000000.root"; - - const int ndomains = 5; - - Node save_mesh, load_mesh, info; - blueprint::mesh::examples::spiral(ndomains, save_mesh); - - remove_path_if_exists(filename); - io::silo::save_mesh(save_mesh, basename, opts); - io::silo::load_mesh(filename, load_mesh); - EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); - - // make changes to save mesh so the diff will pass - for (index_t child = 0; child < save_mesh.number_of_children(); child ++) - { - silo_name_changer("mesh", save_mesh[child]); - } - - EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); - NodeConstIterator l_itr = load_mesh.children(); - NodeConstIterator s_itr = save_mesh.children(); - while (l_itr.has_next()) - { - const Node &l_curr = l_itr.next(); - const Node &s_curr = s_itr.next(); - - EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); - } - } -} - -//----------------------------------------------------------------------------- -TEST(conduit_relay_io_silo, round_trip_save_option_suffix) -{ - const std::vector suffixes = {"default", "default", "cycle", "none"}; - const std::vector file_suffixes = { - "", // cycle is not present - ".cycle_000005", // cycle is present - ".cycle_000005", // cycle is turned on - "", // cycle is turned off - }; - const std::vector include_cycle = {"no", "yes", "yes", "yes"}; - for (int i = 0; i < suffixes.size(); i ++) - { - Node opts; - opts["suffix"] = suffixes[i]; - - const std::string basename = "silo_save_option_suffix_" + suffixes[i] + - "_" + include_cycle[i] + "_basic"; - const std::string filename = basename + file_suffixes[i] + ".root"; - - Node save_mesh, load_mesh, info; - blueprint::mesh::examples::basic("rectilinear", 3, 4, 0, save_mesh); - - if (include_cycle[i] == "yes") - { - save_mesh["state/cycle"] = 5; - } - - remove_path_if_exists(filename); - io::silo::save_mesh(save_mesh, basename, opts); - io::silo::load_mesh(filename, load_mesh); - EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - - silo_name_changer("mesh", save_mesh); - - // the loaded mesh will be in the multidomain format - // but the saved mesh is in the single domain format - EXPECT_EQ(load_mesh.number_of_children(), 1); - EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); - - EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); - } -} - -//----------------------------------------------------------------------------- -TEST(conduit_relay_io_silo, round_trip_save_option_root_file_ext) -{ - const std::vector root_file_exts = {"default", "root", "silo"}; - - for (int i = 0; i < root_file_exts.size(); i ++) - { - Node opts; - opts["root_file_ext"] = root_file_exts[i]; - - std::string actual_file_ext = root_file_exts[i]; - if (actual_file_ext == "default") - { - actual_file_ext = "root"; - } - - const std::string basename = "round_trip_save_option_root_file_ext_" + - root_file_exts[i] + "_basic"; - const std::string filename = basename + "." + actual_file_ext; - - Node save_mesh, load_mesh, info; - blueprint::mesh::examples::basic("rectilinear", 3, 4, 0, save_mesh); - remove_path_if_exists(filename); - io::silo::save_mesh(save_mesh, basename, opts); - io::silo::load_mesh(filename, load_mesh); - EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - - silo_name_changer("mesh", save_mesh); - - // the loaded mesh will be in the multidomain format - // but the saved mesh is in the single domain format - EXPECT_EQ(load_mesh.number_of_children(), 1); - EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); - EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); - } -} - -//----------------------------------------------------------------------------- -TEST(conduit_relay_io_silo, round_trip_save_option_mesh_name) -{ - const std::string basename = "silo_save_option_mesh_name_basic"; - const std::string filename = basename + ".root"; - - Node opts; - opts["mesh_name"] = "mymesh"; - - Node save_mesh, load_mesh, info; - blueprint::mesh::examples::basic("rectilinear", 3, 4, 0, save_mesh); - remove_path_if_exists(filename); - io::silo::save_mesh(save_mesh, basename, opts); - io::silo::load_mesh(filename, load_mesh); - EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - - silo_name_changer("mymesh", save_mesh); - - // the loaded mesh will be in the multidomain format - // but the saved mesh is in the single domain format - EXPECT_EQ(load_mesh.number_of_children(), 1); - EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); - EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); -} - -//----------------------------------------------------------------------------- -TEST(conduit_relay_io_silo, round_trip_save_option_silo_type) -{ - const std::vector silo_types = {"default", "pdb", "hdf5", "unknown"}; - for (int i = 3; i < silo_types.size(); i ++) - { - Node opts; - opts["silo_type"] = silo_types[i]; - - Node save_mesh, load_mesh, info; - blueprint::mesh::examples::basic("rectilinear", 3, 4, 0, save_mesh); - - const std::string basename = "silo_save_option_silo_type_" + silo_types[i] + "_basic"; - const std::string filename = basename + ".root"; - - remove_path_if_exists(filename); - io::silo::save_mesh(save_mesh, basename, opts); - io::silo::load_mesh(filename, load_mesh); - EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); +// // cheat a little bit - we don't have these to start +// n_matset["sizes"].set_external(load_mesh[child]["matsets"]["mesh_matset"]["sizes"]); +// n_matset["offsets"].set_external(load_mesh[child]["matsets"]["mesh_matset"]["offsets"]); +// } + +// silo_name_changer("mesh", save_mesh[child]); +// } +// save_mesh[2].remove_child("matsets"); + +// EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); +// NodeConstIterator l_itr = load_mesh.children(); +// NodeConstIterator s_itr = save_mesh.children(); +// while (l_itr.has_next()) +// { +// const Node &l_curr = l_itr.next(); +// const Node &s_curr = s_itr.next(); + +// EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); +// } +// } + +// //----------------------------------------------------------------------------- +// // mesh is not defined on a domain +// // +// // This case is much less interesting. +// // data passes through the clean mesh filter which +// // deletes domains that are missing topos. +// // They simply are not part of the mesh and so silo +// // doesn't have to deal with it. +// TEST(conduit_relay_io_silo, missing_domain_mesh_trivial) +// { +// Node save_mesh, load_mesh, info; +// const int ndomains = 4; +// blueprint::mesh::examples::spiral(ndomains, save_mesh); + +// // remove information for a particular domain +// save_mesh[2]["topologies"].remove_child("topo"); + +// const std::string basename = "silo_missing_domain_mesh_trivial_spiral"; +// const std::string filename = basename + ".cycle_000000.root"; + +// remove_path_if_exists(filename); +// io::silo::save_mesh(save_mesh, basename); +// io::silo::load_mesh(filename, load_mesh); + +// EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); + +// // make changes to save mesh so the diff will pass +// save_mesh.remove(2); +// save_mesh.rename_child("domain_000003", "domain_000002"); +// save_mesh[2]["state"]["domain_id"].reset(); +// save_mesh[2]["state"]["domain_id"] = 2; +// for (index_t child = 0; child < save_mesh.number_of_children(); child ++) +// { +// silo_name_changer("mesh", save_mesh[child]); +// } + +// EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); +// NodeConstIterator l_itr = load_mesh.children(); +// NodeConstIterator s_itr = save_mesh.children(); +// while (l_itr.has_next()) +// { +// const Node &l_curr = l_itr.next(); +// const Node &s_curr = s_itr.next(); + +// EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); +// } +// } + +// //----------------------------------------------------------------------------- +// // mesh is not defined on a domain but there are multiple meshes +// TEST(conduit_relay_io_silo, missing_domain_mesh) +// { +// Node save_mesh, save_mesh2, load_mesh, load_mesh2, info, opts; +// const int ndomains = 4; +// blueprint::mesh::examples::spiral(ndomains, save_mesh); +// blueprint::mesh::examples::spiral(ndomains, save_mesh2); + +// for (index_t child = 0; child < save_mesh.number_of_children(); child ++) +// { +// save_mesh[child]["coordsets"].rename_child("coords", "coords2"); +// save_mesh[child]["topologies"]["topo"]["coordset"].reset(); +// save_mesh[child]["topologies"]["topo"]["coordset"] = "coords2"; +// save_mesh[child]["topologies"].rename_child("topo", "topo2"); +// save_mesh[child]["fields"]["dist"]["topology"].reset(); +// save_mesh[child]["fields"]["dist"]["topology"] = "topo2"; +// save_mesh[child]["fields"].rename_child("dist", "dist2"); + +// save_mesh[child]["coordsets"]["coords"].set_external(save_mesh2[child]["coordsets"]["coords"]); +// save_mesh[child]["topologies"]["topo"].set_external(save_mesh2[child]["topologies"]["topo"]); +// save_mesh[child]["fields"]["dist"].set_external(save_mesh2[child]["fields"]["dist"]); +// } + +// // remove information for a particular domain +// save_mesh[2]["topologies"].remove_child("topo"); + +// const std::string basename = "silo_missing_domain_mesh_spiral"; +// const std::string filename = basename + ".cycle_000000.root"; + +// remove_path_if_exists(filename); +// io::silo::save_mesh(save_mesh, basename); +// opts["silo_names"]["multimesh_names"] = "mesh_topo2"; +// io::silo::load_mesh(filename, opts, load_mesh); +// opts["silo_names"]["multimesh_names"] = "mesh_topo"; +// io::silo::load_mesh(filename, opts, load_mesh2); +// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); +// EXPECT_TRUE(blueprint::mesh::verify(load_mesh2, info)); + +// // make changes to save mesh so the diff will pass +// save_mesh[2]["coordsets"].remove_child("coords"); +// save_mesh[2]["fields"].remove_child("dist"); +// for (index_t child = 0; child < save_mesh.number_of_children(); child ++) +// { +// silo_name_changer("mesh", save_mesh[child]); +// } + +// // we must merge the two meshes in load mesh +// // the indexing is tricky because one is missing a domain +// load_mesh[0]["coordsets"]["mesh_topo"].set_external(load_mesh2[0]["coordsets"]["mesh_topo"]); +// load_mesh[0]["topologies"]["mesh_topo"].set_external(load_mesh2[0]["topologies"]["mesh_topo"]); +// load_mesh[0]["fields"]["mesh_dist"].set_external(load_mesh2[0]["fields"]["mesh_dist"]); +// load_mesh[1]["coordsets"]["mesh_topo"].set_external(load_mesh2[1]["coordsets"]["mesh_topo"]); +// load_mesh[1]["topologies"]["mesh_topo"].set_external(load_mesh2[1]["topologies"]["mesh_topo"]); +// load_mesh[1]["fields"]["mesh_dist"].set_external(load_mesh2[1]["fields"]["mesh_dist"]); +// load_mesh[3]["coordsets"]["mesh_topo"].set_external(load_mesh2[2]["coordsets"]["mesh_topo"]); +// load_mesh[3]["topologies"]["mesh_topo"].set_external(load_mesh2[2]["topologies"]["mesh_topo"]); +// load_mesh[3]["fields"]["mesh_dist"].set_external(load_mesh2[2]["fields"]["mesh_dist"]); + +// EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); +// NodeConstIterator l_itr = load_mesh.children(); +// NodeConstIterator s_itr = save_mesh.children(); +// while (l_itr.has_next()) +// { +// const Node &l_curr = l_itr.next(); +// const Node &s_curr = s_itr.next(); + +// EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); +// } +// } + +// //----------------------------------------------------------------------------- +// // explicit points (unstructured mesh) do not use every coord +// TEST(conduit_relay_io_silo, unstructured_points) +// { +// Node save_mesh, load_mesh, info; +// blueprint::mesh::examples::braid("points", 2, 2, 2, save_mesh); + +// std::vector new_conn; +// std::vector new_field1; +// std::vector new_field2; +// std::vector new_xcoords, new_ycoords, new_zcoords; + +// int_accessor conn = save_mesh["topologies"]["mesh"]["elements"]["connectivity"].value(); + +// float_accessor field1 = save_mesh["fields"]["braid"]["values"].value(); +// float_accessor field2 = save_mesh["fields"]["radial"]["values"].value(); + +// float_accessor xcoords = save_mesh["coordsets"]["coords"]["values"]["x"].value(); +// float_accessor ycoords = save_mesh["coordsets"]["coords"]["values"]["y"].value(); +// float_accessor zcoords = save_mesh["coordsets"]["coords"]["values"]["z"].value(); + +// for (int i = 1; i < conn.number_of_elements(); i += 2) +// { +// new_conn.push_back(conn[i]); +// new_field1.push_back(field1[i]); +// new_field2.push_back(field2[i]); + +// new_xcoords.push_back(xcoords[conn[i]]); +// new_ycoords.push_back(ycoords[conn[i]]); +// new_zcoords.push_back(zcoords[conn[i]]); +// } +// save_mesh["topologies"]["mesh"]["elements"]["connectivity"].reset(); +// save_mesh["topologies"]["mesh"]["elements"]["connectivity"].set(new_conn); + +// save_mesh["fields"].remove_child("vel"); +// save_mesh["fields"]["braid"]["values"].reset(); +// save_mesh["fields"]["braid"]["values"].set(new_field1); +// save_mesh["fields"]["radial"]["values"].reset(); +// save_mesh["fields"]["radial"]["values"].set(new_field2); + +// // we have modified braid such that it only uses half of the points in the coordset + +// const std::string basename = "silo_unstructured_points_braid"; +// const std::string filename = basename + ".cycle_000100.root"; + +// // remove existing root file, directory and any output files +// remove_path_if_exists(filename); + +// io::silo::save_mesh(save_mesh, basename); +// io::silo::load_mesh(filename, load_mesh); +// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + +// // now we must remove the unused points and change to an implicit points topo so that the diff passes +// save_mesh["coordsets"]["coords"]["values"]["x"].reset(); +// save_mesh["coordsets"]["coords"]["values"]["x"].set(new_xcoords); +// save_mesh["coordsets"]["coords"]["values"]["y"].reset(); +// save_mesh["coordsets"]["coords"]["values"]["y"].set(new_ycoords); +// save_mesh["coordsets"]["coords"]["values"]["z"].reset(); +// save_mesh["coordsets"]["coords"]["values"]["z"].set(new_zcoords); + +// save_mesh["topologies"].remove_child("mesh"); +// save_mesh["topologies"]["mesh"]["type"] = "points"; +// save_mesh["topologies"]["mesh"]["coordset"] = "coords"; + +// // the association doesn't matter for point meshes +// // we choose vertex by convention +// save_mesh["fields"]["radial"]["association"].reset(); +// save_mesh["fields"]["radial"]["association"] = "vertex"; + +// silo_name_changer("mesh", save_mesh); + +// // the loaded mesh will be in the multidomain format +// // but the saved mesh is in the single domain format +// EXPECT_EQ(load_mesh.number_of_children(), 1); +// EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); + +// EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); +// } + +// //----------------------------------------------------------------------------- + +// // +// // save option tests +// // + +// // save options: +// /// opts: +// /// +// /// file_style: "default", "root_only", "multi_file", "overlink" +// /// when # of domains == 1, "default" ==> "root_only" +// /// else, "default" ==> "multi_file" +// /// +// /// silo_type: "default", "pdb", "hdf5", "unknown" +// /// when the file we are writing to exists, "default" ==> "unknown" +// /// else, "default" ==> "hdf5" +// /// note: these are additional silo_type options that we could add +// /// support for in the future: +// /// "hdf5_sec2", "hdf5_stdio", "hdf5_mpio", "hdf5_mpiposix", "taurus" +// /// +// /// suffix: "default", "cycle", "none" +// /// when cycle is present, "default" ==> "cycle" +// /// else, "default" ==> "none" +// /// +// /// root_file_ext: "default", "root", "silo" +// /// "default" ==> "root" +// /// if overlink, this parameter is unused. +// /// +// /// mesh_name: (used if present, default ==> "mesh") +// /// +// /// ovl_topo_name: (used if present, default ==> "") +// /// +// /// number_of_files: {# of files} +// /// when "multi_file" or "overlink": +// /// <= 0, use # of files == # of domains +// /// > 0, # of files == number_of_files + +// //----------------------------------------------------------------------------- +// TEST(conduit_relay_io_silo, round_trip_save_option_file_style) +// { +// // we will do overlink tests separately +// const std::vector file_styles = {"default", "root_only", "multi_file"}; +// for (int i = 0; i < file_styles.size(); i ++) +// { +// Node opts; +// opts["file_style"] = file_styles[i]; + +// const std::string basename = "silo_save_option_file_style_" + file_styles[i] + "_spiral"; +// const std::string filename = basename + ".cycle_000000.root"; + +// for (int ndomains = 1; ndomains < 5; ndomains += 3) +// { +// Node save_mesh, load_mesh, info; +// blueprint::mesh::examples::spiral(ndomains, save_mesh); +// remove_path_if_exists(filename); +// io::silo::save_mesh(save_mesh, basename, opts); +// io::silo::load_mesh(filename, load_mesh); +// EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); + +// // make changes to save mesh so the diff will pass +// for (index_t child = 0; child < save_mesh.number_of_children(); child ++) +// { +// silo_name_changer("mesh", save_mesh[child]); +// } + +// EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); +// NodeConstIterator l_itr = load_mesh.children(); +// NodeConstIterator s_itr = save_mesh.children(); +// while (l_itr.has_next()) +// { +// const Node &l_curr = l_itr.next(); +// const Node &s_curr = s_itr.next(); + +// EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); +// } +// } +// } +// } + +// //----------------------------------------------------------------------------- +// TEST(conduit_relay_io_silo, round_trip_save_option_number_of_files) +// { +// const std::vector number_of_files = {-1, 2}; +// for (int i = 0; i < number_of_files.size(); i ++) +// { +// Node opts; +// opts["file_style"] = "multi_file"; +// opts["number_of_files"] = number_of_files[i]; + +// const std::string basename = "silo_save_option_number_of_files_" + +// std::to_string(number_of_files[i]) + +// "_spiral"; +// const std::string filename = basename + ".cycle_000000.root"; + +// const int ndomains = 5; + +// Node save_mesh, load_mesh, info; +// blueprint::mesh::examples::spiral(ndomains, save_mesh); + +// remove_path_if_exists(filename); +// io::silo::save_mesh(save_mesh, basename, opts); +// io::silo::load_mesh(filename, load_mesh); +// EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); + +// // make changes to save mesh so the diff will pass +// for (index_t child = 0; child < save_mesh.number_of_children(); child ++) +// { +// silo_name_changer("mesh", save_mesh[child]); +// } + +// EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); +// NodeConstIterator l_itr = load_mesh.children(); +// NodeConstIterator s_itr = save_mesh.children(); +// while (l_itr.has_next()) +// { +// const Node &l_curr = l_itr.next(); +// const Node &s_curr = s_itr.next(); + +// EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); +// } +// } +// } + +// //----------------------------------------------------------------------------- +// TEST(conduit_relay_io_silo, round_trip_save_option_suffix) +// { +// const std::vector suffixes = {"default", "default", "cycle", "none"}; +// const std::vector file_suffixes = { +// "", // cycle is not present +// ".cycle_000005", // cycle is present +// ".cycle_000005", // cycle is turned on +// "", // cycle is turned off +// }; +// const std::vector include_cycle = {"no", "yes", "yes", "yes"}; +// for (int i = 0; i < suffixes.size(); i ++) +// { +// Node opts; +// opts["suffix"] = suffixes[i]; + +// const std::string basename = "silo_save_option_suffix_" + suffixes[i] + +// "_" + include_cycle[i] + "_basic"; +// const std::string filename = basename + file_suffixes[i] + ".root"; + +// Node save_mesh, load_mesh, info; +// blueprint::mesh::examples::basic("rectilinear", 3, 4, 0, save_mesh); + +// if (include_cycle[i] == "yes") +// { +// save_mesh["state/cycle"] = 5; +// } + +// remove_path_if_exists(filename); +// io::silo::save_mesh(save_mesh, basename, opts); +// io::silo::load_mesh(filename, load_mesh); +// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + +// silo_name_changer("mesh", save_mesh); + +// // the loaded mesh will be in the multidomain format +// // but the saved mesh is in the single domain format +// EXPECT_EQ(load_mesh.number_of_children(), 1); +// EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); + +// EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); +// } +// } + +// //----------------------------------------------------------------------------- +// TEST(conduit_relay_io_silo, round_trip_save_option_root_file_ext) +// { +// const std::vector root_file_exts = {"default", "root", "silo"}; + +// for (int i = 0; i < root_file_exts.size(); i ++) +// { +// Node opts; +// opts["root_file_ext"] = root_file_exts[i]; + +// std::string actual_file_ext = root_file_exts[i]; +// if (actual_file_ext == "default") +// { +// actual_file_ext = "root"; +// } + +// const std::string basename = "round_trip_save_option_root_file_ext_" + +// root_file_exts[i] + "_basic"; +// const std::string filename = basename + "." + actual_file_ext; + +// Node save_mesh, load_mesh, info; +// blueprint::mesh::examples::basic("rectilinear", 3, 4, 0, save_mesh); +// remove_path_if_exists(filename); +// io::silo::save_mesh(save_mesh, basename, opts); +// io::silo::load_mesh(filename, load_mesh); +// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + +// silo_name_changer("mesh", save_mesh); + +// // the loaded mesh will be in the multidomain format +// // but the saved mesh is in the single domain format +// EXPECT_EQ(load_mesh.number_of_children(), 1); +// EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); +// EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); +// } +// } + +// //----------------------------------------------------------------------------- +// TEST(conduit_relay_io_silo, round_trip_save_option_mesh_name) +// { +// const std::string basename = "silo_save_option_mesh_name_basic"; +// const std::string filename = basename + ".root"; + +// Node opts; +// opts["mesh_name"] = "mymesh"; + +// Node save_mesh, load_mesh, info; +// blueprint::mesh::examples::basic("rectilinear", 3, 4, 0, save_mesh); +// remove_path_if_exists(filename); +// io::silo::save_mesh(save_mesh, basename, opts); +// io::silo::load_mesh(filename, load_mesh); +// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + +// silo_name_changer("mymesh", save_mesh); + +// // the loaded mesh will be in the multidomain format +// // but the saved mesh is in the single domain format +// EXPECT_EQ(load_mesh.number_of_children(), 1); +// EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); +// EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); +// } + +// //----------------------------------------------------------------------------- +// TEST(conduit_relay_io_silo, round_trip_save_option_silo_type) +// { +// const std::vector silo_types = {"default", "pdb", "hdf5", "unknown"}; +// for (int i = 3; i < silo_types.size(); i ++) +// { +// Node opts; +// opts["silo_type"] = silo_types[i]; + +// Node save_mesh, load_mesh, info; +// blueprint::mesh::examples::basic("rectilinear", 3, 4, 0, save_mesh); + +// const std::string basename = "silo_save_option_silo_type_" + silo_types[i] + "_basic"; +// const std::string filename = basename + ".root"; + +// remove_path_if_exists(filename); +// io::silo::save_mesh(save_mesh, basename, opts); +// io::silo::load_mesh(filename, load_mesh); +// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - silo_name_changer("mesh", save_mesh); - - // the loaded mesh will be in the multidomain format - // but the saved mesh is in the single domain format - EXPECT_EQ(load_mesh.number_of_children(), 1); - EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); - - EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); - } -} - -//----------------------------------------------------------------------------- -TEST(conduit_relay_io_silo, round_trip_save_option_overlink1) -{ - const std::vector ovl_topo_names = {"", "topo"}; - for (int i = 0; i < ovl_topo_names.size(); i ++) - { - std::string basename; - if (ovl_topo_names[i].empty()) - { - basename = "silo_save_option_overlink_spiral"; - } - else - { - basename = "silo_save_option_overlink_spiral_" + ovl_topo_names[i]; - } - const std::string filename = basename + "/OvlTop.silo"; - - Node opts; - opts["file_style"] = "overlink"; - opts["ovl_topo_name"] = ovl_topo_names[i]; - - int ndomains = 2; - - Node save_mesh, load_mesh, info; - blueprint::mesh::examples::spiral(ndomains, save_mesh); - add_matset_to_spiral(save_mesh, ndomains); - remove_path_if_exists(filename); - io::silo::save_mesh(save_mesh, basename, opts); - io::silo::load_mesh(filename, load_mesh); - EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); - - // make changes to save mesh so the diff will pass - for (index_t child = 0; child < save_mesh.number_of_children(); child ++) - { - // get the matset for this domain - Node &n_matset = save_mesh[child]["matsets"]["matset"]; - - // clean up volume fractions - Node vf_arr; - n_matset["volume_fractions"].to_float64_array(vf_arr); - n_matset["volume_fractions"].reset(); - n_matset["volume_fractions"].set(vf_arr); +// silo_name_changer("mesh", save_mesh); + +// // the loaded mesh will be in the multidomain format +// // but the saved mesh is in the single domain format +// EXPECT_EQ(load_mesh.number_of_children(), 1); +// EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); + +// EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); +// } +// } + +// //----------------------------------------------------------------------------- +// TEST(conduit_relay_io_silo, round_trip_save_option_overlink1) +// { +// const std::vector ovl_topo_names = {"", "topo"}; +// for (int i = 0; i < ovl_topo_names.size(); i ++) +// { +// std::string basename; +// if (ovl_topo_names[i].empty()) +// { +// basename = "silo_save_option_overlink_spiral"; +// } +// else +// { +// basename = "silo_save_option_overlink_spiral_" + ovl_topo_names[i]; +// } +// const std::string filename = basename + "/OvlTop.silo"; + +// Node opts; +// opts["file_style"] = "overlink"; +// opts["ovl_topo_name"] = ovl_topo_names[i]; + +// int ndomains = 2; + +// Node save_mesh, load_mesh, info; +// blueprint::mesh::examples::spiral(ndomains, save_mesh); +// add_matset_to_spiral(save_mesh, ndomains); +// remove_path_if_exists(filename); +// io::silo::save_mesh(save_mesh, basename, opts); +// io::silo::load_mesh(filename, load_mesh); +// EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); + +// // make changes to save mesh so the diff will pass +// for (index_t child = 0; child < save_mesh.number_of_children(); child ++) +// { +// // get the matset for this domain +// Node &n_matset = save_mesh[child]["matsets"]["matset"]; + +// // clean up volume fractions +// Node vf_arr; +// n_matset["volume_fractions"].to_float64_array(vf_arr); +// n_matset["volume_fractions"].reset(); +// n_matset["volume_fractions"].set(vf_arr); - // cheat a little bit - we don't have these to start - n_matset["sizes"].set_external(load_mesh[child]["matsets"]["MMATERIAL"]["sizes"]); - n_matset["offsets"].set_external(load_mesh[child]["matsets"]["MMATERIAL"]["offsets"]); - - overlink_name_changer(save_mesh[child]); - } - - EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); - NodeConstIterator l_itr = load_mesh.children(); - NodeConstIterator s_itr = save_mesh.children(); - while (l_itr.has_next()) - { - const Node &l_curr = l_itr.next(); - const Node &s_curr = s_itr.next(); - - EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); - } - } -} - -//----------------------------------------------------------------------------- -// this tests var attributes and padding dimensions -TEST(conduit_relay_io_silo, round_trip_save_option_overlink2) -{ - const std::string basename = "silo_save_option_overlink_basic"; - const std::string filename = basename + "/OvlTop.silo"; - - Node write_opts, read_opts; - write_opts["file_style"] = "overlink"; - read_opts["matset_style"] = "multi_buffer_full"; - - Node save_mesh, load_mesh, info; - blueprint::mesh::examples::basic("structured", 3, 3, 1, save_mesh); - - // add another field that is volume dependent - Node &field2 = save_mesh["fields"]["field2"]; - field2["association"] = "element"; - field2["topology"] = "mesh"; - field2["volume_dependent"] = "true"; - field2["values"].set_external(save_mesh["fields"]["field"]["values"]); - - // add a matset to make overlink happy - add_multi_buffer_full_matset(save_mesh, 4, "mesh"); - - remove_path_if_exists(filename); - io::silo::save_mesh(save_mesh, basename, write_opts); - io::silo::load_mesh(filename, read_opts, load_mesh); - EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); - - // make changes to save mesh so the diff will pass - overlink_name_changer(save_mesh); - - // the loaded mesh will be in the multidomain format - // but the saved mesh is in the single domain format - EXPECT_EQ(load_mesh.number_of_children(), 1); - EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); - - EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); - - // open silo files and do some checks - - DBfile *rootfile = DBOpen(filename.c_str(), DB_UNKNOWN, DB_READ); - EXPECT_TRUE(DBInqVarExists(rootfile, "VAR_ATTRIBUTES")); - EXPECT_TRUE(DBInqVarType(rootfile, "VAR_ATTRIBUTES") == DB_ARRAY); - - DBcompoundarray *var_attr = DBGetCompoundarray(rootfile, "VAR_ATTRIBUTES"); - - // fetch pointers to elements inside the compound array - char **elemnames = var_attr->elemnames; - int *elemlengths = var_attr->elemlengths; - int nelems = var_attr->nelems; - int *values = static_cast(var_attr->values); - int nvalues = var_attr->nvalues; - int datatype = var_attr->datatype; - - EXPECT_EQ(std::string(elemnames[0]), "field"); - EXPECT_EQ(std::string(elemnames[1]), "field2"); - EXPECT_EQ(elemlengths[0], 5); - EXPECT_EQ(elemlengths[1], 5); - EXPECT_EQ(nelems, 2); - // for first var - EXPECT_EQ(values[0], 1); - EXPECT_EQ(values[1], 0); - EXPECT_EQ(values[2], 1); - EXPECT_EQ(values[3], 0); - EXPECT_EQ(values[4], 1); - // for second var - EXPECT_EQ(values[5], 1); - EXPECT_EQ(values[6], 1); - EXPECT_EQ(values[7], 1); - EXPECT_EQ(values[8], 0); - EXPECT_EQ(values[9], 1); - EXPECT_EQ(nvalues, 10); - EXPECT_EQ(datatype, DB_INT); - - DBFreeCompoundarray(var_attr); - - EXPECT_TRUE(DBInqVarExists(rootfile, "PAD_DIMS")); - EXPECT_TRUE(DBInqVarType(rootfile, "PAD_DIMS") == DB_ARRAY); - - DBcompoundarray *pad_dims = DBGetCompoundarray(rootfile, "PAD_DIMS"); - - // fetch pointers to elements inside the compound array - elemnames = pad_dims->elemnames; - elemlengths = pad_dims->elemlengths; - nelems = pad_dims->nelems; - values = static_cast(pad_dims->values); - nvalues = pad_dims->nvalues; - datatype = pad_dims->datatype; - - EXPECT_EQ(std::string(elemnames[0]), "paddims"); - EXPECT_EQ(elemlengths[0], 6); - EXPECT_EQ(nelems, 1); - EXPECT_EQ(values[0], 0); - EXPECT_EQ(values[1], 0); - EXPECT_EQ(values[2], 0); - EXPECT_EQ(values[3], 0); - EXPECT_EQ(values[4], 0); - EXPECT_EQ(values[5], 0); - EXPECT_EQ(nvalues, 6); - EXPECT_EQ(datatype, DB_INT); - - DBFreeCompoundarray(pad_dims); - - DBClose(rootfile); - - // now check domain file - - const std::string dom_filename = basename + "/domain0.silo"; - DBfile *domfile = DBOpen(dom_filename.c_str(), DB_UNKNOWN, DB_READ); - - EXPECT_TRUE(DBInqVarExists(domfile, "DOMAIN_NEIGHBOR_NUMS")); - EXPECT_TRUE(DBInqVarType(domfile, "DOMAIN_NEIGHBOR_NUMS") == DB_ARRAY); - - DBcompoundarray *dom_neighbor_nums = DBGetCompoundarray(domfile, "DOMAIN_NEIGHBOR_NUMS"); - - // fetch pointers to elements inside the compound array - elemnames = dom_neighbor_nums->elemnames; - elemlengths = dom_neighbor_nums->elemlengths; - nelems = dom_neighbor_nums->nelems; - values = static_cast(dom_neighbor_nums->values); - nvalues = dom_neighbor_nums->nvalues; - datatype = dom_neighbor_nums->datatype; - - EXPECT_EQ(std::string(elemnames[0]), "num_neighbors"); - EXPECT_EQ(std::string(elemnames[1]), "neighbor_nums"); - EXPECT_EQ(elemlengths[0], 1); - EXPECT_EQ(elemlengths[1], 0); - EXPECT_EQ(nelems, 2); - EXPECT_EQ(values[0], 0); - EXPECT_EQ(nvalues, 1); - EXPECT_EQ(datatype, DB_INT); - - DBFreeCompoundarray(dom_neighbor_nums); - - DBClose(domfile); -} - -//----------------------------------------------------------------------------- -// this tests material i/o -TEST(conduit_relay_io_silo, round_trip_save_option_overlink3) -{ - Node save_mesh, load_mesh, info; - const int nx = 100, ny = 100; - const double radius = 0.25; - blueprint::mesh::examples::venn("sparse_by_element", nx, ny, radius, save_mesh); - - const std::string basename = "silo_save_option_overlink_venn"; - const std::string filename = basename + "/OvlTop.silo"; - - Node opts; - opts["file_style"] = "overlink"; - - remove_path_if_exists(filename); - io::silo::save_mesh(save_mesh, basename, opts); - io::silo::load_mesh(filename, load_mesh); - EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - - // make changes to save mesh so the diff will pass - - // The field mat_check has values that are one type and matset_values - // that are another type. The silo writer converts both to double arrays - // in this case, so we follow suit. - Node mat_check_new_values, mat_check_new_matset_values; - save_mesh["fields"]["mat_check"]["values"].to_double_array(mat_check_new_values); - save_mesh["fields"]["mat_check"]["matset_values"].to_double_array(mat_check_new_matset_values); - save_mesh["fields"]["mat_check"]["values"].set_external(mat_check_new_values); - save_mesh["fields"]["mat_check"]["matset_values"].set_external(mat_check_new_matset_values); - - overlink_name_changer(save_mesh); - - // the loaded mesh will be in the multidomain format - // but the saved mesh is in the single domain format - EXPECT_EQ(load_mesh.number_of_children(), 1); - EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); - - EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); -} - -//----------------------------------------------------------------------------- -// we are testing vector fields get converted to scalars -TEST(conduit_relay_io_silo, round_trip_save_option_overlink4) -{ - const std::vector> mesh_types = { - std::make_pair("rectilinear", "2"), std::make_pair("rectilinear", "3"), - std::make_pair("structured", "2"), std::make_pair("structured", "3"), - std::make_pair("quads", "2"), - std::make_pair("hexs", "3"), - }; - for (int i = 0; i < mesh_types.size(); ++i) - { - std::string dim = mesh_types[i].second; - index_t nx = 3; - index_t ny = 4; - index_t nz = (dim == "2" ? 0 : 2); - - std::string mesh_type = mesh_types[i].first; - - Node save_mesh, load_mesh, info; - blueprint::mesh::examples::braid(mesh_type, nx, ny, nz, save_mesh); - index_t nele_x = nx - 1; - index_t nele_y = ny - 1; - index_t nele_z = (dim == "2" ? 0 : nz - 1); - - // provide a matset for braid - braid_init_example_matset(nele_x, nele_y, nele_z, save_mesh["matsets"]["matset"]); - - const std::string basename = "silo_save_option_overlink_braid_" + mesh_type + "_" + dim + "D"; - const std::string filename = basename + "/OvlTop.silo"; - - Node write_opts, read_opts; - write_opts["file_style"] = "overlink"; - read_opts["matset_style"] = "multi_buffer_full"; - - // remove existing root file, directory and any output files - remove_path_if_exists(filename); - io::silo::save_mesh(save_mesh, basename, write_opts); - io::silo::load_mesh(filename, read_opts, load_mesh); - EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - - Node &field_vel = save_mesh["fields"]["vel"]; - Node &field_vel_u = save_mesh["fields"]["vel_u"]; - Node &field_vel_v = save_mesh["fields"]["vel_v"]; - - field_vel_u["topology"].set(field_vel["topology"]); - field_vel_u["association"].set(field_vel["association"]); - field_vel_u["values"].set(field_vel["values/u"]); - field_vel_v["topology"].set(field_vel["topology"]); - field_vel_v["association"].set(field_vel["association"]); - field_vel_v["values"].set(field_vel["values/v"]); - - if (dim == "3") - { - Node &field_vel_w = save_mesh["fields"]["vel_w"]; - field_vel_w["topology"].set(field_vel["topology"]); - field_vel_w["association"].set(field_vel["association"]); - field_vel_w["values"].set(field_vel["values/w"]); - } - - save_mesh["fields"].remove_child("vel"); - - // make changes to save mesh so the diff will pass - overlink_name_changer(save_mesh); - - // the loaded mesh will be in the multidomain format - // but the saved mesh is in the single domain format - EXPECT_EQ(load_mesh.number_of_children(), 1); - EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); - - EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); - } -} - -//----------------------------------------------------------------------------- -// check that all the shape types work (specifically polytopal ones) -TEST(conduit_relay_io_silo, round_trip_save_option_overlink5) -{ - const std::vector> mesh_types = { - std::make_pair("uniform", "2"), std::make_pair("uniform", "3"), - std::make_pair("rectilinear", "2"), std::make_pair("rectilinear", "3"), - std::make_pair("structured", "2"), std::make_pair("structured", "3"), - std::make_pair("quads", "2"), - std::make_pair("polygons", "2"), - std::make_pair("hexs", "3"), - // std::make_pair("polyhedra", "3") - // Overlink does not support tris, wedges, pyramids, or tets - }; - for (int i = 0; i < mesh_types.size(); ++i) - { - const std::string dim = mesh_types[i].second; - index_t nx = 3; - index_t ny = 4; - index_t nz = (dim == "2" ? 0 : 2); - - const std::string mesh_type = mesh_types[i].first; - - Node save_mesh, load_mesh, info; - blueprint::mesh::examples::basic(mesh_type, nx, ny, nz, save_mesh); - - const std::string basename = "silo_save_option_overlink_basic_" + mesh_type + "_" + dim + "D"; - const std::string filename = basename + "/OvlTop.silo"; - const std::string domfile = basename + "/domain0.silo"; - - Node write_opts, read_opts; - write_opts["file_style"] = "overlink"; - read_opts["matset_style"] = "multi_buffer_full"; - - // add a matset to make overlink happy - int num_elems = (nx - 1) * (ny - 1); - if (mesh_type == "tets") - { - num_elems *= 6; - } - add_multi_buffer_full_matset(save_mesh, num_elems, "mesh"); - - remove_path_if_exists(filename); - remove_path_if_exists(domfile); - io::silo::save_mesh(save_mesh, basename, write_opts); - io::silo::load_mesh(filename, read_opts, load_mesh); - EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); - - // make changes to save mesh so the diff will pass - if (mesh_type == "uniform") - { - silo_uniform_to_rect_conversion("coords", "mesh", save_mesh); - } - overlink_name_changer(save_mesh); - - // the loaded mesh will be in the multidomain format - // but the saved mesh is in the single domain format - EXPECT_EQ(load_mesh.number_of_children(), 1); - EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); - - EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); - } -} - -//----------------------------------------------------------------------------- - -// -// read option tests -// - -// read options: -/// opts: -/// silo_names: -/// multimesh_names: -/// "{name1}" - multimeshes with this name will be read if they exist -/// "{name2}" -/// ... -/// or -/// "{all}" - all multimeshes will be read. -/// or -/// "{none}" - no multimeshes will be read. -/// multivar_names: similar to multimesh_names. -/// multimat_names: similar to multimesh_names. -/// multimatspecies_names: similar to multimesh_names. TODO -/// qmesh_names: similar to multimesh_names. -/// qvar_names: similar to multimesh_names. -/// ucdmesh_names: similar to multimesh_names. -/// ucdvar_names: similar to multimesh_names. -/// ptmesh_names: similar to multimesh_names. -/// ptvar_names: similar to multimesh_names. -/// mat_names: similar to multimesh_names. -/// matspecies_names: similar to multimesh_names. TODO -/// By default, everything in the file will be read unless manually turned off. -/// -/// matset_style: "default", "multi_buffer_full", "sparse_by_element", -/// "multi_buffer_by_material" -/// "default" ==> "sparse_by_element" -/// -/// mesh_name: legacy argument. This is interpreted as a multimesh name. -/// It is added to the list of multimesh names to read, unless the -/// user has specified "all" or "none", which will supersede this. -/// TODO does it make sense to remove this? When? - -//----------------------------------------------------------------------------- -// TODO this is now a legacy feature. Should I remove eventually? -TEST(conduit_relay_io_silo, round_trip_read_option_mesh_name) -{ - Node load_mesh, info, opts; - const std::string path = utils::join_file_path("silo", "multi_curv3d.silo"); - const std::string input_file = relay_test_silo_data_path(path); - - opts["mesh_name"] = "mesh1_dup"; - - io::silo::load_mesh(input_file, opts, load_mesh); - EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - - EXPECT_TRUE(load_mesh[0].has_path("topologies/mesh1_dup")); -} - -//----------------------------------------------------------------------------- -TEST(conduit_relay_io_silo, round_trip_read_option_matset_style) -{ - // the matset type and the type we are requesting on read - const std::vector> matset_types = { - std::make_pair("full", "full"), - std::make_pair("sparse_by_material", "sparse_by_material"), - std::make_pair("sparse_by_element", "sparse_by_element"), - std::make_pair("sparse_by_element", "full"), - std::make_pair("sparse_by_material", "sparse_by_element"), - std::make_pair("sparse_by_material", "default"), - }; - - for (int i = 0; i < matset_types.size(); i ++) - { - std::string matset_type = matset_types[i].first; - std::string matset_request = matset_types[i].second; - - for (int j = 0; j < 2; j ++) - { - Node mesh_full, mesh_sbe, mesh_sbm, baseline_mesh, load_mesh, info; - std::string size; - int nx, ny; - const double radius = 0.25; - if (j == 0) - { - size = "small"; - nx = ny = 4; - } - else - { - size = "large"; - nx = ny = 100; - } - - blueprint::mesh::examples::venn("full", nx, ny, radius, mesh_full); - blueprint::mesh::examples::venn("sparse_by_material", nx, ny, radius, mesh_sbm); - blueprint::mesh::examples::venn("sparse_by_element", nx, ny, radius, mesh_sbe); - - if (matset_type == "full") - { - baseline_mesh.set_external(mesh_full); - } - else if (matset_type == "sparse_by_material") - { - baseline_mesh.set_external(mesh_sbm); - } - else // (matset_type == "sparse_by_element") - { - baseline_mesh.set_external(mesh_sbe); - } - - Node opts; - if (matset_request == "full") - { - opts["matset_style"] = "multi_buffer_full"; - } - else if (matset_request == "sparse_by_material") - { - opts["matset_style"] = "multi_buffer_by_material"; - } - else if (matset_request == "sparse_by_element") - { - opts["matset_style"] = "sparse_by_element"; - } - else - { - opts["matset_style"] = "default"; - } - - const std::string basename = "silo_venn2_" + matset_type + "_" + size; - const std::string filename = basename + ".root"; - - remove_path_if_exists(filename); - io::silo::save_mesh(baseline_mesh, basename); - io::silo::load_mesh(filename, opts, load_mesh); - EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - - if (matset_request == "full") - { - baseline_mesh.set_external(mesh_full); - } - else if (matset_request == "sparse_by_material") - { - baseline_mesh.set_external(mesh_sbm); - } - else if (matset_request == "sparse_by_element") - { - baseline_mesh.set_external(mesh_sbe); - } - else - { - baseline_mesh.set_external(mesh_sbe); - } - - // make changes to save mesh so the diff will pass - - // The field mat_check has values that are one type and matset_values - // that are another type. The silo writer converts both to double arrays - // in this case, so we follow suit. - Node mat_check_new_values, mat_check_new_matset_values; - baseline_mesh["fields"]["mat_check"]["values"].to_double_array(mat_check_new_values); - if (baseline_mesh["fields"]["mat_check"]["matset_values"].dtype().is_object()) - { - auto mat_vals_itr = baseline_mesh["fields"]["mat_check"]["matset_values"].children(); - while (mat_vals_itr.has_next()) - { - Node &mat_vals_for_mat = mat_vals_itr.next(); - const std::string mat_name = mat_vals_itr.name(); - mat_vals_for_mat.to_double_array(mat_check_new_matset_values[mat_name]); - } - } - else - { - baseline_mesh["fields"]["mat_check"]["matset_values"].to_double_array(mat_check_new_matset_values); - } - baseline_mesh["fields"]["mat_check"]["values"].set_external(mat_check_new_values); - baseline_mesh["fields"]["mat_check"]["matset_values"].set_external(mat_check_new_matset_values); - - silo_name_changer("mesh", baseline_mesh); - - // the loaded mesh will be in the multidomain format - // but the saved mesh is in the single domain format - EXPECT_EQ(load_mesh.number_of_children(), 1); - EXPECT_EQ(load_mesh[0].number_of_children(), baseline_mesh.number_of_children()); - EXPECT_FALSE(load_mesh[0].diff(baseline_mesh, info, CONDUIT_EPSILON, true)); - } - } -} - -//----------------------------------------------------------------------------- - -// -// read and write Silo and Overlink tests -// - -//----------------------------------------------------------------------------- -// read normal silo files containing multimeshes, multivars, and multimats -TEST(conduit_relay_io_silo, read_silo) -{ - const std::vector> file_info = { - {".", "multi_curv3d", ".silo"}, - {".", "tire", ".silo"}, - {".", "galaxy0000", ".silo"}, - {".", "emptydomains", ".silo"}, - {"multidir_test_data", "multidir0000", ".root"}, - }; - - // TODO what to do in the case where a multimesh points to no data? (mesh1_back) - // fail silently, as we do now? - - for (int i = 0; i < file_info.size(); i ++) - { - const std::string dirname = file_info[i][0]; - const std::string basename = file_info[i][1]; - const std::string fileext = file_info[i][2]; - - Node load_mesh, info, write_opts; - std::string filepath = utils::join_file_path(dirname, basename) + fileext; - filepath = utils::join_file_path("silo", filepath); - std::string input_file = relay_test_silo_data_path(filepath); - - io::silo::load_mesh(input_file, load_mesh); - EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - - const std::string out_name = "read_silo_" + basename; - - // TODO are these remove paths doing anything? Don't they need filenames? - remove_path_if_exists(out_name + "_write_blueprint"); - io::blueprint::save_mesh(load_mesh, out_name + "_write_blueprint", "hdf5"); - - remove_path_if_exists(out_name + "_write_silo"); - io::silo::save_mesh(load_mesh, out_name + "_write_silo"); - - // overlink requires matsets and does not support point meshes - if (load_mesh[0].has_child("matsets") && basename != "galaxy0000") - { - remove_path_if_exists(out_name + "_write_overlink"); - write_opts["file_style"] = "overlink"; - write_opts["ovl_topo_name"] = "MMESH"; // TODO do I even need this - io::silo::save_mesh(load_mesh, out_name + "_write_overlink", write_opts); - } - } -} +// // cheat a little bit - we don't have these to start +// n_matset["sizes"].set_external(load_mesh[child]["matsets"]["MMATERIAL"]["sizes"]); +// n_matset["offsets"].set_external(load_mesh[child]["matsets"]["MMATERIAL"]["offsets"]); + +// overlink_name_changer(save_mesh[child]); +// } + +// EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); +// NodeConstIterator l_itr = load_mesh.children(); +// NodeConstIterator s_itr = save_mesh.children(); +// while (l_itr.has_next()) +// { +// const Node &l_curr = l_itr.next(); +// const Node &s_curr = s_itr.next(); + +// EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); +// } +// } +// } + +// //----------------------------------------------------------------------------- +// // this tests var attributes and padding dimensions +// TEST(conduit_relay_io_silo, round_trip_save_option_overlink2) +// { +// const std::string basename = "silo_save_option_overlink_basic"; +// const std::string filename = basename + "/OvlTop.silo"; + +// Node write_opts, read_opts; +// write_opts["file_style"] = "overlink"; +// read_opts["matset_style"] = "multi_buffer_full"; + +// Node save_mesh, load_mesh, info; +// blueprint::mesh::examples::basic("structured", 3, 3, 1, save_mesh); + +// // add another field that is volume dependent +// Node &field2 = save_mesh["fields"]["field2"]; +// field2["association"] = "element"; +// field2["topology"] = "mesh"; +// field2["volume_dependent"] = "true"; +// field2["values"].set_external(save_mesh["fields"]["field"]["values"]); + +// // add a matset to make overlink happy +// add_multi_buffer_full_matset(save_mesh, 4, "mesh"); + +// remove_path_if_exists(filename); +// io::silo::save_mesh(save_mesh, basename, write_opts); +// io::silo::load_mesh(filename, read_opts, load_mesh); +// EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); + +// // make changes to save mesh so the diff will pass +// overlink_name_changer(save_mesh); + +// // the loaded mesh will be in the multidomain format +// // but the saved mesh is in the single domain format +// EXPECT_EQ(load_mesh.number_of_children(), 1); +// EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); + +// EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); + +// // open silo files and do some checks + +// DBfile *rootfile = DBOpen(filename.c_str(), DB_UNKNOWN, DB_READ); +// EXPECT_TRUE(DBInqVarExists(rootfile, "VAR_ATTRIBUTES")); +// EXPECT_TRUE(DBInqVarType(rootfile, "VAR_ATTRIBUTES") == DB_ARRAY); + +// DBcompoundarray *var_attr = DBGetCompoundarray(rootfile, "VAR_ATTRIBUTES"); + +// // fetch pointers to elements inside the compound array +// char **elemnames = var_attr->elemnames; +// int *elemlengths = var_attr->elemlengths; +// int nelems = var_attr->nelems; +// int *values = static_cast(var_attr->values); +// int nvalues = var_attr->nvalues; +// int datatype = var_attr->datatype; + +// EXPECT_EQ(std::string(elemnames[0]), "field"); +// EXPECT_EQ(std::string(elemnames[1]), "field2"); +// EXPECT_EQ(elemlengths[0], 5); +// EXPECT_EQ(elemlengths[1], 5); +// EXPECT_EQ(nelems, 2); +// // for first var +// EXPECT_EQ(values[0], 1); +// EXPECT_EQ(values[1], 0); +// EXPECT_EQ(values[2], 1); +// EXPECT_EQ(values[3], 0); +// EXPECT_EQ(values[4], 1); +// // for second var +// EXPECT_EQ(values[5], 1); +// EXPECT_EQ(values[6], 1); +// EXPECT_EQ(values[7], 1); +// EXPECT_EQ(values[8], 0); +// EXPECT_EQ(values[9], 1); +// EXPECT_EQ(nvalues, 10); +// EXPECT_EQ(datatype, DB_INT); + +// DBFreeCompoundarray(var_attr); + +// EXPECT_TRUE(DBInqVarExists(rootfile, "PAD_DIMS")); +// EXPECT_TRUE(DBInqVarType(rootfile, "PAD_DIMS") == DB_ARRAY); + +// DBcompoundarray *pad_dims = DBGetCompoundarray(rootfile, "PAD_DIMS"); + +// // fetch pointers to elements inside the compound array +// elemnames = pad_dims->elemnames; +// elemlengths = pad_dims->elemlengths; +// nelems = pad_dims->nelems; +// values = static_cast(pad_dims->values); +// nvalues = pad_dims->nvalues; +// datatype = pad_dims->datatype; + +// EXPECT_EQ(std::string(elemnames[0]), "paddims"); +// EXPECT_EQ(elemlengths[0], 6); +// EXPECT_EQ(nelems, 1); +// EXPECT_EQ(values[0], 0); +// EXPECT_EQ(values[1], 0); +// EXPECT_EQ(values[2], 0); +// EXPECT_EQ(values[3], 0); +// EXPECT_EQ(values[4], 0); +// EXPECT_EQ(values[5], 0); +// EXPECT_EQ(nvalues, 6); +// EXPECT_EQ(datatype, DB_INT); + +// DBFreeCompoundarray(pad_dims); + +// DBClose(rootfile); + +// // now check domain file + +// const std::string dom_filename = basename + "/domain0.silo"; +// DBfile *domfile = DBOpen(dom_filename.c_str(), DB_UNKNOWN, DB_READ); + +// EXPECT_TRUE(DBInqVarExists(domfile, "DOMAIN_NEIGHBOR_NUMS")); +// EXPECT_TRUE(DBInqVarType(domfile, "DOMAIN_NEIGHBOR_NUMS") == DB_ARRAY); + +// DBcompoundarray *dom_neighbor_nums = DBGetCompoundarray(domfile, "DOMAIN_NEIGHBOR_NUMS"); + +// // fetch pointers to elements inside the compound array +// elemnames = dom_neighbor_nums->elemnames; +// elemlengths = dom_neighbor_nums->elemlengths; +// nelems = dom_neighbor_nums->nelems; +// values = static_cast(dom_neighbor_nums->values); +// nvalues = dom_neighbor_nums->nvalues; +// datatype = dom_neighbor_nums->datatype; + +// EXPECT_EQ(std::string(elemnames[0]), "num_neighbors"); +// EXPECT_EQ(std::string(elemnames[1]), "neighbor_nums"); +// EXPECT_EQ(elemlengths[0], 1); +// EXPECT_EQ(elemlengths[1], 0); +// EXPECT_EQ(nelems, 2); +// EXPECT_EQ(values[0], 0); +// EXPECT_EQ(nvalues, 1); +// EXPECT_EQ(datatype, DB_INT); + +// DBFreeCompoundarray(dom_neighbor_nums); + +// DBClose(domfile); +// } + +// //----------------------------------------------------------------------------- +// // this tests material i/o +// TEST(conduit_relay_io_silo, round_trip_save_option_overlink3) +// { +// Node save_mesh, load_mesh, info; +// const int nx = 100, ny = 100; +// const double radius = 0.25; +// blueprint::mesh::examples::venn("sparse_by_element", nx, ny, radius, save_mesh); + +// const std::string basename = "silo_save_option_overlink_venn"; +// const std::string filename = basename + "/OvlTop.silo"; + +// Node opts; +// opts["file_style"] = "overlink"; + +// remove_path_if_exists(filename); +// io::silo::save_mesh(save_mesh, basename, opts); +// io::silo::load_mesh(filename, load_mesh); +// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + +// // make changes to save mesh so the diff will pass + +// // The field mat_check has values that are one type and matset_values +// // that are another type. The silo writer converts both to double arrays +// // in this case, so we follow suit. +// Node mat_check_new_values, mat_check_new_matset_values; +// save_mesh["fields"]["mat_check"]["values"].to_double_array(mat_check_new_values); +// save_mesh["fields"]["mat_check"]["matset_values"].to_double_array(mat_check_new_matset_values); +// save_mesh["fields"]["mat_check"]["values"].set_external(mat_check_new_values); +// save_mesh["fields"]["mat_check"]["matset_values"].set_external(mat_check_new_matset_values); + +// overlink_name_changer(save_mesh); + +// // the loaded mesh will be in the multidomain format +// // but the saved mesh is in the single domain format +// EXPECT_EQ(load_mesh.number_of_children(), 1); +// EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); + +// EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); +// } + +// //----------------------------------------------------------------------------- +// // we are testing vector fields get converted to scalars +// TEST(conduit_relay_io_silo, round_trip_save_option_overlink4) +// { +// const std::vector> mesh_types = { +// std::make_pair("rectilinear", "2"), std::make_pair("rectilinear", "3"), +// std::make_pair("structured", "2"), std::make_pair("structured", "3"), +// std::make_pair("quads", "2"), +// std::make_pair("hexs", "3"), +// }; +// for (int i = 0; i < mesh_types.size(); ++i) +// { +// std::string dim = mesh_types[i].second; +// index_t nx = 3; +// index_t ny = 4; +// index_t nz = (dim == "2" ? 0 : 2); + +// std::string mesh_type = mesh_types[i].first; + +// Node save_mesh, load_mesh, info; +// blueprint::mesh::examples::braid(mesh_type, nx, ny, nz, save_mesh); +// index_t nele_x = nx - 1; +// index_t nele_y = ny - 1; +// index_t nele_z = (dim == "2" ? 0 : nz - 1); + +// // provide a matset for braid +// braid_init_example_matset(nele_x, nele_y, nele_z, save_mesh["matsets"]["matset"]); + +// const std::string basename = "silo_save_option_overlink_braid_" + mesh_type + "_" + dim + "D"; +// const std::string filename = basename + "/OvlTop.silo"; + +// Node write_opts, read_opts; +// write_opts["file_style"] = "overlink"; +// read_opts["matset_style"] = "multi_buffer_full"; + +// // remove existing root file, directory and any output files +// remove_path_if_exists(filename); +// io::silo::save_mesh(save_mesh, basename, write_opts); +// io::silo::load_mesh(filename, read_opts, load_mesh); +// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + +// Node &field_vel = save_mesh["fields"]["vel"]; +// Node &field_vel_u = save_mesh["fields"]["vel_u"]; +// Node &field_vel_v = save_mesh["fields"]["vel_v"]; + +// field_vel_u["topology"].set(field_vel["topology"]); +// field_vel_u["association"].set(field_vel["association"]); +// field_vel_u["values"].set(field_vel["values/u"]); +// field_vel_v["topology"].set(field_vel["topology"]); +// field_vel_v["association"].set(field_vel["association"]); +// field_vel_v["values"].set(field_vel["values/v"]); + +// if (dim == "3") +// { +// Node &field_vel_w = save_mesh["fields"]["vel_w"]; +// field_vel_w["topology"].set(field_vel["topology"]); +// field_vel_w["association"].set(field_vel["association"]); +// field_vel_w["values"].set(field_vel["values/w"]); +// } + +// save_mesh["fields"].remove_child("vel"); + +// // make changes to save mesh so the diff will pass +// overlink_name_changer(save_mesh); + +// // the loaded mesh will be in the multidomain format +// // but the saved mesh is in the single domain format +// EXPECT_EQ(load_mesh.number_of_children(), 1); +// EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); + +// EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); +// } +// } + +// //----------------------------------------------------------------------------- +// // check that all the shape types work (specifically polytopal ones) +// TEST(conduit_relay_io_silo, round_trip_save_option_overlink5) +// { +// const std::vector> mesh_types = { +// std::make_pair("uniform", "2"), std::make_pair("uniform", "3"), +// std::make_pair("rectilinear", "2"), std::make_pair("rectilinear", "3"), +// std::make_pair("structured", "2"), std::make_pair("structured", "3"), +// std::make_pair("quads", "2"), +// std::make_pair("polygons", "2"), +// std::make_pair("hexs", "3"), +// // std::make_pair("polyhedra", "3") +// // Overlink does not support tris, wedges, pyramids, or tets +// }; +// for (int i = 0; i < mesh_types.size(); ++i) +// { +// const std::string dim = mesh_types[i].second; +// index_t nx = 3; +// index_t ny = 4; +// index_t nz = (dim == "2" ? 0 : 2); + +// const std::string mesh_type = mesh_types[i].first; + +// Node save_mesh, load_mesh, info; +// blueprint::mesh::examples::basic(mesh_type, nx, ny, nz, save_mesh); + +// const std::string basename = "silo_save_option_overlink_basic_" + mesh_type + "_" + dim + "D"; +// const std::string filename = basename + "/OvlTop.silo"; +// const std::string domfile = basename + "/domain0.silo"; + +// Node write_opts, read_opts; +// write_opts["file_style"] = "overlink"; +// read_opts["matset_style"] = "multi_buffer_full"; + +// // add a matset to make overlink happy +// int num_elems = (nx - 1) * (ny - 1); +// if (mesh_type == "tets") +// { +// num_elems *= 6; +// } +// add_multi_buffer_full_matset(save_mesh, num_elems, "mesh"); + +// remove_path_if_exists(filename); +// remove_path_if_exists(domfile); +// io::silo::save_mesh(save_mesh, basename, write_opts); +// io::silo::load_mesh(filename, read_opts, load_mesh); +// EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); + +// // make changes to save mesh so the diff will pass +// if (mesh_type == "uniform") +// { +// silo_uniform_to_rect_conversion("coords", "mesh", save_mesh); +// } +// overlink_name_changer(save_mesh); + +// // the loaded mesh will be in the multidomain format +// // but the saved mesh is in the single domain format +// EXPECT_EQ(load_mesh.number_of_children(), 1); +// EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); + +// EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); +// } +// } + +// //----------------------------------------------------------------------------- + +// // +// // read option tests +// // + +// // read options: +// /// opts: +// /// silo_names: +// /// multimesh_names: +// /// "{name1}" - multimeshes with this name will be read if they exist +// /// "{name2}" +// /// ... +// /// or +// /// "{all}" - all multimeshes will be read. +// /// or +// /// "{none}" - no multimeshes will be read. +// /// multivar_names: similar to multimesh_names. +// /// multimat_names: similar to multimesh_names. +// /// multimatspecies_names: similar to multimesh_names. TODO +// /// qmesh_names: similar to multimesh_names. +// /// qvar_names: similar to multimesh_names. +// /// ucdmesh_names: similar to multimesh_names. +// /// ucdvar_names: similar to multimesh_names. +// /// ptmesh_names: similar to multimesh_names. +// /// ptvar_names: similar to multimesh_names. +// /// mat_names: similar to multimesh_names. +// /// matspecies_names: similar to multimesh_names. TODO +// /// By default, everything in the file will be read unless manually turned off. +// /// +// /// matset_style: "default", "multi_buffer_full", "sparse_by_element", +// /// "multi_buffer_by_material" +// /// "default" ==> "sparse_by_element" +// /// +// /// mesh_name: legacy argument. This is interpreted as a multimesh name. +// /// It is added to the list of multimesh names to read, unless the +// /// user has specified "all" or "none", which will supersede this. +// /// TODO does it make sense to remove this? When? + +// //----------------------------------------------------------------------------- +// // TODO this is now a legacy feature. Should I remove eventually? +// TEST(conduit_relay_io_silo, round_trip_read_option_mesh_name) +// { +// Node load_mesh, info, opts; +// const std::string path = utils::join_file_path("silo", "multi_curv3d.silo"); +// const std::string input_file = relay_test_silo_data_path(path); + +// opts["mesh_name"] = "mesh1_dup"; + +// io::silo::load_mesh(input_file, opts, load_mesh); +// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + +// EXPECT_TRUE(load_mesh[0].has_path("topologies/mesh1_dup")); +// } + +// //----------------------------------------------------------------------------- +// TEST(conduit_relay_io_silo, round_trip_read_option_matset_style) +// { +// // the matset type and the type we are requesting on read +// const std::vector> matset_types = { +// std::make_pair("full", "full"), +// std::make_pair("sparse_by_material", "sparse_by_material"), +// std::make_pair("sparse_by_element", "sparse_by_element"), +// std::make_pair("sparse_by_element", "full"), +// std::make_pair("sparse_by_material", "sparse_by_element"), +// std::make_pair("sparse_by_material", "default"), +// }; + +// for (int i = 0; i < matset_types.size(); i ++) +// { +// std::string matset_type = matset_types[i].first; +// std::string matset_request = matset_types[i].second; + +// for (int j = 0; j < 2; j ++) +// { +// Node mesh_full, mesh_sbe, mesh_sbm, baseline_mesh, load_mesh, info; +// std::string size; +// int nx, ny; +// const double radius = 0.25; +// if (j == 0) +// { +// size = "small"; +// nx = ny = 4; +// } +// else +// { +// size = "large"; +// nx = ny = 100; +// } + +// blueprint::mesh::examples::venn("full", nx, ny, radius, mesh_full); +// blueprint::mesh::examples::venn("sparse_by_material", nx, ny, radius, mesh_sbm); +// blueprint::mesh::examples::venn("sparse_by_element", nx, ny, radius, mesh_sbe); + +// if (matset_type == "full") +// { +// baseline_mesh.set_external(mesh_full); +// } +// else if (matset_type == "sparse_by_material") +// { +// baseline_mesh.set_external(mesh_sbm); +// } +// else // (matset_type == "sparse_by_element") +// { +// baseline_mesh.set_external(mesh_sbe); +// } + +// Node opts; +// if (matset_request == "full") +// { +// opts["matset_style"] = "multi_buffer_full"; +// } +// else if (matset_request == "sparse_by_material") +// { +// opts["matset_style"] = "multi_buffer_by_material"; +// } +// else if (matset_request == "sparse_by_element") +// { +// opts["matset_style"] = "sparse_by_element"; +// } +// else +// { +// opts["matset_style"] = "default"; +// } + +// const std::string basename = "silo_venn2_" + matset_type + "_" + size; +// const std::string filename = basename + ".root"; + +// remove_path_if_exists(filename); +// io::silo::save_mesh(baseline_mesh, basename); +// io::silo::load_mesh(filename, opts, load_mesh); +// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + +// if (matset_request == "full") +// { +// baseline_mesh.set_external(mesh_full); +// } +// else if (matset_request == "sparse_by_material") +// { +// baseline_mesh.set_external(mesh_sbm); +// } +// else if (matset_request == "sparse_by_element") +// { +// baseline_mesh.set_external(mesh_sbe); +// } +// else +// { +// baseline_mesh.set_external(mesh_sbe); +// } + +// // make changes to save mesh so the diff will pass + +// // The field mat_check has values that are one type and matset_values +// // that are another type. The silo writer converts both to double arrays +// // in this case, so we follow suit. +// Node mat_check_new_values, mat_check_new_matset_values; +// baseline_mesh["fields"]["mat_check"]["values"].to_double_array(mat_check_new_values); +// if (baseline_mesh["fields"]["mat_check"]["matset_values"].dtype().is_object()) +// { +// auto mat_vals_itr = baseline_mesh["fields"]["mat_check"]["matset_values"].children(); +// while (mat_vals_itr.has_next()) +// { +// Node &mat_vals_for_mat = mat_vals_itr.next(); +// const std::string mat_name = mat_vals_itr.name(); +// mat_vals_for_mat.to_double_array(mat_check_new_matset_values[mat_name]); +// } +// } +// else +// { +// baseline_mesh["fields"]["mat_check"]["matset_values"].to_double_array(mat_check_new_matset_values); +// } +// baseline_mesh["fields"]["mat_check"]["values"].set_external(mat_check_new_values); +// baseline_mesh["fields"]["mat_check"]["matset_values"].set_external(mat_check_new_matset_values); + +// silo_name_changer("mesh", baseline_mesh); + +// // the loaded mesh will be in the multidomain format +// // but the saved mesh is in the single domain format +// EXPECT_EQ(load_mesh.number_of_children(), 1); +// EXPECT_EQ(load_mesh[0].number_of_children(), baseline_mesh.number_of_children()); +// EXPECT_FALSE(load_mesh[0].diff(baseline_mesh, info, CONDUIT_EPSILON, true)); +// } +// } +// } + +// //----------------------------------------------------------------------------- + +// // +// // read and write Silo and Overlink tests +// // + +// //----------------------------------------------------------------------------- +// // read normal silo files containing multimeshes, multivars, and multimats +// TEST(conduit_relay_io_silo, read_silo) +// { +// const std::vector> file_info = { +// {".", "multi_curv3d", ".silo"}, +// {".", "tire", ".silo"}, +// {".", "galaxy0000", ".silo"}, +// {".", "emptydomains", ".silo"}, +// {"multidir_test_data", "multidir0000", ".root"}, +// }; + +// // TODO what to do in the case where a multimesh points to no data? (mesh1_back) +// // fail silently, as we do now? + +// for (int i = 0; i < file_info.size(); i ++) +// { +// const std::string dirname = file_info[i][0]; +// const std::string basename = file_info[i][1]; +// const std::string fileext = file_info[i][2]; + +// Node load_mesh, info, write_opts; +// std::string filepath = utils::join_file_path(dirname, basename) + fileext; +// filepath = utils::join_file_path("silo", filepath); +// std::string input_file = relay_test_silo_data_path(filepath); + +// io::silo::load_mesh(input_file, load_mesh); +// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + +// const std::string out_name = "read_silo_" + basename; + +// // TODO are these remove paths doing anything? Don't they need filenames? +// remove_path_if_exists(out_name + "_write_blueprint"); +// io::blueprint::save_mesh(load_mesh, out_name + "_write_blueprint", "hdf5"); + +// remove_path_if_exists(out_name + "_write_silo"); +// io::silo::save_mesh(load_mesh, out_name + "_write_silo"); + +// // overlink requires matsets and does not support point meshes +// if (load_mesh[0].has_child("matsets") && basename != "galaxy0000") +// { +// remove_path_if_exists(out_name + "_write_overlink"); +// write_opts["file_style"] = "overlink"; +// write_opts["ovl_topo_name"] = "MMESH"; // TODO do I even need this +// io::silo::save_mesh(load_mesh, out_name + "_write_overlink", write_opts); +// } +// } +// } //----------------------------------------------------------------------------- // test that we can read silo without multimeshes, multivars, and multimats TEST(conduit_relay_io_silo, read_simple_silo) { const std::vector> file_info = { - {"curv2d", ".silo"}, - // {"curv2d_colmajor", ".silo"}, // TODO look at strided structured to handle this case + // {"curv2d", ".silo"}, + {"curv2d_colmajor", ".silo"}, // TODO look at strided structured to handle this case // {"curv3d", ".silo"}, // {"curv3d_colmajor", ".silo"}, // // {"globe", ".silo"}, // TODO need to fix a known bug for this one to work @@ -1849,142 +1849,142 @@ TEST(conduit_relay_io_silo, read_simple_silo) } } -//----------------------------------------------------------------------------- -// test that we can read the fake overlink files from the visit test data -TEST(conduit_relay_io_silo, read_fake_overlink) -{ - const std::vector> file_info = { - // {"ev_0_0_100", "OvlTop", ".silo"}, - // uncomment once silo ucdmesh phzones are supported - {"hl18spec", "OvlTop", ".silo"}, - // {"regrovl_qh_1000_10001_4", "OvlTop", ".silo"}, - // uncomment once silo ucdmesh phzones are supported - {"utpyr4", "OvlTop", ".silo"}, - }; - - for (int i = 0; i < file_info.size(); i ++) - { - const std::string dirname = file_info[i][0]; - const std::string basename = file_info[i][1]; - const std::string fileext = file_info[i][2]; - - Node load_mesh, info, write_opts; - std::string filepath = utils::join_file_path(dirname, basename) + fileext; - filepath = utils::join_file_path("fake_overlink", filepath); - std::string input_file = relay_test_silo_data_path(filepath); - - io::silo::load_mesh(input_file, load_mesh); - EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - - const std::string out_name = "read_fake_overlink_" + dirname; - - remove_path_if_exists(out_name + "_write_blueprint"); - io::blueprint::save_mesh(load_mesh, out_name + "_write_blueprint", "hdf5"); - - remove_path_if_exists(out_name + "_write_silo"); - io::silo::save_mesh(load_mesh, out_name + "_write_silo"); - - remove_path_if_exists(out_name + "_write_overlink"); - write_opts["file_style"] = "overlink"; - write_opts["ovl_topo_name"] = "MMESH"; - io::silo::save_mesh(load_mesh, out_name + "_write_overlink", write_opts); - } -} - -//----------------------------------------------------------------------------- -// read overlink files in symlink format -// should be similar to reading raw silo -TEST(conduit_relay_io_silo, read_overlink_symlink_format) -{ - const std::vector> file_info = { - {".", "box2d", ".silo"}, - {".", "box3d", ".silo"}, - // {".", "diamond", ".silo"}, - // fails b/c polytopal not yet supported - {".", "testDisk2D_a", ".silo"}, - // {".", "donordiv.s2_materials2", ".silo"}, - // fails b/c polytopal not yet supported - {".", "donordiv.s2_materials3", ".silo"}, - }; - - for (int i = 0; i < file_info.size(); i ++) - { - const std::string dirname = file_info[i][0]; - const std::string basename = file_info[i][1]; - const std::string fileext = file_info[i][2]; - - Node load_mesh, info, write_opts; - std::string filepath = utils::join_file_path(dirname, basename) + fileext; - filepath = utils::join_file_path("overlink", filepath); - std::string input_file = relay_test_silo_data_path(filepath); - - io::silo::load_mesh(input_file, load_mesh); - EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - - const std::string out_name = "read_overlink_symlink_" + basename; - - remove_path_if_exists(out_name + "_write_blueprint"); - io::blueprint::save_mesh(load_mesh, out_name + "_write_blueprint", "hdf5"); - - remove_path_if_exists(out_name + "_write_silo"); - io::silo::save_mesh(load_mesh, out_name + "_write_silo"); - - remove_path_if_exists(out_name + "_write_overlink"); - write_opts["file_style"] = "overlink"; - write_opts["ovl_topo_name"] = "MMESH"; - io::silo::save_mesh(load_mesh, out_name + "_write_overlink", write_opts); - } -} - -//----------------------------------------------------------------------------- -// read overlink directly from ovltop.silo -// this case is tricky and involves messing with paths -TEST(conduit_relay_io_silo, read_overlink_directly) -{ - const std::vector> file_info = { - {"box2d", "OvlTop", ".silo"}, - {"box3d", "OvlTop", ".silo"}, - // {"diamond", "OvlTop", ".silo"}, - {"testDisk2D_a", "OvlTop", ".silo"}, - // {"donordiv.s2_materials2", "OvlTop", ".silo"}, - {"donordiv.s2_materials3", "OvlTop", ".silo"}, - }; - - for (int i = 0; i < file_info.size(); i ++) - { - const std::string dirname = file_info[i][0]; - const std::string basename = file_info[i][1]; - const std::string fileext = file_info[i][2]; - - Node load_mesh, info, write_opts; - - std::string filepath = utils::join_file_path(dirname, basename) + fileext; - filepath = utils::join_file_path("overlink", filepath); - std::string input_file = relay_test_silo_data_path(filepath); - - io::silo::load_mesh(input_file, load_mesh); - EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - - const std::string out_name = "read_overlink_direct_" + dirname; - - remove_path_if_exists(out_name + "_write_blueprint"); - io::blueprint::save_mesh(load_mesh, out_name + "_write_blueprint", "hdf5"); - - remove_path_if_exists(out_name + "_write_silo"); - io::silo::save_mesh(load_mesh, out_name + "_write_silo"); - - remove_path_if_exists(out_name + "_write_overlink"); - write_opts["file_style"] = "overlink"; - write_opts["ovl_topo_name"] = "MMESH"; - io::silo::save_mesh(load_mesh, out_name + "_write_overlink", write_opts); - } -} - -// TODO add tests for... -// - polytopal meshes once they are supported -// - units once they are supported -// - etc. - -// TODO add tetra8 and c36_m5 to all the overlink i/o tests - -// TODO somewhere I need to error on overlink when there are different var or mesh types across domains +// //----------------------------------------------------------------------------- +// // test that we can read the fake overlink files from the visit test data +// TEST(conduit_relay_io_silo, read_fake_overlink) +// { +// const std::vector> file_info = { +// // {"ev_0_0_100", "OvlTop", ".silo"}, +// // uncomment once silo ucdmesh phzones are supported +// {"hl18spec", "OvlTop", ".silo"}, +// // {"regrovl_qh_1000_10001_4", "OvlTop", ".silo"}, +// // uncomment once silo ucdmesh phzones are supported +// {"utpyr4", "OvlTop", ".silo"}, +// }; + +// for (int i = 0; i < file_info.size(); i ++) +// { +// const std::string dirname = file_info[i][0]; +// const std::string basename = file_info[i][1]; +// const std::string fileext = file_info[i][2]; + +// Node load_mesh, info, write_opts; +// std::string filepath = utils::join_file_path(dirname, basename) + fileext; +// filepath = utils::join_file_path("fake_overlink", filepath); +// std::string input_file = relay_test_silo_data_path(filepath); + +// io::silo::load_mesh(input_file, load_mesh); +// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + +// const std::string out_name = "read_fake_overlink_" + dirname; + +// remove_path_if_exists(out_name + "_write_blueprint"); +// io::blueprint::save_mesh(load_mesh, out_name + "_write_blueprint", "hdf5"); + +// remove_path_if_exists(out_name + "_write_silo"); +// io::silo::save_mesh(load_mesh, out_name + "_write_silo"); + +// remove_path_if_exists(out_name + "_write_overlink"); +// write_opts["file_style"] = "overlink"; +// write_opts["ovl_topo_name"] = "MMESH"; +// io::silo::save_mesh(load_mesh, out_name + "_write_overlink", write_opts); +// } +// } + +// //----------------------------------------------------------------------------- +// // read overlink files in symlink format +// // should be similar to reading raw silo +// TEST(conduit_relay_io_silo, read_overlink_symlink_format) +// { +// const std::vector> file_info = { +// {".", "box2d", ".silo"}, +// {".", "box3d", ".silo"}, +// // {".", "diamond", ".silo"}, +// // fails b/c polytopal not yet supported +// {".", "testDisk2D_a", ".silo"}, +// // {".", "donordiv.s2_materials2", ".silo"}, +// // fails b/c polytopal not yet supported +// {".", "donordiv.s2_materials3", ".silo"}, +// }; + +// for (int i = 0; i < file_info.size(); i ++) +// { +// const std::string dirname = file_info[i][0]; +// const std::string basename = file_info[i][1]; +// const std::string fileext = file_info[i][2]; + +// Node load_mesh, info, write_opts; +// std::string filepath = utils::join_file_path(dirname, basename) + fileext; +// filepath = utils::join_file_path("overlink", filepath); +// std::string input_file = relay_test_silo_data_path(filepath); + +// io::silo::load_mesh(input_file, load_mesh); +// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + +// const std::string out_name = "read_overlink_symlink_" + basename; + +// remove_path_if_exists(out_name + "_write_blueprint"); +// io::blueprint::save_mesh(load_mesh, out_name + "_write_blueprint", "hdf5"); + +// remove_path_if_exists(out_name + "_write_silo"); +// io::silo::save_mesh(load_mesh, out_name + "_write_silo"); + +// remove_path_if_exists(out_name + "_write_overlink"); +// write_opts["file_style"] = "overlink"; +// write_opts["ovl_topo_name"] = "MMESH"; +// io::silo::save_mesh(load_mesh, out_name + "_write_overlink", write_opts); +// } +// } + +// //----------------------------------------------------------------------------- +// // read overlink directly from ovltop.silo +// // this case is tricky and involves messing with paths +// TEST(conduit_relay_io_silo, read_overlink_directly) +// { +// const std::vector> file_info = { +// {"box2d", "OvlTop", ".silo"}, +// {"box3d", "OvlTop", ".silo"}, +// // {"diamond", "OvlTop", ".silo"}, +// {"testDisk2D_a", "OvlTop", ".silo"}, +// // {"donordiv.s2_materials2", "OvlTop", ".silo"}, +// {"donordiv.s2_materials3", "OvlTop", ".silo"}, +// }; + +// for (int i = 0; i < file_info.size(); i ++) +// { +// const std::string dirname = file_info[i][0]; +// const std::string basename = file_info[i][1]; +// const std::string fileext = file_info[i][2]; + +// Node load_mesh, info, write_opts; + +// std::string filepath = utils::join_file_path(dirname, basename) + fileext; +// filepath = utils::join_file_path("overlink", filepath); +// std::string input_file = relay_test_silo_data_path(filepath); + +// io::silo::load_mesh(input_file, load_mesh); +// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + +// const std::string out_name = "read_overlink_direct_" + dirname; + +// remove_path_if_exists(out_name + "_write_blueprint"); +// io::blueprint::save_mesh(load_mesh, out_name + "_write_blueprint", "hdf5"); + +// remove_path_if_exists(out_name + "_write_silo"); +// io::silo::save_mesh(load_mesh, out_name + "_write_silo"); + +// remove_path_if_exists(out_name + "_write_overlink"); +// write_opts["file_style"] = "overlink"; +// write_opts["ovl_topo_name"] = "MMESH"; +// io::silo::save_mesh(load_mesh, out_name + "_write_overlink", write_opts); +// } +// } + +// // TODO add tests for... +// // - polytopal meshes once they are supported +// // - units once they are supported +// // - etc. + +// // TODO add tetra8 and c36_m5 to all the overlink i/o tests + +// // TODO somewhere I need to error on overlink when there are different var or mesh types across domains From cfe64dc42b6f644598de8b7edd08f218a13229e4 Mon Sep 17 00:00:00 2001 From: Justin Privitera Date: Wed, 10 Apr 2024 11:08:42 -0700 Subject: [PATCH 30/56] sorry have all the tests --- src/tests/relay/t_relay_io_silo.cpp | 3828 +++++++++++++-------------- 1 file changed, 1914 insertions(+), 1914 deletions(-) diff --git a/src/tests/relay/t_relay_io_silo.cpp b/src/tests/relay/t_relay_io_silo.cpp index 9a093a146..c1d5c64f5 100644 --- a/src/tests/relay/t_relay_io_silo.cpp +++ b/src/tests/relay/t_relay_io_silo.cpp @@ -20,1794 +20,1794 @@ using namespace conduit; using namespace conduit::utils; using namespace conduit::relay; -// //----------------------------------------------------------------------------- -// TEST(conduit_relay_io_silo, conduit_silo_cold_storage) -// { -// uint32 a_val = 20; -// uint32 b_val = 8; -// uint32 c_val = 13; - -// Node n; -// n["a"] = a_val; -// n["b"] = b_val; -// n["c"] = c_val; - -// EXPECT_EQ(n["a"].as_uint32(), a_val); -// EXPECT_EQ(n["b"].as_uint32(), b_val); -// EXPECT_EQ(n["c"].as_uint32(), c_val); - -// n.print(); - -// io::silo_write(n,"tout_cold_storage_test.silo:myobj"); - -// Node n_load; -// io::silo_read("tout_cold_storage_test.silo:myobj",n_load); - -// std::cout << "round trip" << std::endl; -// n_load.print(); - -// EXPECT_EQ(n_load["a"].as_uint32(), a_val); -// EXPECT_EQ(n_load["b"].as_uint32(), b_val); -// EXPECT_EQ(n_load["c"].as_uint32(), c_val); -// } - -// //----------------------------------------------------------------------------- -// TEST(conduit_relay_io_silo, conduit_silo_cold_storage_generic_iface) -// { -// uint32 a_val = 20; -// uint32 b_val = 8; -// uint32 c_val = 13; - -// Node n; -// n["a"] = a_val; -// n["b"] = b_val; -// n["c"] = c_val; - -// EXPECT_EQ(n["a"].as_uint32(), a_val); -// EXPECT_EQ(n["b"].as_uint32(), b_val); -// EXPECT_EQ(n["c"].as_uint32(), c_val); - -// io::save(n, "tout_cold_storage_test_generic_iface.silo:myobj"); - -// Node n_load; -// io::load("tout_cold_storage_test_generic_iface.silo:myobj",n_load); - -// EXPECT_EQ(n_load["a"].as_uint32(), a_val); -// EXPECT_EQ(n_load["b"].as_uint32(), b_val); -// EXPECT_EQ(n_load["c"].as_uint32(), c_val); -// } - -// //----------------------------------------------------------------------------- -// // test reading in a handful of different overlink files -// TEST(conduit_relay_io_silo, load_mesh_geometry) -// { -// // TODO: all these files are in overlink symlink format. -// // Symlinks may break on Windows (?) -// // Could make them overlink format without the symlink. -// // But would require modifying the files. -// std::vector filename_vec = { -// "box2d.silo", -// "box3d.silo", -// // "diamond.silo", <--- TODO this one fails because polytopal is not yet supported -// // TODO: rename these files to be more descriptive. -// // would also require modifying the paths stored within the files, -// // and re-symlinking -// "testDisk2D_a.silo", -// // "donordiv.s2_materials2.silo", <--- TODO this one fails because polytopal is not yet supported -// "donordiv.s2_materials3.silo" -// }; -// std::vector dims_vec = {2, 3, /*2,*/ 2, /*2,*/ 2}; -// std::vector coordset_length_vec = {4, 8, /*36,*/ 1994, /*16,*/ 961}; -// std::vector topology_length_vec = {1, 1, /*33,*/ 1920, /*9,*/ 900}; -// for (int i = 0; i < filename_vec.size(); ++i) -// { -// Node mesh, info; -// std::string path = utils::join_file_path("overlink", filename_vec.at(i)); -// std::string input_file = relay_test_silo_data_path(path); -// io::silo::load_mesh(input_file, mesh); - -// EXPECT_TRUE(blueprint::mesh::verify(mesh, info)); -// EXPECT_EQ(blueprint::mesh::number_of_domains(mesh), 1); - -// const Node &domain = *blueprint::mesh::domains(mesh).front(); -// EXPECT_TRUE(domain.has_child("coordsets")); -// EXPECT_EQ(domain["coordsets"].number_of_children(), 1); -// EXPECT_TRUE(domain.has_child("topologies")); -// EXPECT_EQ(domain["topologies"].number_of_children(), 1); - -// { // Coordset Validation // -// const Node &cset = domain["coordsets"].child(0); -// EXPECT_EQ(blueprint::mesh::coordset::dims(cset), dims_vec.at(i)); -// EXPECT_EQ(blueprint::mesh::coordset::length(cset), coordset_length_vec.at(i)); -// EXPECT_TRUE(blueprint::mesh::coordset::_explicit::verify(cset, info)); -// } - -// { // Topology Validation // -// const Node &topo = domain["topologies"].child(0); -// EXPECT_EQ(blueprint::mesh::topology::dims(topo), dims_vec.at(i)); -// EXPECT_EQ(blueprint::mesh::topology::length(topo), topology_length_vec.at(i)); -// EXPECT_TRUE(blueprint::mesh::topology::unstructured::verify(topo, info)); -// } -// } -// } - -// //----------------------------------------------------------------------------- -// TEST(conduit_relay_io_silo, round_trip_basic) -// { -// const std::vector> mesh_types = { -// std::make_pair("uniform", "2"), std::make_pair("uniform", "3"), -// std::make_pair("rectilinear", "2"), std::make_pair("rectilinear", "3"), -// std::make_pair("structured", "2"), std::make_pair("structured", "3"), -// std::make_pair("tris", "2"), -// std::make_pair("quads", "2"), -// std::make_pair("polygons", "2"), -// std::make_pair("tets", "3"), -// std::make_pair("hexs", "3"), -// std::make_pair("wedges", "3"), -// std::make_pair("pyramids", "3"), -// // std::make_pair("polyhedra", "3") -// }; -// for (int i = 0; i < mesh_types.size(); ++i) -// { -// std::string dim = mesh_types[i].second; -// index_t nx = 3; -// index_t ny = 4; -// index_t nz = (dim == "2" ? 0 : 2); - -// const std::string mesh_type = mesh_types[i].first; - -// Node save_mesh, load_mesh, info; -// blueprint::mesh::examples::basic(mesh_type, nx, ny, nz, save_mesh); - -// const std::string basename = "silo_basic_" + mesh_type + "_" + dim + "D"; -// const std::string filename = basename + ".root"; - -// remove_path_if_exists(filename); -// io::silo::save_mesh(save_mesh, basename); -// io::silo::load_mesh(filename, load_mesh); -// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - -// // make changes to save mesh so the diff will pass -// if (mesh_type == "uniform") -// { -// silo_uniform_to_rect_conversion("coords", "mesh", save_mesh); -// } -// silo_name_changer("mesh", save_mesh); - -// // the loaded mesh will be in the multidomain format -// // but the saved mesh is in the single domain format -// EXPECT_EQ(load_mesh.number_of_children(), 1); -// EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); -// EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); -// } -// } - -// //----------------------------------------------------------------------------- -// // we are testing vector fields in this test -// TEST(conduit_relay_io_silo, round_trip_braid) -// { -// const std::vector> mesh_types = { -// std::make_pair("uniform", "2"), std::make_pair("uniform", "3"), -// std::make_pair("rectilinear", "2"), std::make_pair("rectilinear", "3"), -// std::make_pair("structured", "2"), std::make_pair("structured", "3"), -// std::make_pair("points", "2"), std::make_pair("points", "3"), -// std::make_pair("points_implicit", "2"), std::make_pair("points_implicit", "3"), -// std::make_pair("lines", "2"), std::make_pair("lines", "3"), -// std::make_pair("tris", "2"), -// std::make_pair("quads", "2"), -// std::make_pair("tets", "3"), -// std::make_pair("hexs", "3"), -// std::make_pair("wedges", "3"), -// std::make_pair("pyramids", "3"), -// // std::make_pair("mixed_2d", "2"), -// // std::make_pair("mixed", "3"), -// }; -// for (int i = 0; i < mesh_types.size(); ++i) -// { -// std::string dim = mesh_types[i].second; -// index_t nx = 3; -// index_t ny = 4; -// index_t nz = (dim == "2" ? 0 : 2); - -// std::string mesh_type = mesh_types[i].first; - -// Node save_mesh, load_mesh, info; -// blueprint::mesh::examples::braid(mesh_type, nx, ny, nz, save_mesh); - -// const std::string basename = "silo_braid_" + mesh_type + "_" + dim + "D"; -// const std::string filename = basename + ".cycle_000100.root"; - -// // remove existing root file, directory and any output files -// remove_path_if_exists(filename); - -// io::silo::save_mesh(save_mesh, basename); -// io::silo::load_mesh(filename, load_mesh); -// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - -// // make changes to save mesh so the diff will pass -// if (mesh_type == "uniform") -// { -// silo_uniform_to_rect_conversion("coords", "mesh", save_mesh); -// } -// if (mesh_type == "points") -// { -// // this is custom code for braid -// // We know it is correct because the unstructured points version of braid -// // uses every point in the coordset -// save_mesh["topologies"].remove_child("mesh"); -// save_mesh["topologies"]["mesh"]["type"] = "points"; -// save_mesh["topologies"]["mesh"]["coordset"] = "coords"; -// } -// if (mesh_type == "points_implicit" || mesh_type == "points") -// { -// // the association doesn't matter for point meshes -// // we choose vertex by convention -// save_mesh["fields"]["radial"]["association"].reset(); -// save_mesh["fields"]["radial"]["association"] = "vertex"; -// } -// silo_name_changer("mesh", save_mesh); - -// // the loaded mesh will be in the multidomain format -// // but the saved mesh is in the single domain format -// EXPECT_EQ(load_mesh.number_of_children(), 1); -// EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); -// EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); -// } -// } - -// //----------------------------------------------------------------------------- -// // multidomain test -// TEST(conduit_relay_io_silo, round_trip_spiral) -// { -// for (int ndomains = 2; ndomains < 6; ndomains ++) -// { -// Node save_mesh, load_mesh, info; -// blueprint::mesh::examples::spiral(ndomains, save_mesh); - -// const std::string basename = "silo_spiral_" + std::to_string(ndomains) + "_domains"; -// const std::string filename = basename + ".cycle_000000.root"; - -// remove_path_if_exists(filename); -// io::silo::save_mesh(save_mesh, basename); -// io::silo::load_mesh(filename, load_mesh); -// EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); - -// // make changes to save mesh so the diff will pass -// for (index_t child = 0; child < save_mesh.number_of_children(); child ++) -// { -// silo_name_changer("mesh", save_mesh[child]); -// } - -// EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); -// NodeConstIterator l_itr = load_mesh.children(); -// NodeConstIterator s_itr = save_mesh.children(); -// while (l_itr.has_next()) -// { -// const Node &l_curr = l_itr.next(); -// const Node &s_curr = s_itr.next(); - -// EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); -// } -// } -// } - -// //----------------------------------------------------------------------------- -// TEST(conduit_relay_io_silo, round_trip_julia) -// { -// Node save_mesh, load_mesh, info; -// blueprint::mesh::examples::julia(5, // nx -// 5, // ny -// 0, // x_min -// 10, // x_max -// 2, // y_min -// 7, // y_max -// 3, // c_re -// 4, // c_im -// save_mesh); - -// const std::string basename = "silo_julia"; -// const std::string filename = basename + ".root"; - -// remove_path_if_exists(filename); -// io::silo::save_mesh(save_mesh, basename); -// io::silo::load_mesh(filename, load_mesh); -// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - -// // make changes to save mesh so the diff will pass -// silo_name_changer("mesh", save_mesh); - -// // the loaded mesh will be in the multidomain format -// // but the saved mesh is in the single domain format -// EXPECT_EQ(load_mesh.number_of_children(), 1); -// EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); -// EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); -// } - -// //----------------------------------------------------------------------------- -// // test material write and read -// TEST(conduit_relay_io_silo, round_trip_venn) -// { -// std::string matset_type = "sparse_by_element"; -// for (int j = 0; j < 2; j ++) -// { -// Node save_mesh, sbe, load_mesh, info; -// std::string size; -// int nx, ny; -// const double radius = 0.25; -// if (j == 0) -// { -// size = "small"; -// nx = ny = 4; -// } -// else -// { -// size = "large"; -// nx = ny = 100; -// } -// blueprint::mesh::examples::venn(matset_type, nx, ny, radius, save_mesh); - -// const std::string basename = "silo_venn_" + matset_type + "_" + size; -// const std::string filename = basename + ".root"; - -// remove_path_if_exists(filename); -// io::silo::save_mesh(save_mesh, basename); -// io::silo::load_mesh(filename, load_mesh); -// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - -// // make changes to save mesh so the diff will pass - -// // The field mat_check has values that are one type and matset_values -// // that are another type. The silo writer converts both to double arrays -// // in this case, so we follow suit. -// Node mat_check_new_values, mat_check_new_matset_values; -// save_mesh["fields"]["mat_check"]["values"].to_double_array(mat_check_new_values); -// save_mesh["fields"]["mat_check"]["matset_values"].to_double_array(mat_check_new_matset_values); -// save_mesh["fields"]["mat_check"]["values"].set_external(mat_check_new_values); -// save_mesh["fields"]["mat_check"]["matset_values"].set_external(mat_check_new_matset_values); - -// silo_name_changer("mesh", save_mesh); - -// // the loaded mesh will be in the multidomain format -// // but the saved mesh is in the single domain format -// EXPECT_EQ(load_mesh.number_of_children(), 1); -// EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); -// EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); -// } -// } - -// //----------------------------------------------------------------------------- -// TEST(conduit_relay_io_silo, round_trip_venn_modded_matnos) -// { -// const std::string matset_type = "sparse_by_element"; -// Node save_mesh, load_mesh, info; -// const int nx = 4; -// const int ny = 4; -// const double radius = 0.25; -// blueprint::mesh::examples::venn(matset_type, nx, ny, radius, save_mesh); - -// auto replace_matno = [](int matno) -// { -// return (matno == 1 ? 15 : -// (matno == 2 ? 37 : -// (matno == 3 ? 4 : -// (matno == 0 ? 22 : -// -1)))); -// }; - -// auto matmap_itr = save_mesh["matsets"]["matset"]["material_map"].children(); -// while (matmap_itr.has_next()) -// { -// Node &mat = matmap_itr.next(); -// mat.set(replace_matno(mat.as_int())); -// } - -// int_array matids = save_mesh["matsets"]["matset"]["material_ids"].value(); -// for (int i = 0; i < save_mesh["matsets"]["matset"]["material_ids"].dtype().number_of_elements(); i ++) -// { -// matids[i] = replace_matno(matids[i]); -// } - -// const std::string silo_basename = "silo_venn_" + matset_type + "_modded_matnos"; -// const std::string silo_filename = silo_basename + ".root"; -// remove_path_if_exists(silo_filename); -// io::silo::save_mesh(save_mesh, silo_basename); - -// const std::string bp_basename = "bp_venn_" + matset_type + "_modded_matnos"; -// const std::string bp_filename = bp_basename + ".root"; -// remove_path_if_exists(bp_filename); -// io::blueprint::save_mesh(save_mesh, bp_basename, "hdf5"); +//----------------------------------------------------------------------------- +TEST(conduit_relay_io_silo, conduit_silo_cold_storage) +{ + uint32 a_val = 20; + uint32 b_val = 8; + uint32 c_val = 13; + + Node n; + n["a"] = a_val; + n["b"] = b_val; + n["c"] = c_val; + + EXPECT_EQ(n["a"].as_uint32(), a_val); + EXPECT_EQ(n["b"].as_uint32(), b_val); + EXPECT_EQ(n["c"].as_uint32(), c_val); + + n.print(); + + io::silo_write(n,"tout_cold_storage_test.silo:myobj"); + + Node n_load; + io::silo_read("tout_cold_storage_test.silo:myobj",n_load); + + std::cout << "round trip" << std::endl; + n_load.print(); + + EXPECT_EQ(n_load["a"].as_uint32(), a_val); + EXPECT_EQ(n_load["b"].as_uint32(), b_val); + EXPECT_EQ(n_load["c"].as_uint32(), c_val); +} + +//----------------------------------------------------------------------------- +TEST(conduit_relay_io_silo, conduit_silo_cold_storage_generic_iface) +{ + uint32 a_val = 20; + uint32 b_val = 8; + uint32 c_val = 13; + + Node n; + n["a"] = a_val; + n["b"] = b_val; + n["c"] = c_val; + + EXPECT_EQ(n["a"].as_uint32(), a_val); + EXPECT_EQ(n["b"].as_uint32(), b_val); + EXPECT_EQ(n["c"].as_uint32(), c_val); + + io::save(n, "tout_cold_storage_test_generic_iface.silo:myobj"); + + Node n_load; + io::load("tout_cold_storage_test_generic_iface.silo:myobj",n_load); + + EXPECT_EQ(n_load["a"].as_uint32(), a_val); + EXPECT_EQ(n_load["b"].as_uint32(), b_val); + EXPECT_EQ(n_load["c"].as_uint32(), c_val); +} + +//----------------------------------------------------------------------------- +// test reading in a handful of different overlink files +TEST(conduit_relay_io_silo, load_mesh_geometry) +{ + // TODO: all these files are in overlink symlink format. + // Symlinks may break on Windows (?) + // Could make them overlink format without the symlink. + // But would require modifying the files. + std::vector filename_vec = { + "box2d.silo", + "box3d.silo", + // "diamond.silo", <--- TODO this one fails because polytopal is not yet supported + // TODO: rename these files to be more descriptive. + // would also require modifying the paths stored within the files, + // and re-symlinking + "testDisk2D_a.silo", + // "donordiv.s2_materials2.silo", <--- TODO this one fails because polytopal is not yet supported + "donordiv.s2_materials3.silo" + }; + std::vector dims_vec = {2, 3, /*2,*/ 2, /*2,*/ 2}; + std::vector coordset_length_vec = {4, 8, /*36,*/ 1994, /*16,*/ 961}; + std::vector topology_length_vec = {1, 1, /*33,*/ 1920, /*9,*/ 900}; + for (int i = 0; i < filename_vec.size(); ++i) + { + Node mesh, info; + std::string path = utils::join_file_path("overlink", filename_vec.at(i)); + std::string input_file = relay_test_silo_data_path(path); + io::silo::load_mesh(input_file, mesh); + + EXPECT_TRUE(blueprint::mesh::verify(mesh, info)); + EXPECT_EQ(blueprint::mesh::number_of_domains(mesh), 1); + + const Node &domain = *blueprint::mesh::domains(mesh).front(); + EXPECT_TRUE(domain.has_child("coordsets")); + EXPECT_EQ(domain["coordsets"].number_of_children(), 1); + EXPECT_TRUE(domain.has_child("topologies")); + EXPECT_EQ(domain["topologies"].number_of_children(), 1); + + { // Coordset Validation // + const Node &cset = domain["coordsets"].child(0); + EXPECT_EQ(blueprint::mesh::coordset::dims(cset), dims_vec.at(i)); + EXPECT_EQ(blueprint::mesh::coordset::length(cset), coordset_length_vec.at(i)); + EXPECT_TRUE(blueprint::mesh::coordset::_explicit::verify(cset, info)); + } + + { // Topology Validation // + const Node &topo = domain["topologies"].child(0); + EXPECT_EQ(blueprint::mesh::topology::dims(topo), dims_vec.at(i)); + EXPECT_EQ(blueprint::mesh::topology::length(topo), topology_length_vec.at(i)); + EXPECT_TRUE(blueprint::mesh::topology::unstructured::verify(topo, info)); + } + } +} + +//----------------------------------------------------------------------------- +TEST(conduit_relay_io_silo, round_trip_basic) +{ + const std::vector> mesh_types = { + std::make_pair("uniform", "2"), std::make_pair("uniform", "3"), + std::make_pair("rectilinear", "2"), std::make_pair("rectilinear", "3"), + std::make_pair("structured", "2"), std::make_pair("structured", "3"), + std::make_pair("tris", "2"), + std::make_pair("quads", "2"), + std::make_pair("polygons", "2"), + std::make_pair("tets", "3"), + std::make_pair("hexs", "3"), + std::make_pair("wedges", "3"), + std::make_pair("pyramids", "3"), + // std::make_pair("polyhedra", "3") + }; + for (int i = 0; i < mesh_types.size(); ++i) + { + std::string dim = mesh_types[i].second; + index_t nx = 3; + index_t ny = 4; + index_t nz = (dim == "2" ? 0 : 2); + + const std::string mesh_type = mesh_types[i].first; + + Node save_mesh, load_mesh, info; + blueprint::mesh::examples::basic(mesh_type, nx, ny, nz, save_mesh); + + const std::string basename = "silo_basic_" + mesh_type + "_" + dim + "D"; + const std::string filename = basename + ".root"; + + remove_path_if_exists(filename); + io::silo::save_mesh(save_mesh, basename); + io::silo::load_mesh(filename, load_mesh); + EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + + // make changes to save mesh so the diff will pass + if (mesh_type == "uniform") + { + silo_uniform_to_rect_conversion("coords", "mesh", save_mesh); + } + silo_name_changer("mesh", save_mesh); + + // the loaded mesh will be in the multidomain format + // but the saved mesh is in the single domain format + EXPECT_EQ(load_mesh.number_of_children(), 1); + EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); + EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); + } +} + +//----------------------------------------------------------------------------- +// we are testing vector fields in this test +TEST(conduit_relay_io_silo, round_trip_braid) +{ + const std::vector> mesh_types = { + std::make_pair("uniform", "2"), std::make_pair("uniform", "3"), + std::make_pair("rectilinear", "2"), std::make_pair("rectilinear", "3"), + std::make_pair("structured", "2"), std::make_pair("structured", "3"), + std::make_pair("points", "2"), std::make_pair("points", "3"), + std::make_pair("points_implicit", "2"), std::make_pair("points_implicit", "3"), + std::make_pair("lines", "2"), std::make_pair("lines", "3"), + std::make_pair("tris", "2"), + std::make_pair("quads", "2"), + std::make_pair("tets", "3"), + std::make_pair("hexs", "3"), + std::make_pair("wedges", "3"), + std::make_pair("pyramids", "3"), + // std::make_pair("mixed_2d", "2"), + // std::make_pair("mixed", "3"), + }; + for (int i = 0; i < mesh_types.size(); ++i) + { + std::string dim = mesh_types[i].second; + index_t nx = 3; + index_t ny = 4; + index_t nz = (dim == "2" ? 0 : 2); + + std::string mesh_type = mesh_types[i].first; + + Node save_mesh, load_mesh, info; + blueprint::mesh::examples::braid(mesh_type, nx, ny, nz, save_mesh); + + const std::string basename = "silo_braid_" + mesh_type + "_" + dim + "D"; + const std::string filename = basename + ".cycle_000100.root"; + + // remove existing root file, directory and any output files + remove_path_if_exists(filename); + + io::silo::save_mesh(save_mesh, basename); + io::silo::load_mesh(filename, load_mesh); + EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + + // make changes to save mesh so the diff will pass + if (mesh_type == "uniform") + { + silo_uniform_to_rect_conversion("coords", "mesh", save_mesh); + } + if (mesh_type == "points") + { + // this is custom code for braid + // We know it is correct because the unstructured points version of braid + // uses every point in the coordset + save_mesh["topologies"].remove_child("mesh"); + save_mesh["topologies"]["mesh"]["type"] = "points"; + save_mesh["topologies"]["mesh"]["coordset"] = "coords"; + } + if (mesh_type == "points_implicit" || mesh_type == "points") + { + // the association doesn't matter for point meshes + // we choose vertex by convention + save_mesh["fields"]["radial"]["association"].reset(); + save_mesh["fields"]["radial"]["association"] = "vertex"; + } + silo_name_changer("mesh", save_mesh); + + // the loaded mesh will be in the multidomain format + // but the saved mesh is in the single domain format + EXPECT_EQ(load_mesh.number_of_children(), 1); + EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); + EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); + } +} + +//----------------------------------------------------------------------------- +// multidomain test +TEST(conduit_relay_io_silo, round_trip_spiral) +{ + for (int ndomains = 2; ndomains < 6; ndomains ++) + { + Node save_mesh, load_mesh, info; + blueprint::mesh::examples::spiral(ndomains, save_mesh); + + const std::string basename = "silo_spiral_" + std::to_string(ndomains) + "_domains"; + const std::string filename = basename + ".cycle_000000.root"; + + remove_path_if_exists(filename); + io::silo::save_mesh(save_mesh, basename); + io::silo::load_mesh(filename, load_mesh); + EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); + + // make changes to save mesh so the diff will pass + for (index_t child = 0; child < save_mesh.number_of_children(); child ++) + { + silo_name_changer("mesh", save_mesh[child]); + } + + EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); + NodeConstIterator l_itr = load_mesh.children(); + NodeConstIterator s_itr = save_mesh.children(); + while (l_itr.has_next()) + { + const Node &l_curr = l_itr.next(); + const Node &s_curr = s_itr.next(); + + EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); + } + } +} + +//----------------------------------------------------------------------------- +TEST(conduit_relay_io_silo, round_trip_julia) +{ + Node save_mesh, load_mesh, info; + blueprint::mesh::examples::julia(5, // nx + 5, // ny + 0, // x_min + 10, // x_max + 2, // y_min + 7, // y_max + 3, // c_re + 4, // c_im + save_mesh); + + const std::string basename = "silo_julia"; + const std::string filename = basename + ".root"; + + remove_path_if_exists(filename); + io::silo::save_mesh(save_mesh, basename); + io::silo::load_mesh(filename, load_mesh); + EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + + // make changes to save mesh so the diff will pass + silo_name_changer("mesh", save_mesh); + + // the loaded mesh will be in the multidomain format + // but the saved mesh is in the single domain format + EXPECT_EQ(load_mesh.number_of_children(), 1); + EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); + EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); +} + +//----------------------------------------------------------------------------- +// test material write and read +TEST(conduit_relay_io_silo, round_trip_venn) +{ + std::string matset_type = "sparse_by_element"; + for (int j = 0; j < 2; j ++) + { + Node save_mesh, sbe, load_mesh, info; + std::string size; + int nx, ny; + const double radius = 0.25; + if (j == 0) + { + size = "small"; + nx = ny = 4; + } + else + { + size = "large"; + nx = ny = 100; + } + blueprint::mesh::examples::venn(matset_type, nx, ny, radius, save_mesh); + + const std::string basename = "silo_venn_" + matset_type + "_" + size; + const std::string filename = basename + ".root"; + + remove_path_if_exists(filename); + io::silo::save_mesh(save_mesh, basename); + io::silo::load_mesh(filename, load_mesh); + EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + + // make changes to save mesh so the diff will pass + + // The field mat_check has values that are one type and matset_values + // that are another type. The silo writer converts both to double arrays + // in this case, so we follow suit. + Node mat_check_new_values, mat_check_new_matset_values; + save_mesh["fields"]["mat_check"]["values"].to_double_array(mat_check_new_values); + save_mesh["fields"]["mat_check"]["matset_values"].to_double_array(mat_check_new_matset_values); + save_mesh["fields"]["mat_check"]["values"].set_external(mat_check_new_values); + save_mesh["fields"]["mat_check"]["matset_values"].set_external(mat_check_new_matset_values); + + silo_name_changer("mesh", save_mesh); + + // the loaded mesh will be in the multidomain format + // but the saved mesh is in the single domain format + EXPECT_EQ(load_mesh.number_of_children(), 1); + EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); + EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); + } +} + +//----------------------------------------------------------------------------- +TEST(conduit_relay_io_silo, round_trip_venn_modded_matnos) +{ + const std::string matset_type = "sparse_by_element"; + Node save_mesh, load_mesh, info; + const int nx = 4; + const int ny = 4; + const double radius = 0.25; + blueprint::mesh::examples::venn(matset_type, nx, ny, radius, save_mesh); + + auto replace_matno = [](int matno) + { + return (matno == 1 ? 15 : + (matno == 2 ? 37 : + (matno == 3 ? 4 : + (matno == 0 ? 22 : + -1)))); + }; + + auto matmap_itr = save_mesh["matsets"]["matset"]["material_map"].children(); + while (matmap_itr.has_next()) + { + Node &mat = matmap_itr.next(); + mat.set(replace_matno(mat.as_int())); + } + + int_array matids = save_mesh["matsets"]["matset"]["material_ids"].value(); + for (int i = 0; i < save_mesh["matsets"]["matset"]["material_ids"].dtype().number_of_elements(); i ++) + { + matids[i] = replace_matno(matids[i]); + } + + const std::string silo_basename = "silo_venn_" + matset_type + "_modded_matnos"; + const std::string silo_filename = silo_basename + ".root"; + remove_path_if_exists(silo_filename); + io::silo::save_mesh(save_mesh, silo_basename); + + const std::string bp_basename = "bp_venn_" + matset_type + "_modded_matnos"; + const std::string bp_filename = bp_basename + ".root"; + remove_path_if_exists(bp_filename); + io::blueprint::save_mesh(save_mesh, bp_basename, "hdf5"); -// io::silo::load_mesh(silo_filename, load_mesh); -// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - -// // make changes to save mesh so the diff will pass - -// // The field mat_check has values that are one type and matset_values -// // that are another type. The silo writer converts both to double arrays -// // in this case, so we follow suit. -// Node mat_check_new_values, mat_check_new_matset_values; -// save_mesh["fields"]["mat_check"]["values"].to_double_array(mat_check_new_values); -// save_mesh["fields"]["mat_check"]["matset_values"].to_double_array(mat_check_new_matset_values); -// save_mesh["fields"]["mat_check"]["values"].set_external(mat_check_new_values); -// save_mesh["fields"]["mat_check"]["matset_values"].set_external(mat_check_new_matset_values); - -// // to_silo is going to reorder mixed materials least to greatest -// // so we must do the same -// int_array mat_ids = save_mesh["matsets"]["matset"]["material_ids"].value(); -// const auto mat_id10 = mat_ids[10]; -// const auto mat_id11 = mat_ids[11]; -// const auto mat_id12 = mat_ids[12]; -// mat_ids[10] = mat_id12; -// mat_ids[11] = mat_id10; -// mat_ids[12] = mat_id11; -// auto field_itr = save_mesh["fields"].children(); -// while (field_itr.has_next()) -// { -// const Node &n_field = field_itr.next(); -// if (n_field.has_child("matset")) -// { -// double_array matset_vals = n_field["matset_values"].value(); -// const auto matset_val10 = matset_vals[10]; -// const auto matset_val11 = matset_vals[11]; -// const auto matset_val12 = matset_vals[12]; -// matset_vals[10] = matset_val12; -// matset_vals[11] = matset_val10; -// matset_vals[12] = matset_val11; -// } -// } - -// silo_name_changer("mesh", save_mesh); - -// // the loaded mesh will be in the multidomain format -// // but the saved mesh is in the single domain format -// EXPECT_EQ(load_mesh.number_of_children(), 1); -// EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); - -// EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); -// } - -// //----------------------------------------------------------------------------- -// TEST(conduit_relay_io_silo, round_trip_spiral_multi_dom_materials) -// { -// Node save_mesh, load_mesh, info; -// const int ndomains = 4; -// blueprint::mesh::examples::spiral(ndomains, save_mesh); -// add_matset_to_spiral(save_mesh, ndomains); -// EXPECT_TRUE(blueprint::mesh::verify(save_mesh, info)); + io::silo::load_mesh(silo_filename, load_mesh); + EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + + // make changes to save mesh so the diff will pass + + // The field mat_check has values that are one type and matset_values + // that are another type. The silo writer converts both to double arrays + // in this case, so we follow suit. + Node mat_check_new_values, mat_check_new_matset_values; + save_mesh["fields"]["mat_check"]["values"].to_double_array(mat_check_new_values); + save_mesh["fields"]["mat_check"]["matset_values"].to_double_array(mat_check_new_matset_values); + save_mesh["fields"]["mat_check"]["values"].set_external(mat_check_new_values); + save_mesh["fields"]["mat_check"]["matset_values"].set_external(mat_check_new_matset_values); + + // to_silo is going to reorder mixed materials least to greatest + // so we must do the same + int_array mat_ids = save_mesh["matsets"]["matset"]["material_ids"].value(); + const auto mat_id10 = mat_ids[10]; + const auto mat_id11 = mat_ids[11]; + const auto mat_id12 = mat_ids[12]; + mat_ids[10] = mat_id12; + mat_ids[11] = mat_id10; + mat_ids[12] = mat_id11; + auto field_itr = save_mesh["fields"].children(); + while (field_itr.has_next()) + { + const Node &n_field = field_itr.next(); + if (n_field.has_child("matset")) + { + double_array matset_vals = n_field["matset_values"].value(); + const auto matset_val10 = matset_vals[10]; + const auto matset_val11 = matset_vals[11]; + const auto matset_val12 = matset_vals[12]; + matset_vals[10] = matset_val12; + matset_vals[11] = matset_val10; + matset_vals[12] = matset_val11; + } + } + + silo_name_changer("mesh", save_mesh); + + // the loaded mesh will be in the multidomain format + // but the saved mesh is in the single domain format + EXPECT_EQ(load_mesh.number_of_children(), 1); + EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); + + EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); +} + +//----------------------------------------------------------------------------- +TEST(conduit_relay_io_silo, round_trip_spiral_multi_dom_materials) +{ + Node save_mesh, load_mesh, info; + const int ndomains = 4; + blueprint::mesh::examples::spiral(ndomains, save_mesh); + add_matset_to_spiral(save_mesh, ndomains); + EXPECT_TRUE(blueprint::mesh::verify(save_mesh, info)); -// const std::string basename = "silo_multidom_materials_spiral"; -// const std::string filename = basename + ".cycle_000000.root"; - -// remove_path_if_exists(filename); -// io::silo::save_mesh(save_mesh, basename); -// io::silo::load_mesh(filename, load_mesh); -// EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); - -// // make changes to save mesh so the diff will pass -// for (index_t child = 0; child < save_mesh.number_of_children(); child ++) -// { -// // get the matset for this domain -// Node &n_matset = save_mesh[child]["matsets"]["matset"]; - -// // clean up volume fractions -// Node vf_arr; -// n_matset["volume_fractions"].to_float64_array(vf_arr); -// n_matset["volume_fractions"].reset(); -// n_matset["volume_fractions"].set(vf_arr); + const std::string basename = "silo_multidom_materials_spiral"; + const std::string filename = basename + ".cycle_000000.root"; + + remove_path_if_exists(filename); + io::silo::save_mesh(save_mesh, basename); + io::silo::load_mesh(filename, load_mesh); + EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); + + // make changes to save mesh so the diff will pass + for (index_t child = 0; child < save_mesh.number_of_children(); child ++) + { + // get the matset for this domain + Node &n_matset = save_mesh[child]["matsets"]["matset"]; + + // clean up volume fractions + Node vf_arr; + n_matset["volume_fractions"].to_float64_array(vf_arr); + n_matset["volume_fractions"].reset(); + n_matset["volume_fractions"].set(vf_arr); -// // cheat a little bit - we don't have these to start -// n_matset["sizes"].set_external(load_mesh[child]["matsets"]["mesh_matset"]["sizes"]); -// n_matset["offsets"].set_external(load_mesh[child]["matsets"]["mesh_matset"]["offsets"]); - -// silo_name_changer("mesh", save_mesh[child]); -// } - -// EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); -// NodeConstIterator l_itr = load_mesh.children(); -// NodeConstIterator s_itr = save_mesh.children(); -// while (l_itr.has_next()) -// { -// const Node &l_curr = l_itr.next(); -// const Node &s_curr = s_itr.next(); - -// EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); -// } -// } - -// //----------------------------------------------------------------------------- -// TEST(conduit_relay_io_silo, round_trip_grid_adjset) -// { -// Node save_mesh, load_mesh, info; -// blueprint::mesh::examples::grid("structured", 3, 3, 1, 2, 2, 1, save_mesh); - -// // we need a material in order for this to be valid overlink -// for (index_t child = 0; child < save_mesh.number_of_children(); child ++) -// { -// add_multi_buffer_full_matset(save_mesh[child], 4, "mesh"); -// } - -// const std::string basename = "silo_grid_adjset"; -// const std::string filename = basename + "/OvlTop.silo"; - -// Node write_opts; -// write_opts["file_style"] = "overlink"; -// write_opts["ovl_topo_name"] = "mesh"; - -// Node read_opts; -// read_opts["matset_style"] = "multi_buffer_full"; - -// remove_path_if_exists(filename); -// io::silo::save_mesh(save_mesh, basename, write_opts); -// io::blueprint::save_mesh(save_mesh, basename, "hdf5"); -// io::silo::load_mesh(filename, read_opts, load_mesh); -// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - -// for (index_t child = 0; child < save_mesh.number_of_children(); child ++) -// { -// // separate out vector fields -// Node &field_vel = save_mesh[child]["fields"]["vel"]; -// Node &field_vel_u = save_mesh[child]["fields"]["vel_u"]; -// Node &field_vel_v = save_mesh[child]["fields"]["vel_v"]; + // cheat a little bit - we don't have these to start + n_matset["sizes"].set_external(load_mesh[child]["matsets"]["mesh_matset"]["sizes"]); + n_matset["offsets"].set_external(load_mesh[child]["matsets"]["mesh_matset"]["offsets"]); + + silo_name_changer("mesh", save_mesh[child]); + } + + EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); + NodeConstIterator l_itr = load_mesh.children(); + NodeConstIterator s_itr = save_mesh.children(); + while (l_itr.has_next()) + { + const Node &l_curr = l_itr.next(); + const Node &s_curr = s_itr.next(); + + EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); + } +} + +//----------------------------------------------------------------------------- +TEST(conduit_relay_io_silo, round_trip_grid_adjset) +{ + Node save_mesh, load_mesh, info; + blueprint::mesh::examples::grid("structured", 3, 3, 1, 2, 2, 1, save_mesh); + + // we need a material in order for this to be valid overlink + for (index_t child = 0; child < save_mesh.number_of_children(); child ++) + { + add_multi_buffer_full_matset(save_mesh[child], 4, "mesh"); + } + + const std::string basename = "silo_grid_adjset"; + const std::string filename = basename + "/OvlTop.silo"; + + Node write_opts; + write_opts["file_style"] = "overlink"; + write_opts["ovl_topo_name"] = "mesh"; + + Node read_opts; + read_opts["matset_style"] = "multi_buffer_full"; + + remove_path_if_exists(filename); + io::silo::save_mesh(save_mesh, basename, write_opts); + io::blueprint::save_mesh(save_mesh, basename, "hdf5"); + io::silo::load_mesh(filename, read_opts, load_mesh); + EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + + for (index_t child = 0; child < save_mesh.number_of_children(); child ++) + { + // separate out vector fields + Node &field_vel = save_mesh[child]["fields"]["vel"]; + Node &field_vel_u = save_mesh[child]["fields"]["vel_u"]; + Node &field_vel_v = save_mesh[child]["fields"]["vel_v"]; -// field_vel_u["topology"].set(field_vel["topology"]); -// field_vel_u["association"].set(field_vel["association"]); -// field_vel_u["values"].set(field_vel["values/u"]); -// field_vel_v["topology"].set(field_vel["topology"]); -// field_vel_v["association"].set(field_vel["association"]); -// field_vel_v["values"].set(field_vel["values/v"]); - -// save_mesh[child]["fields"].remove_child("vel"); - -// // make adjset pairwise -// Node &pairwise_adjset = save_mesh[child]["adjsets"]["adjset"]; -// conduit::blueprint::mesh::adjset::to_pairwise(save_mesh[child]["adjsets"]["mesh_adj"], pairwise_adjset); -// save_mesh[child]["adjsets"].remove_child("mesh_adj"); - -// // make changes to save mesh so the diff will pass -// overlink_name_changer(save_mesh[child]); -// } - -// EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); -// NodeConstIterator l_itr = load_mesh.children(); -// NodeConstIterator s_itr = save_mesh.children(); -// while (l_itr.has_next()) -// { -// const Node &l_curr = l_itr.next(); -// const Node &s_curr = s_itr.next(); - -// EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); -// } -// } - -// //----------------------------------------------------------------------------- -// // -// // test read and write semantics -// // - -// //----------------------------------------------------------------------------- -// TEST(conduit_relay_io_silo, read_and_write_semantics) -// { -// for (int ndomains = 2; ndomains < 6; ndomains ++) -// { -// Node save_mesh, load_mesh, info; -// blueprint::mesh::examples::spiral(ndomains, save_mesh); - -// const std::string basename = "silo_spiral_" + std::to_string(ndomains) + "_domains"; -// const std::string filename = basename + ".cycle_000000.root"; - -// remove_path_if_exists(filename); -// io::silo::write_mesh(save_mesh, basename); -// io::silo::read_mesh(filename, load_mesh); -// EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); - -// // make changes to save mesh so the diff will pass -// for (index_t child = 0; child < save_mesh.number_of_children(); child ++) -// { -// silo_name_changer("mesh", save_mesh[child]); -// } - -// EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); -// NodeConstIterator l_itr = load_mesh.children(); -// NodeConstIterator s_itr = save_mesh.children(); -// while (l_itr.has_next()) -// { -// const Node &l_curr = l_itr.next(); -// const Node &s_curr = s_itr.next(); - -// EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); -// } -// } -// } - -// //----------------------------------------------------------------------------- -// // -// // special case tests -// // - -// //----------------------------------------------------------------------------- -// // var is not defined on a domain -// // -// // tests the silo "EMPTY" capability -// TEST(conduit_relay_io_silo, missing_domain_var) -// { -// Node save_mesh, load_mesh, info; -// const int ndomains = 4; -// blueprint::mesh::examples::spiral(ndomains, save_mesh); - -// // remove information for a particular domain -// save_mesh[2]["fields"].remove_child("dist"); - -// const std::string basename = "silo_missing_domain_var_spiral"; -// const std::string filename = basename + ".cycle_000000.root"; - -// remove_path_if_exists(filename); -// io::silo::save_mesh(save_mesh, basename); -// io::silo::load_mesh(filename, load_mesh); -// EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); - -// // make changes to save mesh so the diff will pass -// for (index_t child = 0; child < save_mesh.number_of_children(); child ++) -// { -// silo_name_changer("mesh", save_mesh[child]); -// } -// save_mesh[2].remove_child("fields"); - -// EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); -// NodeConstIterator l_itr = load_mesh.children(); -// NodeConstIterator s_itr = save_mesh.children(); -// while (l_itr.has_next()) -// { -// const Node &l_curr = l_itr.next(); -// const Node &s_curr = s_itr.next(); - -// EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); -// } -// } - -// //----------------------------------------------------------------------------- -// // matset is not defined on a domain -// // -// // tests the silo "EMPTY" capability -// TEST(conduit_relay_io_silo, missing_domain_matset) -// { -// Node save_mesh, load_mesh, info; -// const int ndomains = 4; -// blueprint::mesh::examples::spiral(ndomains, save_mesh); -// add_matset_to_spiral(save_mesh, ndomains); -// EXPECT_TRUE(blueprint::mesh::verify(save_mesh, info)); - -// // remove information for a particular domain -// save_mesh[2]["matsets"].remove_child("matset"); - -// const std::string basename = "silo_missing_domain_matset_spiral"; -// const std::string filename = basename + ".cycle_000000.root"; - -// remove_path_if_exists(filename); -// io::silo::save_mesh(save_mesh, basename); -// io::silo::load_mesh(filename, load_mesh); -// EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); - -// // make changes to save mesh so the diff will pass -// for (index_t child = 0; child < save_mesh.number_of_children(); child ++) -// { -// if (save_mesh[child].has_path("matsets/matset")) -// { -// // get the matset for this domain -// Node &n_matset = save_mesh[child]["matsets"]["matset"]; - -// // clean up volume fractions -// Node vf_arr; -// n_matset["volume_fractions"].to_float64_array(vf_arr); -// n_matset["volume_fractions"].reset(); -// n_matset["volume_fractions"].set(vf_arr); + field_vel_u["topology"].set(field_vel["topology"]); + field_vel_u["association"].set(field_vel["association"]); + field_vel_u["values"].set(field_vel["values/u"]); + field_vel_v["topology"].set(field_vel["topology"]); + field_vel_v["association"].set(field_vel["association"]); + field_vel_v["values"].set(field_vel["values/v"]); + + save_mesh[child]["fields"].remove_child("vel"); + + // make adjset pairwise + Node &pairwise_adjset = save_mesh[child]["adjsets"]["adjset"]; + conduit::blueprint::mesh::adjset::to_pairwise(save_mesh[child]["adjsets"]["mesh_adj"], pairwise_adjset); + save_mesh[child]["adjsets"].remove_child("mesh_adj"); + + // make changes to save mesh so the diff will pass + overlink_name_changer(save_mesh[child]); + } + + EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); + NodeConstIterator l_itr = load_mesh.children(); + NodeConstIterator s_itr = save_mesh.children(); + while (l_itr.has_next()) + { + const Node &l_curr = l_itr.next(); + const Node &s_curr = s_itr.next(); + + EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); + } +} + +//----------------------------------------------------------------------------- +// +// test read and write semantics +// + +//----------------------------------------------------------------------------- +TEST(conduit_relay_io_silo, read_and_write_semantics) +{ + for (int ndomains = 2; ndomains < 6; ndomains ++) + { + Node save_mesh, load_mesh, info; + blueprint::mesh::examples::spiral(ndomains, save_mesh); + + const std::string basename = "silo_spiral_" + std::to_string(ndomains) + "_domains"; + const std::string filename = basename + ".cycle_000000.root"; + + remove_path_if_exists(filename); + io::silo::write_mesh(save_mesh, basename); + io::silo::read_mesh(filename, load_mesh); + EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); + + // make changes to save mesh so the diff will pass + for (index_t child = 0; child < save_mesh.number_of_children(); child ++) + { + silo_name_changer("mesh", save_mesh[child]); + } + + EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); + NodeConstIterator l_itr = load_mesh.children(); + NodeConstIterator s_itr = save_mesh.children(); + while (l_itr.has_next()) + { + const Node &l_curr = l_itr.next(); + const Node &s_curr = s_itr.next(); + + EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); + } + } +} + +//----------------------------------------------------------------------------- +// +// special case tests +// + +//----------------------------------------------------------------------------- +// var is not defined on a domain +// +// tests the silo "EMPTY" capability +TEST(conduit_relay_io_silo, missing_domain_var) +{ + Node save_mesh, load_mesh, info; + const int ndomains = 4; + blueprint::mesh::examples::spiral(ndomains, save_mesh); + + // remove information for a particular domain + save_mesh[2]["fields"].remove_child("dist"); + + const std::string basename = "silo_missing_domain_var_spiral"; + const std::string filename = basename + ".cycle_000000.root"; + + remove_path_if_exists(filename); + io::silo::save_mesh(save_mesh, basename); + io::silo::load_mesh(filename, load_mesh); + EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); + + // make changes to save mesh so the diff will pass + for (index_t child = 0; child < save_mesh.number_of_children(); child ++) + { + silo_name_changer("mesh", save_mesh[child]); + } + save_mesh[2].remove_child("fields"); + + EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); + NodeConstIterator l_itr = load_mesh.children(); + NodeConstIterator s_itr = save_mesh.children(); + while (l_itr.has_next()) + { + const Node &l_curr = l_itr.next(); + const Node &s_curr = s_itr.next(); + + EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); + } +} + +//----------------------------------------------------------------------------- +// matset is not defined on a domain +// +// tests the silo "EMPTY" capability +TEST(conduit_relay_io_silo, missing_domain_matset) +{ + Node save_mesh, load_mesh, info; + const int ndomains = 4; + blueprint::mesh::examples::spiral(ndomains, save_mesh); + add_matset_to_spiral(save_mesh, ndomains); + EXPECT_TRUE(blueprint::mesh::verify(save_mesh, info)); + + // remove information for a particular domain + save_mesh[2]["matsets"].remove_child("matset"); + + const std::string basename = "silo_missing_domain_matset_spiral"; + const std::string filename = basename + ".cycle_000000.root"; + + remove_path_if_exists(filename); + io::silo::save_mesh(save_mesh, basename); + io::silo::load_mesh(filename, load_mesh); + EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); + + // make changes to save mesh so the diff will pass + for (index_t child = 0; child < save_mesh.number_of_children(); child ++) + { + if (save_mesh[child].has_path("matsets/matset")) + { + // get the matset for this domain + Node &n_matset = save_mesh[child]["matsets"]["matset"]; + + // clean up volume fractions + Node vf_arr; + n_matset["volume_fractions"].to_float64_array(vf_arr); + n_matset["volume_fractions"].reset(); + n_matset["volume_fractions"].set(vf_arr); -// // cheat a little bit - we don't have these to start -// n_matset["sizes"].set_external(load_mesh[child]["matsets"]["mesh_matset"]["sizes"]); -// n_matset["offsets"].set_external(load_mesh[child]["matsets"]["mesh_matset"]["offsets"]); -// } - -// silo_name_changer("mesh", save_mesh[child]); -// } -// save_mesh[2].remove_child("matsets"); - -// EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); -// NodeConstIterator l_itr = load_mesh.children(); -// NodeConstIterator s_itr = save_mesh.children(); -// while (l_itr.has_next()) -// { -// const Node &l_curr = l_itr.next(); -// const Node &s_curr = s_itr.next(); - -// EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); -// } -// } - -// //----------------------------------------------------------------------------- -// // mesh is not defined on a domain -// // -// // This case is much less interesting. -// // data passes through the clean mesh filter which -// // deletes domains that are missing topos. -// // They simply are not part of the mesh and so silo -// // doesn't have to deal with it. -// TEST(conduit_relay_io_silo, missing_domain_mesh_trivial) -// { -// Node save_mesh, load_mesh, info; -// const int ndomains = 4; -// blueprint::mesh::examples::spiral(ndomains, save_mesh); - -// // remove information for a particular domain -// save_mesh[2]["topologies"].remove_child("topo"); - -// const std::string basename = "silo_missing_domain_mesh_trivial_spiral"; -// const std::string filename = basename + ".cycle_000000.root"; - -// remove_path_if_exists(filename); -// io::silo::save_mesh(save_mesh, basename); -// io::silo::load_mesh(filename, load_mesh); - -// EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); - -// // make changes to save mesh so the diff will pass -// save_mesh.remove(2); -// save_mesh.rename_child("domain_000003", "domain_000002"); -// save_mesh[2]["state"]["domain_id"].reset(); -// save_mesh[2]["state"]["domain_id"] = 2; -// for (index_t child = 0; child < save_mesh.number_of_children(); child ++) -// { -// silo_name_changer("mesh", save_mesh[child]); -// } - -// EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); -// NodeConstIterator l_itr = load_mesh.children(); -// NodeConstIterator s_itr = save_mesh.children(); -// while (l_itr.has_next()) -// { -// const Node &l_curr = l_itr.next(); -// const Node &s_curr = s_itr.next(); - -// EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); -// } -// } - -// //----------------------------------------------------------------------------- -// // mesh is not defined on a domain but there are multiple meshes -// TEST(conduit_relay_io_silo, missing_domain_mesh) -// { -// Node save_mesh, save_mesh2, load_mesh, load_mesh2, info, opts; -// const int ndomains = 4; -// blueprint::mesh::examples::spiral(ndomains, save_mesh); -// blueprint::mesh::examples::spiral(ndomains, save_mesh2); - -// for (index_t child = 0; child < save_mesh.number_of_children(); child ++) -// { -// save_mesh[child]["coordsets"].rename_child("coords", "coords2"); -// save_mesh[child]["topologies"]["topo"]["coordset"].reset(); -// save_mesh[child]["topologies"]["topo"]["coordset"] = "coords2"; -// save_mesh[child]["topologies"].rename_child("topo", "topo2"); -// save_mesh[child]["fields"]["dist"]["topology"].reset(); -// save_mesh[child]["fields"]["dist"]["topology"] = "topo2"; -// save_mesh[child]["fields"].rename_child("dist", "dist2"); - -// save_mesh[child]["coordsets"]["coords"].set_external(save_mesh2[child]["coordsets"]["coords"]); -// save_mesh[child]["topologies"]["topo"].set_external(save_mesh2[child]["topologies"]["topo"]); -// save_mesh[child]["fields"]["dist"].set_external(save_mesh2[child]["fields"]["dist"]); -// } - -// // remove information for a particular domain -// save_mesh[2]["topologies"].remove_child("topo"); - -// const std::string basename = "silo_missing_domain_mesh_spiral"; -// const std::string filename = basename + ".cycle_000000.root"; - -// remove_path_if_exists(filename); -// io::silo::save_mesh(save_mesh, basename); -// opts["silo_names"]["multimesh_names"] = "mesh_topo2"; -// io::silo::load_mesh(filename, opts, load_mesh); -// opts["silo_names"]["multimesh_names"] = "mesh_topo"; -// io::silo::load_mesh(filename, opts, load_mesh2); -// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); -// EXPECT_TRUE(blueprint::mesh::verify(load_mesh2, info)); - -// // make changes to save mesh so the diff will pass -// save_mesh[2]["coordsets"].remove_child("coords"); -// save_mesh[2]["fields"].remove_child("dist"); -// for (index_t child = 0; child < save_mesh.number_of_children(); child ++) -// { -// silo_name_changer("mesh", save_mesh[child]); -// } - -// // we must merge the two meshes in load mesh -// // the indexing is tricky because one is missing a domain -// load_mesh[0]["coordsets"]["mesh_topo"].set_external(load_mesh2[0]["coordsets"]["mesh_topo"]); -// load_mesh[0]["topologies"]["mesh_topo"].set_external(load_mesh2[0]["topologies"]["mesh_topo"]); -// load_mesh[0]["fields"]["mesh_dist"].set_external(load_mesh2[0]["fields"]["mesh_dist"]); -// load_mesh[1]["coordsets"]["mesh_topo"].set_external(load_mesh2[1]["coordsets"]["mesh_topo"]); -// load_mesh[1]["topologies"]["mesh_topo"].set_external(load_mesh2[1]["topologies"]["mesh_topo"]); -// load_mesh[1]["fields"]["mesh_dist"].set_external(load_mesh2[1]["fields"]["mesh_dist"]); -// load_mesh[3]["coordsets"]["mesh_topo"].set_external(load_mesh2[2]["coordsets"]["mesh_topo"]); -// load_mesh[3]["topologies"]["mesh_topo"].set_external(load_mesh2[2]["topologies"]["mesh_topo"]); -// load_mesh[3]["fields"]["mesh_dist"].set_external(load_mesh2[2]["fields"]["mesh_dist"]); - -// EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); -// NodeConstIterator l_itr = load_mesh.children(); -// NodeConstIterator s_itr = save_mesh.children(); -// while (l_itr.has_next()) -// { -// const Node &l_curr = l_itr.next(); -// const Node &s_curr = s_itr.next(); - -// EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); -// } -// } - -// //----------------------------------------------------------------------------- -// // explicit points (unstructured mesh) do not use every coord -// TEST(conduit_relay_io_silo, unstructured_points) -// { -// Node save_mesh, load_mesh, info; -// blueprint::mesh::examples::braid("points", 2, 2, 2, save_mesh); - -// std::vector new_conn; -// std::vector new_field1; -// std::vector new_field2; -// std::vector new_xcoords, new_ycoords, new_zcoords; - -// int_accessor conn = save_mesh["topologies"]["mesh"]["elements"]["connectivity"].value(); - -// float_accessor field1 = save_mesh["fields"]["braid"]["values"].value(); -// float_accessor field2 = save_mesh["fields"]["radial"]["values"].value(); - -// float_accessor xcoords = save_mesh["coordsets"]["coords"]["values"]["x"].value(); -// float_accessor ycoords = save_mesh["coordsets"]["coords"]["values"]["y"].value(); -// float_accessor zcoords = save_mesh["coordsets"]["coords"]["values"]["z"].value(); - -// for (int i = 1; i < conn.number_of_elements(); i += 2) -// { -// new_conn.push_back(conn[i]); -// new_field1.push_back(field1[i]); -// new_field2.push_back(field2[i]); - -// new_xcoords.push_back(xcoords[conn[i]]); -// new_ycoords.push_back(ycoords[conn[i]]); -// new_zcoords.push_back(zcoords[conn[i]]); -// } -// save_mesh["topologies"]["mesh"]["elements"]["connectivity"].reset(); -// save_mesh["topologies"]["mesh"]["elements"]["connectivity"].set(new_conn); - -// save_mesh["fields"].remove_child("vel"); -// save_mesh["fields"]["braid"]["values"].reset(); -// save_mesh["fields"]["braid"]["values"].set(new_field1); -// save_mesh["fields"]["radial"]["values"].reset(); -// save_mesh["fields"]["radial"]["values"].set(new_field2); - -// // we have modified braid such that it only uses half of the points in the coordset - -// const std::string basename = "silo_unstructured_points_braid"; -// const std::string filename = basename + ".cycle_000100.root"; - -// // remove existing root file, directory and any output files -// remove_path_if_exists(filename); - -// io::silo::save_mesh(save_mesh, basename); -// io::silo::load_mesh(filename, load_mesh); -// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - -// // now we must remove the unused points and change to an implicit points topo so that the diff passes -// save_mesh["coordsets"]["coords"]["values"]["x"].reset(); -// save_mesh["coordsets"]["coords"]["values"]["x"].set(new_xcoords); -// save_mesh["coordsets"]["coords"]["values"]["y"].reset(); -// save_mesh["coordsets"]["coords"]["values"]["y"].set(new_ycoords); -// save_mesh["coordsets"]["coords"]["values"]["z"].reset(); -// save_mesh["coordsets"]["coords"]["values"]["z"].set(new_zcoords); - -// save_mesh["topologies"].remove_child("mesh"); -// save_mesh["topologies"]["mesh"]["type"] = "points"; -// save_mesh["topologies"]["mesh"]["coordset"] = "coords"; - -// // the association doesn't matter for point meshes -// // we choose vertex by convention -// save_mesh["fields"]["radial"]["association"].reset(); -// save_mesh["fields"]["radial"]["association"] = "vertex"; - -// silo_name_changer("mesh", save_mesh); - -// // the loaded mesh will be in the multidomain format -// // but the saved mesh is in the single domain format -// EXPECT_EQ(load_mesh.number_of_children(), 1); -// EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); - -// EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); -// } - -// //----------------------------------------------------------------------------- - -// // -// // save option tests -// // - -// // save options: -// /// opts: -// /// -// /// file_style: "default", "root_only", "multi_file", "overlink" -// /// when # of domains == 1, "default" ==> "root_only" -// /// else, "default" ==> "multi_file" -// /// -// /// silo_type: "default", "pdb", "hdf5", "unknown" -// /// when the file we are writing to exists, "default" ==> "unknown" -// /// else, "default" ==> "hdf5" -// /// note: these are additional silo_type options that we could add -// /// support for in the future: -// /// "hdf5_sec2", "hdf5_stdio", "hdf5_mpio", "hdf5_mpiposix", "taurus" -// /// -// /// suffix: "default", "cycle", "none" -// /// when cycle is present, "default" ==> "cycle" -// /// else, "default" ==> "none" -// /// -// /// root_file_ext: "default", "root", "silo" -// /// "default" ==> "root" -// /// if overlink, this parameter is unused. -// /// -// /// mesh_name: (used if present, default ==> "mesh") -// /// -// /// ovl_topo_name: (used if present, default ==> "") -// /// -// /// number_of_files: {# of files} -// /// when "multi_file" or "overlink": -// /// <= 0, use # of files == # of domains -// /// > 0, # of files == number_of_files - -// //----------------------------------------------------------------------------- -// TEST(conduit_relay_io_silo, round_trip_save_option_file_style) -// { -// // we will do overlink tests separately -// const std::vector file_styles = {"default", "root_only", "multi_file"}; -// for (int i = 0; i < file_styles.size(); i ++) -// { -// Node opts; -// opts["file_style"] = file_styles[i]; - -// const std::string basename = "silo_save_option_file_style_" + file_styles[i] + "_spiral"; -// const std::string filename = basename + ".cycle_000000.root"; - -// for (int ndomains = 1; ndomains < 5; ndomains += 3) -// { -// Node save_mesh, load_mesh, info; -// blueprint::mesh::examples::spiral(ndomains, save_mesh); -// remove_path_if_exists(filename); -// io::silo::save_mesh(save_mesh, basename, opts); -// io::silo::load_mesh(filename, load_mesh); -// EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); - -// // make changes to save mesh so the diff will pass -// for (index_t child = 0; child < save_mesh.number_of_children(); child ++) -// { -// silo_name_changer("mesh", save_mesh[child]); -// } - -// EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); -// NodeConstIterator l_itr = load_mesh.children(); -// NodeConstIterator s_itr = save_mesh.children(); -// while (l_itr.has_next()) -// { -// const Node &l_curr = l_itr.next(); -// const Node &s_curr = s_itr.next(); - -// EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); -// } -// } -// } -// } - -// //----------------------------------------------------------------------------- -// TEST(conduit_relay_io_silo, round_trip_save_option_number_of_files) -// { -// const std::vector number_of_files = {-1, 2}; -// for (int i = 0; i < number_of_files.size(); i ++) -// { -// Node opts; -// opts["file_style"] = "multi_file"; -// opts["number_of_files"] = number_of_files[i]; - -// const std::string basename = "silo_save_option_number_of_files_" + -// std::to_string(number_of_files[i]) + -// "_spiral"; -// const std::string filename = basename + ".cycle_000000.root"; - -// const int ndomains = 5; - -// Node save_mesh, load_mesh, info; -// blueprint::mesh::examples::spiral(ndomains, save_mesh); - -// remove_path_if_exists(filename); -// io::silo::save_mesh(save_mesh, basename, opts); -// io::silo::load_mesh(filename, load_mesh); -// EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); - -// // make changes to save mesh so the diff will pass -// for (index_t child = 0; child < save_mesh.number_of_children(); child ++) -// { -// silo_name_changer("mesh", save_mesh[child]); -// } - -// EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); -// NodeConstIterator l_itr = load_mesh.children(); -// NodeConstIterator s_itr = save_mesh.children(); -// while (l_itr.has_next()) -// { -// const Node &l_curr = l_itr.next(); -// const Node &s_curr = s_itr.next(); - -// EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); -// } -// } -// } - -// //----------------------------------------------------------------------------- -// TEST(conduit_relay_io_silo, round_trip_save_option_suffix) -// { -// const std::vector suffixes = {"default", "default", "cycle", "none"}; -// const std::vector file_suffixes = { -// "", // cycle is not present -// ".cycle_000005", // cycle is present -// ".cycle_000005", // cycle is turned on -// "", // cycle is turned off -// }; -// const std::vector include_cycle = {"no", "yes", "yes", "yes"}; -// for (int i = 0; i < suffixes.size(); i ++) -// { -// Node opts; -// opts["suffix"] = suffixes[i]; - -// const std::string basename = "silo_save_option_suffix_" + suffixes[i] + -// "_" + include_cycle[i] + "_basic"; -// const std::string filename = basename + file_suffixes[i] + ".root"; - -// Node save_mesh, load_mesh, info; -// blueprint::mesh::examples::basic("rectilinear", 3, 4, 0, save_mesh); - -// if (include_cycle[i] == "yes") -// { -// save_mesh["state/cycle"] = 5; -// } - -// remove_path_if_exists(filename); -// io::silo::save_mesh(save_mesh, basename, opts); -// io::silo::load_mesh(filename, load_mesh); -// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - -// silo_name_changer("mesh", save_mesh); - -// // the loaded mesh will be in the multidomain format -// // but the saved mesh is in the single domain format -// EXPECT_EQ(load_mesh.number_of_children(), 1); -// EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); - -// EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); -// } -// } - -// //----------------------------------------------------------------------------- -// TEST(conduit_relay_io_silo, round_trip_save_option_root_file_ext) -// { -// const std::vector root_file_exts = {"default", "root", "silo"}; - -// for (int i = 0; i < root_file_exts.size(); i ++) -// { -// Node opts; -// opts["root_file_ext"] = root_file_exts[i]; - -// std::string actual_file_ext = root_file_exts[i]; -// if (actual_file_ext == "default") -// { -// actual_file_ext = "root"; -// } - -// const std::string basename = "round_trip_save_option_root_file_ext_" + -// root_file_exts[i] + "_basic"; -// const std::string filename = basename + "." + actual_file_ext; - -// Node save_mesh, load_mesh, info; -// blueprint::mesh::examples::basic("rectilinear", 3, 4, 0, save_mesh); -// remove_path_if_exists(filename); -// io::silo::save_mesh(save_mesh, basename, opts); -// io::silo::load_mesh(filename, load_mesh); -// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - -// silo_name_changer("mesh", save_mesh); - -// // the loaded mesh will be in the multidomain format -// // but the saved mesh is in the single domain format -// EXPECT_EQ(load_mesh.number_of_children(), 1); -// EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); -// EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); -// } -// } - -// //----------------------------------------------------------------------------- -// TEST(conduit_relay_io_silo, round_trip_save_option_mesh_name) -// { -// const std::string basename = "silo_save_option_mesh_name_basic"; -// const std::string filename = basename + ".root"; - -// Node opts; -// opts["mesh_name"] = "mymesh"; - -// Node save_mesh, load_mesh, info; -// blueprint::mesh::examples::basic("rectilinear", 3, 4, 0, save_mesh); -// remove_path_if_exists(filename); -// io::silo::save_mesh(save_mesh, basename, opts); -// io::silo::load_mesh(filename, load_mesh); -// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - -// silo_name_changer("mymesh", save_mesh); - -// // the loaded mesh will be in the multidomain format -// // but the saved mesh is in the single domain format -// EXPECT_EQ(load_mesh.number_of_children(), 1); -// EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); -// EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); -// } - -// //----------------------------------------------------------------------------- -// TEST(conduit_relay_io_silo, round_trip_save_option_silo_type) -// { -// const std::vector silo_types = {"default", "pdb", "hdf5", "unknown"}; -// for (int i = 3; i < silo_types.size(); i ++) -// { -// Node opts; -// opts["silo_type"] = silo_types[i]; - -// Node save_mesh, load_mesh, info; -// blueprint::mesh::examples::basic("rectilinear", 3, 4, 0, save_mesh); - -// const std::string basename = "silo_save_option_silo_type_" + silo_types[i] + "_basic"; -// const std::string filename = basename + ".root"; - -// remove_path_if_exists(filename); -// io::silo::save_mesh(save_mesh, basename, opts); -// io::silo::load_mesh(filename, load_mesh); -// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + // cheat a little bit - we don't have these to start + n_matset["sizes"].set_external(load_mesh[child]["matsets"]["mesh_matset"]["sizes"]); + n_matset["offsets"].set_external(load_mesh[child]["matsets"]["mesh_matset"]["offsets"]); + } + + silo_name_changer("mesh", save_mesh[child]); + } + save_mesh[2].remove_child("matsets"); + + EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); + NodeConstIterator l_itr = load_mesh.children(); + NodeConstIterator s_itr = save_mesh.children(); + while (l_itr.has_next()) + { + const Node &l_curr = l_itr.next(); + const Node &s_curr = s_itr.next(); + + EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); + } +} + +//----------------------------------------------------------------------------- +// mesh is not defined on a domain +// +// This case is much less interesting. +// data passes through the clean mesh filter which +// deletes domains that are missing topos. +// They simply are not part of the mesh and so silo +// doesn't have to deal with it. +TEST(conduit_relay_io_silo, missing_domain_mesh_trivial) +{ + Node save_mesh, load_mesh, info; + const int ndomains = 4; + blueprint::mesh::examples::spiral(ndomains, save_mesh); + + // remove information for a particular domain + save_mesh[2]["topologies"].remove_child("topo"); + + const std::string basename = "silo_missing_domain_mesh_trivial_spiral"; + const std::string filename = basename + ".cycle_000000.root"; + + remove_path_if_exists(filename); + io::silo::save_mesh(save_mesh, basename); + io::silo::load_mesh(filename, load_mesh); + + EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); + + // make changes to save mesh so the diff will pass + save_mesh.remove(2); + save_mesh.rename_child("domain_000003", "domain_000002"); + save_mesh[2]["state"]["domain_id"].reset(); + save_mesh[2]["state"]["domain_id"] = 2; + for (index_t child = 0; child < save_mesh.number_of_children(); child ++) + { + silo_name_changer("mesh", save_mesh[child]); + } + + EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); + NodeConstIterator l_itr = load_mesh.children(); + NodeConstIterator s_itr = save_mesh.children(); + while (l_itr.has_next()) + { + const Node &l_curr = l_itr.next(); + const Node &s_curr = s_itr.next(); + + EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); + } +} + +//----------------------------------------------------------------------------- +// mesh is not defined on a domain but there are multiple meshes +TEST(conduit_relay_io_silo, missing_domain_mesh) +{ + Node save_mesh, save_mesh2, load_mesh, load_mesh2, info, opts; + const int ndomains = 4; + blueprint::mesh::examples::spiral(ndomains, save_mesh); + blueprint::mesh::examples::spiral(ndomains, save_mesh2); + + for (index_t child = 0; child < save_mesh.number_of_children(); child ++) + { + save_mesh[child]["coordsets"].rename_child("coords", "coords2"); + save_mesh[child]["topologies"]["topo"]["coordset"].reset(); + save_mesh[child]["topologies"]["topo"]["coordset"] = "coords2"; + save_mesh[child]["topologies"].rename_child("topo", "topo2"); + save_mesh[child]["fields"]["dist"]["topology"].reset(); + save_mesh[child]["fields"]["dist"]["topology"] = "topo2"; + save_mesh[child]["fields"].rename_child("dist", "dist2"); + + save_mesh[child]["coordsets"]["coords"].set_external(save_mesh2[child]["coordsets"]["coords"]); + save_mesh[child]["topologies"]["topo"].set_external(save_mesh2[child]["topologies"]["topo"]); + save_mesh[child]["fields"]["dist"].set_external(save_mesh2[child]["fields"]["dist"]); + } + + // remove information for a particular domain + save_mesh[2]["topologies"].remove_child("topo"); + + const std::string basename = "silo_missing_domain_mesh_spiral"; + const std::string filename = basename + ".cycle_000000.root"; + + remove_path_if_exists(filename); + io::silo::save_mesh(save_mesh, basename); + opts["silo_names"]["multimesh_names"] = "mesh_topo2"; + io::silo::load_mesh(filename, opts, load_mesh); + opts["silo_names"]["multimesh_names"] = "mesh_topo"; + io::silo::load_mesh(filename, opts, load_mesh2); + EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + EXPECT_TRUE(blueprint::mesh::verify(load_mesh2, info)); + + // make changes to save mesh so the diff will pass + save_mesh[2]["coordsets"].remove_child("coords"); + save_mesh[2]["fields"].remove_child("dist"); + for (index_t child = 0; child < save_mesh.number_of_children(); child ++) + { + silo_name_changer("mesh", save_mesh[child]); + } + + // we must merge the two meshes in load mesh + // the indexing is tricky because one is missing a domain + load_mesh[0]["coordsets"]["mesh_topo"].set_external(load_mesh2[0]["coordsets"]["mesh_topo"]); + load_mesh[0]["topologies"]["mesh_topo"].set_external(load_mesh2[0]["topologies"]["mesh_topo"]); + load_mesh[0]["fields"]["mesh_dist"].set_external(load_mesh2[0]["fields"]["mesh_dist"]); + load_mesh[1]["coordsets"]["mesh_topo"].set_external(load_mesh2[1]["coordsets"]["mesh_topo"]); + load_mesh[1]["topologies"]["mesh_topo"].set_external(load_mesh2[1]["topologies"]["mesh_topo"]); + load_mesh[1]["fields"]["mesh_dist"].set_external(load_mesh2[1]["fields"]["mesh_dist"]); + load_mesh[3]["coordsets"]["mesh_topo"].set_external(load_mesh2[2]["coordsets"]["mesh_topo"]); + load_mesh[3]["topologies"]["mesh_topo"].set_external(load_mesh2[2]["topologies"]["mesh_topo"]); + load_mesh[3]["fields"]["mesh_dist"].set_external(load_mesh2[2]["fields"]["mesh_dist"]); + + EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); + NodeConstIterator l_itr = load_mesh.children(); + NodeConstIterator s_itr = save_mesh.children(); + while (l_itr.has_next()) + { + const Node &l_curr = l_itr.next(); + const Node &s_curr = s_itr.next(); + + EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); + } +} + +//----------------------------------------------------------------------------- +// explicit points (unstructured mesh) do not use every coord +TEST(conduit_relay_io_silo, unstructured_points) +{ + Node save_mesh, load_mesh, info; + blueprint::mesh::examples::braid("points", 2, 2, 2, save_mesh); + + std::vector new_conn; + std::vector new_field1; + std::vector new_field2; + std::vector new_xcoords, new_ycoords, new_zcoords; + + int_accessor conn = save_mesh["topologies"]["mesh"]["elements"]["connectivity"].value(); + + float_accessor field1 = save_mesh["fields"]["braid"]["values"].value(); + float_accessor field2 = save_mesh["fields"]["radial"]["values"].value(); + + float_accessor xcoords = save_mesh["coordsets"]["coords"]["values"]["x"].value(); + float_accessor ycoords = save_mesh["coordsets"]["coords"]["values"]["y"].value(); + float_accessor zcoords = save_mesh["coordsets"]["coords"]["values"]["z"].value(); + + for (int i = 1; i < conn.number_of_elements(); i += 2) + { + new_conn.push_back(conn[i]); + new_field1.push_back(field1[i]); + new_field2.push_back(field2[i]); + + new_xcoords.push_back(xcoords[conn[i]]); + new_ycoords.push_back(ycoords[conn[i]]); + new_zcoords.push_back(zcoords[conn[i]]); + } + save_mesh["topologies"]["mesh"]["elements"]["connectivity"].reset(); + save_mesh["topologies"]["mesh"]["elements"]["connectivity"].set(new_conn); + + save_mesh["fields"].remove_child("vel"); + save_mesh["fields"]["braid"]["values"].reset(); + save_mesh["fields"]["braid"]["values"].set(new_field1); + save_mesh["fields"]["radial"]["values"].reset(); + save_mesh["fields"]["radial"]["values"].set(new_field2); + + // we have modified braid such that it only uses half of the points in the coordset + + const std::string basename = "silo_unstructured_points_braid"; + const std::string filename = basename + ".cycle_000100.root"; + + // remove existing root file, directory and any output files + remove_path_if_exists(filename); + + io::silo::save_mesh(save_mesh, basename); + io::silo::load_mesh(filename, load_mesh); + EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + + // now we must remove the unused points and change to an implicit points topo so that the diff passes + save_mesh["coordsets"]["coords"]["values"]["x"].reset(); + save_mesh["coordsets"]["coords"]["values"]["x"].set(new_xcoords); + save_mesh["coordsets"]["coords"]["values"]["y"].reset(); + save_mesh["coordsets"]["coords"]["values"]["y"].set(new_ycoords); + save_mesh["coordsets"]["coords"]["values"]["z"].reset(); + save_mesh["coordsets"]["coords"]["values"]["z"].set(new_zcoords); + + save_mesh["topologies"].remove_child("mesh"); + save_mesh["topologies"]["mesh"]["type"] = "points"; + save_mesh["topologies"]["mesh"]["coordset"] = "coords"; + + // the association doesn't matter for point meshes + // we choose vertex by convention + save_mesh["fields"]["radial"]["association"].reset(); + save_mesh["fields"]["radial"]["association"] = "vertex"; + + silo_name_changer("mesh", save_mesh); + + // the loaded mesh will be in the multidomain format + // but the saved mesh is in the single domain format + EXPECT_EQ(load_mesh.number_of_children(), 1); + EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); + + EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); +} + +//----------------------------------------------------------------------------- + +// +// save option tests +// + +// save options: +/// opts: +/// +/// file_style: "default", "root_only", "multi_file", "overlink" +/// when # of domains == 1, "default" ==> "root_only" +/// else, "default" ==> "multi_file" +/// +/// silo_type: "default", "pdb", "hdf5", "unknown" +/// when the file we are writing to exists, "default" ==> "unknown" +/// else, "default" ==> "hdf5" +/// note: these are additional silo_type options that we could add +/// support for in the future: +/// "hdf5_sec2", "hdf5_stdio", "hdf5_mpio", "hdf5_mpiposix", "taurus" +/// +/// suffix: "default", "cycle", "none" +/// when cycle is present, "default" ==> "cycle" +/// else, "default" ==> "none" +/// +/// root_file_ext: "default", "root", "silo" +/// "default" ==> "root" +/// if overlink, this parameter is unused. +/// +/// mesh_name: (used if present, default ==> "mesh") +/// +/// ovl_topo_name: (used if present, default ==> "") +/// +/// number_of_files: {# of files} +/// when "multi_file" or "overlink": +/// <= 0, use # of files == # of domains +/// > 0, # of files == number_of_files + +//----------------------------------------------------------------------------- +TEST(conduit_relay_io_silo, round_trip_save_option_file_style) +{ + // we will do overlink tests separately + const std::vector file_styles = {"default", "root_only", "multi_file"}; + for (int i = 0; i < file_styles.size(); i ++) + { + Node opts; + opts["file_style"] = file_styles[i]; + + const std::string basename = "silo_save_option_file_style_" + file_styles[i] + "_spiral"; + const std::string filename = basename + ".cycle_000000.root"; + + for (int ndomains = 1; ndomains < 5; ndomains += 3) + { + Node save_mesh, load_mesh, info; + blueprint::mesh::examples::spiral(ndomains, save_mesh); + remove_path_if_exists(filename); + io::silo::save_mesh(save_mesh, basename, opts); + io::silo::load_mesh(filename, load_mesh); + EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); + + // make changes to save mesh so the diff will pass + for (index_t child = 0; child < save_mesh.number_of_children(); child ++) + { + silo_name_changer("mesh", save_mesh[child]); + } + + EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); + NodeConstIterator l_itr = load_mesh.children(); + NodeConstIterator s_itr = save_mesh.children(); + while (l_itr.has_next()) + { + const Node &l_curr = l_itr.next(); + const Node &s_curr = s_itr.next(); + + EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); + } + } + } +} + +//----------------------------------------------------------------------------- +TEST(conduit_relay_io_silo, round_trip_save_option_number_of_files) +{ + const std::vector number_of_files = {-1, 2}; + for (int i = 0; i < number_of_files.size(); i ++) + { + Node opts; + opts["file_style"] = "multi_file"; + opts["number_of_files"] = number_of_files[i]; + + const std::string basename = "silo_save_option_number_of_files_" + + std::to_string(number_of_files[i]) + + "_spiral"; + const std::string filename = basename + ".cycle_000000.root"; + + const int ndomains = 5; + + Node save_mesh, load_mesh, info; + blueprint::mesh::examples::spiral(ndomains, save_mesh); + + remove_path_if_exists(filename); + io::silo::save_mesh(save_mesh, basename, opts); + io::silo::load_mesh(filename, load_mesh); + EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); + + // make changes to save mesh so the diff will pass + for (index_t child = 0; child < save_mesh.number_of_children(); child ++) + { + silo_name_changer("mesh", save_mesh[child]); + } + + EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); + NodeConstIterator l_itr = load_mesh.children(); + NodeConstIterator s_itr = save_mesh.children(); + while (l_itr.has_next()) + { + const Node &l_curr = l_itr.next(); + const Node &s_curr = s_itr.next(); + + EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); + } + } +} + +//----------------------------------------------------------------------------- +TEST(conduit_relay_io_silo, round_trip_save_option_suffix) +{ + const std::vector suffixes = {"default", "default", "cycle", "none"}; + const std::vector file_suffixes = { + "", // cycle is not present + ".cycle_000005", // cycle is present + ".cycle_000005", // cycle is turned on + "", // cycle is turned off + }; + const std::vector include_cycle = {"no", "yes", "yes", "yes"}; + for (int i = 0; i < suffixes.size(); i ++) + { + Node opts; + opts["suffix"] = suffixes[i]; + + const std::string basename = "silo_save_option_suffix_" + suffixes[i] + + "_" + include_cycle[i] + "_basic"; + const std::string filename = basename + file_suffixes[i] + ".root"; + + Node save_mesh, load_mesh, info; + blueprint::mesh::examples::basic("rectilinear", 3, 4, 0, save_mesh); + + if (include_cycle[i] == "yes") + { + save_mesh["state/cycle"] = 5; + } + + remove_path_if_exists(filename); + io::silo::save_mesh(save_mesh, basename, opts); + io::silo::load_mesh(filename, load_mesh); + EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + + silo_name_changer("mesh", save_mesh); + + // the loaded mesh will be in the multidomain format + // but the saved mesh is in the single domain format + EXPECT_EQ(load_mesh.number_of_children(), 1); + EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); + + EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); + } +} + +//----------------------------------------------------------------------------- +TEST(conduit_relay_io_silo, round_trip_save_option_root_file_ext) +{ + const std::vector root_file_exts = {"default", "root", "silo"}; + + for (int i = 0; i < root_file_exts.size(); i ++) + { + Node opts; + opts["root_file_ext"] = root_file_exts[i]; + + std::string actual_file_ext = root_file_exts[i]; + if (actual_file_ext == "default") + { + actual_file_ext = "root"; + } + + const std::string basename = "round_trip_save_option_root_file_ext_" + + root_file_exts[i] + "_basic"; + const std::string filename = basename + "." + actual_file_ext; + + Node save_mesh, load_mesh, info; + blueprint::mesh::examples::basic("rectilinear", 3, 4, 0, save_mesh); + remove_path_if_exists(filename); + io::silo::save_mesh(save_mesh, basename, opts); + io::silo::load_mesh(filename, load_mesh); + EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + + silo_name_changer("mesh", save_mesh); + + // the loaded mesh will be in the multidomain format + // but the saved mesh is in the single domain format + EXPECT_EQ(load_mesh.number_of_children(), 1); + EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); + EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); + } +} + +//----------------------------------------------------------------------------- +TEST(conduit_relay_io_silo, round_trip_save_option_mesh_name) +{ + const std::string basename = "silo_save_option_mesh_name_basic"; + const std::string filename = basename + ".root"; + + Node opts; + opts["mesh_name"] = "mymesh"; + + Node save_mesh, load_mesh, info; + blueprint::mesh::examples::basic("rectilinear", 3, 4, 0, save_mesh); + remove_path_if_exists(filename); + io::silo::save_mesh(save_mesh, basename, opts); + io::silo::load_mesh(filename, load_mesh); + EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + + silo_name_changer("mymesh", save_mesh); + + // the loaded mesh will be in the multidomain format + // but the saved mesh is in the single domain format + EXPECT_EQ(load_mesh.number_of_children(), 1); + EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); + EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); +} + +//----------------------------------------------------------------------------- +TEST(conduit_relay_io_silo, round_trip_save_option_silo_type) +{ + const std::vector silo_types = {"default", "pdb", "hdf5", "unknown"}; + for (int i = 3; i < silo_types.size(); i ++) + { + Node opts; + opts["silo_type"] = silo_types[i]; + + Node save_mesh, load_mesh, info; + blueprint::mesh::examples::basic("rectilinear", 3, 4, 0, save_mesh); + + const std::string basename = "silo_save_option_silo_type_" + silo_types[i] + "_basic"; + const std::string filename = basename + ".root"; + + remove_path_if_exists(filename); + io::silo::save_mesh(save_mesh, basename, opts); + io::silo::load_mesh(filename, load_mesh); + EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); -// silo_name_changer("mesh", save_mesh); - -// // the loaded mesh will be in the multidomain format -// // but the saved mesh is in the single domain format -// EXPECT_EQ(load_mesh.number_of_children(), 1); -// EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); - -// EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); -// } -// } - -// //----------------------------------------------------------------------------- -// TEST(conduit_relay_io_silo, round_trip_save_option_overlink1) -// { -// const std::vector ovl_topo_names = {"", "topo"}; -// for (int i = 0; i < ovl_topo_names.size(); i ++) -// { -// std::string basename; -// if (ovl_topo_names[i].empty()) -// { -// basename = "silo_save_option_overlink_spiral"; -// } -// else -// { -// basename = "silo_save_option_overlink_spiral_" + ovl_topo_names[i]; -// } -// const std::string filename = basename + "/OvlTop.silo"; - -// Node opts; -// opts["file_style"] = "overlink"; -// opts["ovl_topo_name"] = ovl_topo_names[i]; - -// int ndomains = 2; - -// Node save_mesh, load_mesh, info; -// blueprint::mesh::examples::spiral(ndomains, save_mesh); -// add_matset_to_spiral(save_mesh, ndomains); -// remove_path_if_exists(filename); -// io::silo::save_mesh(save_mesh, basename, opts); -// io::silo::load_mesh(filename, load_mesh); -// EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); - -// // make changes to save mesh so the diff will pass -// for (index_t child = 0; child < save_mesh.number_of_children(); child ++) -// { -// // get the matset for this domain -// Node &n_matset = save_mesh[child]["matsets"]["matset"]; - -// // clean up volume fractions -// Node vf_arr; -// n_matset["volume_fractions"].to_float64_array(vf_arr); -// n_matset["volume_fractions"].reset(); -// n_matset["volume_fractions"].set(vf_arr); + silo_name_changer("mesh", save_mesh); + + // the loaded mesh will be in the multidomain format + // but the saved mesh is in the single domain format + EXPECT_EQ(load_mesh.number_of_children(), 1); + EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); + + EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); + } +} + +//----------------------------------------------------------------------------- +TEST(conduit_relay_io_silo, round_trip_save_option_overlink1) +{ + const std::vector ovl_topo_names = {"", "topo"}; + for (int i = 0; i < ovl_topo_names.size(); i ++) + { + std::string basename; + if (ovl_topo_names[i].empty()) + { + basename = "silo_save_option_overlink_spiral"; + } + else + { + basename = "silo_save_option_overlink_spiral_" + ovl_topo_names[i]; + } + const std::string filename = basename + "/OvlTop.silo"; + + Node opts; + opts["file_style"] = "overlink"; + opts["ovl_topo_name"] = ovl_topo_names[i]; + + int ndomains = 2; + + Node save_mesh, load_mesh, info; + blueprint::mesh::examples::spiral(ndomains, save_mesh); + add_matset_to_spiral(save_mesh, ndomains); + remove_path_if_exists(filename); + io::silo::save_mesh(save_mesh, basename, opts); + io::silo::load_mesh(filename, load_mesh); + EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); + + // make changes to save mesh so the diff will pass + for (index_t child = 0; child < save_mesh.number_of_children(); child ++) + { + // get the matset for this domain + Node &n_matset = save_mesh[child]["matsets"]["matset"]; + + // clean up volume fractions + Node vf_arr; + n_matset["volume_fractions"].to_float64_array(vf_arr); + n_matset["volume_fractions"].reset(); + n_matset["volume_fractions"].set(vf_arr); -// // cheat a little bit - we don't have these to start -// n_matset["sizes"].set_external(load_mesh[child]["matsets"]["MMATERIAL"]["sizes"]); -// n_matset["offsets"].set_external(load_mesh[child]["matsets"]["MMATERIAL"]["offsets"]); - -// overlink_name_changer(save_mesh[child]); -// } - -// EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); -// NodeConstIterator l_itr = load_mesh.children(); -// NodeConstIterator s_itr = save_mesh.children(); -// while (l_itr.has_next()) -// { -// const Node &l_curr = l_itr.next(); -// const Node &s_curr = s_itr.next(); - -// EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); -// } -// } -// } - -// //----------------------------------------------------------------------------- -// // this tests var attributes and padding dimensions -// TEST(conduit_relay_io_silo, round_trip_save_option_overlink2) -// { -// const std::string basename = "silo_save_option_overlink_basic"; -// const std::string filename = basename + "/OvlTop.silo"; - -// Node write_opts, read_opts; -// write_opts["file_style"] = "overlink"; -// read_opts["matset_style"] = "multi_buffer_full"; - -// Node save_mesh, load_mesh, info; -// blueprint::mesh::examples::basic("structured", 3, 3, 1, save_mesh); - -// // add another field that is volume dependent -// Node &field2 = save_mesh["fields"]["field2"]; -// field2["association"] = "element"; -// field2["topology"] = "mesh"; -// field2["volume_dependent"] = "true"; -// field2["values"].set_external(save_mesh["fields"]["field"]["values"]); - -// // add a matset to make overlink happy -// add_multi_buffer_full_matset(save_mesh, 4, "mesh"); - -// remove_path_if_exists(filename); -// io::silo::save_mesh(save_mesh, basename, write_opts); -// io::silo::load_mesh(filename, read_opts, load_mesh); -// EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); - -// // make changes to save mesh so the diff will pass -// overlink_name_changer(save_mesh); - -// // the loaded mesh will be in the multidomain format -// // but the saved mesh is in the single domain format -// EXPECT_EQ(load_mesh.number_of_children(), 1); -// EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); - -// EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); - -// // open silo files and do some checks - -// DBfile *rootfile = DBOpen(filename.c_str(), DB_UNKNOWN, DB_READ); -// EXPECT_TRUE(DBInqVarExists(rootfile, "VAR_ATTRIBUTES")); -// EXPECT_TRUE(DBInqVarType(rootfile, "VAR_ATTRIBUTES") == DB_ARRAY); - -// DBcompoundarray *var_attr = DBGetCompoundarray(rootfile, "VAR_ATTRIBUTES"); - -// // fetch pointers to elements inside the compound array -// char **elemnames = var_attr->elemnames; -// int *elemlengths = var_attr->elemlengths; -// int nelems = var_attr->nelems; -// int *values = static_cast(var_attr->values); -// int nvalues = var_attr->nvalues; -// int datatype = var_attr->datatype; - -// EXPECT_EQ(std::string(elemnames[0]), "field"); -// EXPECT_EQ(std::string(elemnames[1]), "field2"); -// EXPECT_EQ(elemlengths[0], 5); -// EXPECT_EQ(elemlengths[1], 5); -// EXPECT_EQ(nelems, 2); -// // for first var -// EXPECT_EQ(values[0], 1); -// EXPECT_EQ(values[1], 0); -// EXPECT_EQ(values[2], 1); -// EXPECT_EQ(values[3], 0); -// EXPECT_EQ(values[4], 1); -// // for second var -// EXPECT_EQ(values[5], 1); -// EXPECT_EQ(values[6], 1); -// EXPECT_EQ(values[7], 1); -// EXPECT_EQ(values[8], 0); -// EXPECT_EQ(values[9], 1); -// EXPECT_EQ(nvalues, 10); -// EXPECT_EQ(datatype, DB_INT); - -// DBFreeCompoundarray(var_attr); - -// EXPECT_TRUE(DBInqVarExists(rootfile, "PAD_DIMS")); -// EXPECT_TRUE(DBInqVarType(rootfile, "PAD_DIMS") == DB_ARRAY); - -// DBcompoundarray *pad_dims = DBGetCompoundarray(rootfile, "PAD_DIMS"); - -// // fetch pointers to elements inside the compound array -// elemnames = pad_dims->elemnames; -// elemlengths = pad_dims->elemlengths; -// nelems = pad_dims->nelems; -// values = static_cast(pad_dims->values); -// nvalues = pad_dims->nvalues; -// datatype = pad_dims->datatype; - -// EXPECT_EQ(std::string(elemnames[0]), "paddims"); -// EXPECT_EQ(elemlengths[0], 6); -// EXPECT_EQ(nelems, 1); -// EXPECT_EQ(values[0], 0); -// EXPECT_EQ(values[1], 0); -// EXPECT_EQ(values[2], 0); -// EXPECT_EQ(values[3], 0); -// EXPECT_EQ(values[4], 0); -// EXPECT_EQ(values[5], 0); -// EXPECT_EQ(nvalues, 6); -// EXPECT_EQ(datatype, DB_INT); - -// DBFreeCompoundarray(pad_dims); - -// DBClose(rootfile); - -// // now check domain file - -// const std::string dom_filename = basename + "/domain0.silo"; -// DBfile *domfile = DBOpen(dom_filename.c_str(), DB_UNKNOWN, DB_READ); - -// EXPECT_TRUE(DBInqVarExists(domfile, "DOMAIN_NEIGHBOR_NUMS")); -// EXPECT_TRUE(DBInqVarType(domfile, "DOMAIN_NEIGHBOR_NUMS") == DB_ARRAY); - -// DBcompoundarray *dom_neighbor_nums = DBGetCompoundarray(domfile, "DOMAIN_NEIGHBOR_NUMS"); - -// // fetch pointers to elements inside the compound array -// elemnames = dom_neighbor_nums->elemnames; -// elemlengths = dom_neighbor_nums->elemlengths; -// nelems = dom_neighbor_nums->nelems; -// values = static_cast(dom_neighbor_nums->values); -// nvalues = dom_neighbor_nums->nvalues; -// datatype = dom_neighbor_nums->datatype; - -// EXPECT_EQ(std::string(elemnames[0]), "num_neighbors"); -// EXPECT_EQ(std::string(elemnames[1]), "neighbor_nums"); -// EXPECT_EQ(elemlengths[0], 1); -// EXPECT_EQ(elemlengths[1], 0); -// EXPECT_EQ(nelems, 2); -// EXPECT_EQ(values[0], 0); -// EXPECT_EQ(nvalues, 1); -// EXPECT_EQ(datatype, DB_INT); - -// DBFreeCompoundarray(dom_neighbor_nums); - -// DBClose(domfile); -// } - -// //----------------------------------------------------------------------------- -// // this tests material i/o -// TEST(conduit_relay_io_silo, round_trip_save_option_overlink3) -// { -// Node save_mesh, load_mesh, info; -// const int nx = 100, ny = 100; -// const double radius = 0.25; -// blueprint::mesh::examples::venn("sparse_by_element", nx, ny, radius, save_mesh); - -// const std::string basename = "silo_save_option_overlink_venn"; -// const std::string filename = basename + "/OvlTop.silo"; - -// Node opts; -// opts["file_style"] = "overlink"; - -// remove_path_if_exists(filename); -// io::silo::save_mesh(save_mesh, basename, opts); -// io::silo::load_mesh(filename, load_mesh); -// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - -// // make changes to save mesh so the diff will pass - -// // The field mat_check has values that are one type and matset_values -// // that are another type. The silo writer converts both to double arrays -// // in this case, so we follow suit. -// Node mat_check_new_values, mat_check_new_matset_values; -// save_mesh["fields"]["mat_check"]["values"].to_double_array(mat_check_new_values); -// save_mesh["fields"]["mat_check"]["matset_values"].to_double_array(mat_check_new_matset_values); -// save_mesh["fields"]["mat_check"]["values"].set_external(mat_check_new_values); -// save_mesh["fields"]["mat_check"]["matset_values"].set_external(mat_check_new_matset_values); - -// overlink_name_changer(save_mesh); - -// // the loaded mesh will be in the multidomain format -// // but the saved mesh is in the single domain format -// EXPECT_EQ(load_mesh.number_of_children(), 1); -// EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); - -// EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); -// } - -// //----------------------------------------------------------------------------- -// // we are testing vector fields get converted to scalars -// TEST(conduit_relay_io_silo, round_trip_save_option_overlink4) -// { -// const std::vector> mesh_types = { -// std::make_pair("rectilinear", "2"), std::make_pair("rectilinear", "3"), -// std::make_pair("structured", "2"), std::make_pair("structured", "3"), -// std::make_pair("quads", "2"), -// std::make_pair("hexs", "3"), -// }; -// for (int i = 0; i < mesh_types.size(); ++i) -// { -// std::string dim = mesh_types[i].second; -// index_t nx = 3; -// index_t ny = 4; -// index_t nz = (dim == "2" ? 0 : 2); - -// std::string mesh_type = mesh_types[i].first; - -// Node save_mesh, load_mesh, info; -// blueprint::mesh::examples::braid(mesh_type, nx, ny, nz, save_mesh); -// index_t nele_x = nx - 1; -// index_t nele_y = ny - 1; -// index_t nele_z = (dim == "2" ? 0 : nz - 1); - -// // provide a matset for braid -// braid_init_example_matset(nele_x, nele_y, nele_z, save_mesh["matsets"]["matset"]); - -// const std::string basename = "silo_save_option_overlink_braid_" + mesh_type + "_" + dim + "D"; -// const std::string filename = basename + "/OvlTop.silo"; - -// Node write_opts, read_opts; -// write_opts["file_style"] = "overlink"; -// read_opts["matset_style"] = "multi_buffer_full"; - -// // remove existing root file, directory and any output files -// remove_path_if_exists(filename); -// io::silo::save_mesh(save_mesh, basename, write_opts); -// io::silo::load_mesh(filename, read_opts, load_mesh); -// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - -// Node &field_vel = save_mesh["fields"]["vel"]; -// Node &field_vel_u = save_mesh["fields"]["vel_u"]; -// Node &field_vel_v = save_mesh["fields"]["vel_v"]; - -// field_vel_u["topology"].set(field_vel["topology"]); -// field_vel_u["association"].set(field_vel["association"]); -// field_vel_u["values"].set(field_vel["values/u"]); -// field_vel_v["topology"].set(field_vel["topology"]); -// field_vel_v["association"].set(field_vel["association"]); -// field_vel_v["values"].set(field_vel["values/v"]); - -// if (dim == "3") -// { -// Node &field_vel_w = save_mesh["fields"]["vel_w"]; -// field_vel_w["topology"].set(field_vel["topology"]); -// field_vel_w["association"].set(field_vel["association"]); -// field_vel_w["values"].set(field_vel["values/w"]); -// } - -// save_mesh["fields"].remove_child("vel"); - -// // make changes to save mesh so the diff will pass -// overlink_name_changer(save_mesh); - -// // the loaded mesh will be in the multidomain format -// // but the saved mesh is in the single domain format -// EXPECT_EQ(load_mesh.number_of_children(), 1); -// EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); - -// EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); -// } -// } - -// //----------------------------------------------------------------------------- -// // check that all the shape types work (specifically polytopal ones) -// TEST(conduit_relay_io_silo, round_trip_save_option_overlink5) -// { -// const std::vector> mesh_types = { -// std::make_pair("uniform", "2"), std::make_pair("uniform", "3"), -// std::make_pair("rectilinear", "2"), std::make_pair("rectilinear", "3"), -// std::make_pair("structured", "2"), std::make_pair("structured", "3"), -// std::make_pair("quads", "2"), -// std::make_pair("polygons", "2"), -// std::make_pair("hexs", "3"), -// // std::make_pair("polyhedra", "3") -// // Overlink does not support tris, wedges, pyramids, or tets -// }; -// for (int i = 0; i < mesh_types.size(); ++i) -// { -// const std::string dim = mesh_types[i].second; -// index_t nx = 3; -// index_t ny = 4; -// index_t nz = (dim == "2" ? 0 : 2); - -// const std::string mesh_type = mesh_types[i].first; - -// Node save_mesh, load_mesh, info; -// blueprint::mesh::examples::basic(mesh_type, nx, ny, nz, save_mesh); - -// const std::string basename = "silo_save_option_overlink_basic_" + mesh_type + "_" + dim + "D"; -// const std::string filename = basename + "/OvlTop.silo"; -// const std::string domfile = basename + "/domain0.silo"; - -// Node write_opts, read_opts; -// write_opts["file_style"] = "overlink"; -// read_opts["matset_style"] = "multi_buffer_full"; - -// // add a matset to make overlink happy -// int num_elems = (nx - 1) * (ny - 1); -// if (mesh_type == "tets") -// { -// num_elems *= 6; -// } -// add_multi_buffer_full_matset(save_mesh, num_elems, "mesh"); - -// remove_path_if_exists(filename); -// remove_path_if_exists(domfile); -// io::silo::save_mesh(save_mesh, basename, write_opts); -// io::silo::load_mesh(filename, read_opts, load_mesh); -// EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); - -// // make changes to save mesh so the diff will pass -// if (mesh_type == "uniform") -// { -// silo_uniform_to_rect_conversion("coords", "mesh", save_mesh); -// } -// overlink_name_changer(save_mesh); - -// // the loaded mesh will be in the multidomain format -// // but the saved mesh is in the single domain format -// EXPECT_EQ(load_mesh.number_of_children(), 1); -// EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); - -// EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); -// } -// } - -// //----------------------------------------------------------------------------- - -// // -// // read option tests -// // - -// // read options: -// /// opts: -// /// silo_names: -// /// multimesh_names: -// /// "{name1}" - multimeshes with this name will be read if they exist -// /// "{name2}" -// /// ... -// /// or -// /// "{all}" - all multimeshes will be read. -// /// or -// /// "{none}" - no multimeshes will be read. -// /// multivar_names: similar to multimesh_names. -// /// multimat_names: similar to multimesh_names. -// /// multimatspecies_names: similar to multimesh_names. TODO -// /// qmesh_names: similar to multimesh_names. -// /// qvar_names: similar to multimesh_names. -// /// ucdmesh_names: similar to multimesh_names. -// /// ucdvar_names: similar to multimesh_names. -// /// ptmesh_names: similar to multimesh_names. -// /// ptvar_names: similar to multimesh_names. -// /// mat_names: similar to multimesh_names. -// /// matspecies_names: similar to multimesh_names. TODO -// /// By default, everything in the file will be read unless manually turned off. -// /// -// /// matset_style: "default", "multi_buffer_full", "sparse_by_element", -// /// "multi_buffer_by_material" -// /// "default" ==> "sparse_by_element" -// /// -// /// mesh_name: legacy argument. This is interpreted as a multimesh name. -// /// It is added to the list of multimesh names to read, unless the -// /// user has specified "all" or "none", which will supersede this. -// /// TODO does it make sense to remove this? When? - -// //----------------------------------------------------------------------------- -// // TODO this is now a legacy feature. Should I remove eventually? -// TEST(conduit_relay_io_silo, round_trip_read_option_mesh_name) -// { -// Node load_mesh, info, opts; -// const std::string path = utils::join_file_path("silo", "multi_curv3d.silo"); -// const std::string input_file = relay_test_silo_data_path(path); - -// opts["mesh_name"] = "mesh1_dup"; - -// io::silo::load_mesh(input_file, opts, load_mesh); -// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - -// EXPECT_TRUE(load_mesh[0].has_path("topologies/mesh1_dup")); -// } - -// //----------------------------------------------------------------------------- -// TEST(conduit_relay_io_silo, round_trip_read_option_matset_style) -// { -// // the matset type and the type we are requesting on read -// const std::vector> matset_types = { -// std::make_pair("full", "full"), -// std::make_pair("sparse_by_material", "sparse_by_material"), -// std::make_pair("sparse_by_element", "sparse_by_element"), -// std::make_pair("sparse_by_element", "full"), -// std::make_pair("sparse_by_material", "sparse_by_element"), -// std::make_pair("sparse_by_material", "default"), -// }; - -// for (int i = 0; i < matset_types.size(); i ++) -// { -// std::string matset_type = matset_types[i].first; -// std::string matset_request = matset_types[i].second; - -// for (int j = 0; j < 2; j ++) -// { -// Node mesh_full, mesh_sbe, mesh_sbm, baseline_mesh, load_mesh, info; -// std::string size; -// int nx, ny; -// const double radius = 0.25; -// if (j == 0) -// { -// size = "small"; -// nx = ny = 4; -// } -// else -// { -// size = "large"; -// nx = ny = 100; -// } - -// blueprint::mesh::examples::venn("full", nx, ny, radius, mesh_full); -// blueprint::mesh::examples::venn("sparse_by_material", nx, ny, radius, mesh_sbm); -// blueprint::mesh::examples::venn("sparse_by_element", nx, ny, radius, mesh_sbe); - -// if (matset_type == "full") -// { -// baseline_mesh.set_external(mesh_full); -// } -// else if (matset_type == "sparse_by_material") -// { -// baseline_mesh.set_external(mesh_sbm); -// } -// else // (matset_type == "sparse_by_element") -// { -// baseline_mesh.set_external(mesh_sbe); -// } - -// Node opts; -// if (matset_request == "full") -// { -// opts["matset_style"] = "multi_buffer_full"; -// } -// else if (matset_request == "sparse_by_material") -// { -// opts["matset_style"] = "multi_buffer_by_material"; -// } -// else if (matset_request == "sparse_by_element") -// { -// opts["matset_style"] = "sparse_by_element"; -// } -// else -// { -// opts["matset_style"] = "default"; -// } - -// const std::string basename = "silo_venn2_" + matset_type + "_" + size; -// const std::string filename = basename + ".root"; - -// remove_path_if_exists(filename); -// io::silo::save_mesh(baseline_mesh, basename); -// io::silo::load_mesh(filename, opts, load_mesh); -// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - -// if (matset_request == "full") -// { -// baseline_mesh.set_external(mesh_full); -// } -// else if (matset_request == "sparse_by_material") -// { -// baseline_mesh.set_external(mesh_sbm); -// } -// else if (matset_request == "sparse_by_element") -// { -// baseline_mesh.set_external(mesh_sbe); -// } -// else -// { -// baseline_mesh.set_external(mesh_sbe); -// } - -// // make changes to save mesh so the diff will pass - -// // The field mat_check has values that are one type and matset_values -// // that are another type. The silo writer converts both to double arrays -// // in this case, so we follow suit. -// Node mat_check_new_values, mat_check_new_matset_values; -// baseline_mesh["fields"]["mat_check"]["values"].to_double_array(mat_check_new_values); -// if (baseline_mesh["fields"]["mat_check"]["matset_values"].dtype().is_object()) -// { -// auto mat_vals_itr = baseline_mesh["fields"]["mat_check"]["matset_values"].children(); -// while (mat_vals_itr.has_next()) -// { -// Node &mat_vals_for_mat = mat_vals_itr.next(); -// const std::string mat_name = mat_vals_itr.name(); -// mat_vals_for_mat.to_double_array(mat_check_new_matset_values[mat_name]); -// } -// } -// else -// { -// baseline_mesh["fields"]["mat_check"]["matset_values"].to_double_array(mat_check_new_matset_values); -// } -// baseline_mesh["fields"]["mat_check"]["values"].set_external(mat_check_new_values); -// baseline_mesh["fields"]["mat_check"]["matset_values"].set_external(mat_check_new_matset_values); - -// silo_name_changer("mesh", baseline_mesh); - -// // the loaded mesh will be in the multidomain format -// // but the saved mesh is in the single domain format -// EXPECT_EQ(load_mesh.number_of_children(), 1); -// EXPECT_EQ(load_mesh[0].number_of_children(), baseline_mesh.number_of_children()); -// EXPECT_FALSE(load_mesh[0].diff(baseline_mesh, info, CONDUIT_EPSILON, true)); -// } -// } -// } - -// //----------------------------------------------------------------------------- - -// // -// // read and write Silo and Overlink tests -// // - -// //----------------------------------------------------------------------------- -// // read normal silo files containing multimeshes, multivars, and multimats -// TEST(conduit_relay_io_silo, read_silo) -// { -// const std::vector> file_info = { -// {".", "multi_curv3d", ".silo"}, -// {".", "tire", ".silo"}, -// {".", "galaxy0000", ".silo"}, -// {".", "emptydomains", ".silo"}, -// {"multidir_test_data", "multidir0000", ".root"}, -// }; - -// // TODO what to do in the case where a multimesh points to no data? (mesh1_back) -// // fail silently, as we do now? - -// for (int i = 0; i < file_info.size(); i ++) -// { -// const std::string dirname = file_info[i][0]; -// const std::string basename = file_info[i][1]; -// const std::string fileext = file_info[i][2]; - -// Node load_mesh, info, write_opts; -// std::string filepath = utils::join_file_path(dirname, basename) + fileext; -// filepath = utils::join_file_path("silo", filepath); -// std::string input_file = relay_test_silo_data_path(filepath); - -// io::silo::load_mesh(input_file, load_mesh); -// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - -// const std::string out_name = "read_silo_" + basename; - -// // TODO are these remove paths doing anything? Don't they need filenames? -// remove_path_if_exists(out_name + "_write_blueprint"); -// io::blueprint::save_mesh(load_mesh, out_name + "_write_blueprint", "hdf5"); - -// remove_path_if_exists(out_name + "_write_silo"); -// io::silo::save_mesh(load_mesh, out_name + "_write_silo"); - -// // overlink requires matsets and does not support point meshes -// if (load_mesh[0].has_child("matsets") && basename != "galaxy0000") -// { -// remove_path_if_exists(out_name + "_write_overlink"); -// write_opts["file_style"] = "overlink"; -// write_opts["ovl_topo_name"] = "MMESH"; // TODO do I even need this -// io::silo::save_mesh(load_mesh, out_name + "_write_overlink", write_opts); -// } -// } -// } + // cheat a little bit - we don't have these to start + n_matset["sizes"].set_external(load_mesh[child]["matsets"]["MMATERIAL"]["sizes"]); + n_matset["offsets"].set_external(load_mesh[child]["matsets"]["MMATERIAL"]["offsets"]); + + overlink_name_changer(save_mesh[child]); + } + + EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); + NodeConstIterator l_itr = load_mesh.children(); + NodeConstIterator s_itr = save_mesh.children(); + while (l_itr.has_next()) + { + const Node &l_curr = l_itr.next(); + const Node &s_curr = s_itr.next(); + + EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); + } + } +} + +//----------------------------------------------------------------------------- +// this tests var attributes and padding dimensions +TEST(conduit_relay_io_silo, round_trip_save_option_overlink2) +{ + const std::string basename = "silo_save_option_overlink_basic"; + const std::string filename = basename + "/OvlTop.silo"; + + Node write_opts, read_opts; + write_opts["file_style"] = "overlink"; + read_opts["matset_style"] = "multi_buffer_full"; + + Node save_mesh, load_mesh, info; + blueprint::mesh::examples::basic("structured", 3, 3, 1, save_mesh); + + // add another field that is volume dependent + Node &field2 = save_mesh["fields"]["field2"]; + field2["association"] = "element"; + field2["topology"] = "mesh"; + field2["volume_dependent"] = "true"; + field2["values"].set_external(save_mesh["fields"]["field"]["values"]); + + // add a matset to make overlink happy + add_multi_buffer_full_matset(save_mesh, 4, "mesh"); + + remove_path_if_exists(filename); + io::silo::save_mesh(save_mesh, basename, write_opts); + io::silo::load_mesh(filename, read_opts, load_mesh); + EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); + + // make changes to save mesh so the diff will pass + overlink_name_changer(save_mesh); + + // the loaded mesh will be in the multidomain format + // but the saved mesh is in the single domain format + EXPECT_EQ(load_mesh.number_of_children(), 1); + EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); + + EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); + + // open silo files and do some checks + + DBfile *rootfile = DBOpen(filename.c_str(), DB_UNKNOWN, DB_READ); + EXPECT_TRUE(DBInqVarExists(rootfile, "VAR_ATTRIBUTES")); + EXPECT_TRUE(DBInqVarType(rootfile, "VAR_ATTRIBUTES") == DB_ARRAY); + + DBcompoundarray *var_attr = DBGetCompoundarray(rootfile, "VAR_ATTRIBUTES"); + + // fetch pointers to elements inside the compound array + char **elemnames = var_attr->elemnames; + int *elemlengths = var_attr->elemlengths; + int nelems = var_attr->nelems; + int *values = static_cast(var_attr->values); + int nvalues = var_attr->nvalues; + int datatype = var_attr->datatype; + + EXPECT_EQ(std::string(elemnames[0]), "field"); + EXPECT_EQ(std::string(elemnames[1]), "field2"); + EXPECT_EQ(elemlengths[0], 5); + EXPECT_EQ(elemlengths[1], 5); + EXPECT_EQ(nelems, 2); + // for first var + EXPECT_EQ(values[0], 1); + EXPECT_EQ(values[1], 0); + EXPECT_EQ(values[2], 1); + EXPECT_EQ(values[3], 0); + EXPECT_EQ(values[4], 1); + // for second var + EXPECT_EQ(values[5], 1); + EXPECT_EQ(values[6], 1); + EXPECT_EQ(values[7], 1); + EXPECT_EQ(values[8], 0); + EXPECT_EQ(values[9], 1); + EXPECT_EQ(nvalues, 10); + EXPECT_EQ(datatype, DB_INT); + + DBFreeCompoundarray(var_attr); + + EXPECT_TRUE(DBInqVarExists(rootfile, "PAD_DIMS")); + EXPECT_TRUE(DBInqVarType(rootfile, "PAD_DIMS") == DB_ARRAY); + + DBcompoundarray *pad_dims = DBGetCompoundarray(rootfile, "PAD_DIMS"); + + // fetch pointers to elements inside the compound array + elemnames = pad_dims->elemnames; + elemlengths = pad_dims->elemlengths; + nelems = pad_dims->nelems; + values = static_cast(pad_dims->values); + nvalues = pad_dims->nvalues; + datatype = pad_dims->datatype; + + EXPECT_EQ(std::string(elemnames[0]), "paddims"); + EXPECT_EQ(elemlengths[0], 6); + EXPECT_EQ(nelems, 1); + EXPECT_EQ(values[0], 0); + EXPECT_EQ(values[1], 0); + EXPECT_EQ(values[2], 0); + EXPECT_EQ(values[3], 0); + EXPECT_EQ(values[4], 0); + EXPECT_EQ(values[5], 0); + EXPECT_EQ(nvalues, 6); + EXPECT_EQ(datatype, DB_INT); + + DBFreeCompoundarray(pad_dims); + + DBClose(rootfile); + + // now check domain file + + const std::string dom_filename = basename + "/domain0.silo"; + DBfile *domfile = DBOpen(dom_filename.c_str(), DB_UNKNOWN, DB_READ); + + EXPECT_TRUE(DBInqVarExists(domfile, "DOMAIN_NEIGHBOR_NUMS")); + EXPECT_TRUE(DBInqVarType(domfile, "DOMAIN_NEIGHBOR_NUMS") == DB_ARRAY); + + DBcompoundarray *dom_neighbor_nums = DBGetCompoundarray(domfile, "DOMAIN_NEIGHBOR_NUMS"); + + // fetch pointers to elements inside the compound array + elemnames = dom_neighbor_nums->elemnames; + elemlengths = dom_neighbor_nums->elemlengths; + nelems = dom_neighbor_nums->nelems; + values = static_cast(dom_neighbor_nums->values); + nvalues = dom_neighbor_nums->nvalues; + datatype = dom_neighbor_nums->datatype; + + EXPECT_EQ(std::string(elemnames[0]), "num_neighbors"); + EXPECT_EQ(std::string(elemnames[1]), "neighbor_nums"); + EXPECT_EQ(elemlengths[0], 1); + EXPECT_EQ(elemlengths[1], 0); + EXPECT_EQ(nelems, 2); + EXPECT_EQ(values[0], 0); + EXPECT_EQ(nvalues, 1); + EXPECT_EQ(datatype, DB_INT); + + DBFreeCompoundarray(dom_neighbor_nums); + + DBClose(domfile); +} + +//----------------------------------------------------------------------------- +// this tests material i/o +TEST(conduit_relay_io_silo, round_trip_save_option_overlink3) +{ + Node save_mesh, load_mesh, info; + const int nx = 100, ny = 100; + const double radius = 0.25; + blueprint::mesh::examples::venn("sparse_by_element", nx, ny, radius, save_mesh); + + const std::string basename = "silo_save_option_overlink_venn"; + const std::string filename = basename + "/OvlTop.silo"; + + Node opts; + opts["file_style"] = "overlink"; + + remove_path_if_exists(filename); + io::silo::save_mesh(save_mesh, basename, opts); + io::silo::load_mesh(filename, load_mesh); + EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + + // make changes to save mesh so the diff will pass + + // The field mat_check has values that are one type and matset_values + // that are another type. The silo writer converts both to double arrays + // in this case, so we follow suit. + Node mat_check_new_values, mat_check_new_matset_values; + save_mesh["fields"]["mat_check"]["values"].to_double_array(mat_check_new_values); + save_mesh["fields"]["mat_check"]["matset_values"].to_double_array(mat_check_new_matset_values); + save_mesh["fields"]["mat_check"]["values"].set_external(mat_check_new_values); + save_mesh["fields"]["mat_check"]["matset_values"].set_external(mat_check_new_matset_values); + + overlink_name_changer(save_mesh); + + // the loaded mesh will be in the multidomain format + // but the saved mesh is in the single domain format + EXPECT_EQ(load_mesh.number_of_children(), 1); + EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); + + EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); +} + +//----------------------------------------------------------------------------- +// we are testing vector fields get converted to scalars +TEST(conduit_relay_io_silo, round_trip_save_option_overlink4) +{ + const std::vector> mesh_types = { + std::make_pair("rectilinear", "2"), std::make_pair("rectilinear", "3"), + std::make_pair("structured", "2"), std::make_pair("structured", "3"), + std::make_pair("quads", "2"), + std::make_pair("hexs", "3"), + }; + for (int i = 0; i < mesh_types.size(); ++i) + { + std::string dim = mesh_types[i].second; + index_t nx = 3; + index_t ny = 4; + index_t nz = (dim == "2" ? 0 : 2); + + std::string mesh_type = mesh_types[i].first; + + Node save_mesh, load_mesh, info; + blueprint::mesh::examples::braid(mesh_type, nx, ny, nz, save_mesh); + index_t nele_x = nx - 1; + index_t nele_y = ny - 1; + index_t nele_z = (dim == "2" ? 0 : nz - 1); + + // provide a matset for braid + braid_init_example_matset(nele_x, nele_y, nele_z, save_mesh["matsets"]["matset"]); + + const std::string basename = "silo_save_option_overlink_braid_" + mesh_type + "_" + dim + "D"; + const std::string filename = basename + "/OvlTop.silo"; + + Node write_opts, read_opts; + write_opts["file_style"] = "overlink"; + read_opts["matset_style"] = "multi_buffer_full"; + + // remove existing root file, directory and any output files + remove_path_if_exists(filename); + io::silo::save_mesh(save_mesh, basename, write_opts); + io::silo::load_mesh(filename, read_opts, load_mesh); + EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + + Node &field_vel = save_mesh["fields"]["vel"]; + Node &field_vel_u = save_mesh["fields"]["vel_u"]; + Node &field_vel_v = save_mesh["fields"]["vel_v"]; + + field_vel_u["topology"].set(field_vel["topology"]); + field_vel_u["association"].set(field_vel["association"]); + field_vel_u["values"].set(field_vel["values/u"]); + field_vel_v["topology"].set(field_vel["topology"]); + field_vel_v["association"].set(field_vel["association"]); + field_vel_v["values"].set(field_vel["values/v"]); + + if (dim == "3") + { + Node &field_vel_w = save_mesh["fields"]["vel_w"]; + field_vel_w["topology"].set(field_vel["topology"]); + field_vel_w["association"].set(field_vel["association"]); + field_vel_w["values"].set(field_vel["values/w"]); + } + + save_mesh["fields"].remove_child("vel"); + + // make changes to save mesh so the diff will pass + overlink_name_changer(save_mesh); + + // the loaded mesh will be in the multidomain format + // but the saved mesh is in the single domain format + EXPECT_EQ(load_mesh.number_of_children(), 1); + EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); + + EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); + } +} + +//----------------------------------------------------------------------------- +// check that all the shape types work (specifically polytopal ones) +TEST(conduit_relay_io_silo, round_trip_save_option_overlink5) +{ + const std::vector> mesh_types = { + std::make_pair("uniform", "2"), std::make_pair("uniform", "3"), + std::make_pair("rectilinear", "2"), std::make_pair("rectilinear", "3"), + std::make_pair("structured", "2"), std::make_pair("structured", "3"), + std::make_pair("quads", "2"), + std::make_pair("polygons", "2"), + std::make_pair("hexs", "3"), + // std::make_pair("polyhedra", "3") + // Overlink does not support tris, wedges, pyramids, or tets + }; + for (int i = 0; i < mesh_types.size(); ++i) + { + const std::string dim = mesh_types[i].second; + index_t nx = 3; + index_t ny = 4; + index_t nz = (dim == "2" ? 0 : 2); + + const std::string mesh_type = mesh_types[i].first; + + Node save_mesh, load_mesh, info; + blueprint::mesh::examples::basic(mesh_type, nx, ny, nz, save_mesh); + + const std::string basename = "silo_save_option_overlink_basic_" + mesh_type + "_" + dim + "D"; + const std::string filename = basename + "/OvlTop.silo"; + const std::string domfile = basename + "/domain0.silo"; + + Node write_opts, read_opts; + write_opts["file_style"] = "overlink"; + read_opts["matset_style"] = "multi_buffer_full"; + + // add a matset to make overlink happy + int num_elems = (nx - 1) * (ny - 1); + if (mesh_type == "tets") + { + num_elems *= 6; + } + add_multi_buffer_full_matset(save_mesh, num_elems, "mesh"); + + remove_path_if_exists(filename); + remove_path_if_exists(domfile); + io::silo::save_mesh(save_mesh, basename, write_opts); + io::silo::load_mesh(filename, read_opts, load_mesh); + EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); + + // make changes to save mesh so the diff will pass + if (mesh_type == "uniform") + { + silo_uniform_to_rect_conversion("coords", "mesh", save_mesh); + } + overlink_name_changer(save_mesh); + + // the loaded mesh will be in the multidomain format + // but the saved mesh is in the single domain format + EXPECT_EQ(load_mesh.number_of_children(), 1); + EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); + + EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); + } +} + +//----------------------------------------------------------------------------- + +// +// read option tests +// + +// read options: +/// opts: +/// silo_names: +/// multimesh_names: +/// "{name1}" - multimeshes with this name will be read if they exist +/// "{name2}" +/// ... +/// or +/// "{all}" - all multimeshes will be read. +/// or +/// "{none}" - no multimeshes will be read. +/// multivar_names: similar to multimesh_names. +/// multimat_names: similar to multimesh_names. +/// multimatspecies_names: similar to multimesh_names. TODO +/// qmesh_names: similar to multimesh_names. +/// qvar_names: similar to multimesh_names. +/// ucdmesh_names: similar to multimesh_names. +/// ucdvar_names: similar to multimesh_names. +/// ptmesh_names: similar to multimesh_names. +/// ptvar_names: similar to multimesh_names. +/// mat_names: similar to multimesh_names. +/// matspecies_names: similar to multimesh_names. TODO +/// By default, everything in the file will be read unless manually turned off. +/// +/// matset_style: "default", "multi_buffer_full", "sparse_by_element", +/// "multi_buffer_by_material" +/// "default" ==> "sparse_by_element" +/// +/// mesh_name: legacy argument. This is interpreted as a multimesh name. +/// It is added to the list of multimesh names to read, unless the +/// user has specified "all" or "none", which will supersede this. +/// TODO does it make sense to remove this? When? + +//----------------------------------------------------------------------------- +// TODO this is now a legacy feature. Should I remove eventually? +TEST(conduit_relay_io_silo, round_trip_read_option_mesh_name) +{ + Node load_mesh, info, opts; + const std::string path = utils::join_file_path("silo", "multi_curv3d.silo"); + const std::string input_file = relay_test_silo_data_path(path); + + opts["mesh_name"] = "mesh1_dup"; + + io::silo::load_mesh(input_file, opts, load_mesh); + EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + + EXPECT_TRUE(load_mesh[0].has_path("topologies/mesh1_dup")); +} + +//----------------------------------------------------------------------------- +TEST(conduit_relay_io_silo, round_trip_read_option_matset_style) +{ + // the matset type and the type we are requesting on read + const std::vector> matset_types = { + std::make_pair("full", "full"), + std::make_pair("sparse_by_material", "sparse_by_material"), + std::make_pair("sparse_by_element", "sparse_by_element"), + std::make_pair("sparse_by_element", "full"), + std::make_pair("sparse_by_material", "sparse_by_element"), + std::make_pair("sparse_by_material", "default"), + }; + + for (int i = 0; i < matset_types.size(); i ++) + { + std::string matset_type = matset_types[i].first; + std::string matset_request = matset_types[i].second; + + for (int j = 0; j < 2; j ++) + { + Node mesh_full, mesh_sbe, mesh_sbm, baseline_mesh, load_mesh, info; + std::string size; + int nx, ny; + const double radius = 0.25; + if (j == 0) + { + size = "small"; + nx = ny = 4; + } + else + { + size = "large"; + nx = ny = 100; + } + + blueprint::mesh::examples::venn("full", nx, ny, radius, mesh_full); + blueprint::mesh::examples::venn("sparse_by_material", nx, ny, radius, mesh_sbm); + blueprint::mesh::examples::venn("sparse_by_element", nx, ny, radius, mesh_sbe); + + if (matset_type == "full") + { + baseline_mesh.set_external(mesh_full); + } + else if (matset_type == "sparse_by_material") + { + baseline_mesh.set_external(mesh_sbm); + } + else // (matset_type == "sparse_by_element") + { + baseline_mesh.set_external(mesh_sbe); + } + + Node opts; + if (matset_request == "full") + { + opts["matset_style"] = "multi_buffer_full"; + } + else if (matset_request == "sparse_by_material") + { + opts["matset_style"] = "multi_buffer_by_material"; + } + else if (matset_request == "sparse_by_element") + { + opts["matset_style"] = "sparse_by_element"; + } + else + { + opts["matset_style"] = "default"; + } + + const std::string basename = "silo_venn2_" + matset_type + "_" + size; + const std::string filename = basename + ".root"; + + remove_path_if_exists(filename); + io::silo::save_mesh(baseline_mesh, basename); + io::silo::load_mesh(filename, opts, load_mesh); + EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + + if (matset_request == "full") + { + baseline_mesh.set_external(mesh_full); + } + else if (matset_request == "sparse_by_material") + { + baseline_mesh.set_external(mesh_sbm); + } + else if (matset_request == "sparse_by_element") + { + baseline_mesh.set_external(mesh_sbe); + } + else + { + baseline_mesh.set_external(mesh_sbe); + } + + // make changes to save mesh so the diff will pass + + // The field mat_check has values that are one type and matset_values + // that are another type. The silo writer converts both to double arrays + // in this case, so we follow suit. + Node mat_check_new_values, mat_check_new_matset_values; + baseline_mesh["fields"]["mat_check"]["values"].to_double_array(mat_check_new_values); + if (baseline_mesh["fields"]["mat_check"]["matset_values"].dtype().is_object()) + { + auto mat_vals_itr = baseline_mesh["fields"]["mat_check"]["matset_values"].children(); + while (mat_vals_itr.has_next()) + { + Node &mat_vals_for_mat = mat_vals_itr.next(); + const std::string mat_name = mat_vals_itr.name(); + mat_vals_for_mat.to_double_array(mat_check_new_matset_values[mat_name]); + } + } + else + { + baseline_mesh["fields"]["mat_check"]["matset_values"].to_double_array(mat_check_new_matset_values); + } + baseline_mesh["fields"]["mat_check"]["values"].set_external(mat_check_new_values); + baseline_mesh["fields"]["mat_check"]["matset_values"].set_external(mat_check_new_matset_values); + + silo_name_changer("mesh", baseline_mesh); + + // the loaded mesh will be in the multidomain format + // but the saved mesh is in the single domain format + EXPECT_EQ(load_mesh.number_of_children(), 1); + EXPECT_EQ(load_mesh[0].number_of_children(), baseline_mesh.number_of_children()); + EXPECT_FALSE(load_mesh[0].diff(baseline_mesh, info, CONDUIT_EPSILON, true)); + } + } +} + +//----------------------------------------------------------------------------- + +// +// read and write Silo and Overlink tests +// + +//----------------------------------------------------------------------------- +// read normal silo files containing multimeshes, multivars, and multimats +TEST(conduit_relay_io_silo, read_silo) +{ + const std::vector> file_info = { + {".", "multi_curv3d", ".silo"}, + {".", "tire", ".silo"}, + {".", "galaxy0000", ".silo"}, + {".", "emptydomains", ".silo"}, + {"multidir_test_data", "multidir0000", ".root"}, + }; + + // TODO what to do in the case where a multimesh points to no data? (mesh1_back) + // fail silently, as we do now? + + for (int i = 0; i < file_info.size(); i ++) + { + const std::string dirname = file_info[i][0]; + const std::string basename = file_info[i][1]; + const std::string fileext = file_info[i][2]; + + Node load_mesh, info, write_opts; + std::string filepath = utils::join_file_path(dirname, basename) + fileext; + filepath = utils::join_file_path("silo", filepath); + std::string input_file = relay_test_silo_data_path(filepath); + + io::silo::load_mesh(input_file, load_mesh); + EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + + const std::string out_name = "read_silo_" + basename; + + // TODO are these remove paths doing anything? Don't they need filenames? + remove_path_if_exists(out_name + "_write_blueprint"); + io::blueprint::save_mesh(load_mesh, out_name + "_write_blueprint", "hdf5"); + + remove_path_if_exists(out_name + "_write_silo"); + io::silo::save_mesh(load_mesh, out_name + "_write_silo"); + + // overlink requires matsets and does not support point meshes + if (load_mesh[0].has_child("matsets") && basename != "galaxy0000") + { + remove_path_if_exists(out_name + "_write_overlink"); + write_opts["file_style"] = "overlink"; + write_opts["ovl_topo_name"] = "MMESH"; // TODO do I even need this + io::silo::save_mesh(load_mesh, out_name + "_write_overlink", write_opts); + } + } +} //----------------------------------------------------------------------------- // test that we can read silo without multimeshes, multivars, and multimats TEST(conduit_relay_io_silo, read_simple_silo) { const std::vector> file_info = { - // {"curv2d", ".silo"}, - {"curv2d_colmajor", ".silo"}, // TODO look at strided structured to handle this case - // {"curv3d", ".silo"}, + {"curv2d", ".silo"}, + // {"curv2d_colmajor", ".silo"}, // TODO look at strided structured to handle this case + {"curv3d", ".silo"}, // {"curv3d_colmajor", ".silo"}, // // {"globe", ".silo"}, // TODO need to fix a known bug for this one to work }; @@ -1849,142 +1849,142 @@ TEST(conduit_relay_io_silo, read_simple_silo) } } -// //----------------------------------------------------------------------------- -// // test that we can read the fake overlink files from the visit test data -// TEST(conduit_relay_io_silo, read_fake_overlink) -// { -// const std::vector> file_info = { -// // {"ev_0_0_100", "OvlTop", ".silo"}, -// // uncomment once silo ucdmesh phzones are supported -// {"hl18spec", "OvlTop", ".silo"}, -// // {"regrovl_qh_1000_10001_4", "OvlTop", ".silo"}, -// // uncomment once silo ucdmesh phzones are supported -// {"utpyr4", "OvlTop", ".silo"}, -// }; - -// for (int i = 0; i < file_info.size(); i ++) -// { -// const std::string dirname = file_info[i][0]; -// const std::string basename = file_info[i][1]; -// const std::string fileext = file_info[i][2]; - -// Node load_mesh, info, write_opts; -// std::string filepath = utils::join_file_path(dirname, basename) + fileext; -// filepath = utils::join_file_path("fake_overlink", filepath); -// std::string input_file = relay_test_silo_data_path(filepath); - -// io::silo::load_mesh(input_file, load_mesh); -// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - -// const std::string out_name = "read_fake_overlink_" + dirname; - -// remove_path_if_exists(out_name + "_write_blueprint"); -// io::blueprint::save_mesh(load_mesh, out_name + "_write_blueprint", "hdf5"); - -// remove_path_if_exists(out_name + "_write_silo"); -// io::silo::save_mesh(load_mesh, out_name + "_write_silo"); - -// remove_path_if_exists(out_name + "_write_overlink"); -// write_opts["file_style"] = "overlink"; -// write_opts["ovl_topo_name"] = "MMESH"; -// io::silo::save_mesh(load_mesh, out_name + "_write_overlink", write_opts); -// } -// } - -// //----------------------------------------------------------------------------- -// // read overlink files in symlink format -// // should be similar to reading raw silo -// TEST(conduit_relay_io_silo, read_overlink_symlink_format) -// { -// const std::vector> file_info = { -// {".", "box2d", ".silo"}, -// {".", "box3d", ".silo"}, -// // {".", "diamond", ".silo"}, -// // fails b/c polytopal not yet supported -// {".", "testDisk2D_a", ".silo"}, -// // {".", "donordiv.s2_materials2", ".silo"}, -// // fails b/c polytopal not yet supported -// {".", "donordiv.s2_materials3", ".silo"}, -// }; - -// for (int i = 0; i < file_info.size(); i ++) -// { -// const std::string dirname = file_info[i][0]; -// const std::string basename = file_info[i][1]; -// const std::string fileext = file_info[i][2]; - -// Node load_mesh, info, write_opts; -// std::string filepath = utils::join_file_path(dirname, basename) + fileext; -// filepath = utils::join_file_path("overlink", filepath); -// std::string input_file = relay_test_silo_data_path(filepath); - -// io::silo::load_mesh(input_file, load_mesh); -// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - -// const std::string out_name = "read_overlink_symlink_" + basename; - -// remove_path_if_exists(out_name + "_write_blueprint"); -// io::blueprint::save_mesh(load_mesh, out_name + "_write_blueprint", "hdf5"); - -// remove_path_if_exists(out_name + "_write_silo"); -// io::silo::save_mesh(load_mesh, out_name + "_write_silo"); - -// remove_path_if_exists(out_name + "_write_overlink"); -// write_opts["file_style"] = "overlink"; -// write_opts["ovl_topo_name"] = "MMESH"; -// io::silo::save_mesh(load_mesh, out_name + "_write_overlink", write_opts); -// } -// } - -// //----------------------------------------------------------------------------- -// // read overlink directly from ovltop.silo -// // this case is tricky and involves messing with paths -// TEST(conduit_relay_io_silo, read_overlink_directly) -// { -// const std::vector> file_info = { -// {"box2d", "OvlTop", ".silo"}, -// {"box3d", "OvlTop", ".silo"}, -// // {"diamond", "OvlTop", ".silo"}, -// {"testDisk2D_a", "OvlTop", ".silo"}, -// // {"donordiv.s2_materials2", "OvlTop", ".silo"}, -// {"donordiv.s2_materials3", "OvlTop", ".silo"}, -// }; - -// for (int i = 0; i < file_info.size(); i ++) -// { -// const std::string dirname = file_info[i][0]; -// const std::string basename = file_info[i][1]; -// const std::string fileext = file_info[i][2]; - -// Node load_mesh, info, write_opts; - -// std::string filepath = utils::join_file_path(dirname, basename) + fileext; -// filepath = utils::join_file_path("overlink", filepath); -// std::string input_file = relay_test_silo_data_path(filepath); - -// io::silo::load_mesh(input_file, load_mesh); -// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - -// const std::string out_name = "read_overlink_direct_" + dirname; - -// remove_path_if_exists(out_name + "_write_blueprint"); -// io::blueprint::save_mesh(load_mesh, out_name + "_write_blueprint", "hdf5"); - -// remove_path_if_exists(out_name + "_write_silo"); -// io::silo::save_mesh(load_mesh, out_name + "_write_silo"); - -// remove_path_if_exists(out_name + "_write_overlink"); -// write_opts["file_style"] = "overlink"; -// write_opts["ovl_topo_name"] = "MMESH"; -// io::silo::save_mesh(load_mesh, out_name + "_write_overlink", write_opts); -// } -// } - -// // TODO add tests for... -// // - polytopal meshes once they are supported -// // - units once they are supported -// // - etc. - -// // TODO add tetra8 and c36_m5 to all the overlink i/o tests - -// // TODO somewhere I need to error on overlink when there are different var or mesh types across domains +//----------------------------------------------------------------------------- +// test that we can read the fake overlink files from the visit test data +TEST(conduit_relay_io_silo, read_fake_overlink) +{ + const std::vector> file_info = { + // {"ev_0_0_100", "OvlTop", ".silo"}, + // uncomment once silo ucdmesh phzones are supported + {"hl18spec", "OvlTop", ".silo"}, + // {"regrovl_qh_1000_10001_4", "OvlTop", ".silo"}, + // uncomment once silo ucdmesh phzones are supported + {"utpyr4", "OvlTop", ".silo"}, + }; + + for (int i = 0; i < file_info.size(); i ++) + { + const std::string dirname = file_info[i][0]; + const std::string basename = file_info[i][1]; + const std::string fileext = file_info[i][2]; + + Node load_mesh, info, write_opts; + std::string filepath = utils::join_file_path(dirname, basename) + fileext; + filepath = utils::join_file_path("fake_overlink", filepath); + std::string input_file = relay_test_silo_data_path(filepath); + + io::silo::load_mesh(input_file, load_mesh); + EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + + const std::string out_name = "read_fake_overlink_" + dirname; + + remove_path_if_exists(out_name + "_write_blueprint"); + io::blueprint::save_mesh(load_mesh, out_name + "_write_blueprint", "hdf5"); + + remove_path_if_exists(out_name + "_write_silo"); + io::silo::save_mesh(load_mesh, out_name + "_write_silo"); + + remove_path_if_exists(out_name + "_write_overlink"); + write_opts["file_style"] = "overlink"; + write_opts["ovl_topo_name"] = "MMESH"; + io::silo::save_mesh(load_mesh, out_name + "_write_overlink", write_opts); + } +} + +//----------------------------------------------------------------------------- +// read overlink files in symlink format +// should be similar to reading raw silo +TEST(conduit_relay_io_silo, read_overlink_symlink_format) +{ + const std::vector> file_info = { + {".", "box2d", ".silo"}, + {".", "box3d", ".silo"}, + // {".", "diamond", ".silo"}, + // fails b/c polytopal not yet supported + {".", "testDisk2D_a", ".silo"}, + // {".", "donordiv.s2_materials2", ".silo"}, + // fails b/c polytopal not yet supported + {".", "donordiv.s2_materials3", ".silo"}, + }; + + for (int i = 0; i < file_info.size(); i ++) + { + const std::string dirname = file_info[i][0]; + const std::string basename = file_info[i][1]; + const std::string fileext = file_info[i][2]; + + Node load_mesh, info, write_opts; + std::string filepath = utils::join_file_path(dirname, basename) + fileext; + filepath = utils::join_file_path("overlink", filepath); + std::string input_file = relay_test_silo_data_path(filepath); + + io::silo::load_mesh(input_file, load_mesh); + EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + + const std::string out_name = "read_overlink_symlink_" + basename; + + remove_path_if_exists(out_name + "_write_blueprint"); + io::blueprint::save_mesh(load_mesh, out_name + "_write_blueprint", "hdf5"); + + remove_path_if_exists(out_name + "_write_silo"); + io::silo::save_mesh(load_mesh, out_name + "_write_silo"); + + remove_path_if_exists(out_name + "_write_overlink"); + write_opts["file_style"] = "overlink"; + write_opts["ovl_topo_name"] = "MMESH"; + io::silo::save_mesh(load_mesh, out_name + "_write_overlink", write_opts); + } +} + +//----------------------------------------------------------------------------- +// read overlink directly from ovltop.silo +// this case is tricky and involves messing with paths +TEST(conduit_relay_io_silo, read_overlink_directly) +{ + const std::vector> file_info = { + {"box2d", "OvlTop", ".silo"}, + {"box3d", "OvlTop", ".silo"}, + // {"diamond", "OvlTop", ".silo"}, + {"testDisk2D_a", "OvlTop", ".silo"}, + // {"donordiv.s2_materials2", "OvlTop", ".silo"}, + {"donordiv.s2_materials3", "OvlTop", ".silo"}, + }; + + for (int i = 0; i < file_info.size(); i ++) + { + const std::string dirname = file_info[i][0]; + const std::string basename = file_info[i][1]; + const std::string fileext = file_info[i][2]; + + Node load_mesh, info, write_opts; + + std::string filepath = utils::join_file_path(dirname, basename) + fileext; + filepath = utils::join_file_path("overlink", filepath); + std::string input_file = relay_test_silo_data_path(filepath); + + io::silo::load_mesh(input_file, load_mesh); + EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + + const std::string out_name = "read_overlink_direct_" + dirname; + + remove_path_if_exists(out_name + "_write_blueprint"); + io::blueprint::save_mesh(load_mesh, out_name + "_write_blueprint", "hdf5"); + + remove_path_if_exists(out_name + "_write_silo"); + io::silo::save_mesh(load_mesh, out_name + "_write_silo"); + + remove_path_if_exists(out_name + "_write_overlink"); + write_opts["file_style"] = "overlink"; + write_opts["ovl_topo_name"] = "MMESH"; + io::silo::save_mesh(load_mesh, out_name + "_write_overlink", write_opts); + } +} + +// TODO add tests for... +// - polytopal meshes once they are supported +// - units once they are supported +// - etc. + +// TODO add tetra8 and c36_m5 to all the overlink i/o tests + +// TODO somewhere I need to error on overlink when there are different var or mesh types across domains From 91ce5cd134cae1f1af5817ee06c27280ae47f088 Mon Sep 17 00:00:00 2001 From: Justin Privitera Date: Thu, 11 Apr 2024 10:08:07 -0700 Subject: [PATCH 31/56] I'm in structured strided jail --- src/libs/relay/conduit_relay_io_silo.cpp | 159 +- src/tests/relay/t_relay_io_silo.cpp | 3950 +++++++++++----------- 2 files changed, 2060 insertions(+), 2049 deletions(-) diff --git a/src/libs/relay/conduit_relay_io_silo.cpp b/src/libs/relay/conduit_relay_io_silo.cpp index 2c59de2b5..49cf5387e 100644 --- a/src/libs/relay/conduit_relay_io_silo.cpp +++ b/src/libs/relay/conduit_relay_io_silo.cpp @@ -1213,6 +1213,7 @@ read_quadmesh_domain(DBquadmesh *quadmesh_ptr, flipped_strides[1] = quadmesh_ptr->stride[1]; flipped_strides[2] = quadmesh_ptr->stride[0]; } + topo_out["elements/dims/strides"].set(flipped_strides, ndims); } } @@ -2641,7 +2642,7 @@ read_root_silo_index(const std::string &root_file_path, { const Node &silo_names_entry = silo_names_itr.next(); const std::string silo_names_name = silo_names_itr.name(); - if (silo_names_name != "multimesh_names") + if (silo_names_name != "mesh_names") { real_opts["silo_names"][silo_names_name].set_external(silo_names_entry); } @@ -2649,29 +2650,29 @@ read_root_silo_index(const std::string &root_file_path, } } - if (opts.has_path("silo_names/multimesh_names")) + if (opts.has_path("silo_names/mesh_names")) { - if (opts["silo_names"]["multimesh_names"].has_child("all") || - opts["silo_names"]["multimesh_names"].has_child("none") || - opts["silo_names"]["multimesh_names"].has_child(opts_mesh_name)) + if (opts["silo_names"]["mesh_names"].has_child("all") || + opts["silo_names"]["mesh_names"].has_child("none") || + opts["silo_names"]["mesh_names"].has_child(opts_mesh_name)) { - real_opts["silo_names"]["multimesh_names"].set_external(opts["silo_names"]["multimesh_names"]); + real_opts["silo_names"]["mesh_names"].set_external(opts["silo_names"]["mesh_names"]); } else { - auto mmesh_names_itr = opts["silo_names"]["multimesh_names"].children(); - while (mmesh_names_itr.has_next()) + auto mesh_names_itr = opts["silo_names"]["mesh_names"].children(); + while (mesh_names_itr.has_next()) { - const Node &mmesh_names_entry = mmesh_names_itr.next(); - const std::string mmesh_name = mmesh_names_itr.name(); - real_opts["silo_names"]["multimesh_names"][mmesh_name].set_external(mmesh_names_entry); + const Node &mesh_names_entry = mesh_names_itr.next(); + const std::string mesh_name = mesh_names_itr.name(); + real_opts["silo_names"]["mesh_names"][mesh_name].set_external(mesh_names_entry); } - real_opts["silo_names"]["multimesh_names"][opts_mesh_name]; + real_opts["silo_names"]["mesh_names"][opts_mesh_name]; } } else { - real_opts["silo_names"]["multimesh_names"].set(opts_mesh_name); + real_opts["silo_names"]["mesh_names"].set(opts_mesh_name); } } else @@ -2687,16 +2688,8 @@ read_root_silo_index(const std::string &root_file_path, std::map reading_info; // read all is turned on, and read none is turned off - reading_info["multimesh_names"] = detail::SiloReadBookkeeping(); - reading_info["multivar_names"] = detail::SiloReadBookkeeping(); - reading_info["multimat_names"] = detail::SiloReadBookkeeping(); - // reading_info["multimatspecies_names"] = detail::SiloReadBookkeeping(); - reading_info["qmesh_names"] = detail::SiloReadBookkeeping(); - reading_info["qvar_names"] = detail::SiloReadBookkeeping(); - reading_info["ucdmesh_names"] = detail::SiloReadBookkeeping(); - reading_info["ucdvar_names"] = detail::SiloReadBookkeeping(); - reading_info["ptmesh_names"] = detail::SiloReadBookkeeping(); - reading_info["ptvar_names"] = detail::SiloReadBookkeeping(); + reading_info["mesh_names"] = detail::SiloReadBookkeeping(); + reading_info["var_names"] = detail::SiloReadBookkeeping(); reading_info["mat_names"] = detail::SiloReadBookkeeping(); // reading_info["matspecies_names"] = detail::SiloReadBookkeeping(); @@ -2775,13 +2768,13 @@ read_root_silo_index(const std::string &root_file_path, } } - // names to read get stored in reading_info["multimesh_names"].names_to_read - auto generate_read_list = [&](const std::string silo_obj_name, // e.g. "multimesh_names" - const std::string obj_name, // e.g. "multimesh" - just for errors + // names to read get stored in reading_info["mesh_names"].names_to_read + auto generate_read_list = [&](const std::string silo_obj_name, // e.g. "mesh_names" + const std::string obj_name, // e.g. "mesh" - just for errors const int num_silo_objects_in_toc, char** toc_names) { - // if we are not reading no multimeshes --> we are reading multimeshes + // if we are *not* reading *no* meshes --> we are reading meshes if (! reading_info[silo_obj_name].read_none) { if (reading_info[silo_obj_name].read_all) @@ -2825,7 +2818,7 @@ read_root_silo_index(const std::string &root_file_path, return true; }; - // silo doesn't let us have any names that are the same in the same directory, + // Silo doesn't let us have any names that are the same in the same directory, // even if they are different types. So we don't have to worry about name collisions. if (! (generate_read_list("multimesh_names", "multimesh", toc->nmultimesh, toc->multimesh_names) && generate_read_list("multivar_names", "multivar", toc->nmultivar, toc->multivar_names) && @@ -4426,105 +4419,39 @@ void silo_write_structured_mesh(DBfile *dbfile, int pts_dims[3]; int ele_dims[3]; index_t num_elems; - int min_index[3]; - int max_index[3]; // check for strided structured case if (n_topo.has_path("elements/dims/offsets") && n_topo.has_path("elements/dims/strides")) { - // TODO this case is BUSTED - cyrus says punt on this - - - // we must reverse engineer the dims from the strides and the num coords - const int num_pts = n_mesh_info[topo_name]["num_pts"].as_int(); - - // TODO this can't be right, what if strides[0] != 1? - - const int_accessor strides = n_topo["elements"]["dims"]["strides"].value(); - const int_accessor offsets = n_topo["elements"]["dims"]["offsets"].value(); - - if (2 == ndims) - { - pts_dims[0] = strides[1]; - pts_dims[1] = num_pts / strides[1]; - - ele_dims[0] = pts_dims[0] - 1; - ele_dims[1] = pts_dims[1] - 1; - - num_elems = ele_dims[0] * ele_dims[1]; - } - else // 3 == ndims - { - pts_dims[0] = strides[1]; - pts_dims[1] = strides[2] / strides[1]; - pts_dims[2] = num_pts / strides[2]; - - ele_dims[0] = pts_dims[0] - 1; - ele_dims[1] = pts_dims[1] - 1; - ele_dims[2] = pts_dims[2] - 1; - - num_elems = ele_dims[0] * ele_dims[1] * ele_dims[2]; - } - - n_mesh_info[topo_name]["num_elems"].set(num_elems); - n_mesh_info[topo_name]["elements/i"] = ele_dims[0]; - n_mesh_info[topo_name]["elements/j"] = ele_dims[1]; - - if (3 == ndims) - { - n_mesh_info[topo_name]["elements/k"] = ele_dims[2]; - } - - // TODO why aren't these values getting to silo correctly? - min_index[0] = offsets[0]; - max_index[0] = offsets[0] + n_topo["elements"]["dims"]["i"].to_int(); - min_index[1] = offsets[1]; - max_index[1] = offsets[1] + n_topo["elements"]["dims"]["j"].to_int(); - if (3 == ndims) - { - min_index[2] = offsets[2]; - max_index[2] = offsets[2] + n_topo["elements"]["dims"]["k"].to_int(); - } - - CONDUIT_CHECK_SILO_ERROR(DBAddOption(optlist, - DBOPT_LO_OFFSET, - min_index), - "Error adding option"); - - CONDUIT_CHECK_SILO_ERROR(DBAddOption(optlist, - DBOPT_HI_OFFSET, - max_index), - "Error adding option"); + CONDUIT_ERROR("Strided Structured Blueprint case does not have a general " + "analog in Silo."); } - else - { - ele_dims[0] = n_topo["elements/dims/i"].to_value(); - ele_dims[1] = n_topo["elements/dims/j"].to_value(); - ele_dims[2] = 0; + ele_dims[0] = n_topo["elements/dims/i"].to_value(); + ele_dims[1] = n_topo["elements/dims/j"].to_value(); + ele_dims[2] = 0; - num_elems = ele_dims[0] * ele_dims[1]; + num_elems = ele_dims[0] * ele_dims[1]; - if (ndims == 3) - { - ele_dims[2] = n_topo["elements/dims/k"].to_value(); - num_elems *= ele_dims[2]; - } + if (ndims == 3) + { + ele_dims[2] = n_topo["elements/dims/k"].to_value(); + num_elems *= ele_dims[2]; + } - // silo needs the node dims to define a structured grid - pts_dims[0] = ele_dims[0] + 1; - pts_dims[1] = ele_dims[1] + 1; - pts_dims[2] = 1; + // silo needs the node dims to define a structured grid + pts_dims[0] = ele_dims[0] + 1; + pts_dims[1] = ele_dims[1] + 1; + pts_dims[2] = 1; - n_mesh_info[topo_name]["num_elems"].set(num_elems); - n_mesh_info[topo_name]["elements/i"] = ele_dims[0]; - n_mesh_info[topo_name]["elements/j"] = ele_dims[1]; + n_mesh_info[topo_name]["num_elems"].set(num_elems); + n_mesh_info[topo_name]["elements/i"] = ele_dims[0]; + n_mesh_info[topo_name]["elements/j"] = ele_dims[1]; - if (ndims == 3) - { - n_mesh_info[topo_name]["elements/k"] = ele_dims[2]; - pts_dims[2] = ele_dims[2] + 1; - } + if (ndims == 3) + { + n_mesh_info[topo_name]["elements/k"] = ele_dims[2]; + pts_dims[2] = ele_dims[2] + 1; } int base_index[] = {0,0,0}; diff --git a/src/tests/relay/t_relay_io_silo.cpp b/src/tests/relay/t_relay_io_silo.cpp index c1d5c64f5..c22b06e52 100644 --- a/src/tests/relay/t_relay_io_silo.cpp +++ b/src/tests/relay/t_relay_io_silo.cpp @@ -20,1801 +20,1802 @@ using namespace conduit; using namespace conduit::utils; using namespace conduit::relay; -//----------------------------------------------------------------------------- -TEST(conduit_relay_io_silo, conduit_silo_cold_storage) -{ - uint32 a_val = 20; - uint32 b_val = 8; - uint32 c_val = 13; - - Node n; - n["a"] = a_val; - n["b"] = b_val; - n["c"] = c_val; - - EXPECT_EQ(n["a"].as_uint32(), a_val); - EXPECT_EQ(n["b"].as_uint32(), b_val); - EXPECT_EQ(n["c"].as_uint32(), c_val); - - n.print(); - - io::silo_write(n,"tout_cold_storage_test.silo:myobj"); - - Node n_load; - io::silo_read("tout_cold_storage_test.silo:myobj",n_load); - - std::cout << "round trip" << std::endl; - n_load.print(); - - EXPECT_EQ(n_load["a"].as_uint32(), a_val); - EXPECT_EQ(n_load["b"].as_uint32(), b_val); - EXPECT_EQ(n_load["c"].as_uint32(), c_val); -} - -//----------------------------------------------------------------------------- -TEST(conduit_relay_io_silo, conduit_silo_cold_storage_generic_iface) -{ - uint32 a_val = 20; - uint32 b_val = 8; - uint32 c_val = 13; - - Node n; - n["a"] = a_val; - n["b"] = b_val; - n["c"] = c_val; - - EXPECT_EQ(n["a"].as_uint32(), a_val); - EXPECT_EQ(n["b"].as_uint32(), b_val); - EXPECT_EQ(n["c"].as_uint32(), c_val); - - io::save(n, "tout_cold_storage_test_generic_iface.silo:myobj"); - - Node n_load; - io::load("tout_cold_storage_test_generic_iface.silo:myobj",n_load); - - EXPECT_EQ(n_load["a"].as_uint32(), a_val); - EXPECT_EQ(n_load["b"].as_uint32(), b_val); - EXPECT_EQ(n_load["c"].as_uint32(), c_val); -} - -//----------------------------------------------------------------------------- -// test reading in a handful of different overlink files -TEST(conduit_relay_io_silo, load_mesh_geometry) -{ - // TODO: all these files are in overlink symlink format. - // Symlinks may break on Windows (?) - // Could make them overlink format without the symlink. - // But would require modifying the files. - std::vector filename_vec = { - "box2d.silo", - "box3d.silo", - // "diamond.silo", <--- TODO this one fails because polytopal is not yet supported - // TODO: rename these files to be more descriptive. - // would also require modifying the paths stored within the files, - // and re-symlinking - "testDisk2D_a.silo", - // "donordiv.s2_materials2.silo", <--- TODO this one fails because polytopal is not yet supported - "donordiv.s2_materials3.silo" - }; - std::vector dims_vec = {2, 3, /*2,*/ 2, /*2,*/ 2}; - std::vector coordset_length_vec = {4, 8, /*36,*/ 1994, /*16,*/ 961}; - std::vector topology_length_vec = {1, 1, /*33,*/ 1920, /*9,*/ 900}; - for (int i = 0; i < filename_vec.size(); ++i) - { - Node mesh, info; - std::string path = utils::join_file_path("overlink", filename_vec.at(i)); - std::string input_file = relay_test_silo_data_path(path); - io::silo::load_mesh(input_file, mesh); - - EXPECT_TRUE(blueprint::mesh::verify(mesh, info)); - EXPECT_EQ(blueprint::mesh::number_of_domains(mesh), 1); - - const Node &domain = *blueprint::mesh::domains(mesh).front(); - EXPECT_TRUE(domain.has_child("coordsets")); - EXPECT_EQ(domain["coordsets"].number_of_children(), 1); - EXPECT_TRUE(domain.has_child("topologies")); - EXPECT_EQ(domain["topologies"].number_of_children(), 1); - - { // Coordset Validation // - const Node &cset = domain["coordsets"].child(0); - EXPECT_EQ(blueprint::mesh::coordset::dims(cset), dims_vec.at(i)); - EXPECT_EQ(blueprint::mesh::coordset::length(cset), coordset_length_vec.at(i)); - EXPECT_TRUE(blueprint::mesh::coordset::_explicit::verify(cset, info)); - } - - { // Topology Validation // - const Node &topo = domain["topologies"].child(0); - EXPECT_EQ(blueprint::mesh::topology::dims(topo), dims_vec.at(i)); - EXPECT_EQ(blueprint::mesh::topology::length(topo), topology_length_vec.at(i)); - EXPECT_TRUE(blueprint::mesh::topology::unstructured::verify(topo, info)); - } - } -} - -//----------------------------------------------------------------------------- -TEST(conduit_relay_io_silo, round_trip_basic) -{ - const std::vector> mesh_types = { - std::make_pair("uniform", "2"), std::make_pair("uniform", "3"), - std::make_pair("rectilinear", "2"), std::make_pair("rectilinear", "3"), - std::make_pair("structured", "2"), std::make_pair("structured", "3"), - std::make_pair("tris", "2"), - std::make_pair("quads", "2"), - std::make_pair("polygons", "2"), - std::make_pair("tets", "3"), - std::make_pair("hexs", "3"), - std::make_pair("wedges", "3"), - std::make_pair("pyramids", "3"), - // std::make_pair("polyhedra", "3") - }; - for (int i = 0; i < mesh_types.size(); ++i) - { - std::string dim = mesh_types[i].second; - index_t nx = 3; - index_t ny = 4; - index_t nz = (dim == "2" ? 0 : 2); - - const std::string mesh_type = mesh_types[i].first; - - Node save_mesh, load_mesh, info; - blueprint::mesh::examples::basic(mesh_type, nx, ny, nz, save_mesh); - - const std::string basename = "silo_basic_" + mesh_type + "_" + dim + "D"; - const std::string filename = basename + ".root"; - - remove_path_if_exists(filename); - io::silo::save_mesh(save_mesh, basename); - io::silo::load_mesh(filename, load_mesh); - EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - - // make changes to save mesh so the diff will pass - if (mesh_type == "uniform") - { - silo_uniform_to_rect_conversion("coords", "mesh", save_mesh); - } - silo_name_changer("mesh", save_mesh); - - // the loaded mesh will be in the multidomain format - // but the saved mesh is in the single domain format - EXPECT_EQ(load_mesh.number_of_children(), 1); - EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); - EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); - } -} - -//----------------------------------------------------------------------------- -// we are testing vector fields in this test -TEST(conduit_relay_io_silo, round_trip_braid) -{ - const std::vector> mesh_types = { - std::make_pair("uniform", "2"), std::make_pair("uniform", "3"), - std::make_pair("rectilinear", "2"), std::make_pair("rectilinear", "3"), - std::make_pair("structured", "2"), std::make_pair("structured", "3"), - std::make_pair("points", "2"), std::make_pair("points", "3"), - std::make_pair("points_implicit", "2"), std::make_pair("points_implicit", "3"), - std::make_pair("lines", "2"), std::make_pair("lines", "3"), - std::make_pair("tris", "2"), - std::make_pair("quads", "2"), - std::make_pair("tets", "3"), - std::make_pair("hexs", "3"), - std::make_pair("wedges", "3"), - std::make_pair("pyramids", "3"), - // std::make_pair("mixed_2d", "2"), - // std::make_pair("mixed", "3"), - }; - for (int i = 0; i < mesh_types.size(); ++i) - { - std::string dim = mesh_types[i].second; - index_t nx = 3; - index_t ny = 4; - index_t nz = (dim == "2" ? 0 : 2); - - std::string mesh_type = mesh_types[i].first; - - Node save_mesh, load_mesh, info; - blueprint::mesh::examples::braid(mesh_type, nx, ny, nz, save_mesh); - - const std::string basename = "silo_braid_" + mesh_type + "_" + dim + "D"; - const std::string filename = basename + ".cycle_000100.root"; - - // remove existing root file, directory and any output files - remove_path_if_exists(filename); - - io::silo::save_mesh(save_mesh, basename); - io::silo::load_mesh(filename, load_mesh); - EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - - // make changes to save mesh so the diff will pass - if (mesh_type == "uniform") - { - silo_uniform_to_rect_conversion("coords", "mesh", save_mesh); - } - if (mesh_type == "points") - { - // this is custom code for braid - // We know it is correct because the unstructured points version of braid - // uses every point in the coordset - save_mesh["topologies"].remove_child("mesh"); - save_mesh["topologies"]["mesh"]["type"] = "points"; - save_mesh["topologies"]["mesh"]["coordset"] = "coords"; - } - if (mesh_type == "points_implicit" || mesh_type == "points") - { - // the association doesn't matter for point meshes - // we choose vertex by convention - save_mesh["fields"]["radial"]["association"].reset(); - save_mesh["fields"]["radial"]["association"] = "vertex"; - } - silo_name_changer("mesh", save_mesh); - - // the loaded mesh will be in the multidomain format - // but the saved mesh is in the single domain format - EXPECT_EQ(load_mesh.number_of_children(), 1); - EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); - EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); - } -} - -//----------------------------------------------------------------------------- -// multidomain test -TEST(conduit_relay_io_silo, round_trip_spiral) -{ - for (int ndomains = 2; ndomains < 6; ndomains ++) - { - Node save_mesh, load_mesh, info; - blueprint::mesh::examples::spiral(ndomains, save_mesh); - - const std::string basename = "silo_spiral_" + std::to_string(ndomains) + "_domains"; - const std::string filename = basename + ".cycle_000000.root"; - - remove_path_if_exists(filename); - io::silo::save_mesh(save_mesh, basename); - io::silo::load_mesh(filename, load_mesh); - EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); - - // make changes to save mesh so the diff will pass - for (index_t child = 0; child < save_mesh.number_of_children(); child ++) - { - silo_name_changer("mesh", save_mesh[child]); - } - - EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); - NodeConstIterator l_itr = load_mesh.children(); - NodeConstIterator s_itr = save_mesh.children(); - while (l_itr.has_next()) - { - const Node &l_curr = l_itr.next(); - const Node &s_curr = s_itr.next(); - - EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); - } - } -} - -//----------------------------------------------------------------------------- -TEST(conduit_relay_io_silo, round_trip_julia) -{ - Node save_mesh, load_mesh, info; - blueprint::mesh::examples::julia(5, // nx - 5, // ny - 0, // x_min - 10, // x_max - 2, // y_min - 7, // y_max - 3, // c_re - 4, // c_im - save_mesh); - - const std::string basename = "silo_julia"; - const std::string filename = basename + ".root"; - - remove_path_if_exists(filename); - io::silo::save_mesh(save_mesh, basename); - io::silo::load_mesh(filename, load_mesh); - EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - - // make changes to save mesh so the diff will pass - silo_name_changer("mesh", save_mesh); - - // the loaded mesh will be in the multidomain format - // but the saved mesh is in the single domain format - EXPECT_EQ(load_mesh.number_of_children(), 1); - EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); - EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); -} - -//----------------------------------------------------------------------------- -// test material write and read -TEST(conduit_relay_io_silo, round_trip_venn) -{ - std::string matset_type = "sparse_by_element"; - for (int j = 0; j < 2; j ++) - { - Node save_mesh, sbe, load_mesh, info; - std::string size; - int nx, ny; - const double radius = 0.25; - if (j == 0) - { - size = "small"; - nx = ny = 4; - } - else - { - size = "large"; - nx = ny = 100; - } - blueprint::mesh::examples::venn(matset_type, nx, ny, radius, save_mesh); - - const std::string basename = "silo_venn_" + matset_type + "_" + size; - const std::string filename = basename + ".root"; - - remove_path_if_exists(filename); - io::silo::save_mesh(save_mesh, basename); - io::silo::load_mesh(filename, load_mesh); - EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - - // make changes to save mesh so the diff will pass - - // The field mat_check has values that are one type and matset_values - // that are another type. The silo writer converts both to double arrays - // in this case, so we follow suit. - Node mat_check_new_values, mat_check_new_matset_values; - save_mesh["fields"]["mat_check"]["values"].to_double_array(mat_check_new_values); - save_mesh["fields"]["mat_check"]["matset_values"].to_double_array(mat_check_new_matset_values); - save_mesh["fields"]["mat_check"]["values"].set_external(mat_check_new_values); - save_mesh["fields"]["mat_check"]["matset_values"].set_external(mat_check_new_matset_values); - - silo_name_changer("mesh", save_mesh); - - // the loaded mesh will be in the multidomain format - // but the saved mesh is in the single domain format - EXPECT_EQ(load_mesh.number_of_children(), 1); - EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); - EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); - } -} - -//----------------------------------------------------------------------------- -TEST(conduit_relay_io_silo, round_trip_venn_modded_matnos) -{ - const std::string matset_type = "sparse_by_element"; - Node save_mesh, load_mesh, info; - const int nx = 4; - const int ny = 4; - const double radius = 0.25; - blueprint::mesh::examples::venn(matset_type, nx, ny, radius, save_mesh); - - auto replace_matno = [](int matno) - { - return (matno == 1 ? 15 : - (matno == 2 ? 37 : - (matno == 3 ? 4 : - (matno == 0 ? 22 : - -1)))); - }; - - auto matmap_itr = save_mesh["matsets"]["matset"]["material_map"].children(); - while (matmap_itr.has_next()) - { - Node &mat = matmap_itr.next(); - mat.set(replace_matno(mat.as_int())); - } - - int_array matids = save_mesh["matsets"]["matset"]["material_ids"].value(); - for (int i = 0; i < save_mesh["matsets"]["matset"]["material_ids"].dtype().number_of_elements(); i ++) - { - matids[i] = replace_matno(matids[i]); - } - - const std::string silo_basename = "silo_venn_" + matset_type + "_modded_matnos"; - const std::string silo_filename = silo_basename + ".root"; - remove_path_if_exists(silo_filename); - io::silo::save_mesh(save_mesh, silo_basename); - - const std::string bp_basename = "bp_venn_" + matset_type + "_modded_matnos"; - const std::string bp_filename = bp_basename + ".root"; - remove_path_if_exists(bp_filename); - io::blueprint::save_mesh(save_mesh, bp_basename, "hdf5"); - - io::silo::load_mesh(silo_filename, load_mesh); - EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - - // make changes to save mesh so the diff will pass - - // The field mat_check has values that are one type and matset_values - // that are another type. The silo writer converts both to double arrays - // in this case, so we follow suit. - Node mat_check_new_values, mat_check_new_matset_values; - save_mesh["fields"]["mat_check"]["values"].to_double_array(mat_check_new_values); - save_mesh["fields"]["mat_check"]["matset_values"].to_double_array(mat_check_new_matset_values); - save_mesh["fields"]["mat_check"]["values"].set_external(mat_check_new_values); - save_mesh["fields"]["mat_check"]["matset_values"].set_external(mat_check_new_matset_values); - - // to_silo is going to reorder mixed materials least to greatest - // so we must do the same - int_array mat_ids = save_mesh["matsets"]["matset"]["material_ids"].value(); - const auto mat_id10 = mat_ids[10]; - const auto mat_id11 = mat_ids[11]; - const auto mat_id12 = mat_ids[12]; - mat_ids[10] = mat_id12; - mat_ids[11] = mat_id10; - mat_ids[12] = mat_id11; - auto field_itr = save_mesh["fields"].children(); - while (field_itr.has_next()) - { - const Node &n_field = field_itr.next(); - if (n_field.has_child("matset")) - { - double_array matset_vals = n_field["matset_values"].value(); - const auto matset_val10 = matset_vals[10]; - const auto matset_val11 = matset_vals[11]; - const auto matset_val12 = matset_vals[12]; - matset_vals[10] = matset_val12; - matset_vals[11] = matset_val10; - matset_vals[12] = matset_val11; - } - } - - silo_name_changer("mesh", save_mesh); - - // the loaded mesh will be in the multidomain format - // but the saved mesh is in the single domain format - EXPECT_EQ(load_mesh.number_of_children(), 1); - EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); - - EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); -} - -//----------------------------------------------------------------------------- -TEST(conduit_relay_io_silo, round_trip_spiral_multi_dom_materials) -{ - Node save_mesh, load_mesh, info; - const int ndomains = 4; - blueprint::mesh::examples::spiral(ndomains, save_mesh); - add_matset_to_spiral(save_mesh, ndomains); - EXPECT_TRUE(blueprint::mesh::verify(save_mesh, info)); - - const std::string basename = "silo_multidom_materials_spiral"; - const std::string filename = basename + ".cycle_000000.root"; - - remove_path_if_exists(filename); - io::silo::save_mesh(save_mesh, basename); - io::silo::load_mesh(filename, load_mesh); - EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); - - // make changes to save mesh so the diff will pass - for (index_t child = 0; child < save_mesh.number_of_children(); child ++) - { - // get the matset for this domain - Node &n_matset = save_mesh[child]["matsets"]["matset"]; - - // clean up volume fractions - Node vf_arr; - n_matset["volume_fractions"].to_float64_array(vf_arr); - n_matset["volume_fractions"].reset(); - n_matset["volume_fractions"].set(vf_arr); - - // cheat a little bit - we don't have these to start - n_matset["sizes"].set_external(load_mesh[child]["matsets"]["mesh_matset"]["sizes"]); - n_matset["offsets"].set_external(load_mesh[child]["matsets"]["mesh_matset"]["offsets"]); - - silo_name_changer("mesh", save_mesh[child]); - } - - EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); - NodeConstIterator l_itr = load_mesh.children(); - NodeConstIterator s_itr = save_mesh.children(); - while (l_itr.has_next()) - { - const Node &l_curr = l_itr.next(); - const Node &s_curr = s_itr.next(); - - EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); - } -} - -//----------------------------------------------------------------------------- -TEST(conduit_relay_io_silo, round_trip_grid_adjset) -{ - Node save_mesh, load_mesh, info; - blueprint::mesh::examples::grid("structured", 3, 3, 1, 2, 2, 1, save_mesh); - - // we need a material in order for this to be valid overlink - for (index_t child = 0; child < save_mesh.number_of_children(); child ++) - { - add_multi_buffer_full_matset(save_mesh[child], 4, "mesh"); - } - - const std::string basename = "silo_grid_adjset"; - const std::string filename = basename + "/OvlTop.silo"; - - Node write_opts; - write_opts["file_style"] = "overlink"; - write_opts["ovl_topo_name"] = "mesh"; - - Node read_opts; - read_opts["matset_style"] = "multi_buffer_full"; - - remove_path_if_exists(filename); - io::silo::save_mesh(save_mesh, basename, write_opts); - io::blueprint::save_mesh(save_mesh, basename, "hdf5"); - io::silo::load_mesh(filename, read_opts, load_mesh); - EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - - for (index_t child = 0; child < save_mesh.number_of_children(); child ++) - { - // separate out vector fields - Node &field_vel = save_mesh[child]["fields"]["vel"]; - Node &field_vel_u = save_mesh[child]["fields"]["vel_u"]; - Node &field_vel_v = save_mesh[child]["fields"]["vel_v"]; - - field_vel_u["topology"].set(field_vel["topology"]); - field_vel_u["association"].set(field_vel["association"]); - field_vel_u["values"].set(field_vel["values/u"]); - field_vel_v["topology"].set(field_vel["topology"]); - field_vel_v["association"].set(field_vel["association"]); - field_vel_v["values"].set(field_vel["values/v"]); - - save_mesh[child]["fields"].remove_child("vel"); - - // make adjset pairwise - Node &pairwise_adjset = save_mesh[child]["adjsets"]["adjset"]; - conduit::blueprint::mesh::adjset::to_pairwise(save_mesh[child]["adjsets"]["mesh_adj"], pairwise_adjset); - save_mesh[child]["adjsets"].remove_child("mesh_adj"); - - // make changes to save mesh so the diff will pass - overlink_name_changer(save_mesh[child]); - } - - EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); - NodeConstIterator l_itr = load_mesh.children(); - NodeConstIterator s_itr = save_mesh.children(); - while (l_itr.has_next()) - { - const Node &l_curr = l_itr.next(); - const Node &s_curr = s_itr.next(); - - EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); - } -} - -//----------------------------------------------------------------------------- -// -// test read and write semantics -// - -//----------------------------------------------------------------------------- -TEST(conduit_relay_io_silo, read_and_write_semantics) -{ - for (int ndomains = 2; ndomains < 6; ndomains ++) - { - Node save_mesh, load_mesh, info; - blueprint::mesh::examples::spiral(ndomains, save_mesh); - - const std::string basename = "silo_spiral_" + std::to_string(ndomains) + "_domains"; - const std::string filename = basename + ".cycle_000000.root"; - - remove_path_if_exists(filename); - io::silo::write_mesh(save_mesh, basename); - io::silo::read_mesh(filename, load_mesh); - EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); - - // make changes to save mesh so the diff will pass - for (index_t child = 0; child < save_mesh.number_of_children(); child ++) - { - silo_name_changer("mesh", save_mesh[child]); - } - - EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); - NodeConstIterator l_itr = load_mesh.children(); - NodeConstIterator s_itr = save_mesh.children(); - while (l_itr.has_next()) - { - const Node &l_curr = l_itr.next(); - const Node &s_curr = s_itr.next(); - - EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); - } - } -} - -//----------------------------------------------------------------------------- -// -// special case tests -// - -//----------------------------------------------------------------------------- -// var is not defined on a domain -// -// tests the silo "EMPTY" capability -TEST(conduit_relay_io_silo, missing_domain_var) -{ - Node save_mesh, load_mesh, info; - const int ndomains = 4; - blueprint::mesh::examples::spiral(ndomains, save_mesh); - - // remove information for a particular domain - save_mesh[2]["fields"].remove_child("dist"); - - const std::string basename = "silo_missing_domain_var_spiral"; - const std::string filename = basename + ".cycle_000000.root"; - - remove_path_if_exists(filename); - io::silo::save_mesh(save_mesh, basename); - io::silo::load_mesh(filename, load_mesh); - EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); - - // make changes to save mesh so the diff will pass - for (index_t child = 0; child < save_mesh.number_of_children(); child ++) - { - silo_name_changer("mesh", save_mesh[child]); - } - save_mesh[2].remove_child("fields"); - - EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); - NodeConstIterator l_itr = load_mesh.children(); - NodeConstIterator s_itr = save_mesh.children(); - while (l_itr.has_next()) - { - const Node &l_curr = l_itr.next(); - const Node &s_curr = s_itr.next(); - - EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); - } -} - -//----------------------------------------------------------------------------- -// matset is not defined on a domain -// -// tests the silo "EMPTY" capability -TEST(conduit_relay_io_silo, missing_domain_matset) -{ - Node save_mesh, load_mesh, info; - const int ndomains = 4; - blueprint::mesh::examples::spiral(ndomains, save_mesh); - add_matset_to_spiral(save_mesh, ndomains); - EXPECT_TRUE(blueprint::mesh::verify(save_mesh, info)); - - // remove information for a particular domain - save_mesh[2]["matsets"].remove_child("matset"); - - const std::string basename = "silo_missing_domain_matset_spiral"; - const std::string filename = basename + ".cycle_000000.root"; - - remove_path_if_exists(filename); - io::silo::save_mesh(save_mesh, basename); - io::silo::load_mesh(filename, load_mesh); - EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); - - // make changes to save mesh so the diff will pass - for (index_t child = 0; child < save_mesh.number_of_children(); child ++) - { - if (save_mesh[child].has_path("matsets/matset")) - { - // get the matset for this domain - Node &n_matset = save_mesh[child]["matsets"]["matset"]; - - // clean up volume fractions - Node vf_arr; - n_matset["volume_fractions"].to_float64_array(vf_arr); - n_matset["volume_fractions"].reset(); - n_matset["volume_fractions"].set(vf_arr); - - // cheat a little bit - we don't have these to start - n_matset["sizes"].set_external(load_mesh[child]["matsets"]["mesh_matset"]["sizes"]); - n_matset["offsets"].set_external(load_mesh[child]["matsets"]["mesh_matset"]["offsets"]); - } - - silo_name_changer("mesh", save_mesh[child]); - } - save_mesh[2].remove_child("matsets"); - - EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); - NodeConstIterator l_itr = load_mesh.children(); - NodeConstIterator s_itr = save_mesh.children(); - while (l_itr.has_next()) - { - const Node &l_curr = l_itr.next(); - const Node &s_curr = s_itr.next(); - - EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); - } -} - -//----------------------------------------------------------------------------- -// mesh is not defined on a domain -// -// This case is much less interesting. -// data passes through the clean mesh filter which -// deletes domains that are missing topos. -// They simply are not part of the mesh and so silo -// doesn't have to deal with it. -TEST(conduit_relay_io_silo, missing_domain_mesh_trivial) -{ - Node save_mesh, load_mesh, info; - const int ndomains = 4; - blueprint::mesh::examples::spiral(ndomains, save_mesh); - - // remove information for a particular domain - save_mesh[2]["topologies"].remove_child("topo"); - - const std::string basename = "silo_missing_domain_mesh_trivial_spiral"; - const std::string filename = basename + ".cycle_000000.root"; - - remove_path_if_exists(filename); - io::silo::save_mesh(save_mesh, basename); - io::silo::load_mesh(filename, load_mesh); - - EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); - - // make changes to save mesh so the diff will pass - save_mesh.remove(2); - save_mesh.rename_child("domain_000003", "domain_000002"); - save_mesh[2]["state"]["domain_id"].reset(); - save_mesh[2]["state"]["domain_id"] = 2; - for (index_t child = 0; child < save_mesh.number_of_children(); child ++) - { - silo_name_changer("mesh", save_mesh[child]); - } - - EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); - NodeConstIterator l_itr = load_mesh.children(); - NodeConstIterator s_itr = save_mesh.children(); - while (l_itr.has_next()) - { - const Node &l_curr = l_itr.next(); - const Node &s_curr = s_itr.next(); - - EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); - } -} - -//----------------------------------------------------------------------------- -// mesh is not defined on a domain but there are multiple meshes -TEST(conduit_relay_io_silo, missing_domain_mesh) -{ - Node save_mesh, save_mesh2, load_mesh, load_mesh2, info, opts; - const int ndomains = 4; - blueprint::mesh::examples::spiral(ndomains, save_mesh); - blueprint::mesh::examples::spiral(ndomains, save_mesh2); - - for (index_t child = 0; child < save_mesh.number_of_children(); child ++) - { - save_mesh[child]["coordsets"].rename_child("coords", "coords2"); - save_mesh[child]["topologies"]["topo"]["coordset"].reset(); - save_mesh[child]["topologies"]["topo"]["coordset"] = "coords2"; - save_mesh[child]["topologies"].rename_child("topo", "topo2"); - save_mesh[child]["fields"]["dist"]["topology"].reset(); - save_mesh[child]["fields"]["dist"]["topology"] = "topo2"; - save_mesh[child]["fields"].rename_child("dist", "dist2"); - - save_mesh[child]["coordsets"]["coords"].set_external(save_mesh2[child]["coordsets"]["coords"]); - save_mesh[child]["topologies"]["topo"].set_external(save_mesh2[child]["topologies"]["topo"]); - save_mesh[child]["fields"]["dist"].set_external(save_mesh2[child]["fields"]["dist"]); - } - - // remove information for a particular domain - save_mesh[2]["topologies"].remove_child("topo"); - - const std::string basename = "silo_missing_domain_mesh_spiral"; - const std::string filename = basename + ".cycle_000000.root"; - - remove_path_if_exists(filename); - io::silo::save_mesh(save_mesh, basename); - opts["silo_names"]["multimesh_names"] = "mesh_topo2"; - io::silo::load_mesh(filename, opts, load_mesh); - opts["silo_names"]["multimesh_names"] = "mesh_topo"; - io::silo::load_mesh(filename, opts, load_mesh2); - EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - EXPECT_TRUE(blueprint::mesh::verify(load_mesh2, info)); - - // make changes to save mesh so the diff will pass - save_mesh[2]["coordsets"].remove_child("coords"); - save_mesh[2]["fields"].remove_child("dist"); - for (index_t child = 0; child < save_mesh.number_of_children(); child ++) - { - silo_name_changer("mesh", save_mesh[child]); - } - - // we must merge the two meshes in load mesh - // the indexing is tricky because one is missing a domain - load_mesh[0]["coordsets"]["mesh_topo"].set_external(load_mesh2[0]["coordsets"]["mesh_topo"]); - load_mesh[0]["topologies"]["mesh_topo"].set_external(load_mesh2[0]["topologies"]["mesh_topo"]); - load_mesh[0]["fields"]["mesh_dist"].set_external(load_mesh2[0]["fields"]["mesh_dist"]); - load_mesh[1]["coordsets"]["mesh_topo"].set_external(load_mesh2[1]["coordsets"]["mesh_topo"]); - load_mesh[1]["topologies"]["mesh_topo"].set_external(load_mesh2[1]["topologies"]["mesh_topo"]); - load_mesh[1]["fields"]["mesh_dist"].set_external(load_mesh2[1]["fields"]["mesh_dist"]); - load_mesh[3]["coordsets"]["mesh_topo"].set_external(load_mesh2[2]["coordsets"]["mesh_topo"]); - load_mesh[3]["topologies"]["mesh_topo"].set_external(load_mesh2[2]["topologies"]["mesh_topo"]); - load_mesh[3]["fields"]["mesh_dist"].set_external(load_mesh2[2]["fields"]["mesh_dist"]); - - EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); - NodeConstIterator l_itr = load_mesh.children(); - NodeConstIterator s_itr = save_mesh.children(); - while (l_itr.has_next()) - { - const Node &l_curr = l_itr.next(); - const Node &s_curr = s_itr.next(); - - EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); - } -} - -//----------------------------------------------------------------------------- -// explicit points (unstructured mesh) do not use every coord -TEST(conduit_relay_io_silo, unstructured_points) -{ - Node save_mesh, load_mesh, info; - blueprint::mesh::examples::braid("points", 2, 2, 2, save_mesh); - - std::vector new_conn; - std::vector new_field1; - std::vector new_field2; - std::vector new_xcoords, new_ycoords, new_zcoords; - - int_accessor conn = save_mesh["topologies"]["mesh"]["elements"]["connectivity"].value(); - - float_accessor field1 = save_mesh["fields"]["braid"]["values"].value(); - float_accessor field2 = save_mesh["fields"]["radial"]["values"].value(); - - float_accessor xcoords = save_mesh["coordsets"]["coords"]["values"]["x"].value(); - float_accessor ycoords = save_mesh["coordsets"]["coords"]["values"]["y"].value(); - float_accessor zcoords = save_mesh["coordsets"]["coords"]["values"]["z"].value(); - - for (int i = 1; i < conn.number_of_elements(); i += 2) - { - new_conn.push_back(conn[i]); - new_field1.push_back(field1[i]); - new_field2.push_back(field2[i]); - - new_xcoords.push_back(xcoords[conn[i]]); - new_ycoords.push_back(ycoords[conn[i]]); - new_zcoords.push_back(zcoords[conn[i]]); - } - save_mesh["topologies"]["mesh"]["elements"]["connectivity"].reset(); - save_mesh["topologies"]["mesh"]["elements"]["connectivity"].set(new_conn); - - save_mesh["fields"].remove_child("vel"); - save_mesh["fields"]["braid"]["values"].reset(); - save_mesh["fields"]["braid"]["values"].set(new_field1); - save_mesh["fields"]["radial"]["values"].reset(); - save_mesh["fields"]["radial"]["values"].set(new_field2); - - // we have modified braid such that it only uses half of the points in the coordset - - const std::string basename = "silo_unstructured_points_braid"; - const std::string filename = basename + ".cycle_000100.root"; - - // remove existing root file, directory and any output files - remove_path_if_exists(filename); - - io::silo::save_mesh(save_mesh, basename); - io::silo::load_mesh(filename, load_mesh); - EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - - // now we must remove the unused points and change to an implicit points topo so that the diff passes - save_mesh["coordsets"]["coords"]["values"]["x"].reset(); - save_mesh["coordsets"]["coords"]["values"]["x"].set(new_xcoords); - save_mesh["coordsets"]["coords"]["values"]["y"].reset(); - save_mesh["coordsets"]["coords"]["values"]["y"].set(new_ycoords); - save_mesh["coordsets"]["coords"]["values"]["z"].reset(); - save_mesh["coordsets"]["coords"]["values"]["z"].set(new_zcoords); - - save_mesh["topologies"].remove_child("mesh"); - save_mesh["topologies"]["mesh"]["type"] = "points"; - save_mesh["topologies"]["mesh"]["coordset"] = "coords"; - - // the association doesn't matter for point meshes - // we choose vertex by convention - save_mesh["fields"]["radial"]["association"].reset(); - save_mesh["fields"]["radial"]["association"] = "vertex"; - - silo_name_changer("mesh", save_mesh); - - // the loaded mesh will be in the multidomain format - // but the saved mesh is in the single domain format - EXPECT_EQ(load_mesh.number_of_children(), 1); - EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); - - EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); -} - -//----------------------------------------------------------------------------- - -// -// save option tests -// - -// save options: -/// opts: -/// -/// file_style: "default", "root_only", "multi_file", "overlink" -/// when # of domains == 1, "default" ==> "root_only" -/// else, "default" ==> "multi_file" -/// -/// silo_type: "default", "pdb", "hdf5", "unknown" -/// when the file we are writing to exists, "default" ==> "unknown" -/// else, "default" ==> "hdf5" -/// note: these are additional silo_type options that we could add -/// support for in the future: -/// "hdf5_sec2", "hdf5_stdio", "hdf5_mpio", "hdf5_mpiposix", "taurus" -/// -/// suffix: "default", "cycle", "none" -/// when cycle is present, "default" ==> "cycle" -/// else, "default" ==> "none" -/// -/// root_file_ext: "default", "root", "silo" -/// "default" ==> "root" -/// if overlink, this parameter is unused. -/// -/// mesh_name: (used if present, default ==> "mesh") -/// -/// ovl_topo_name: (used if present, default ==> "") -/// -/// number_of_files: {# of files} -/// when "multi_file" or "overlink": -/// <= 0, use # of files == # of domains -/// > 0, # of files == number_of_files - -//----------------------------------------------------------------------------- -TEST(conduit_relay_io_silo, round_trip_save_option_file_style) -{ - // we will do overlink tests separately - const std::vector file_styles = {"default", "root_only", "multi_file"}; - for (int i = 0; i < file_styles.size(); i ++) - { - Node opts; - opts["file_style"] = file_styles[i]; - - const std::string basename = "silo_save_option_file_style_" + file_styles[i] + "_spiral"; - const std::string filename = basename + ".cycle_000000.root"; - - for (int ndomains = 1; ndomains < 5; ndomains += 3) - { - Node save_mesh, load_mesh, info; - blueprint::mesh::examples::spiral(ndomains, save_mesh); - remove_path_if_exists(filename); - io::silo::save_mesh(save_mesh, basename, opts); - io::silo::load_mesh(filename, load_mesh); - EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); - - // make changes to save mesh so the diff will pass - for (index_t child = 0; child < save_mesh.number_of_children(); child ++) - { - silo_name_changer("mesh", save_mesh[child]); - } - - EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); - NodeConstIterator l_itr = load_mesh.children(); - NodeConstIterator s_itr = save_mesh.children(); - while (l_itr.has_next()) - { - const Node &l_curr = l_itr.next(); - const Node &s_curr = s_itr.next(); - - EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); - } - } - } -} - -//----------------------------------------------------------------------------- -TEST(conduit_relay_io_silo, round_trip_save_option_number_of_files) -{ - const std::vector number_of_files = {-1, 2}; - for (int i = 0; i < number_of_files.size(); i ++) - { - Node opts; - opts["file_style"] = "multi_file"; - opts["number_of_files"] = number_of_files[i]; - - const std::string basename = "silo_save_option_number_of_files_" + - std::to_string(number_of_files[i]) + - "_spiral"; - const std::string filename = basename + ".cycle_000000.root"; - - const int ndomains = 5; - - Node save_mesh, load_mesh, info; - blueprint::mesh::examples::spiral(ndomains, save_mesh); - - remove_path_if_exists(filename); - io::silo::save_mesh(save_mesh, basename, opts); - io::silo::load_mesh(filename, load_mesh); - EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); - - // make changes to save mesh so the diff will pass - for (index_t child = 0; child < save_mesh.number_of_children(); child ++) - { - silo_name_changer("mesh", save_mesh[child]); - } - - EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); - NodeConstIterator l_itr = load_mesh.children(); - NodeConstIterator s_itr = save_mesh.children(); - while (l_itr.has_next()) - { - const Node &l_curr = l_itr.next(); - const Node &s_curr = s_itr.next(); - - EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); - } - } -} - -//----------------------------------------------------------------------------- -TEST(conduit_relay_io_silo, round_trip_save_option_suffix) -{ - const std::vector suffixes = {"default", "default", "cycle", "none"}; - const std::vector file_suffixes = { - "", // cycle is not present - ".cycle_000005", // cycle is present - ".cycle_000005", // cycle is turned on - "", // cycle is turned off - }; - const std::vector include_cycle = {"no", "yes", "yes", "yes"}; - for (int i = 0; i < suffixes.size(); i ++) - { - Node opts; - opts["suffix"] = suffixes[i]; - - const std::string basename = "silo_save_option_suffix_" + suffixes[i] + - "_" + include_cycle[i] + "_basic"; - const std::string filename = basename + file_suffixes[i] + ".root"; - - Node save_mesh, load_mesh, info; - blueprint::mesh::examples::basic("rectilinear", 3, 4, 0, save_mesh); - - if (include_cycle[i] == "yes") - { - save_mesh["state/cycle"] = 5; - } - - remove_path_if_exists(filename); - io::silo::save_mesh(save_mesh, basename, opts); - io::silo::load_mesh(filename, load_mesh); - EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - - silo_name_changer("mesh", save_mesh); - - // the loaded mesh will be in the multidomain format - // but the saved mesh is in the single domain format - EXPECT_EQ(load_mesh.number_of_children(), 1); - EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); - - EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); - } -} - -//----------------------------------------------------------------------------- -TEST(conduit_relay_io_silo, round_trip_save_option_root_file_ext) -{ - const std::vector root_file_exts = {"default", "root", "silo"}; - - for (int i = 0; i < root_file_exts.size(); i ++) - { - Node opts; - opts["root_file_ext"] = root_file_exts[i]; - - std::string actual_file_ext = root_file_exts[i]; - if (actual_file_ext == "default") - { - actual_file_ext = "root"; - } - - const std::string basename = "round_trip_save_option_root_file_ext_" + - root_file_exts[i] + "_basic"; - const std::string filename = basename + "." + actual_file_ext; - - Node save_mesh, load_mesh, info; - blueprint::mesh::examples::basic("rectilinear", 3, 4, 0, save_mesh); - remove_path_if_exists(filename); - io::silo::save_mesh(save_mesh, basename, opts); - io::silo::load_mesh(filename, load_mesh); - EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - - silo_name_changer("mesh", save_mesh); - - // the loaded mesh will be in the multidomain format - // but the saved mesh is in the single domain format - EXPECT_EQ(load_mesh.number_of_children(), 1); - EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); - EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); - } -} - -//----------------------------------------------------------------------------- -TEST(conduit_relay_io_silo, round_trip_save_option_mesh_name) -{ - const std::string basename = "silo_save_option_mesh_name_basic"; - const std::string filename = basename + ".root"; - - Node opts; - opts["mesh_name"] = "mymesh"; - - Node save_mesh, load_mesh, info; - blueprint::mesh::examples::basic("rectilinear", 3, 4, 0, save_mesh); - remove_path_if_exists(filename); - io::silo::save_mesh(save_mesh, basename, opts); - io::silo::load_mesh(filename, load_mesh); - EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - - silo_name_changer("mymesh", save_mesh); - - // the loaded mesh will be in the multidomain format - // but the saved mesh is in the single domain format - EXPECT_EQ(load_mesh.number_of_children(), 1); - EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); - EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); -} - -//----------------------------------------------------------------------------- -TEST(conduit_relay_io_silo, round_trip_save_option_silo_type) -{ - const std::vector silo_types = {"default", "pdb", "hdf5", "unknown"}; - for (int i = 3; i < silo_types.size(); i ++) - { - Node opts; - opts["silo_type"] = silo_types[i]; - - Node save_mesh, load_mesh, info; - blueprint::mesh::examples::basic("rectilinear", 3, 4, 0, save_mesh); - - const std::string basename = "silo_save_option_silo_type_" + silo_types[i] + "_basic"; - const std::string filename = basename + ".root"; - - remove_path_if_exists(filename); - io::silo::save_mesh(save_mesh, basename, opts); - io::silo::load_mesh(filename, load_mesh); - EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); +// //----------------------------------------------------------------------------- +// TEST(conduit_relay_io_silo, conduit_silo_cold_storage) +// { +// uint32 a_val = 20; +// uint32 b_val = 8; +// uint32 c_val = 13; + +// Node n; +// n["a"] = a_val; +// n["b"] = b_val; +// n["c"] = c_val; + +// EXPECT_EQ(n["a"].as_uint32(), a_val); +// EXPECT_EQ(n["b"].as_uint32(), b_val); +// EXPECT_EQ(n["c"].as_uint32(), c_val); + +// n.print(); + +// io::silo_write(n,"tout_cold_storage_test.silo:myobj"); + +// Node n_load; +// io::silo_read("tout_cold_storage_test.silo:myobj",n_load); + +// std::cout << "round trip" << std::endl; +// n_load.print(); + +// EXPECT_EQ(n_load["a"].as_uint32(), a_val); +// EXPECT_EQ(n_load["b"].as_uint32(), b_val); +// EXPECT_EQ(n_load["c"].as_uint32(), c_val); +// } + +// //----------------------------------------------------------------------------- +// TEST(conduit_relay_io_silo, conduit_silo_cold_storage_generic_iface) +// { +// uint32 a_val = 20; +// uint32 b_val = 8; +// uint32 c_val = 13; + +// Node n; +// n["a"] = a_val; +// n["b"] = b_val; +// n["c"] = c_val; + +// EXPECT_EQ(n["a"].as_uint32(), a_val); +// EXPECT_EQ(n["b"].as_uint32(), b_val); +// EXPECT_EQ(n["c"].as_uint32(), c_val); + +// io::save(n, "tout_cold_storage_test_generic_iface.silo:myobj"); + +// Node n_load; +// io::load("tout_cold_storage_test_generic_iface.silo:myobj",n_load); + +// EXPECT_EQ(n_load["a"].as_uint32(), a_val); +// EXPECT_EQ(n_load["b"].as_uint32(), b_val); +// EXPECT_EQ(n_load["c"].as_uint32(), c_val); +// } + +// //----------------------------------------------------------------------------- +// // test reading in a handful of different overlink files +// TEST(conduit_relay_io_silo, load_mesh_geometry) +// { +// // TODO: all these files are in overlink symlink format. +// // Symlinks may break on Windows (?) +// // Could make them overlink format without the symlink. +// // But would require modifying the files. +// std::vector filename_vec = { +// "box2d.silo", +// "box3d.silo", +// // "diamond.silo", <--- TODO this one fails because polytopal is not yet supported +// // TODO: rename these files to be more descriptive. +// // would also require modifying the paths stored within the files, +// // and re-symlinking +// "testDisk2D_a.silo", +// // "donordiv.s2_materials2.silo", <--- TODO this one fails because polytopal is not yet supported +// "donordiv.s2_materials3.silo" +// }; +// std::vector dims_vec = {2, 3, /*2,*/ 2, /*2,*/ 2}; +// std::vector coordset_length_vec = {4, 8, /*36,*/ 1994, /*16,*/ 961}; +// std::vector topology_length_vec = {1, 1, /*33,*/ 1920, /*9,*/ 900}; +// for (int i = 0; i < filename_vec.size(); ++i) +// { +// Node mesh, info; +// std::string path = utils::join_file_path("overlink", filename_vec.at(i)); +// std::string input_file = relay_test_silo_data_path(path); +// io::silo::load_mesh(input_file, mesh); + +// EXPECT_TRUE(blueprint::mesh::verify(mesh, info)); +// EXPECT_EQ(blueprint::mesh::number_of_domains(mesh), 1); + +// const Node &domain = *blueprint::mesh::domains(mesh).front(); +// EXPECT_TRUE(domain.has_child("coordsets")); +// EXPECT_EQ(domain["coordsets"].number_of_children(), 1); +// EXPECT_TRUE(domain.has_child("topologies")); +// EXPECT_EQ(domain["topologies"].number_of_children(), 1); + +// { // Coordset Validation // +// const Node &cset = domain["coordsets"].child(0); +// EXPECT_EQ(blueprint::mesh::coordset::dims(cset), dims_vec.at(i)); +// EXPECT_EQ(blueprint::mesh::coordset::length(cset), coordset_length_vec.at(i)); +// EXPECT_TRUE(blueprint::mesh::coordset::_explicit::verify(cset, info)); +// } + +// { // Topology Validation // +// const Node &topo = domain["topologies"].child(0); +// EXPECT_EQ(blueprint::mesh::topology::dims(topo), dims_vec.at(i)); +// EXPECT_EQ(blueprint::mesh::topology::length(topo), topology_length_vec.at(i)); +// EXPECT_TRUE(blueprint::mesh::topology::unstructured::verify(topo, info)); +// } +// } +// } + +// //----------------------------------------------------------------------------- +// TEST(conduit_relay_io_silo, round_trip_basic) +// { +// const std::vector> mesh_types = { +// std::make_pair("uniform", "2"), std::make_pair("uniform", "3"), +// std::make_pair("rectilinear", "2"), std::make_pair("rectilinear", "3"), +// std::make_pair("structured", "2"), std::make_pair("structured", "3"), +// std::make_pair("tris", "2"), +// std::make_pair("quads", "2"), +// std::make_pair("polygons", "2"), +// std::make_pair("tets", "3"), +// std::make_pair("hexs", "3"), +// std::make_pair("wedges", "3"), +// std::make_pair("pyramids", "3"), +// // std::make_pair("polyhedra", "3") +// }; +// for (int i = 0; i < mesh_types.size(); ++i) +// { +// std::string dim = mesh_types[i].second; +// index_t nx = 3; +// index_t ny = 4; +// index_t nz = (dim == "2" ? 0 : 2); + +// const std::string mesh_type = mesh_types[i].first; + +// Node save_mesh, load_mesh, info; +// blueprint::mesh::examples::basic(mesh_type, nx, ny, nz, save_mesh); + +// const std::string basename = "silo_basic_" + mesh_type + "_" + dim + "D"; +// const std::string filename = basename + ".root"; + +// remove_path_if_exists(filename); +// io::silo::save_mesh(save_mesh, basename); +// io::silo::load_mesh(filename, load_mesh); +// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + +// // make changes to save mesh so the diff will pass +// if (mesh_type == "uniform") +// { +// silo_uniform_to_rect_conversion("coords", "mesh", save_mesh); +// } +// silo_name_changer("mesh", save_mesh); + +// // the loaded mesh will be in the multidomain format +// // but the saved mesh is in the single domain format +// EXPECT_EQ(load_mesh.number_of_children(), 1); +// EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); +// EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); +// } +// } + +// //----------------------------------------------------------------------------- +// // we are testing vector fields in this test +// TEST(conduit_relay_io_silo, round_trip_braid) +// { +// const std::vector> mesh_types = { +// std::make_pair("uniform", "2"), std::make_pair("uniform", "3"), +// std::make_pair("rectilinear", "2"), std::make_pair("rectilinear", "3"), +// std::make_pair("structured", "2"), std::make_pair("structured", "3"), +// std::make_pair("points", "2"), std::make_pair("points", "3"), +// std::make_pair("points_implicit", "2"), std::make_pair("points_implicit", "3"), +// std::make_pair("lines", "2"), std::make_pair("lines", "3"), +// std::make_pair("tris", "2"), +// std::make_pair("quads", "2"), +// std::make_pair("tets", "3"), +// std::make_pair("hexs", "3"), +// std::make_pair("wedges", "3"), +// std::make_pair("pyramids", "3"), +// // std::make_pair("mixed_2d", "2"), +// // std::make_pair("mixed", "3"), +// }; +// for (int i = 0; i < mesh_types.size(); ++i) +// { +// std::string dim = mesh_types[i].second; +// index_t nx = 3; +// index_t ny = 4; +// index_t nz = (dim == "2" ? 0 : 2); + +// std::string mesh_type = mesh_types[i].first; + +// Node save_mesh, load_mesh, info; +// blueprint::mesh::examples::braid(mesh_type, nx, ny, nz, save_mesh); + +// const std::string basename = "silo_braid_" + mesh_type + "_" + dim + "D"; +// const std::string filename = basename + ".cycle_000100.root"; + +// // remove existing root file, directory and any output files +// remove_path_if_exists(filename); + +// io::silo::save_mesh(save_mesh, basename); +// io::silo::load_mesh(filename, load_mesh); +// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + +// // make changes to save mesh so the diff will pass +// if (mesh_type == "uniform") +// { +// silo_uniform_to_rect_conversion("coords", "mesh", save_mesh); +// } +// if (mesh_type == "points") +// { +// // this is custom code for braid +// // We know it is correct because the unstructured points version of braid +// // uses every point in the coordset +// save_mesh["topologies"].remove_child("mesh"); +// save_mesh["topologies"]["mesh"]["type"] = "points"; +// save_mesh["topologies"]["mesh"]["coordset"] = "coords"; +// } +// if (mesh_type == "points_implicit" || mesh_type == "points") +// { +// // the association doesn't matter for point meshes +// // we choose vertex by convention +// save_mesh["fields"]["radial"]["association"].reset(); +// save_mesh["fields"]["radial"]["association"] = "vertex"; +// } +// silo_name_changer("mesh", save_mesh); + +// // the loaded mesh will be in the multidomain format +// // but the saved mesh is in the single domain format +// EXPECT_EQ(load_mesh.number_of_children(), 1); +// EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); +// EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); +// } +// } + +// //----------------------------------------------------------------------------- +// // multidomain test +// TEST(conduit_relay_io_silo, round_trip_spiral) +// { +// for (int ndomains = 2; ndomains < 6; ndomains ++) +// { +// Node save_mesh, load_mesh, info; +// blueprint::mesh::examples::spiral(ndomains, save_mesh); + +// const std::string basename = "silo_spiral_" + std::to_string(ndomains) + "_domains"; +// const std::string filename = basename + ".cycle_000000.root"; + +// remove_path_if_exists(filename); +// io::silo::save_mesh(save_mesh, basename); +// io::silo::load_mesh(filename, load_mesh); +// EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); + +// // make changes to save mesh so the diff will pass +// for (index_t child = 0; child < save_mesh.number_of_children(); child ++) +// { +// silo_name_changer("mesh", save_mesh[child]); +// } + +// EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); +// NodeConstIterator l_itr = load_mesh.children(); +// NodeConstIterator s_itr = save_mesh.children(); +// while (l_itr.has_next()) +// { +// const Node &l_curr = l_itr.next(); +// const Node &s_curr = s_itr.next(); + +// EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); +// } +// } +// } + +// //----------------------------------------------------------------------------- +// TEST(conduit_relay_io_silo, round_trip_julia) +// { +// Node save_mesh, load_mesh, info; +// blueprint::mesh::examples::julia(5, // nx +// 5, // ny +// 0, // x_min +// 10, // x_max +// 2, // y_min +// 7, // y_max +// 3, // c_re +// 4, // c_im +// save_mesh); + +// const std::string basename = "silo_julia"; +// const std::string filename = basename + ".root"; + +// remove_path_if_exists(filename); +// io::silo::save_mesh(save_mesh, basename); +// io::silo::load_mesh(filename, load_mesh); +// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + +// // make changes to save mesh so the diff will pass +// silo_name_changer("mesh", save_mesh); + +// // the loaded mesh will be in the multidomain format +// // but the saved mesh is in the single domain format +// EXPECT_EQ(load_mesh.number_of_children(), 1); +// EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); +// EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); +// } + +// //----------------------------------------------------------------------------- +// // test material write and read +// TEST(conduit_relay_io_silo, round_trip_venn) +// { +// std::string matset_type = "sparse_by_element"; +// for (int j = 0; j < 2; j ++) +// { +// Node save_mesh, sbe, load_mesh, info; +// std::string size; +// int nx, ny; +// const double radius = 0.25; +// if (j == 0) +// { +// size = "small"; +// nx = ny = 4; +// } +// else +// { +// size = "large"; +// nx = ny = 100; +// } +// blueprint::mesh::examples::venn(matset_type, nx, ny, radius, save_mesh); + +// const std::string basename = "silo_venn_" + matset_type + "_" + size; +// const std::string filename = basename + ".root"; + +// remove_path_if_exists(filename); +// io::silo::save_mesh(save_mesh, basename); +// io::silo::load_mesh(filename, load_mesh); +// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + +// // make changes to save mesh so the diff will pass + +// // The field mat_check has values that are one type and matset_values +// // that are another type. The silo writer converts both to double arrays +// // in this case, so we follow suit. +// Node mat_check_new_values, mat_check_new_matset_values; +// save_mesh["fields"]["mat_check"]["values"].to_double_array(mat_check_new_values); +// save_mesh["fields"]["mat_check"]["matset_values"].to_double_array(mat_check_new_matset_values); +// save_mesh["fields"]["mat_check"]["values"].set_external(mat_check_new_values); +// save_mesh["fields"]["mat_check"]["matset_values"].set_external(mat_check_new_matset_values); + +// silo_name_changer("mesh", save_mesh); + +// // the loaded mesh will be in the multidomain format +// // but the saved mesh is in the single domain format +// EXPECT_EQ(load_mesh.number_of_children(), 1); +// EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); +// EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); +// } +// } + +// //----------------------------------------------------------------------------- +// TEST(conduit_relay_io_silo, round_trip_venn_modded_matnos) +// { +// const std::string matset_type = "sparse_by_element"; +// Node save_mesh, load_mesh, info; +// const int nx = 4; +// const int ny = 4; +// const double radius = 0.25; +// blueprint::mesh::examples::venn(matset_type, nx, ny, radius, save_mesh); + +// auto replace_matno = [](int matno) +// { +// return (matno == 1 ? 15 : +// (matno == 2 ? 37 : +// (matno == 3 ? 4 : +// (matno == 0 ? 22 : +// -1)))); +// }; + +// auto matmap_itr = save_mesh["matsets"]["matset"]["material_map"].children(); +// while (matmap_itr.has_next()) +// { +// Node &mat = matmap_itr.next(); +// mat.set(replace_matno(mat.as_int())); +// } + +// int_array matids = save_mesh["matsets"]["matset"]["material_ids"].value(); +// for (int i = 0; i < save_mesh["matsets"]["matset"]["material_ids"].dtype().number_of_elements(); i ++) +// { +// matids[i] = replace_matno(matids[i]); +// } + +// const std::string silo_basename = "silo_venn_" + matset_type + "_modded_matnos"; +// const std::string silo_filename = silo_basename + ".root"; +// remove_path_if_exists(silo_filename); +// io::silo::save_mesh(save_mesh, silo_basename); + +// const std::string bp_basename = "bp_venn_" + matset_type + "_modded_matnos"; +// const std::string bp_filename = bp_basename + ".root"; +// remove_path_if_exists(bp_filename); +// io::blueprint::save_mesh(save_mesh, bp_basename, "hdf5"); + +// io::silo::load_mesh(silo_filename, load_mesh); +// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + +// // make changes to save mesh so the diff will pass + +// // The field mat_check has values that are one type and matset_values +// // that are another type. The silo writer converts both to double arrays +// // in this case, so we follow suit. +// Node mat_check_new_values, mat_check_new_matset_values; +// save_mesh["fields"]["mat_check"]["values"].to_double_array(mat_check_new_values); +// save_mesh["fields"]["mat_check"]["matset_values"].to_double_array(mat_check_new_matset_values); +// save_mesh["fields"]["mat_check"]["values"].set_external(mat_check_new_values); +// save_mesh["fields"]["mat_check"]["matset_values"].set_external(mat_check_new_matset_values); + +// // to_silo is going to reorder mixed materials least to greatest +// // so we must do the same +// int_array mat_ids = save_mesh["matsets"]["matset"]["material_ids"].value(); +// const auto mat_id10 = mat_ids[10]; +// const auto mat_id11 = mat_ids[11]; +// const auto mat_id12 = mat_ids[12]; +// mat_ids[10] = mat_id12; +// mat_ids[11] = mat_id10; +// mat_ids[12] = mat_id11; +// auto field_itr = save_mesh["fields"].children(); +// while (field_itr.has_next()) +// { +// const Node &n_field = field_itr.next(); +// if (n_field.has_child("matset")) +// { +// double_array matset_vals = n_field["matset_values"].value(); +// const auto matset_val10 = matset_vals[10]; +// const auto matset_val11 = matset_vals[11]; +// const auto matset_val12 = matset_vals[12]; +// matset_vals[10] = matset_val12; +// matset_vals[11] = matset_val10; +// matset_vals[12] = matset_val11; +// } +// } + +// silo_name_changer("mesh", save_mesh); + +// // the loaded mesh will be in the multidomain format +// // but the saved mesh is in the single domain format +// EXPECT_EQ(load_mesh.number_of_children(), 1); +// EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); + +// EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); +// } + +// //----------------------------------------------------------------------------- +// TEST(conduit_relay_io_silo, round_trip_spiral_multi_dom_materials) +// { +// Node save_mesh, load_mesh, info; +// const int ndomains = 4; +// blueprint::mesh::examples::spiral(ndomains, save_mesh); +// add_matset_to_spiral(save_mesh, ndomains); +// EXPECT_TRUE(blueprint::mesh::verify(save_mesh, info)); + +// const std::string basename = "silo_multidom_materials_spiral"; +// const std::string filename = basename + ".cycle_000000.root"; + +// remove_path_if_exists(filename); +// io::silo::save_mesh(save_mesh, basename); +// io::silo::load_mesh(filename, load_mesh); +// EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); + +// // make changes to save mesh so the diff will pass +// for (index_t child = 0; child < save_mesh.number_of_children(); child ++) +// { +// // get the matset for this domain +// Node &n_matset = save_mesh[child]["matsets"]["matset"]; + +// // clean up volume fractions +// Node vf_arr; +// n_matset["volume_fractions"].to_float64_array(vf_arr); +// n_matset["volume_fractions"].reset(); +// n_matset["volume_fractions"].set(vf_arr); - silo_name_changer("mesh", save_mesh); - - // the loaded mesh will be in the multidomain format - // but the saved mesh is in the single domain format - EXPECT_EQ(load_mesh.number_of_children(), 1); - EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); - - EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); - } -} - -//----------------------------------------------------------------------------- -TEST(conduit_relay_io_silo, round_trip_save_option_overlink1) -{ - const std::vector ovl_topo_names = {"", "topo"}; - for (int i = 0; i < ovl_topo_names.size(); i ++) - { - std::string basename; - if (ovl_topo_names[i].empty()) - { - basename = "silo_save_option_overlink_spiral"; - } - else - { - basename = "silo_save_option_overlink_spiral_" + ovl_topo_names[i]; - } - const std::string filename = basename + "/OvlTop.silo"; - - Node opts; - opts["file_style"] = "overlink"; - opts["ovl_topo_name"] = ovl_topo_names[i]; - - int ndomains = 2; - - Node save_mesh, load_mesh, info; - blueprint::mesh::examples::spiral(ndomains, save_mesh); - add_matset_to_spiral(save_mesh, ndomains); - remove_path_if_exists(filename); - io::silo::save_mesh(save_mesh, basename, opts); - io::silo::load_mesh(filename, load_mesh); - EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); - - // make changes to save mesh so the diff will pass - for (index_t child = 0; child < save_mesh.number_of_children(); child ++) - { - // get the matset for this domain - Node &n_matset = save_mesh[child]["matsets"]["matset"]; - - // clean up volume fractions - Node vf_arr; - n_matset["volume_fractions"].to_float64_array(vf_arr); - n_matset["volume_fractions"].reset(); - n_matset["volume_fractions"].set(vf_arr); +// // cheat a little bit - we don't have these to start +// n_matset["sizes"].set_external(load_mesh[child]["matsets"]["mesh_matset"]["sizes"]); +// n_matset["offsets"].set_external(load_mesh[child]["matsets"]["mesh_matset"]["offsets"]); + +// silo_name_changer("mesh", save_mesh[child]); +// } + +// EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); +// NodeConstIterator l_itr = load_mesh.children(); +// NodeConstIterator s_itr = save_mesh.children(); +// while (l_itr.has_next()) +// { +// const Node &l_curr = l_itr.next(); +// const Node &s_curr = s_itr.next(); + +// EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); +// } +// } + +// //----------------------------------------------------------------------------- +// TEST(conduit_relay_io_silo, round_trip_grid_adjset) +// { +// Node save_mesh, load_mesh, info; +// blueprint::mesh::examples::grid("structured", 3, 3, 1, 2, 2, 1, save_mesh); + +// // we need a material in order for this to be valid overlink +// for (index_t child = 0; child < save_mesh.number_of_children(); child ++) +// { +// add_multi_buffer_full_matset(save_mesh[child], 4, "mesh"); +// } + +// const std::string basename = "silo_grid_adjset"; +// const std::string filename = basename + "/OvlTop.silo"; + +// Node write_opts; +// write_opts["file_style"] = "overlink"; +// write_opts["ovl_topo_name"] = "mesh"; + +// Node read_opts; +// read_opts["matset_style"] = "multi_buffer_full"; + +// remove_path_if_exists(filename); +// io::silo::save_mesh(save_mesh, basename, write_opts); +// io::blueprint::save_mesh(save_mesh, basename, "hdf5"); +// io::silo::load_mesh(filename, read_opts, load_mesh); +// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + +// for (index_t child = 0; child < save_mesh.number_of_children(); child ++) +// { +// // separate out vector fields +// Node &field_vel = save_mesh[child]["fields"]["vel"]; +// Node &field_vel_u = save_mesh[child]["fields"]["vel_u"]; +// Node &field_vel_v = save_mesh[child]["fields"]["vel_v"]; + +// field_vel_u["topology"].set(field_vel["topology"]); +// field_vel_u["association"].set(field_vel["association"]); +// field_vel_u["values"].set(field_vel["values/u"]); +// field_vel_v["topology"].set(field_vel["topology"]); +// field_vel_v["association"].set(field_vel["association"]); +// field_vel_v["values"].set(field_vel["values/v"]); + +// save_mesh[child]["fields"].remove_child("vel"); + +// // make adjset pairwise +// Node &pairwise_adjset = save_mesh[child]["adjsets"]["adjset"]; +// conduit::blueprint::mesh::adjset::to_pairwise(save_mesh[child]["adjsets"]["mesh_adj"], pairwise_adjset); +// save_mesh[child]["adjsets"].remove_child("mesh_adj"); + +// // make changes to save mesh so the diff will pass +// overlink_name_changer(save_mesh[child]); +// } + +// EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); +// NodeConstIterator l_itr = load_mesh.children(); +// NodeConstIterator s_itr = save_mesh.children(); +// while (l_itr.has_next()) +// { +// const Node &l_curr = l_itr.next(); +// const Node &s_curr = s_itr.next(); + +// EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); +// } +// } + +// //----------------------------------------------------------------------------- +// // +// // test read and write semantics +// // + +// //----------------------------------------------------------------------------- +// TEST(conduit_relay_io_silo, read_and_write_semantics) +// { +// for (int ndomains = 2; ndomains < 6; ndomains ++) +// { +// Node save_mesh, load_mesh, info; +// blueprint::mesh::examples::spiral(ndomains, save_mesh); + +// const std::string basename = "silo_spiral_" + std::to_string(ndomains) + "_domains"; +// const std::string filename = basename + ".cycle_000000.root"; + +// remove_path_if_exists(filename); +// io::silo::write_mesh(save_mesh, basename); +// io::silo::read_mesh(filename, load_mesh); +// EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); + +// // make changes to save mesh so the diff will pass +// for (index_t child = 0; child < save_mesh.number_of_children(); child ++) +// { +// silo_name_changer("mesh", save_mesh[child]); +// } + +// EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); +// NodeConstIterator l_itr = load_mesh.children(); +// NodeConstIterator s_itr = save_mesh.children(); +// while (l_itr.has_next()) +// { +// const Node &l_curr = l_itr.next(); +// const Node &s_curr = s_itr.next(); + +// EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); +// } +// } +// } + +// //----------------------------------------------------------------------------- +// // +// // special case tests +// // + +// //----------------------------------------------------------------------------- +// // var is not defined on a domain +// // +// // tests the silo "EMPTY" capability +// TEST(conduit_relay_io_silo, missing_domain_var) +// { +// Node save_mesh, load_mesh, info; +// const int ndomains = 4; +// blueprint::mesh::examples::spiral(ndomains, save_mesh); + +// // remove information for a particular domain +// save_mesh[2]["fields"].remove_child("dist"); + +// const std::string basename = "silo_missing_domain_var_spiral"; +// const std::string filename = basename + ".cycle_000000.root"; + +// remove_path_if_exists(filename); +// io::silo::save_mesh(save_mesh, basename); +// io::silo::load_mesh(filename, load_mesh); +// EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); + +// // make changes to save mesh so the diff will pass +// for (index_t child = 0; child < save_mesh.number_of_children(); child ++) +// { +// silo_name_changer("mesh", save_mesh[child]); +// } +// save_mesh[2].remove_child("fields"); + +// EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); +// NodeConstIterator l_itr = load_mesh.children(); +// NodeConstIterator s_itr = save_mesh.children(); +// while (l_itr.has_next()) +// { +// const Node &l_curr = l_itr.next(); +// const Node &s_curr = s_itr.next(); + +// EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); +// } +// } + +// //----------------------------------------------------------------------------- +// // matset is not defined on a domain +// // +// // tests the silo "EMPTY" capability +// TEST(conduit_relay_io_silo, missing_domain_matset) +// { +// Node save_mesh, load_mesh, info; +// const int ndomains = 4; +// blueprint::mesh::examples::spiral(ndomains, save_mesh); +// add_matset_to_spiral(save_mesh, ndomains); +// EXPECT_TRUE(blueprint::mesh::verify(save_mesh, info)); + +// // remove information for a particular domain +// save_mesh[2]["matsets"].remove_child("matset"); + +// const std::string basename = "silo_missing_domain_matset_spiral"; +// const std::string filename = basename + ".cycle_000000.root"; + +// remove_path_if_exists(filename); +// io::silo::save_mesh(save_mesh, basename); +// io::silo::load_mesh(filename, load_mesh); +// EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); + +// // make changes to save mesh so the diff will pass +// for (index_t child = 0; child < save_mesh.number_of_children(); child ++) +// { +// if (save_mesh[child].has_path("matsets/matset")) +// { +// // get the matset for this domain +// Node &n_matset = save_mesh[child]["matsets"]["matset"]; + +// // clean up volume fractions +// Node vf_arr; +// n_matset["volume_fractions"].to_float64_array(vf_arr); +// n_matset["volume_fractions"].reset(); +// n_matset["volume_fractions"].set(vf_arr); - // cheat a little bit - we don't have these to start - n_matset["sizes"].set_external(load_mesh[child]["matsets"]["MMATERIAL"]["sizes"]); - n_matset["offsets"].set_external(load_mesh[child]["matsets"]["MMATERIAL"]["offsets"]); - - overlink_name_changer(save_mesh[child]); - } - - EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); - NodeConstIterator l_itr = load_mesh.children(); - NodeConstIterator s_itr = save_mesh.children(); - while (l_itr.has_next()) - { - const Node &l_curr = l_itr.next(); - const Node &s_curr = s_itr.next(); - - EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); - } - } -} - -//----------------------------------------------------------------------------- -// this tests var attributes and padding dimensions -TEST(conduit_relay_io_silo, round_trip_save_option_overlink2) -{ - const std::string basename = "silo_save_option_overlink_basic"; - const std::string filename = basename + "/OvlTop.silo"; - - Node write_opts, read_opts; - write_opts["file_style"] = "overlink"; - read_opts["matset_style"] = "multi_buffer_full"; - - Node save_mesh, load_mesh, info; - blueprint::mesh::examples::basic("structured", 3, 3, 1, save_mesh); - - // add another field that is volume dependent - Node &field2 = save_mesh["fields"]["field2"]; - field2["association"] = "element"; - field2["topology"] = "mesh"; - field2["volume_dependent"] = "true"; - field2["values"].set_external(save_mesh["fields"]["field"]["values"]); - - // add a matset to make overlink happy - add_multi_buffer_full_matset(save_mesh, 4, "mesh"); - - remove_path_if_exists(filename); - io::silo::save_mesh(save_mesh, basename, write_opts); - io::silo::load_mesh(filename, read_opts, load_mesh); - EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); - - // make changes to save mesh so the diff will pass - overlink_name_changer(save_mesh); - - // the loaded mesh will be in the multidomain format - // but the saved mesh is in the single domain format - EXPECT_EQ(load_mesh.number_of_children(), 1); - EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); - - EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); - - // open silo files and do some checks - - DBfile *rootfile = DBOpen(filename.c_str(), DB_UNKNOWN, DB_READ); - EXPECT_TRUE(DBInqVarExists(rootfile, "VAR_ATTRIBUTES")); - EXPECT_TRUE(DBInqVarType(rootfile, "VAR_ATTRIBUTES") == DB_ARRAY); - - DBcompoundarray *var_attr = DBGetCompoundarray(rootfile, "VAR_ATTRIBUTES"); - - // fetch pointers to elements inside the compound array - char **elemnames = var_attr->elemnames; - int *elemlengths = var_attr->elemlengths; - int nelems = var_attr->nelems; - int *values = static_cast(var_attr->values); - int nvalues = var_attr->nvalues; - int datatype = var_attr->datatype; - - EXPECT_EQ(std::string(elemnames[0]), "field"); - EXPECT_EQ(std::string(elemnames[1]), "field2"); - EXPECT_EQ(elemlengths[0], 5); - EXPECT_EQ(elemlengths[1], 5); - EXPECT_EQ(nelems, 2); - // for first var - EXPECT_EQ(values[0], 1); - EXPECT_EQ(values[1], 0); - EXPECT_EQ(values[2], 1); - EXPECT_EQ(values[3], 0); - EXPECT_EQ(values[4], 1); - // for second var - EXPECT_EQ(values[5], 1); - EXPECT_EQ(values[6], 1); - EXPECT_EQ(values[7], 1); - EXPECT_EQ(values[8], 0); - EXPECT_EQ(values[9], 1); - EXPECT_EQ(nvalues, 10); - EXPECT_EQ(datatype, DB_INT); - - DBFreeCompoundarray(var_attr); - - EXPECT_TRUE(DBInqVarExists(rootfile, "PAD_DIMS")); - EXPECT_TRUE(DBInqVarType(rootfile, "PAD_DIMS") == DB_ARRAY); - - DBcompoundarray *pad_dims = DBGetCompoundarray(rootfile, "PAD_DIMS"); - - // fetch pointers to elements inside the compound array - elemnames = pad_dims->elemnames; - elemlengths = pad_dims->elemlengths; - nelems = pad_dims->nelems; - values = static_cast(pad_dims->values); - nvalues = pad_dims->nvalues; - datatype = pad_dims->datatype; - - EXPECT_EQ(std::string(elemnames[0]), "paddims"); - EXPECT_EQ(elemlengths[0], 6); - EXPECT_EQ(nelems, 1); - EXPECT_EQ(values[0], 0); - EXPECT_EQ(values[1], 0); - EXPECT_EQ(values[2], 0); - EXPECT_EQ(values[3], 0); - EXPECT_EQ(values[4], 0); - EXPECT_EQ(values[5], 0); - EXPECT_EQ(nvalues, 6); - EXPECT_EQ(datatype, DB_INT); - - DBFreeCompoundarray(pad_dims); - - DBClose(rootfile); - - // now check domain file - - const std::string dom_filename = basename + "/domain0.silo"; - DBfile *domfile = DBOpen(dom_filename.c_str(), DB_UNKNOWN, DB_READ); - - EXPECT_TRUE(DBInqVarExists(domfile, "DOMAIN_NEIGHBOR_NUMS")); - EXPECT_TRUE(DBInqVarType(domfile, "DOMAIN_NEIGHBOR_NUMS") == DB_ARRAY); - - DBcompoundarray *dom_neighbor_nums = DBGetCompoundarray(domfile, "DOMAIN_NEIGHBOR_NUMS"); - - // fetch pointers to elements inside the compound array - elemnames = dom_neighbor_nums->elemnames; - elemlengths = dom_neighbor_nums->elemlengths; - nelems = dom_neighbor_nums->nelems; - values = static_cast(dom_neighbor_nums->values); - nvalues = dom_neighbor_nums->nvalues; - datatype = dom_neighbor_nums->datatype; - - EXPECT_EQ(std::string(elemnames[0]), "num_neighbors"); - EXPECT_EQ(std::string(elemnames[1]), "neighbor_nums"); - EXPECT_EQ(elemlengths[0], 1); - EXPECT_EQ(elemlengths[1], 0); - EXPECT_EQ(nelems, 2); - EXPECT_EQ(values[0], 0); - EXPECT_EQ(nvalues, 1); - EXPECT_EQ(datatype, DB_INT); - - DBFreeCompoundarray(dom_neighbor_nums); - - DBClose(domfile); -} - -//----------------------------------------------------------------------------- -// this tests material i/o -TEST(conduit_relay_io_silo, round_trip_save_option_overlink3) -{ - Node save_mesh, load_mesh, info; - const int nx = 100, ny = 100; - const double radius = 0.25; - blueprint::mesh::examples::venn("sparse_by_element", nx, ny, radius, save_mesh); - - const std::string basename = "silo_save_option_overlink_venn"; - const std::string filename = basename + "/OvlTop.silo"; - - Node opts; - opts["file_style"] = "overlink"; - - remove_path_if_exists(filename); - io::silo::save_mesh(save_mesh, basename, opts); - io::silo::load_mesh(filename, load_mesh); - EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - - // make changes to save mesh so the diff will pass - - // The field mat_check has values that are one type and matset_values - // that are another type. The silo writer converts both to double arrays - // in this case, so we follow suit. - Node mat_check_new_values, mat_check_new_matset_values; - save_mesh["fields"]["mat_check"]["values"].to_double_array(mat_check_new_values); - save_mesh["fields"]["mat_check"]["matset_values"].to_double_array(mat_check_new_matset_values); - save_mesh["fields"]["mat_check"]["values"].set_external(mat_check_new_values); - save_mesh["fields"]["mat_check"]["matset_values"].set_external(mat_check_new_matset_values); - - overlink_name_changer(save_mesh); - - // the loaded mesh will be in the multidomain format - // but the saved mesh is in the single domain format - EXPECT_EQ(load_mesh.number_of_children(), 1); - EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); - - EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); -} - -//----------------------------------------------------------------------------- -// we are testing vector fields get converted to scalars -TEST(conduit_relay_io_silo, round_trip_save_option_overlink4) -{ - const std::vector> mesh_types = { - std::make_pair("rectilinear", "2"), std::make_pair("rectilinear", "3"), - std::make_pair("structured", "2"), std::make_pair("structured", "3"), - std::make_pair("quads", "2"), - std::make_pair("hexs", "3"), - }; - for (int i = 0; i < mesh_types.size(); ++i) - { - std::string dim = mesh_types[i].second; - index_t nx = 3; - index_t ny = 4; - index_t nz = (dim == "2" ? 0 : 2); - - std::string mesh_type = mesh_types[i].first; - - Node save_mesh, load_mesh, info; - blueprint::mesh::examples::braid(mesh_type, nx, ny, nz, save_mesh); - index_t nele_x = nx - 1; - index_t nele_y = ny - 1; - index_t nele_z = (dim == "2" ? 0 : nz - 1); - - // provide a matset for braid - braid_init_example_matset(nele_x, nele_y, nele_z, save_mesh["matsets"]["matset"]); - - const std::string basename = "silo_save_option_overlink_braid_" + mesh_type + "_" + dim + "D"; - const std::string filename = basename + "/OvlTop.silo"; - - Node write_opts, read_opts; - write_opts["file_style"] = "overlink"; - read_opts["matset_style"] = "multi_buffer_full"; - - // remove existing root file, directory and any output files - remove_path_if_exists(filename); - io::silo::save_mesh(save_mesh, basename, write_opts); - io::silo::load_mesh(filename, read_opts, load_mesh); - EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - - Node &field_vel = save_mesh["fields"]["vel"]; - Node &field_vel_u = save_mesh["fields"]["vel_u"]; - Node &field_vel_v = save_mesh["fields"]["vel_v"]; - - field_vel_u["topology"].set(field_vel["topology"]); - field_vel_u["association"].set(field_vel["association"]); - field_vel_u["values"].set(field_vel["values/u"]); - field_vel_v["topology"].set(field_vel["topology"]); - field_vel_v["association"].set(field_vel["association"]); - field_vel_v["values"].set(field_vel["values/v"]); - - if (dim == "3") - { - Node &field_vel_w = save_mesh["fields"]["vel_w"]; - field_vel_w["topology"].set(field_vel["topology"]); - field_vel_w["association"].set(field_vel["association"]); - field_vel_w["values"].set(field_vel["values/w"]); - } - - save_mesh["fields"].remove_child("vel"); - - // make changes to save mesh so the diff will pass - overlink_name_changer(save_mesh); - - // the loaded mesh will be in the multidomain format - // but the saved mesh is in the single domain format - EXPECT_EQ(load_mesh.number_of_children(), 1); - EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); - - EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); - } -} - -//----------------------------------------------------------------------------- -// check that all the shape types work (specifically polytopal ones) -TEST(conduit_relay_io_silo, round_trip_save_option_overlink5) -{ - const std::vector> mesh_types = { - std::make_pair("uniform", "2"), std::make_pair("uniform", "3"), - std::make_pair("rectilinear", "2"), std::make_pair("rectilinear", "3"), - std::make_pair("structured", "2"), std::make_pair("structured", "3"), - std::make_pair("quads", "2"), - std::make_pair("polygons", "2"), - std::make_pair("hexs", "3"), - // std::make_pair("polyhedra", "3") - // Overlink does not support tris, wedges, pyramids, or tets - }; - for (int i = 0; i < mesh_types.size(); ++i) - { - const std::string dim = mesh_types[i].second; - index_t nx = 3; - index_t ny = 4; - index_t nz = (dim == "2" ? 0 : 2); - - const std::string mesh_type = mesh_types[i].first; - - Node save_mesh, load_mesh, info; - blueprint::mesh::examples::basic(mesh_type, nx, ny, nz, save_mesh); - - const std::string basename = "silo_save_option_overlink_basic_" + mesh_type + "_" + dim + "D"; - const std::string filename = basename + "/OvlTop.silo"; - const std::string domfile = basename + "/domain0.silo"; - - Node write_opts, read_opts; - write_opts["file_style"] = "overlink"; - read_opts["matset_style"] = "multi_buffer_full"; - - // add a matset to make overlink happy - int num_elems = (nx - 1) * (ny - 1); - if (mesh_type == "tets") - { - num_elems *= 6; - } - add_multi_buffer_full_matset(save_mesh, num_elems, "mesh"); - - remove_path_if_exists(filename); - remove_path_if_exists(domfile); - io::silo::save_mesh(save_mesh, basename, write_opts); - io::silo::load_mesh(filename, read_opts, load_mesh); - EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); - - // make changes to save mesh so the diff will pass - if (mesh_type == "uniform") - { - silo_uniform_to_rect_conversion("coords", "mesh", save_mesh); - } - overlink_name_changer(save_mesh); - - // the loaded mesh will be in the multidomain format - // but the saved mesh is in the single domain format - EXPECT_EQ(load_mesh.number_of_children(), 1); - EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); - - EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); - } -} - -//----------------------------------------------------------------------------- - -// -// read option tests -// - -// read options: -/// opts: -/// silo_names: -/// multimesh_names: -/// "{name1}" - multimeshes with this name will be read if they exist -/// "{name2}" -/// ... -/// or -/// "{all}" - all multimeshes will be read. -/// or -/// "{none}" - no multimeshes will be read. -/// multivar_names: similar to multimesh_names. -/// multimat_names: similar to multimesh_names. -/// multimatspecies_names: similar to multimesh_names. TODO -/// qmesh_names: similar to multimesh_names. -/// qvar_names: similar to multimesh_names. -/// ucdmesh_names: similar to multimesh_names. -/// ucdvar_names: similar to multimesh_names. -/// ptmesh_names: similar to multimesh_names. -/// ptvar_names: similar to multimesh_names. -/// mat_names: similar to multimesh_names. -/// matspecies_names: similar to multimesh_names. TODO -/// By default, everything in the file will be read unless manually turned off. -/// -/// matset_style: "default", "multi_buffer_full", "sparse_by_element", -/// "multi_buffer_by_material" -/// "default" ==> "sparse_by_element" -/// -/// mesh_name: legacy argument. This is interpreted as a multimesh name. -/// It is added to the list of multimesh names to read, unless the -/// user has specified "all" or "none", which will supersede this. -/// TODO does it make sense to remove this? When? - -//----------------------------------------------------------------------------- -// TODO this is now a legacy feature. Should I remove eventually? -TEST(conduit_relay_io_silo, round_trip_read_option_mesh_name) -{ - Node load_mesh, info, opts; - const std::string path = utils::join_file_path("silo", "multi_curv3d.silo"); - const std::string input_file = relay_test_silo_data_path(path); - - opts["mesh_name"] = "mesh1_dup"; - - io::silo::load_mesh(input_file, opts, load_mesh); - EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - - EXPECT_TRUE(load_mesh[0].has_path("topologies/mesh1_dup")); -} - -//----------------------------------------------------------------------------- -TEST(conduit_relay_io_silo, round_trip_read_option_matset_style) -{ - // the matset type and the type we are requesting on read - const std::vector> matset_types = { - std::make_pair("full", "full"), - std::make_pair("sparse_by_material", "sparse_by_material"), - std::make_pair("sparse_by_element", "sparse_by_element"), - std::make_pair("sparse_by_element", "full"), - std::make_pair("sparse_by_material", "sparse_by_element"), - std::make_pair("sparse_by_material", "default"), - }; - - for (int i = 0; i < matset_types.size(); i ++) - { - std::string matset_type = matset_types[i].first; - std::string matset_request = matset_types[i].second; - - for (int j = 0; j < 2; j ++) - { - Node mesh_full, mesh_sbe, mesh_sbm, baseline_mesh, load_mesh, info; - std::string size; - int nx, ny; - const double radius = 0.25; - if (j == 0) - { - size = "small"; - nx = ny = 4; - } - else - { - size = "large"; - nx = ny = 100; - } - - blueprint::mesh::examples::venn("full", nx, ny, radius, mesh_full); - blueprint::mesh::examples::venn("sparse_by_material", nx, ny, radius, mesh_sbm); - blueprint::mesh::examples::venn("sparse_by_element", nx, ny, radius, mesh_sbe); - - if (matset_type == "full") - { - baseline_mesh.set_external(mesh_full); - } - else if (matset_type == "sparse_by_material") - { - baseline_mesh.set_external(mesh_sbm); - } - else // (matset_type == "sparse_by_element") - { - baseline_mesh.set_external(mesh_sbe); - } - - Node opts; - if (matset_request == "full") - { - opts["matset_style"] = "multi_buffer_full"; - } - else if (matset_request == "sparse_by_material") - { - opts["matset_style"] = "multi_buffer_by_material"; - } - else if (matset_request == "sparse_by_element") - { - opts["matset_style"] = "sparse_by_element"; - } - else - { - opts["matset_style"] = "default"; - } - - const std::string basename = "silo_venn2_" + matset_type + "_" + size; - const std::string filename = basename + ".root"; - - remove_path_if_exists(filename); - io::silo::save_mesh(baseline_mesh, basename); - io::silo::load_mesh(filename, opts, load_mesh); - EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - - if (matset_request == "full") - { - baseline_mesh.set_external(mesh_full); - } - else if (matset_request == "sparse_by_material") - { - baseline_mesh.set_external(mesh_sbm); - } - else if (matset_request == "sparse_by_element") - { - baseline_mesh.set_external(mesh_sbe); - } - else - { - baseline_mesh.set_external(mesh_sbe); - } - - // make changes to save mesh so the diff will pass - - // The field mat_check has values that are one type and matset_values - // that are another type. The silo writer converts both to double arrays - // in this case, so we follow suit. - Node mat_check_new_values, mat_check_new_matset_values; - baseline_mesh["fields"]["mat_check"]["values"].to_double_array(mat_check_new_values); - if (baseline_mesh["fields"]["mat_check"]["matset_values"].dtype().is_object()) - { - auto mat_vals_itr = baseline_mesh["fields"]["mat_check"]["matset_values"].children(); - while (mat_vals_itr.has_next()) - { - Node &mat_vals_for_mat = mat_vals_itr.next(); - const std::string mat_name = mat_vals_itr.name(); - mat_vals_for_mat.to_double_array(mat_check_new_matset_values[mat_name]); - } - } - else - { - baseline_mesh["fields"]["mat_check"]["matset_values"].to_double_array(mat_check_new_matset_values); - } - baseline_mesh["fields"]["mat_check"]["values"].set_external(mat_check_new_values); - baseline_mesh["fields"]["mat_check"]["matset_values"].set_external(mat_check_new_matset_values); - - silo_name_changer("mesh", baseline_mesh); - - // the loaded mesh will be in the multidomain format - // but the saved mesh is in the single domain format - EXPECT_EQ(load_mesh.number_of_children(), 1); - EXPECT_EQ(load_mesh[0].number_of_children(), baseline_mesh.number_of_children()); - EXPECT_FALSE(load_mesh[0].diff(baseline_mesh, info, CONDUIT_EPSILON, true)); - } - } -} - -//----------------------------------------------------------------------------- - -// -// read and write Silo and Overlink tests -// - -//----------------------------------------------------------------------------- -// read normal silo files containing multimeshes, multivars, and multimats -TEST(conduit_relay_io_silo, read_silo) -{ - const std::vector> file_info = { - {".", "multi_curv3d", ".silo"}, - {".", "tire", ".silo"}, - {".", "galaxy0000", ".silo"}, - {".", "emptydomains", ".silo"}, - {"multidir_test_data", "multidir0000", ".root"}, - }; - - // TODO what to do in the case where a multimesh points to no data? (mesh1_back) - // fail silently, as we do now? - - for (int i = 0; i < file_info.size(); i ++) - { - const std::string dirname = file_info[i][0]; - const std::string basename = file_info[i][1]; - const std::string fileext = file_info[i][2]; - - Node load_mesh, info, write_opts; - std::string filepath = utils::join_file_path(dirname, basename) + fileext; - filepath = utils::join_file_path("silo", filepath); - std::string input_file = relay_test_silo_data_path(filepath); - - io::silo::load_mesh(input_file, load_mesh); - EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - - const std::string out_name = "read_silo_" + basename; - - // TODO are these remove paths doing anything? Don't they need filenames? - remove_path_if_exists(out_name + "_write_blueprint"); - io::blueprint::save_mesh(load_mesh, out_name + "_write_blueprint", "hdf5"); - - remove_path_if_exists(out_name + "_write_silo"); - io::silo::save_mesh(load_mesh, out_name + "_write_silo"); - - // overlink requires matsets and does not support point meshes - if (load_mesh[0].has_child("matsets") && basename != "galaxy0000") - { - remove_path_if_exists(out_name + "_write_overlink"); - write_opts["file_style"] = "overlink"; - write_opts["ovl_topo_name"] = "MMESH"; // TODO do I even need this - io::silo::save_mesh(load_mesh, out_name + "_write_overlink", write_opts); - } - } -} +// // cheat a little bit - we don't have these to start +// n_matset["sizes"].set_external(load_mesh[child]["matsets"]["mesh_matset"]["sizes"]); +// n_matset["offsets"].set_external(load_mesh[child]["matsets"]["mesh_matset"]["offsets"]); +// } + +// silo_name_changer("mesh", save_mesh[child]); +// } +// save_mesh[2].remove_child("matsets"); + +// EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); +// NodeConstIterator l_itr = load_mesh.children(); +// NodeConstIterator s_itr = save_mesh.children(); +// while (l_itr.has_next()) +// { +// const Node &l_curr = l_itr.next(); +// const Node &s_curr = s_itr.next(); + +// EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); +// } +// } + +// //----------------------------------------------------------------------------- +// // mesh is not defined on a domain +// // +// // This case is much less interesting. +// // data passes through the clean mesh filter which +// // deletes domains that are missing topos. +// // They simply are not part of the mesh and so silo +// // doesn't have to deal with it. +// TEST(conduit_relay_io_silo, missing_domain_mesh_trivial) +// { +// Node save_mesh, load_mesh, info; +// const int ndomains = 4; +// blueprint::mesh::examples::spiral(ndomains, save_mesh); + +// // remove information for a particular domain +// save_mesh[2]["topologies"].remove_child("topo"); + +// const std::string basename = "silo_missing_domain_mesh_trivial_spiral"; +// const std::string filename = basename + ".cycle_000000.root"; + +// remove_path_if_exists(filename); +// io::silo::save_mesh(save_mesh, basename); +// io::silo::load_mesh(filename, load_mesh); + +// EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); + +// // make changes to save mesh so the diff will pass +// save_mesh.remove(2); +// save_mesh.rename_child("domain_000003", "domain_000002"); +// save_mesh[2]["state"]["domain_id"].reset(); +// save_mesh[2]["state"]["domain_id"] = 2; +// for (index_t child = 0; child < save_mesh.number_of_children(); child ++) +// { +// silo_name_changer("mesh", save_mesh[child]); +// } + +// EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); +// NodeConstIterator l_itr = load_mesh.children(); +// NodeConstIterator s_itr = save_mesh.children(); +// while (l_itr.has_next()) +// { +// const Node &l_curr = l_itr.next(); +// const Node &s_curr = s_itr.next(); + +// EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); +// } +// } + +// //----------------------------------------------------------------------------- +// // mesh is not defined on a domain but there are multiple meshes +// TEST(conduit_relay_io_silo, missing_domain_mesh) +// { +// Node save_mesh, save_mesh2, load_mesh, load_mesh2, info, opts; +// const int ndomains = 4; +// blueprint::mesh::examples::spiral(ndomains, save_mesh); +// blueprint::mesh::examples::spiral(ndomains, save_mesh2); + +// for (index_t child = 0; child < save_mesh.number_of_children(); child ++) +// { +// save_mesh[child]["coordsets"].rename_child("coords", "coords2"); +// save_mesh[child]["topologies"]["topo"]["coordset"].reset(); +// save_mesh[child]["topologies"]["topo"]["coordset"] = "coords2"; +// save_mesh[child]["topologies"].rename_child("topo", "topo2"); +// save_mesh[child]["fields"]["dist"]["topology"].reset(); +// save_mesh[child]["fields"]["dist"]["topology"] = "topo2"; +// save_mesh[child]["fields"].rename_child("dist", "dist2"); + +// save_mesh[child]["coordsets"]["coords"].set_external(save_mesh2[child]["coordsets"]["coords"]); +// save_mesh[child]["topologies"]["topo"].set_external(save_mesh2[child]["topologies"]["topo"]); +// save_mesh[child]["fields"]["dist"].set_external(save_mesh2[child]["fields"]["dist"]); +// } + +// // remove information for a particular domain +// save_mesh[2]["topologies"].remove_child("topo"); + +// const std::string basename = "silo_missing_domain_mesh_spiral"; +// const std::string filename = basename + ".cycle_000000.root"; + +// remove_path_if_exists(filename); +// io::silo::save_mesh(save_mesh, basename); +// opts["silo_names"]["multimesh_names"] = "mesh_topo2"; +// io::silo::load_mesh(filename, opts, load_mesh); +// opts["silo_names"]["multimesh_names"] = "mesh_topo"; +// io::silo::load_mesh(filename, opts, load_mesh2); +// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); +// EXPECT_TRUE(blueprint::mesh::verify(load_mesh2, info)); + +// // make changes to save mesh so the diff will pass +// save_mesh[2]["coordsets"].remove_child("coords"); +// save_mesh[2]["fields"].remove_child("dist"); +// for (index_t child = 0; child < save_mesh.number_of_children(); child ++) +// { +// silo_name_changer("mesh", save_mesh[child]); +// } + +// // we must merge the two meshes in load mesh +// // the indexing is tricky because one is missing a domain +// load_mesh[0]["coordsets"]["mesh_topo"].set_external(load_mesh2[0]["coordsets"]["mesh_topo"]); +// load_mesh[0]["topologies"]["mesh_topo"].set_external(load_mesh2[0]["topologies"]["mesh_topo"]); +// load_mesh[0]["fields"]["mesh_dist"].set_external(load_mesh2[0]["fields"]["mesh_dist"]); +// load_mesh[1]["coordsets"]["mesh_topo"].set_external(load_mesh2[1]["coordsets"]["mesh_topo"]); +// load_mesh[1]["topologies"]["mesh_topo"].set_external(load_mesh2[1]["topologies"]["mesh_topo"]); +// load_mesh[1]["fields"]["mesh_dist"].set_external(load_mesh2[1]["fields"]["mesh_dist"]); +// load_mesh[3]["coordsets"]["mesh_topo"].set_external(load_mesh2[2]["coordsets"]["mesh_topo"]); +// load_mesh[3]["topologies"]["mesh_topo"].set_external(load_mesh2[2]["topologies"]["mesh_topo"]); +// load_mesh[3]["fields"]["mesh_dist"].set_external(load_mesh2[2]["fields"]["mesh_dist"]); + +// EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); +// NodeConstIterator l_itr = load_mesh.children(); +// NodeConstIterator s_itr = save_mesh.children(); +// while (l_itr.has_next()) +// { +// const Node &l_curr = l_itr.next(); +// const Node &s_curr = s_itr.next(); + +// EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); +// } +// } + +// //----------------------------------------------------------------------------- +// // explicit points (unstructured mesh) do not use every coord +// TEST(conduit_relay_io_silo, unstructured_points) +// { +// Node save_mesh, load_mesh, info; +// blueprint::mesh::examples::braid("points", 2, 2, 2, save_mesh); + +// std::vector new_conn; +// std::vector new_field1; +// std::vector new_field2; +// std::vector new_xcoords, new_ycoords, new_zcoords; + +// int_accessor conn = save_mesh["topologies"]["mesh"]["elements"]["connectivity"].value(); + +// float_accessor field1 = save_mesh["fields"]["braid"]["values"].value(); +// float_accessor field2 = save_mesh["fields"]["radial"]["values"].value(); + +// float_accessor xcoords = save_mesh["coordsets"]["coords"]["values"]["x"].value(); +// float_accessor ycoords = save_mesh["coordsets"]["coords"]["values"]["y"].value(); +// float_accessor zcoords = save_mesh["coordsets"]["coords"]["values"]["z"].value(); + +// for (int i = 1; i < conn.number_of_elements(); i += 2) +// { +// new_conn.push_back(conn[i]); +// new_field1.push_back(field1[i]); +// new_field2.push_back(field2[i]); + +// new_xcoords.push_back(xcoords[conn[i]]); +// new_ycoords.push_back(ycoords[conn[i]]); +// new_zcoords.push_back(zcoords[conn[i]]); +// } +// save_mesh["topologies"]["mesh"]["elements"]["connectivity"].reset(); +// save_mesh["topologies"]["mesh"]["elements"]["connectivity"].set(new_conn); + +// save_mesh["fields"].remove_child("vel"); +// save_mesh["fields"]["braid"]["values"].reset(); +// save_mesh["fields"]["braid"]["values"].set(new_field1); +// save_mesh["fields"]["radial"]["values"].reset(); +// save_mesh["fields"]["radial"]["values"].set(new_field2); + +// // we have modified braid such that it only uses half of the points in the coordset + +// const std::string basename = "silo_unstructured_points_braid"; +// const std::string filename = basename + ".cycle_000100.root"; + +// // remove existing root file, directory and any output files +// remove_path_if_exists(filename); + +// io::silo::save_mesh(save_mesh, basename); +// io::silo::load_mesh(filename, load_mesh); +// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + +// // now we must remove the unused points and change to an implicit points topo so that the diff passes +// save_mesh["coordsets"]["coords"]["values"]["x"].reset(); +// save_mesh["coordsets"]["coords"]["values"]["x"].set(new_xcoords); +// save_mesh["coordsets"]["coords"]["values"]["y"].reset(); +// save_mesh["coordsets"]["coords"]["values"]["y"].set(new_ycoords); +// save_mesh["coordsets"]["coords"]["values"]["z"].reset(); +// save_mesh["coordsets"]["coords"]["values"]["z"].set(new_zcoords); + +// save_mesh["topologies"].remove_child("mesh"); +// save_mesh["topologies"]["mesh"]["type"] = "points"; +// save_mesh["topologies"]["mesh"]["coordset"] = "coords"; + +// // the association doesn't matter for point meshes +// // we choose vertex by convention +// save_mesh["fields"]["radial"]["association"].reset(); +// save_mesh["fields"]["radial"]["association"] = "vertex"; + +// silo_name_changer("mesh", save_mesh); + +// // the loaded mesh will be in the multidomain format +// // but the saved mesh is in the single domain format +// EXPECT_EQ(load_mesh.number_of_children(), 1); +// EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); + +// EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); +// } + +// //----------------------------------------------------------------------------- + +// // +// // save option tests +// // + +// // save options: +// /// opts: +// /// +// /// file_style: "default", "root_only", "multi_file", "overlink" +// /// when # of domains == 1, "default" ==> "root_only" +// /// else, "default" ==> "multi_file" +// /// +// /// silo_type: "default", "pdb", "hdf5", "unknown" +// /// when the file we are writing to exists, "default" ==> "unknown" +// /// else, "default" ==> "hdf5" +// /// note: these are additional silo_type options that we could add +// /// support for in the future: +// /// "hdf5_sec2", "hdf5_stdio", "hdf5_mpio", "hdf5_mpiposix", "taurus" +// /// +// /// suffix: "default", "cycle", "none" +// /// when cycle is present, "default" ==> "cycle" +// /// else, "default" ==> "none" +// /// +// /// root_file_ext: "default", "root", "silo" +// /// "default" ==> "root" +// /// if overlink, this parameter is unused. +// /// +// /// mesh_name: (used if present, default ==> "mesh") +// /// +// /// ovl_topo_name: (used if present, default ==> "") +// /// +// /// number_of_files: {# of files} +// /// when "multi_file" or "overlink": +// /// <= 0, use # of files == # of domains +// /// > 0, # of files == number_of_files + +// //----------------------------------------------------------------------------- +// TEST(conduit_relay_io_silo, round_trip_save_option_file_style) +// { +// // we will do overlink tests separately +// const std::vector file_styles = {"default", "root_only", "multi_file"}; +// for (int i = 0; i < file_styles.size(); i ++) +// { +// Node opts; +// opts["file_style"] = file_styles[i]; + +// const std::string basename = "silo_save_option_file_style_" + file_styles[i] + "_spiral"; +// const std::string filename = basename + ".cycle_000000.root"; + +// for (int ndomains = 1; ndomains < 5; ndomains += 3) +// { +// Node save_mesh, load_mesh, info; +// blueprint::mesh::examples::spiral(ndomains, save_mesh); +// remove_path_if_exists(filename); +// io::silo::save_mesh(save_mesh, basename, opts); +// io::silo::load_mesh(filename, load_mesh); +// EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); + +// // make changes to save mesh so the diff will pass +// for (index_t child = 0; child < save_mesh.number_of_children(); child ++) +// { +// silo_name_changer("mesh", save_mesh[child]); +// } + +// EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); +// NodeConstIterator l_itr = load_mesh.children(); +// NodeConstIterator s_itr = save_mesh.children(); +// while (l_itr.has_next()) +// { +// const Node &l_curr = l_itr.next(); +// const Node &s_curr = s_itr.next(); + +// EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); +// } +// } +// } +// } + +// //----------------------------------------------------------------------------- +// TEST(conduit_relay_io_silo, round_trip_save_option_number_of_files) +// { +// const std::vector number_of_files = {-1, 2}; +// for (int i = 0; i < number_of_files.size(); i ++) +// { +// Node opts; +// opts["file_style"] = "multi_file"; +// opts["number_of_files"] = number_of_files[i]; + +// const std::string basename = "silo_save_option_number_of_files_" + +// std::to_string(number_of_files[i]) + +// "_spiral"; +// const std::string filename = basename + ".cycle_000000.root"; + +// const int ndomains = 5; + +// Node save_mesh, load_mesh, info; +// blueprint::mesh::examples::spiral(ndomains, save_mesh); + +// remove_path_if_exists(filename); +// io::silo::save_mesh(save_mesh, basename, opts); +// io::silo::load_mesh(filename, load_mesh); +// EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); + +// // make changes to save mesh so the diff will pass +// for (index_t child = 0; child < save_mesh.number_of_children(); child ++) +// { +// silo_name_changer("mesh", save_mesh[child]); +// } + +// EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); +// NodeConstIterator l_itr = load_mesh.children(); +// NodeConstIterator s_itr = save_mesh.children(); +// while (l_itr.has_next()) +// { +// const Node &l_curr = l_itr.next(); +// const Node &s_curr = s_itr.next(); + +// EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); +// } +// } +// } + +// //----------------------------------------------------------------------------- +// TEST(conduit_relay_io_silo, round_trip_save_option_suffix) +// { +// const std::vector suffixes = {"default", "default", "cycle", "none"}; +// const std::vector file_suffixes = { +// "", // cycle is not present +// ".cycle_000005", // cycle is present +// ".cycle_000005", // cycle is turned on +// "", // cycle is turned off +// }; +// const std::vector include_cycle = {"no", "yes", "yes", "yes"}; +// for (int i = 0; i < suffixes.size(); i ++) +// { +// Node opts; +// opts["suffix"] = suffixes[i]; + +// const std::string basename = "silo_save_option_suffix_" + suffixes[i] + +// "_" + include_cycle[i] + "_basic"; +// const std::string filename = basename + file_suffixes[i] + ".root"; + +// Node save_mesh, load_mesh, info; +// blueprint::mesh::examples::basic("rectilinear", 3, 4, 0, save_mesh); + +// if (include_cycle[i] == "yes") +// { +// save_mesh["state/cycle"] = 5; +// } + +// remove_path_if_exists(filename); +// io::silo::save_mesh(save_mesh, basename, opts); +// io::silo::load_mesh(filename, load_mesh); +// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + +// silo_name_changer("mesh", save_mesh); + +// // the loaded mesh will be in the multidomain format +// // but the saved mesh is in the single domain format +// EXPECT_EQ(load_mesh.number_of_children(), 1); +// EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); + +// EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); +// } +// } + +// //----------------------------------------------------------------------------- +// TEST(conduit_relay_io_silo, round_trip_save_option_root_file_ext) +// { +// const std::vector root_file_exts = {"default", "root", "silo"}; + +// for (int i = 0; i < root_file_exts.size(); i ++) +// { +// Node opts; +// opts["root_file_ext"] = root_file_exts[i]; + +// std::string actual_file_ext = root_file_exts[i]; +// if (actual_file_ext == "default") +// { +// actual_file_ext = "root"; +// } + +// const std::string basename = "round_trip_save_option_root_file_ext_" + +// root_file_exts[i] + "_basic"; +// const std::string filename = basename + "." + actual_file_ext; + +// Node save_mesh, load_mesh, info; +// blueprint::mesh::examples::basic("rectilinear", 3, 4, 0, save_mesh); +// remove_path_if_exists(filename); +// io::silo::save_mesh(save_mesh, basename, opts); +// io::silo::load_mesh(filename, load_mesh); +// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + +// silo_name_changer("mesh", save_mesh); + +// // the loaded mesh will be in the multidomain format +// // but the saved mesh is in the single domain format +// EXPECT_EQ(load_mesh.number_of_children(), 1); +// EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); +// EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); +// } +// } + +// //----------------------------------------------------------------------------- +// TEST(conduit_relay_io_silo, round_trip_save_option_mesh_name) +// { +// const std::string basename = "silo_save_option_mesh_name_basic"; +// const std::string filename = basename + ".root"; + +// Node opts; +// opts["mesh_name"] = "mymesh"; + +// Node save_mesh, load_mesh, info; +// blueprint::mesh::examples::basic("rectilinear", 3, 4, 0, save_mesh); +// remove_path_if_exists(filename); +// io::silo::save_mesh(save_mesh, basename, opts); +// io::silo::load_mesh(filename, load_mesh); +// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + +// silo_name_changer("mymesh", save_mesh); + +// // the loaded mesh will be in the multidomain format +// // but the saved mesh is in the single domain format +// EXPECT_EQ(load_mesh.number_of_children(), 1); +// EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); +// EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); +// } + +// //----------------------------------------------------------------------------- +// TEST(conduit_relay_io_silo, round_trip_save_option_silo_type) +// { +// const std::vector silo_types = {"default", "pdb", "hdf5", "unknown"}; +// for (int i = 3; i < silo_types.size(); i ++) +// { +// Node opts; +// opts["silo_type"] = silo_types[i]; + +// Node save_mesh, load_mesh, info; +// blueprint::mesh::examples::basic("rectilinear", 3, 4, 0, save_mesh); + +// const std::string basename = "silo_save_option_silo_type_" + silo_types[i] + "_basic"; +// const std::string filename = basename + ".root"; + +// remove_path_if_exists(filename); +// io::silo::save_mesh(save_mesh, basename, opts); +// io::silo::load_mesh(filename, load_mesh); +// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + +// silo_name_changer("mesh", save_mesh); + +// // the loaded mesh will be in the multidomain format +// // but the saved mesh is in the single domain format +// EXPECT_EQ(load_mesh.number_of_children(), 1); +// EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); + +// EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); +// } +// } + +// //----------------------------------------------------------------------------- +// TEST(conduit_relay_io_silo, round_trip_save_option_overlink1) +// { +// const std::vector ovl_topo_names = {"", "topo"}; +// for (int i = 0; i < ovl_topo_names.size(); i ++) +// { +// std::string basename; +// if (ovl_topo_names[i].empty()) +// { +// basename = "silo_save_option_overlink_spiral"; +// } +// else +// { +// basename = "silo_save_option_overlink_spiral_" + ovl_topo_names[i]; +// } +// const std::string filename = basename + "/OvlTop.silo"; + +// Node opts; +// opts["file_style"] = "overlink"; +// opts["ovl_topo_name"] = ovl_topo_names[i]; + +// int ndomains = 2; + +// Node save_mesh, load_mesh, info; +// blueprint::mesh::examples::spiral(ndomains, save_mesh); +// add_matset_to_spiral(save_mesh, ndomains); +// remove_path_if_exists(filename); +// io::silo::save_mesh(save_mesh, basename, opts); +// io::silo::load_mesh(filename, load_mesh); +// EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); + +// // make changes to save mesh so the diff will pass +// for (index_t child = 0; child < save_mesh.number_of_children(); child ++) +// { +// // get the matset for this domain +// Node &n_matset = save_mesh[child]["matsets"]["matset"]; + +// // clean up volume fractions +// Node vf_arr; +// n_matset["volume_fractions"].to_float64_array(vf_arr); +// n_matset["volume_fractions"].reset(); +// n_matset["volume_fractions"].set(vf_arr); + +// // cheat a little bit - we don't have these to start +// n_matset["sizes"].set_external(load_mesh[child]["matsets"]["MMATERIAL"]["sizes"]); +// n_matset["offsets"].set_external(load_mesh[child]["matsets"]["MMATERIAL"]["offsets"]); + +// overlink_name_changer(save_mesh[child]); +// } + +// EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); +// NodeConstIterator l_itr = load_mesh.children(); +// NodeConstIterator s_itr = save_mesh.children(); +// while (l_itr.has_next()) +// { +// const Node &l_curr = l_itr.next(); +// const Node &s_curr = s_itr.next(); + +// EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); +// } +// } +// } + +// //----------------------------------------------------------------------------- +// // this tests var attributes and padding dimensions +// TEST(conduit_relay_io_silo, round_trip_save_option_overlink2) +// { +// const std::string basename = "silo_save_option_overlink_basic"; +// const std::string filename = basename + "/OvlTop.silo"; + +// Node write_opts, read_opts; +// write_opts["file_style"] = "overlink"; +// read_opts["matset_style"] = "multi_buffer_full"; + +// Node save_mesh, load_mesh, info; +// blueprint::mesh::examples::basic("structured", 3, 3, 1, save_mesh); + +// // add another field that is volume dependent +// Node &field2 = save_mesh["fields"]["field2"]; +// field2["association"] = "element"; +// field2["topology"] = "mesh"; +// field2["volume_dependent"] = "true"; +// field2["values"].set_external(save_mesh["fields"]["field"]["values"]); + +// // add a matset to make overlink happy +// add_multi_buffer_full_matset(save_mesh, 4, "mesh"); + +// remove_path_if_exists(filename); +// io::silo::save_mesh(save_mesh, basename, write_opts); +// io::silo::load_mesh(filename, read_opts, load_mesh); +// EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); + +// // make changes to save mesh so the diff will pass +// overlink_name_changer(save_mesh); + +// // the loaded mesh will be in the multidomain format +// // but the saved mesh is in the single domain format +// EXPECT_EQ(load_mesh.number_of_children(), 1); +// EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); + +// EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); + +// // open silo files and do some checks + +// DBfile *rootfile = DBOpen(filename.c_str(), DB_UNKNOWN, DB_READ); +// EXPECT_TRUE(DBInqVarExists(rootfile, "VAR_ATTRIBUTES")); +// EXPECT_TRUE(DBInqVarType(rootfile, "VAR_ATTRIBUTES") == DB_ARRAY); + +// DBcompoundarray *var_attr = DBGetCompoundarray(rootfile, "VAR_ATTRIBUTES"); + +// // fetch pointers to elements inside the compound array +// char **elemnames = var_attr->elemnames; +// int *elemlengths = var_attr->elemlengths; +// int nelems = var_attr->nelems; +// int *values = static_cast(var_attr->values); +// int nvalues = var_attr->nvalues; +// int datatype = var_attr->datatype; + +// EXPECT_EQ(std::string(elemnames[0]), "field"); +// EXPECT_EQ(std::string(elemnames[1]), "field2"); +// EXPECT_EQ(elemlengths[0], 5); +// EXPECT_EQ(elemlengths[1], 5); +// EXPECT_EQ(nelems, 2); +// // for first var +// EXPECT_EQ(values[0], 1); +// EXPECT_EQ(values[1], 0); +// EXPECT_EQ(values[2], 1); +// EXPECT_EQ(values[3], 0); +// EXPECT_EQ(values[4], 1); +// // for second var +// EXPECT_EQ(values[5], 1); +// EXPECT_EQ(values[6], 1); +// EXPECT_EQ(values[7], 1); +// EXPECT_EQ(values[8], 0); +// EXPECT_EQ(values[9], 1); +// EXPECT_EQ(nvalues, 10); +// EXPECT_EQ(datatype, DB_INT); + +// DBFreeCompoundarray(var_attr); + +// EXPECT_TRUE(DBInqVarExists(rootfile, "PAD_DIMS")); +// EXPECT_TRUE(DBInqVarType(rootfile, "PAD_DIMS") == DB_ARRAY); + +// DBcompoundarray *pad_dims = DBGetCompoundarray(rootfile, "PAD_DIMS"); + +// // fetch pointers to elements inside the compound array +// elemnames = pad_dims->elemnames; +// elemlengths = pad_dims->elemlengths; +// nelems = pad_dims->nelems; +// values = static_cast(pad_dims->values); +// nvalues = pad_dims->nvalues; +// datatype = pad_dims->datatype; + +// EXPECT_EQ(std::string(elemnames[0]), "paddims"); +// EXPECT_EQ(elemlengths[0], 6); +// EXPECT_EQ(nelems, 1); +// EXPECT_EQ(values[0], 0); +// EXPECT_EQ(values[1], 0); +// EXPECT_EQ(values[2], 0); +// EXPECT_EQ(values[3], 0); +// EXPECT_EQ(values[4], 0); +// EXPECT_EQ(values[5], 0); +// EXPECT_EQ(nvalues, 6); +// EXPECT_EQ(datatype, DB_INT); + +// DBFreeCompoundarray(pad_dims); + +// DBClose(rootfile); + +// // now check domain file + +// const std::string dom_filename = basename + "/domain0.silo"; +// DBfile *domfile = DBOpen(dom_filename.c_str(), DB_UNKNOWN, DB_READ); + +// EXPECT_TRUE(DBInqVarExists(domfile, "DOMAIN_NEIGHBOR_NUMS")); +// EXPECT_TRUE(DBInqVarType(domfile, "DOMAIN_NEIGHBOR_NUMS") == DB_ARRAY); + +// DBcompoundarray *dom_neighbor_nums = DBGetCompoundarray(domfile, "DOMAIN_NEIGHBOR_NUMS"); + +// // fetch pointers to elements inside the compound array +// elemnames = dom_neighbor_nums->elemnames; +// elemlengths = dom_neighbor_nums->elemlengths; +// nelems = dom_neighbor_nums->nelems; +// values = static_cast(dom_neighbor_nums->values); +// nvalues = dom_neighbor_nums->nvalues; +// datatype = dom_neighbor_nums->datatype; + +// EXPECT_EQ(std::string(elemnames[0]), "num_neighbors"); +// EXPECT_EQ(std::string(elemnames[1]), "neighbor_nums"); +// EXPECT_EQ(elemlengths[0], 1); +// EXPECT_EQ(elemlengths[1], 0); +// EXPECT_EQ(nelems, 2); +// EXPECT_EQ(values[0], 0); +// EXPECT_EQ(nvalues, 1); +// EXPECT_EQ(datatype, DB_INT); + +// DBFreeCompoundarray(dom_neighbor_nums); + +// DBClose(domfile); +// } + +// //----------------------------------------------------------------------------- +// // this tests material i/o +// TEST(conduit_relay_io_silo, round_trip_save_option_overlink3) +// { +// Node save_mesh, load_mesh, info; +// const int nx = 100, ny = 100; +// const double radius = 0.25; +// blueprint::mesh::examples::venn("sparse_by_element", nx, ny, radius, save_mesh); + +// const std::string basename = "silo_save_option_overlink_venn"; +// const std::string filename = basename + "/OvlTop.silo"; + +// Node opts; +// opts["file_style"] = "overlink"; + +// remove_path_if_exists(filename); +// io::silo::save_mesh(save_mesh, basename, opts); +// io::silo::load_mesh(filename, load_mesh); +// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + +// // make changes to save mesh so the diff will pass + +// // The field mat_check has values that are one type and matset_values +// // that are another type. The silo writer converts both to double arrays +// // in this case, so we follow suit. +// Node mat_check_new_values, mat_check_new_matset_values; +// save_mesh["fields"]["mat_check"]["values"].to_double_array(mat_check_new_values); +// save_mesh["fields"]["mat_check"]["matset_values"].to_double_array(mat_check_new_matset_values); +// save_mesh["fields"]["mat_check"]["values"].set_external(mat_check_new_values); +// save_mesh["fields"]["mat_check"]["matset_values"].set_external(mat_check_new_matset_values); + +// overlink_name_changer(save_mesh); + +// // the loaded mesh will be in the multidomain format +// // but the saved mesh is in the single domain format +// EXPECT_EQ(load_mesh.number_of_children(), 1); +// EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); + +// EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); +// } + +// //----------------------------------------------------------------------------- +// // we are testing vector fields get converted to scalars +// TEST(conduit_relay_io_silo, round_trip_save_option_overlink4) +// { +// const std::vector> mesh_types = { +// std::make_pair("rectilinear", "2"), std::make_pair("rectilinear", "3"), +// std::make_pair("structured", "2"), std::make_pair("structured", "3"), +// std::make_pair("quads", "2"), +// std::make_pair("hexs", "3"), +// }; +// for (int i = 0; i < mesh_types.size(); ++i) +// { +// std::string dim = mesh_types[i].second; +// index_t nx = 3; +// index_t ny = 4; +// index_t nz = (dim == "2" ? 0 : 2); + +// std::string mesh_type = mesh_types[i].first; + +// Node save_mesh, load_mesh, info; +// blueprint::mesh::examples::braid(mesh_type, nx, ny, nz, save_mesh); +// index_t nele_x = nx - 1; +// index_t nele_y = ny - 1; +// index_t nele_z = (dim == "2" ? 0 : nz - 1); + +// // provide a matset for braid +// braid_init_example_matset(nele_x, nele_y, nele_z, save_mesh["matsets"]["matset"]); + +// const std::string basename = "silo_save_option_overlink_braid_" + mesh_type + "_" + dim + "D"; +// const std::string filename = basename + "/OvlTop.silo"; + +// Node write_opts, read_opts; +// write_opts["file_style"] = "overlink"; +// read_opts["matset_style"] = "multi_buffer_full"; + +// // remove existing root file, directory and any output files +// remove_path_if_exists(filename); +// io::silo::save_mesh(save_mesh, basename, write_opts); +// io::silo::load_mesh(filename, read_opts, load_mesh); +// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + +// Node &field_vel = save_mesh["fields"]["vel"]; +// Node &field_vel_u = save_mesh["fields"]["vel_u"]; +// Node &field_vel_v = save_mesh["fields"]["vel_v"]; + +// field_vel_u["topology"].set(field_vel["topology"]); +// field_vel_u["association"].set(field_vel["association"]); +// field_vel_u["values"].set(field_vel["values/u"]); +// field_vel_v["topology"].set(field_vel["topology"]); +// field_vel_v["association"].set(field_vel["association"]); +// field_vel_v["values"].set(field_vel["values/v"]); + +// if (dim == "3") +// { +// Node &field_vel_w = save_mesh["fields"]["vel_w"]; +// field_vel_w["topology"].set(field_vel["topology"]); +// field_vel_w["association"].set(field_vel["association"]); +// field_vel_w["values"].set(field_vel["values/w"]); +// } + +// save_mesh["fields"].remove_child("vel"); + +// // make changes to save mesh so the diff will pass +// overlink_name_changer(save_mesh); + +// // the loaded mesh will be in the multidomain format +// // but the saved mesh is in the single domain format +// EXPECT_EQ(load_mesh.number_of_children(), 1); +// EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); + +// EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); +// } +// } + +// //----------------------------------------------------------------------------- +// // check that all the shape types work (specifically polytopal ones) +// TEST(conduit_relay_io_silo, round_trip_save_option_overlink5) +// { +// const std::vector> mesh_types = { +// std::make_pair("uniform", "2"), std::make_pair("uniform", "3"), +// std::make_pair("rectilinear", "2"), std::make_pair("rectilinear", "3"), +// std::make_pair("structured", "2"), std::make_pair("structured", "3"), +// std::make_pair("quads", "2"), +// std::make_pair("polygons", "2"), +// std::make_pair("hexs", "3"), +// // std::make_pair("polyhedra", "3") +// // Overlink does not support tris, wedges, pyramids, or tets +// }; +// for (int i = 0; i < mesh_types.size(); ++i) +// { +// const std::string dim = mesh_types[i].second; +// index_t nx = 3; +// index_t ny = 4; +// index_t nz = (dim == "2" ? 0 : 2); + +// const std::string mesh_type = mesh_types[i].first; + +// Node save_mesh, load_mesh, info; +// blueprint::mesh::examples::basic(mesh_type, nx, ny, nz, save_mesh); + +// const std::string basename = "silo_save_option_overlink_basic_" + mesh_type + "_" + dim + "D"; +// const std::string filename = basename + "/OvlTop.silo"; +// const std::string domfile = basename + "/domain0.silo"; + +// Node write_opts, read_opts; +// write_opts["file_style"] = "overlink"; +// read_opts["matset_style"] = "multi_buffer_full"; + +// // add a matset to make overlink happy +// int num_elems = (nx - 1) * (ny - 1); +// if (mesh_type == "tets") +// { +// num_elems *= 6; +// } +// add_multi_buffer_full_matset(save_mesh, num_elems, "mesh"); + +// remove_path_if_exists(filename); +// remove_path_if_exists(domfile); +// io::silo::save_mesh(save_mesh, basename, write_opts); +// io::silo::load_mesh(filename, read_opts, load_mesh); +// EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); + +// // make changes to save mesh so the diff will pass +// if (mesh_type == "uniform") +// { +// silo_uniform_to_rect_conversion("coords", "mesh", save_mesh); +// } +// overlink_name_changer(save_mesh); + +// // the loaded mesh will be in the multidomain format +// // but the saved mesh is in the single domain format +// EXPECT_EQ(load_mesh.number_of_children(), 1); +// EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); + +// EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); +// } +// } + +// //----------------------------------------------------------------------------- + +// // +// // read option tests +// // + +// // read options: +// /// opts: +// /// silo_names: +// /// multimesh_names: +// /// "{name1}" - multimeshes with this name will be read if they exist +// /// "{name2}" +// /// ... +// /// or +// /// "{all}" - all multimeshes will be read. +// /// or +// /// "{none}" - no multimeshes will be read. +// /// multivar_names: similar to multimesh_names. +// /// multimat_names: similar to multimesh_names. +// /// multimatspecies_names: similar to multimesh_names. TODO +// /// qmesh_names: similar to multimesh_names. +// /// qvar_names: similar to multimesh_names. +// /// ucdmesh_names: similar to multimesh_names. +// /// ucdvar_names: similar to multimesh_names. +// /// ptmesh_names: similar to multimesh_names. +// /// ptvar_names: similar to multimesh_names. +// /// mat_names: similar to multimesh_names. +// /// matspecies_names: similar to multimesh_names. TODO +// /// By default, everything in the file will be read unless manually turned off. +// /// +// /// matset_style: "default", "multi_buffer_full", "sparse_by_element", +// /// "multi_buffer_by_material" +// /// "default" ==> "sparse_by_element" +// /// +// /// mesh_name: legacy argument. This is interpreted as a multimesh name. +// /// It is added to the list of multimesh names to read, unless the +// /// user has specified "all" or "none", which will supersede this. +// /// TODO does it make sense to remove this? When? + +// //----------------------------------------------------------------------------- +// // TODO this is now a legacy feature. Should I remove eventually? +// TEST(conduit_relay_io_silo, round_trip_read_option_mesh_name) +// { +// Node load_mesh, info, opts; +// const std::string path = utils::join_file_path("silo", "multi_curv3d.silo"); +// const std::string input_file = relay_test_silo_data_path(path); + +// opts["mesh_name"] = "mesh1_dup"; + +// io::silo::load_mesh(input_file, opts, load_mesh); +// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + +// EXPECT_TRUE(load_mesh[0].has_path("topologies/mesh1_dup")); +// } + +// //----------------------------------------------------------------------------- +// TEST(conduit_relay_io_silo, round_trip_read_option_matset_style) +// { +// // the matset type and the type we are requesting on read +// const std::vector> matset_types = { +// std::make_pair("full", "full"), +// std::make_pair("sparse_by_material", "sparse_by_material"), +// std::make_pair("sparse_by_element", "sparse_by_element"), +// std::make_pair("sparse_by_element", "full"), +// std::make_pair("sparse_by_material", "sparse_by_element"), +// std::make_pair("sparse_by_material", "default"), +// }; + +// for (int i = 0; i < matset_types.size(); i ++) +// { +// std::string matset_type = matset_types[i].first; +// std::string matset_request = matset_types[i].second; + +// for (int j = 0; j < 2; j ++) +// { +// Node mesh_full, mesh_sbe, mesh_sbm, baseline_mesh, load_mesh, info; +// std::string size; +// int nx, ny; +// const double radius = 0.25; +// if (j == 0) +// { +// size = "small"; +// nx = ny = 4; +// } +// else +// { +// size = "large"; +// nx = ny = 100; +// } + +// blueprint::mesh::examples::venn("full", nx, ny, radius, mesh_full); +// blueprint::mesh::examples::venn("sparse_by_material", nx, ny, radius, mesh_sbm); +// blueprint::mesh::examples::venn("sparse_by_element", nx, ny, radius, mesh_sbe); + +// if (matset_type == "full") +// { +// baseline_mesh.set_external(mesh_full); +// } +// else if (matset_type == "sparse_by_material") +// { +// baseline_mesh.set_external(mesh_sbm); +// } +// else // (matset_type == "sparse_by_element") +// { +// baseline_mesh.set_external(mesh_sbe); +// } + +// Node opts; +// if (matset_request == "full") +// { +// opts["matset_style"] = "multi_buffer_full"; +// } +// else if (matset_request == "sparse_by_material") +// { +// opts["matset_style"] = "multi_buffer_by_material"; +// } +// else if (matset_request == "sparse_by_element") +// { +// opts["matset_style"] = "sparse_by_element"; +// } +// else +// { +// opts["matset_style"] = "default"; +// } + +// const std::string basename = "silo_venn2_" + matset_type + "_" + size; +// const std::string filename = basename + ".root"; + +// remove_path_if_exists(filename); +// io::silo::save_mesh(baseline_mesh, basename); +// io::silo::load_mesh(filename, opts, load_mesh); +// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + +// if (matset_request == "full") +// { +// baseline_mesh.set_external(mesh_full); +// } +// else if (matset_request == "sparse_by_material") +// { +// baseline_mesh.set_external(mesh_sbm); +// } +// else if (matset_request == "sparse_by_element") +// { +// baseline_mesh.set_external(mesh_sbe); +// } +// else +// { +// baseline_mesh.set_external(mesh_sbe); +// } + +// // make changes to save mesh so the diff will pass + +// // The field mat_check has values that are one type and matset_values +// // that are another type. The silo writer converts both to double arrays +// // in this case, so we follow suit. +// Node mat_check_new_values, mat_check_new_matset_values; +// baseline_mesh["fields"]["mat_check"]["values"].to_double_array(mat_check_new_values); +// if (baseline_mesh["fields"]["mat_check"]["matset_values"].dtype().is_object()) +// { +// auto mat_vals_itr = baseline_mesh["fields"]["mat_check"]["matset_values"].children(); +// while (mat_vals_itr.has_next()) +// { +// Node &mat_vals_for_mat = mat_vals_itr.next(); +// const std::string mat_name = mat_vals_itr.name(); +// mat_vals_for_mat.to_double_array(mat_check_new_matset_values[mat_name]); +// } +// } +// else +// { +// baseline_mesh["fields"]["mat_check"]["matset_values"].to_double_array(mat_check_new_matset_values); +// } +// baseline_mesh["fields"]["mat_check"]["values"].set_external(mat_check_new_values); +// baseline_mesh["fields"]["mat_check"]["matset_values"].set_external(mat_check_new_matset_values); + +// silo_name_changer("mesh", baseline_mesh); + +// // the loaded mesh will be in the multidomain format +// // but the saved mesh is in the single domain format +// EXPECT_EQ(load_mesh.number_of_children(), 1); +// EXPECT_EQ(load_mesh[0].number_of_children(), baseline_mesh.number_of_children()); +// EXPECT_FALSE(load_mesh[0].diff(baseline_mesh, info, CONDUIT_EPSILON, true)); +// } +// } +// } + +// //----------------------------------------------------------------------------- + +// // +// // read and write Silo and Overlink tests +// // + +// //----------------------------------------------------------------------------- +// // read normal silo files containing multimeshes, multivars, and multimats +// TEST(conduit_relay_io_silo, read_silo) +// { +// const std::vector> file_info = { +// {".", "multi_curv3d", ".silo"}, +// {".", "tire", ".silo"}, +// {".", "galaxy0000", ".silo"}, +// {".", "emptydomains", ".silo"}, +// {"multidir_test_data", "multidir0000", ".root"}, +// }; + +// // TODO what to do in the case where a multimesh points to no data? (mesh1_back) +// // fail silently, as we do now? + +// for (int i = 0; i < file_info.size(); i ++) +// { +// const std::string dirname = file_info[i][0]; +// const std::string basename = file_info[i][1]; +// const std::string fileext = file_info[i][2]; + +// Node load_mesh, info, write_opts; +// std::string filepath = utils::join_file_path(dirname, basename) + fileext; +// filepath = utils::join_file_path("silo", filepath); +// std::string input_file = relay_test_silo_data_path(filepath); + +// io::silo::load_mesh(input_file, load_mesh); +// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + +// const std::string out_name = "read_silo_" + basename; + +// // TODO are these remove paths doing anything? Don't they need filenames? +// remove_path_if_exists(out_name + "_write_blueprint"); +// io::blueprint::save_mesh(load_mesh, out_name + "_write_blueprint", "hdf5"); + +// remove_path_if_exists(out_name + "_write_silo"); +// io::silo::save_mesh(load_mesh, out_name + "_write_silo"); + +// // overlink requires matsets and does not support point meshes +// if (load_mesh[0].has_child("matsets") && basename != "galaxy0000") +// { +// remove_path_if_exists(out_name + "_write_overlink"); +// write_opts["file_style"] = "overlink"; +// write_opts["ovl_topo_name"] = "MMESH"; // TODO do I even need this +// io::silo::save_mesh(load_mesh, out_name + "_write_overlink", write_opts); +// } +// } +// } //----------------------------------------------------------------------------- // test that we can read silo without multimeshes, multivars, and multimats TEST(conduit_relay_io_silo, read_simple_silo) { - const std::vector> file_info = { - {"curv2d", ".silo"}, - // {"curv2d_colmajor", ".silo"}, // TODO look at strided structured to handle this case - {"curv3d", ".silo"}, - // {"curv3d_colmajor", ".silo"}, - // // {"globe", ".silo"}, // TODO need to fix a known bug for this one to work + const std::vector> file_info = { + // {"curv2d", ".silo", "no"}, + {"curv2d_colmajor", ".silo", "no"}, // TODO look at strided structured to handle this case + // {"curv3d", ".silo", "yes"}, + // {"curv3d_colmajor", ".silo", "no"}, // TODO + // {"globe", ".silo", "yes"}, // TODO need to fix a known bug for this one to work }; for (int i = 0; i < file_info.size(); i ++) { - const std::string basename = file_info[i].first; - const std::string fileext = file_info[i].second; + const std::string basename = file_info[i][0]; + const std::string fileext = file_info[i][1]; + const std::string round_trip = file_info[i][2]; Node load_mesh, info, write_opts; std::string filepath = basename + fileext; @@ -1826,165 +1827,248 @@ TEST(conduit_relay_io_silo, read_simple_silo) load_mesh.print(); - std::cout << load_mesh[0]["coordsets/curvmesh2d/values/z"].dtype().number_of_elements() << std::endl; - std::cout << load_mesh[0]["coordsets/curvmesh2d/values/r"].dtype().number_of_elements() << std::endl; - const std::string out_name = "read_silo_" + basename; // TODO are these remove paths doing anything? Don't they need filenames? remove_path_if_exists(out_name + "_write_blueprint"); io::blueprint::save_mesh(load_mesh, out_name + "_write_blueprint", "hdf5"); - // remove_path_if_exists(out_name + "_write_silo"); - // io::silo::save_mesh(load_mesh, out_name + "_write_silo"); - - // // overlink requires matsets - // if (load_mesh[0].has_child("matsets")) - // { - // remove_path_if_exists(out_name + "_write_overlink"); - // write_opts["file_style"] = "overlink"; - // write_opts["ovl_topo_name"] = "MMESH"; // TODO do I even need this - // io::silo::save_mesh(load_mesh, out_name + "_write_overlink", write_opts); - // } - } -} - -//----------------------------------------------------------------------------- -// test that we can read the fake overlink files from the visit test data -TEST(conduit_relay_io_silo, read_fake_overlink) -{ - const std::vector> file_info = { - // {"ev_0_0_100", "OvlTop", ".silo"}, - // uncomment once silo ucdmesh phzones are supported - {"hl18spec", "OvlTop", ".silo"}, - // {"regrovl_qh_1000_10001_4", "OvlTop", ".silo"}, - // uncomment once silo ucdmesh phzones are supported - {"utpyr4", "OvlTop", ".silo"}, - }; - - for (int i = 0; i < file_info.size(); i ++) - { - const std::string dirname = file_info[i][0]; - const std::string basename = file_info[i][1]; - const std::string fileext = file_info[i][2]; - - Node load_mesh, info, write_opts; - std::string filepath = utils::join_file_path(dirname, basename) + fileext; - filepath = utils::join_file_path("fake_overlink", filepath); - std::string input_file = relay_test_silo_data_path(filepath); - - io::silo::load_mesh(input_file, load_mesh); - EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - - const std::string out_name = "read_fake_overlink_" + dirname; - - remove_path_if_exists(out_name + "_write_blueprint"); - io::blueprint::save_mesh(load_mesh, out_name + "_write_blueprint", "hdf5"); - - remove_path_if_exists(out_name + "_write_silo"); - io::silo::save_mesh(load_mesh, out_name + "_write_silo"); - - remove_path_if_exists(out_name + "_write_overlink"); - write_opts["file_style"] = "overlink"; - write_opts["ovl_topo_name"] = "MMESH"; - io::silo::save_mesh(load_mesh, out_name + "_write_overlink", write_opts); - } -} - -//----------------------------------------------------------------------------- -// read overlink files in symlink format -// should be similar to reading raw silo -TEST(conduit_relay_io_silo, read_overlink_symlink_format) -{ - const std::vector> file_info = { - {".", "box2d", ".silo"}, - {".", "box3d", ".silo"}, - // {".", "diamond", ".silo"}, - // fails b/c polytopal not yet supported - {".", "testDisk2D_a", ".silo"}, - // {".", "donordiv.s2_materials2", ".silo"}, - // fails b/c polytopal not yet supported - {".", "donordiv.s2_materials3", ".silo"}, - }; - - for (int i = 0; i < file_info.size(); i ++) - { - const std::string dirname = file_info[i][0]; - const std::string basename = file_info[i][1]; - const std::string fileext = file_info[i][2]; - - Node load_mesh, info, write_opts; - std::string filepath = utils::join_file_path(dirname, basename) + fileext; - filepath = utils::join_file_path("overlink", filepath); - std::string input_file = relay_test_silo_data_path(filepath); - - io::silo::load_mesh(input_file, load_mesh); - EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - - const std::string out_name = "read_overlink_symlink_" + basename; - - remove_path_if_exists(out_name + "_write_blueprint"); - io::blueprint::save_mesh(load_mesh, out_name + "_write_blueprint", "hdf5"); - - remove_path_if_exists(out_name + "_write_silo"); - io::silo::save_mesh(load_mesh, out_name + "_write_silo"); + if (round_trip == "yes") + { + remove_path_if_exists(out_name + "_write_silo"); + io::silo::save_mesh(load_mesh, out_name + "_write_silo"); - remove_path_if_exists(out_name + "_write_overlink"); - write_opts["file_style"] = "overlink"; - write_opts["ovl_topo_name"] = "MMESH"; - io::silo::save_mesh(load_mesh, out_name + "_write_overlink", write_opts); + // overlink requires matsets + if (load_mesh[0].has_child("matsets")) + { + remove_path_if_exists(out_name + "_write_overlink"); + write_opts["file_style"] = "overlink"; + write_opts["ovl_topo_name"] = "MMESH"; // TODO do I even need this + io::silo::save_mesh(load_mesh, out_name + "_write_overlink", write_opts); + } + } } } //----------------------------------------------------------------------------- -// read overlink directly from ovltop.silo -// this case is tricky and involves messing with paths -TEST(conduit_relay_io_silo, read_overlink_directly) +TEST(conduit_relay_io_silo, bungus) { - const std::vector> file_info = { - {"box2d", "OvlTop", ".silo"}, - {"box3d", "OvlTop", ".silo"}, - // {"diamond", "OvlTop", ".silo"}, - {"testDisk2D_a", "OvlTop", ".silo"}, - // {"donordiv.s2_materials2", "OvlTop", ".silo"}, - {"donordiv.s2_materials3", "OvlTop", ".silo"}, - }; - - for (int i = 0; i < file_info.size(); i ++) - { - const std::string dirname = file_info[i][0]; - const std::string basename = file_info[i][1]; - const std::string fileext = file_info[i][2]; - - Node load_mesh, info, write_opts; - - std::string filepath = utils::join_file_path(dirname, basename) + fileext; - filepath = utils::join_file_path("overlink", filepath); - std::string input_file = relay_test_silo_data_path(filepath); - - io::silo::load_mesh(input_file, load_mesh); - EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - - const std::string out_name = "read_overlink_direct_" + dirname; - - remove_path_if_exists(out_name + "_write_blueprint"); - io::blueprint::save_mesh(load_mesh, out_name + "_write_blueprint", "hdf5"); - - remove_path_if_exists(out_name + "_write_silo"); - io::silo::save_mesh(load_mesh, out_name + "_write_silo"); - - remove_path_if_exists(out_name + "_write_overlink"); - write_opts["file_style"] = "overlink"; - write_opts["ovl_topo_name"] = "MMESH"; - io::silo::save_mesh(load_mesh, out_name + "_write_overlink", write_opts); - } + std::string yaml_text = "" + "coordsets:\n" + " coords:\n" + " type: \"explicit\"\n" + " values:\n" + " x: [-10.0, -6.6, -3.3, 0.0, 3.3, 6.6, 10.0,\n" + " -10.0, -6.6, -3.3, 0.0, 3.3, 6.6, 10.0,\n" + " -10.0, -6.6, -3.3, 0.0, 3.3, 6.6, 10.0,\n" + " -10.0, -6.6, -3.3, 0.0, 3.3, 6.6, 10.0,\n" + " -10.0, -6.6, -3.3, 0.0, 3.3, 6.6, 10.0,\n" + " -10.0, -6.6, -3.3, 0.0, 3.3, 6.6, 10.0]\n" + " y: [-10.0, -10.0, -10.0, -10.0, -10.0, -10.0, -10.0,\n" + " -6.0, -6.0, -6.0, -6.0, -6.0, -6.0, -6.0,\n" + " -2.0, -2.0, -2.0, -2.0, -2.0, -2.0, -2.0,\n" + " 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0,\n" + " 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0,\n" + " 10.0, 10.0, 10.0, 10.0, 10.0, 10.0, 10.0]\n" + " col_maj_coords:\n" + " type: \"explicit\"\n" + " values:\n" + " x: [-10.0, -10.0, -10.0, -10.0, -10.0, -10.0,\n" + " -6.6, -6.6, -6.6, -6.6, -6.6, -6.6,\n" + " -3.3, -3.3, -3.3, -3.3, -3.3, -3.3,\n" + " 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\n" + " 3.3, 3.3, 3.3, 3.3, 3.3, 3.3,\n" + " 6.6, 6.6, 6.6, 6.6, 6.6, 6.6,\n" + " 10.0, 10.0, 10.0, 10.0, 10.0, 10.0]\n" + " y: [-10.0, -6.0, -2.0, 2.0, 6.0, 10.0,\n" + " -10.0, -6.0, -2.0, 2.0, 6.0, 10.0\n" + " -10.0, -6.0, -2.0, 2.0, 6.0, 10.0,\n" + " -10.0, -6.0, -2.0, 2.0, 6.0, 10.0,\n" + " -10.0, -6.0, -2.0, 2.0, 6.0, 10.0,\n" + " -10.0, -6.0, -2.0, 2.0, 6.0, 10.0,\n" + " -10.0, -6.0, -2.0, 2.0, 6.0, 10.0]\n" + "topologies:\n" + " mesh:\n" + " type: \"structured\"\n" + " coordset: \"coords\"\n" + " elements:\n" + " dims:\n" + " i: 3\n" + " j: 2\n" + " offsets: [2, 2]\n" + " strides: [1, 7]\n" + " full_mesh:\n" + " type: \"structured\"\n" + " coordset: \"coords\"\n" + " elements:\n" + " dims:\n" + " i: 6\n" + " j: 5\n" + // " mesh_col_maj:\n" + // " type: \"structured\"\n" + // " coordset: \"col_maj_coords\"\n" + // " elements:\n" + // " dims:\n" + // " i: 3\n" + // " j: 2\n" + // " offsets: [2, 2]\n" + // " strides: [6, 1]\n" + " full_mesh_col_maj:\n" + " type: \"structured\"\n" + " coordset: \"col_maj_coords\"\n" + " elements:\n" + " dims:\n" + " i: 5\n" + " j: 6\n" + // " offsets: [0, 0]\n" + // " strides: [6, 1]\n" + ""; + + Node my_structured_strided_mesh; + my_structured_strided_mesh.parse(yaml_text, "yaml"); + + std::cout << my_structured_strided_mesh.to_yaml() << std::endl; + + remove_path_if_exists("bungus"); + io::blueprint::save_mesh(my_structured_strided_mesh, "bungus", "hdf5"); } -// TODO add tests for... -// - polytopal meshes once they are supported -// - units once they are supported -// - etc. - -// TODO add tetra8 and c36_m5 to all the overlink i/o tests - -// TODO somewhere I need to error on overlink when there are different var or mesh types across domains +// //----------------------------------------------------------------------------- +// // test that we can read the fake overlink files from the visit test data +// TEST(conduit_relay_io_silo, read_fake_overlink) +// { +// const std::vector> file_info = { +// // {"ev_0_0_100", "OvlTop", ".silo"}, +// // uncomment once silo ucdmesh phzones are supported +// {"hl18spec", "OvlTop", ".silo"}, +// // {"regrovl_qh_1000_10001_4", "OvlTop", ".silo"}, +// // uncomment once silo ucdmesh phzones are supported +// {"utpyr4", "OvlTop", ".silo"}, +// }; + +// for (int i = 0; i < file_info.size(); i ++) +// { +// const std::string dirname = file_info[i][0]; +// const std::string basename = file_info[i][1]; +// const std::string fileext = file_info[i][2]; + +// Node load_mesh, info, write_opts; +// std::string filepath = utils::join_file_path(dirname, basename) + fileext; +// filepath = utils::join_file_path("fake_overlink", filepath); +// std::string input_file = relay_test_silo_data_path(filepath); + +// io::silo::load_mesh(input_file, load_mesh); +// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + +// const std::string out_name = "read_fake_overlink_" + dirname; + +// remove_path_if_exists(out_name + "_write_blueprint"); +// io::blueprint::save_mesh(load_mesh, out_name + "_write_blueprint", "hdf5"); + +// remove_path_if_exists(out_name + "_write_silo"); +// io::silo::save_mesh(load_mesh, out_name + "_write_silo"); + +// remove_path_if_exists(out_name + "_write_overlink"); +// write_opts["file_style"] = "overlink"; +// write_opts["ovl_topo_name"] = "MMESH"; +// io::silo::save_mesh(load_mesh, out_name + "_write_overlink", write_opts); +// } +// } + +// //----------------------------------------------------------------------------- +// // read overlink files in symlink format +// // should be similar to reading raw silo +// TEST(conduit_relay_io_silo, read_overlink_symlink_format) +// { +// const std::vector> file_info = { +// {".", "box2d", ".silo"}, +// {".", "box3d", ".silo"}, +// // {".", "diamond", ".silo"}, +// // fails b/c polytopal not yet supported +// {".", "testDisk2D_a", ".silo"}, +// // {".", "donordiv.s2_materials2", ".silo"}, +// // fails b/c polytopal not yet supported +// {".", "donordiv.s2_materials3", ".silo"}, +// }; + +// for (int i = 0; i < file_info.size(); i ++) +// { +// const std::string dirname = file_info[i][0]; +// const std::string basename = file_info[i][1]; +// const std::string fileext = file_info[i][2]; + +// Node load_mesh, info, write_opts; +// std::string filepath = utils::join_file_path(dirname, basename) + fileext; +// filepath = utils::join_file_path("overlink", filepath); +// std::string input_file = relay_test_silo_data_path(filepath); + +// io::silo::load_mesh(input_file, load_mesh); +// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + +// const std::string out_name = "read_overlink_symlink_" + basename; + +// remove_path_if_exists(out_name + "_write_blueprint"); +// io::blueprint::save_mesh(load_mesh, out_name + "_write_blueprint", "hdf5"); + +// remove_path_if_exists(out_name + "_write_silo"); +// io::silo::save_mesh(load_mesh, out_name + "_write_silo"); + +// remove_path_if_exists(out_name + "_write_overlink"); +// write_opts["file_style"] = "overlink"; +// write_opts["ovl_topo_name"] = "MMESH"; +// io::silo::save_mesh(load_mesh, out_name + "_write_overlink", write_opts); +// } +// } + +// //----------------------------------------------------------------------------- +// // read overlink directly from ovltop.silo +// // this case is tricky and involves messing with paths +// TEST(conduit_relay_io_silo, read_overlink_directly) +// { +// const std::vector> file_info = { +// {"box2d", "OvlTop", ".silo"}, +// {"box3d", "OvlTop", ".silo"}, +// // {"diamond", "OvlTop", ".silo"}, +// {"testDisk2D_a", "OvlTop", ".silo"}, +// // {"donordiv.s2_materials2", "OvlTop", ".silo"}, +// {"donordiv.s2_materials3", "OvlTop", ".silo"}, +// }; + +// for (int i = 0; i < file_info.size(); i ++) +// { +// const std::string dirname = file_info[i][0]; +// const std::string basename = file_info[i][1]; +// const std::string fileext = file_info[i][2]; + +// Node load_mesh, info, write_opts; + +// std::string filepath = utils::join_file_path(dirname, basename) + fileext; +// filepath = utils::join_file_path("overlink", filepath); +// std::string input_file = relay_test_silo_data_path(filepath); + +// io::silo::load_mesh(input_file, load_mesh); +// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + +// const std::string out_name = "read_overlink_direct_" + dirname; + +// remove_path_if_exists(out_name + "_write_blueprint"); +// io::blueprint::save_mesh(load_mesh, out_name + "_write_blueprint", "hdf5"); + +// remove_path_if_exists(out_name + "_write_silo"); +// io::silo::save_mesh(load_mesh, out_name + "_write_silo"); + +// remove_path_if_exists(out_name + "_write_overlink"); +// write_opts["file_style"] = "overlink"; +// write_opts["ovl_topo_name"] = "MMESH"; +// io::silo::save_mesh(load_mesh, out_name + "_write_overlink", write_opts); +// } +// } + +// // TODO add tests for... +// // - polytopal meshes once they are supported +// // - units once they are supported +// // - etc. + +// // TODO add tetra8 and c36_m5 to all the overlink i/o tests + +// // TODO somewhere I need to error on overlink when there are different var or mesh types across domains From 70340b70818cf286e91ae6373cc5658d4b00837a Mon Sep 17 00:00:00 2001 From: Justin Privitera Date: Thu, 11 Apr 2024 13:32:00 -0700 Subject: [PATCH 32/56] colmajor mesh case is handled --- src/libs/relay/conduit_relay_io_silo.cpp | 36 +++++++++++++++--------- src/tests/relay/t_relay_io_silo.cpp | 32 ++++++++++++--------- 2 files changed, 41 insertions(+), 27 deletions(-) diff --git a/src/libs/relay/conduit_relay_io_silo.cpp b/src/libs/relay/conduit_relay_io_silo.cpp index 49cf5387e..2e7e64c95 100644 --- a/src/libs/relay/conduit_relay_io_silo.cpp +++ b/src/libs/relay/conduit_relay_io_silo.cpp @@ -1180,12 +1180,8 @@ read_quadmesh_domain(DBquadmesh *quadmesh_ptr, topo_out["elements/dims/offsets"].set(quadmesh_ptr->min_index, ndims); topo_out["elements/dims/strides"].set(quadmesh_ptr->stride, ndims); } - else + else // colmajor { - // TODO I don't know what I'm doing - // this doesn't work - // I'm not convinced I'm the one who's wrong - // could be docs, could be bp reader, or could be me topo_out["elements/dims/i"] = quadmesh_ptr->max_index[0] - quadmesh_ptr->min_index[0]; if (ndims > 1) { @@ -1197,24 +1193,36 @@ read_quadmesh_domain(DBquadmesh *quadmesh_ptr, } topo_out["elements/dims/offsets"].set(quadmesh_ptr->min_index, ndims); - int flipped_strides[] = {0,0,0}; + + int actual_strides[] = {0,0,0}; + + // we can only succeed here if the data is regularly strided if (1 == ndims) { - flipped_strides[0] = quadmesh_ptr->stride[0]; + CONDUIT_ASSERT(quadmesh_ptr->stride[0] == 1, + "TODO BAD"); + actual_strides[0] = 1; } else if (2 == ndims) { - flipped_strides[0] = quadmesh_ptr->stride[1]; - flipped_strides[1] = quadmesh_ptr->stride[0]; + CONDUIT_ASSERT(quadmesh_ptr->stride[0] == 1 && + quadmesh_ptr->stride[1] == quadmesh_ptr->dims[0], + "TODO BAD"); + actual_strides[0] = quadmesh_ptr->dims[1]; + actual_strides[1] = 1; } - else // 3 == ndims + else // (3 == ndims) { - flipped_strides[0] = quadmesh_ptr->stride[2]; - flipped_strides[1] = quadmesh_ptr->stride[1]; - flipped_strides[2] = quadmesh_ptr->stride[0]; + CONDUIT_ASSERT(quadmesh_ptr->stride[0] == 1 && + quadmesh_ptr->stride[1] == quadmesh_ptr->dims[0] && + quadmesh_ptr->stride[2] == quadmesh_ptr->dims[0] * quadmesh_ptr->dims[1], + "TODO BAD"); + actual_strides[0] = quadmesh_ptr->dims[1] * quadmesh_ptr->dims[2]; + actual_strides[1] = quadmesh_ptr->dims[2]; + actual_strides[2] = 1; } - topo_out["elements/dims/strides"].set(flipped_strides, ndims); + topo_out["elements/dims/strides"].set(actual_strides, ndims); } } } diff --git a/src/tests/relay/t_relay_io_silo.cpp b/src/tests/relay/t_relay_io_silo.cpp index c22b06e52..d45d70bdf 100644 --- a/src/tests/relay/t_relay_io_silo.cpp +++ b/src/tests/relay/t_relay_io_silo.cpp @@ -1881,7 +1881,7 @@ TEST(conduit_relay_io_silo, bungus) " 6.6, 6.6, 6.6, 6.6, 6.6, 6.6,\n" " 10.0, 10.0, 10.0, 10.0, 10.0, 10.0]\n" " y: [-10.0, -6.0, -2.0, 2.0, 6.0, 10.0,\n" - " -10.0, -6.0, -2.0, 2.0, 6.0, 10.0\n" + " -10.0, -6.0, -2.0, 2.0, 6.0, 10.0,\n" " -10.0, -6.0, -2.0, 2.0, 6.0, 10.0,\n" " -10.0, -6.0, -2.0, 2.0, 6.0, 10.0,\n" " -10.0, -6.0, -2.0, 2.0, 6.0, 10.0,\n" @@ -1904,26 +1904,32 @@ TEST(conduit_relay_io_silo, bungus) " dims:\n" " i: 6\n" " j: 5\n" - // " mesh_col_maj:\n" - // " type: \"structured\"\n" - // " coordset: \"col_maj_coords\"\n" - // " elements:\n" - // " dims:\n" - // " i: 3\n" - // " j: 2\n" - // " offsets: [2, 2]\n" - // " strides: [6, 1]\n" + " offsets: [0, 0]\n" + " strides: [1, 7]\n" + " mesh_col_maj:\n" + " type: \"structured\"\n" + " coordset: \"col_maj_coords\"\n" + " elements:\n" + " dims:\n" + " i: 3\n" + " j: 2\n" + " offsets: [2, 2]\n" + " strides: [6, 1]\n" " full_mesh_col_maj:\n" " type: \"structured\"\n" " coordset: \"col_maj_coords\"\n" " elements:\n" " dims:\n" - " i: 5\n" - " j: 6\n" + " i: 6\n" + " j: 5\n" // " offsets: [0, 0]\n" - // " strides: [6, 1]\n" + " strides: [6, 1]\n" ""; + // TODO justin add a new test case to t_blueprint_mesh_examples + // also might want to add as a visit test + // just take the yaml and save for conduit test + Node my_structured_strided_mesh; my_structured_strided_mesh.parse(yaml_text, "yaml"); From f522344f917a345b7e2cc02bb5e161a71fbfb93f Mon Sep 17 00:00:00 2001 From: Justin Privitera Date: Thu, 11 Apr 2024 13:40:38 -0700 Subject: [PATCH 33/56] bring back tests and add new one --- .../blueprint/t_blueprint_mesh_examples.cpp | 85 + src/tests/relay/t_relay_io_silo.cpp | 3903 ++++++++--------- 2 files changed, 1992 insertions(+), 1996 deletions(-) diff --git a/src/tests/blueprint/t_blueprint_mesh_examples.cpp b/src/tests/blueprint/t_blueprint_mesh_examples.cpp index ed096de85..e49047316 100644 --- a/src/tests/blueprint/t_blueprint_mesh_examples.cpp +++ b/src/tests/blueprint/t_blueprint_mesh_examples.cpp @@ -579,6 +579,91 @@ TEST(conduit_blueprint_mesh_examples, strided_structured_3d) } +//----------------------------------------------------------------------------- +TEST(conduit_blueprint_mesh_examples, strided_structured_colmajor) +{ + Node res; + + const std::string yaml_text = "" + "coordsets:\n" + " coords:\n" + " type: \"explicit\"\n" + " values:\n" + " x: [-10.0, -6.6, -3.3, 0.0, 3.3, 6.6, 10.0,\n" + " -10.0, -6.6, -3.3, 0.0, 3.3, 6.6, 10.0,\n" + " -10.0, -6.6, -3.3, 0.0, 3.3, 6.6, 10.0,\n" + " -10.0, -6.6, -3.3, 0.0, 3.3, 6.6, 10.0,\n" + " -10.0, -6.6, -3.3, 0.0, 3.3, 6.6, 10.0,\n" + " -10.0, -6.6, -3.3, 0.0, 3.3, 6.6, 10.0]\n" + " y: [-10.0, -10.0, -10.0, -10.0, -10.0, -10.0, -10.0,\n" + " -6.0, -6.0, -6.0, -6.0, -6.0, -6.0, -6.0,\n" + " -2.0, -2.0, -2.0, -2.0, -2.0, -2.0, -2.0,\n" + " 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0,\n" + " 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0,\n" + " 10.0, 10.0, 10.0, 10.0, 10.0, 10.0, 10.0]\n" + " col_maj_coords:\n" + " type: \"explicit\"\n" + " values:\n" + " x: [-10.0, -10.0, -10.0, -10.0, -10.0, -10.0,\n" + " -6.6, -6.6, -6.6, -6.6, -6.6, -6.6,\n" + " -3.3, -3.3, -3.3, -3.3, -3.3, -3.3,\n" + " 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\n" + " 3.3, 3.3, 3.3, 3.3, 3.3, 3.3,\n" + " 6.6, 6.6, 6.6, 6.6, 6.6, 6.6,\n" + " 10.0, 10.0, 10.0, 10.0, 10.0, 10.0]\n" + " y: [-10.0, -6.0, -2.0, 2.0, 6.0, 10.0,\n" + " -10.0, -6.0, -2.0, 2.0, 6.0, 10.0,\n" + " -10.0, -6.0, -2.0, 2.0, 6.0, 10.0,\n" + " -10.0, -6.0, -2.0, 2.0, 6.0, 10.0,\n" + " -10.0, -6.0, -2.0, 2.0, 6.0, 10.0,\n" + " -10.0, -6.0, -2.0, 2.0, 6.0, 10.0,\n" + " -10.0, -6.0, -2.0, 2.0, 6.0, 10.0]\n" + "topologies:\n" + " mesh:\n" + " type: \"structured\"\n" + " coordset: \"coords\"\n" + " elements:\n" + " dims:\n" + " i: 3\n" + " j: 2\n" + " offsets: [2, 2]\n" + " strides: [1, 7]\n" + " full_mesh:\n" + " type: \"structured\"\n" + " coordset: \"coords\"\n" + " elements:\n" + " dims:\n" + " i: 6\n" + " j: 5\n" + " offsets: [0, 0]\n" + " strides: [1, 7]\n" + " mesh_col_maj:\n" + " type: \"structured\"\n" + " coordset: \"col_maj_coords\"\n" + " elements:\n" + " dims:\n" + " i: 3\n" + " j: 2\n" + " offsets: [2, 2]\n" + " strides: [6, 1]\n" + " full_mesh_col_maj:\n" + " type: \"structured\"\n" + " coordset: \"col_maj_coords\"\n" + " elements:\n" + " dims:\n" + " i: 6\n" + " j: 5\n" + " strides: [6, 1]\n"; + + res.parse(yaml_text, "yaml"); + + Node info; + EXPECT_TRUE(blueprint::mesh::verify(res, info)); + CONDUIT_INFO(info.to_yaml()); + + test_save_mesh_helper(res, "strided_structured_colmajor"); +} + //----------------------------------------------------------------------------- TEST(conduit_blueprint_mesh_examples, julia) diff --git a/src/tests/relay/t_relay_io_silo.cpp b/src/tests/relay/t_relay_io_silo.cpp index d45d70bdf..0c577d685 100644 --- a/src/tests/relay/t_relay_io_silo.cpp +++ b/src/tests/relay/t_relay_io_silo.cpp @@ -20,1785 +20,1785 @@ using namespace conduit; using namespace conduit::utils; using namespace conduit::relay; -// //----------------------------------------------------------------------------- -// TEST(conduit_relay_io_silo, conduit_silo_cold_storage) -// { -// uint32 a_val = 20; -// uint32 b_val = 8; -// uint32 c_val = 13; - -// Node n; -// n["a"] = a_val; -// n["b"] = b_val; -// n["c"] = c_val; - -// EXPECT_EQ(n["a"].as_uint32(), a_val); -// EXPECT_EQ(n["b"].as_uint32(), b_val); -// EXPECT_EQ(n["c"].as_uint32(), c_val); - -// n.print(); - -// io::silo_write(n,"tout_cold_storage_test.silo:myobj"); - -// Node n_load; -// io::silo_read("tout_cold_storage_test.silo:myobj",n_load); - -// std::cout << "round trip" << std::endl; -// n_load.print(); - -// EXPECT_EQ(n_load["a"].as_uint32(), a_val); -// EXPECT_EQ(n_load["b"].as_uint32(), b_val); -// EXPECT_EQ(n_load["c"].as_uint32(), c_val); -// } - -// //----------------------------------------------------------------------------- -// TEST(conduit_relay_io_silo, conduit_silo_cold_storage_generic_iface) -// { -// uint32 a_val = 20; -// uint32 b_val = 8; -// uint32 c_val = 13; - -// Node n; -// n["a"] = a_val; -// n["b"] = b_val; -// n["c"] = c_val; - -// EXPECT_EQ(n["a"].as_uint32(), a_val); -// EXPECT_EQ(n["b"].as_uint32(), b_val); -// EXPECT_EQ(n["c"].as_uint32(), c_val); - -// io::save(n, "tout_cold_storage_test_generic_iface.silo:myobj"); - -// Node n_load; -// io::load("tout_cold_storage_test_generic_iface.silo:myobj",n_load); - -// EXPECT_EQ(n_load["a"].as_uint32(), a_val); -// EXPECT_EQ(n_load["b"].as_uint32(), b_val); -// EXPECT_EQ(n_load["c"].as_uint32(), c_val); -// } - -// //----------------------------------------------------------------------------- -// // test reading in a handful of different overlink files -// TEST(conduit_relay_io_silo, load_mesh_geometry) -// { -// // TODO: all these files are in overlink symlink format. -// // Symlinks may break on Windows (?) -// // Could make them overlink format without the symlink. -// // But would require modifying the files. -// std::vector filename_vec = { -// "box2d.silo", -// "box3d.silo", -// // "diamond.silo", <--- TODO this one fails because polytopal is not yet supported -// // TODO: rename these files to be more descriptive. -// // would also require modifying the paths stored within the files, -// // and re-symlinking -// "testDisk2D_a.silo", -// // "donordiv.s2_materials2.silo", <--- TODO this one fails because polytopal is not yet supported -// "donordiv.s2_materials3.silo" -// }; -// std::vector dims_vec = {2, 3, /*2,*/ 2, /*2,*/ 2}; -// std::vector coordset_length_vec = {4, 8, /*36,*/ 1994, /*16,*/ 961}; -// std::vector topology_length_vec = {1, 1, /*33,*/ 1920, /*9,*/ 900}; -// for (int i = 0; i < filename_vec.size(); ++i) -// { -// Node mesh, info; -// std::string path = utils::join_file_path("overlink", filename_vec.at(i)); -// std::string input_file = relay_test_silo_data_path(path); -// io::silo::load_mesh(input_file, mesh); - -// EXPECT_TRUE(blueprint::mesh::verify(mesh, info)); -// EXPECT_EQ(blueprint::mesh::number_of_domains(mesh), 1); - -// const Node &domain = *blueprint::mesh::domains(mesh).front(); -// EXPECT_TRUE(domain.has_child("coordsets")); -// EXPECT_EQ(domain["coordsets"].number_of_children(), 1); -// EXPECT_TRUE(domain.has_child("topologies")); -// EXPECT_EQ(domain["topologies"].number_of_children(), 1); - -// { // Coordset Validation // -// const Node &cset = domain["coordsets"].child(0); -// EXPECT_EQ(blueprint::mesh::coordset::dims(cset), dims_vec.at(i)); -// EXPECT_EQ(blueprint::mesh::coordset::length(cset), coordset_length_vec.at(i)); -// EXPECT_TRUE(blueprint::mesh::coordset::_explicit::verify(cset, info)); -// } - -// { // Topology Validation // -// const Node &topo = domain["topologies"].child(0); -// EXPECT_EQ(blueprint::mesh::topology::dims(topo), dims_vec.at(i)); -// EXPECT_EQ(blueprint::mesh::topology::length(topo), topology_length_vec.at(i)); -// EXPECT_TRUE(blueprint::mesh::topology::unstructured::verify(topo, info)); -// } -// } -// } - -// //----------------------------------------------------------------------------- -// TEST(conduit_relay_io_silo, round_trip_basic) -// { -// const std::vector> mesh_types = { -// std::make_pair("uniform", "2"), std::make_pair("uniform", "3"), -// std::make_pair("rectilinear", "2"), std::make_pair("rectilinear", "3"), -// std::make_pair("structured", "2"), std::make_pair("structured", "3"), -// std::make_pair("tris", "2"), -// std::make_pair("quads", "2"), -// std::make_pair("polygons", "2"), -// std::make_pair("tets", "3"), -// std::make_pair("hexs", "3"), -// std::make_pair("wedges", "3"), -// std::make_pair("pyramids", "3"), -// // std::make_pair("polyhedra", "3") -// }; -// for (int i = 0; i < mesh_types.size(); ++i) -// { -// std::string dim = mesh_types[i].second; -// index_t nx = 3; -// index_t ny = 4; -// index_t nz = (dim == "2" ? 0 : 2); - -// const std::string mesh_type = mesh_types[i].first; - -// Node save_mesh, load_mesh, info; -// blueprint::mesh::examples::basic(mesh_type, nx, ny, nz, save_mesh); - -// const std::string basename = "silo_basic_" + mesh_type + "_" + dim + "D"; -// const std::string filename = basename + ".root"; - -// remove_path_if_exists(filename); -// io::silo::save_mesh(save_mesh, basename); -// io::silo::load_mesh(filename, load_mesh); -// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - -// // make changes to save mesh so the diff will pass -// if (mesh_type == "uniform") -// { -// silo_uniform_to_rect_conversion("coords", "mesh", save_mesh); -// } -// silo_name_changer("mesh", save_mesh); - -// // the loaded mesh will be in the multidomain format -// // but the saved mesh is in the single domain format -// EXPECT_EQ(load_mesh.number_of_children(), 1); -// EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); -// EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); -// } -// } - -// //----------------------------------------------------------------------------- -// // we are testing vector fields in this test -// TEST(conduit_relay_io_silo, round_trip_braid) -// { -// const std::vector> mesh_types = { -// std::make_pair("uniform", "2"), std::make_pair("uniform", "3"), -// std::make_pair("rectilinear", "2"), std::make_pair("rectilinear", "3"), -// std::make_pair("structured", "2"), std::make_pair("structured", "3"), -// std::make_pair("points", "2"), std::make_pair("points", "3"), -// std::make_pair("points_implicit", "2"), std::make_pair("points_implicit", "3"), -// std::make_pair("lines", "2"), std::make_pair("lines", "3"), -// std::make_pair("tris", "2"), -// std::make_pair("quads", "2"), -// std::make_pair("tets", "3"), -// std::make_pair("hexs", "3"), -// std::make_pair("wedges", "3"), -// std::make_pair("pyramids", "3"), -// // std::make_pair("mixed_2d", "2"), -// // std::make_pair("mixed", "3"), -// }; -// for (int i = 0; i < mesh_types.size(); ++i) -// { -// std::string dim = mesh_types[i].second; -// index_t nx = 3; -// index_t ny = 4; -// index_t nz = (dim == "2" ? 0 : 2); - -// std::string mesh_type = mesh_types[i].first; - -// Node save_mesh, load_mesh, info; -// blueprint::mesh::examples::braid(mesh_type, nx, ny, nz, save_mesh); - -// const std::string basename = "silo_braid_" + mesh_type + "_" + dim + "D"; -// const std::string filename = basename + ".cycle_000100.root"; - -// // remove existing root file, directory and any output files -// remove_path_if_exists(filename); - -// io::silo::save_mesh(save_mesh, basename); -// io::silo::load_mesh(filename, load_mesh); -// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - -// // make changes to save mesh so the diff will pass -// if (mesh_type == "uniform") -// { -// silo_uniform_to_rect_conversion("coords", "mesh", save_mesh); -// } -// if (mesh_type == "points") -// { -// // this is custom code for braid -// // We know it is correct because the unstructured points version of braid -// // uses every point in the coordset -// save_mesh["topologies"].remove_child("mesh"); -// save_mesh["topologies"]["mesh"]["type"] = "points"; -// save_mesh["topologies"]["mesh"]["coordset"] = "coords"; -// } -// if (mesh_type == "points_implicit" || mesh_type == "points") -// { -// // the association doesn't matter for point meshes -// // we choose vertex by convention -// save_mesh["fields"]["radial"]["association"].reset(); -// save_mesh["fields"]["radial"]["association"] = "vertex"; -// } -// silo_name_changer("mesh", save_mesh); - -// // the loaded mesh will be in the multidomain format -// // but the saved mesh is in the single domain format -// EXPECT_EQ(load_mesh.number_of_children(), 1); -// EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); -// EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); -// } -// } - -// //----------------------------------------------------------------------------- -// // multidomain test -// TEST(conduit_relay_io_silo, round_trip_spiral) -// { -// for (int ndomains = 2; ndomains < 6; ndomains ++) -// { -// Node save_mesh, load_mesh, info; -// blueprint::mesh::examples::spiral(ndomains, save_mesh); - -// const std::string basename = "silo_spiral_" + std::to_string(ndomains) + "_domains"; -// const std::string filename = basename + ".cycle_000000.root"; - -// remove_path_if_exists(filename); -// io::silo::save_mesh(save_mesh, basename); -// io::silo::load_mesh(filename, load_mesh); -// EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); - -// // make changes to save mesh so the diff will pass -// for (index_t child = 0; child < save_mesh.number_of_children(); child ++) -// { -// silo_name_changer("mesh", save_mesh[child]); -// } - -// EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); -// NodeConstIterator l_itr = load_mesh.children(); -// NodeConstIterator s_itr = save_mesh.children(); -// while (l_itr.has_next()) -// { -// const Node &l_curr = l_itr.next(); -// const Node &s_curr = s_itr.next(); - -// EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); -// } -// } -// } - -// //----------------------------------------------------------------------------- -// TEST(conduit_relay_io_silo, round_trip_julia) -// { -// Node save_mesh, load_mesh, info; -// blueprint::mesh::examples::julia(5, // nx -// 5, // ny -// 0, // x_min -// 10, // x_max -// 2, // y_min -// 7, // y_max -// 3, // c_re -// 4, // c_im -// save_mesh); - -// const std::string basename = "silo_julia"; -// const std::string filename = basename + ".root"; - -// remove_path_if_exists(filename); -// io::silo::save_mesh(save_mesh, basename); -// io::silo::load_mesh(filename, load_mesh); -// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - -// // make changes to save mesh so the diff will pass -// silo_name_changer("mesh", save_mesh); - -// // the loaded mesh will be in the multidomain format -// // but the saved mesh is in the single domain format -// EXPECT_EQ(load_mesh.number_of_children(), 1); -// EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); -// EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); -// } - -// //----------------------------------------------------------------------------- -// // test material write and read -// TEST(conduit_relay_io_silo, round_trip_venn) -// { -// std::string matset_type = "sparse_by_element"; -// for (int j = 0; j < 2; j ++) -// { -// Node save_mesh, sbe, load_mesh, info; -// std::string size; -// int nx, ny; -// const double radius = 0.25; -// if (j == 0) -// { -// size = "small"; -// nx = ny = 4; -// } -// else -// { -// size = "large"; -// nx = ny = 100; -// } -// blueprint::mesh::examples::venn(matset_type, nx, ny, radius, save_mesh); - -// const std::string basename = "silo_venn_" + matset_type + "_" + size; -// const std::string filename = basename + ".root"; - -// remove_path_if_exists(filename); -// io::silo::save_mesh(save_mesh, basename); -// io::silo::load_mesh(filename, load_mesh); -// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - -// // make changes to save mesh so the diff will pass - -// // The field mat_check has values that are one type and matset_values -// // that are another type. The silo writer converts both to double arrays -// // in this case, so we follow suit. -// Node mat_check_new_values, mat_check_new_matset_values; -// save_mesh["fields"]["mat_check"]["values"].to_double_array(mat_check_new_values); -// save_mesh["fields"]["mat_check"]["matset_values"].to_double_array(mat_check_new_matset_values); -// save_mesh["fields"]["mat_check"]["values"].set_external(mat_check_new_values); -// save_mesh["fields"]["mat_check"]["matset_values"].set_external(mat_check_new_matset_values); - -// silo_name_changer("mesh", save_mesh); - -// // the loaded mesh will be in the multidomain format -// // but the saved mesh is in the single domain format -// EXPECT_EQ(load_mesh.number_of_children(), 1); -// EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); -// EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); -// } -// } - -// //----------------------------------------------------------------------------- -// TEST(conduit_relay_io_silo, round_trip_venn_modded_matnos) -// { -// const std::string matset_type = "sparse_by_element"; -// Node save_mesh, load_mesh, info; -// const int nx = 4; -// const int ny = 4; -// const double radius = 0.25; -// blueprint::mesh::examples::venn(matset_type, nx, ny, radius, save_mesh); - -// auto replace_matno = [](int matno) -// { -// return (matno == 1 ? 15 : -// (matno == 2 ? 37 : -// (matno == 3 ? 4 : -// (matno == 0 ? 22 : -// -1)))); -// }; - -// auto matmap_itr = save_mesh["matsets"]["matset"]["material_map"].children(); -// while (matmap_itr.has_next()) -// { -// Node &mat = matmap_itr.next(); -// mat.set(replace_matno(mat.as_int())); -// } - -// int_array matids = save_mesh["matsets"]["matset"]["material_ids"].value(); -// for (int i = 0; i < save_mesh["matsets"]["matset"]["material_ids"].dtype().number_of_elements(); i ++) -// { -// matids[i] = replace_matno(matids[i]); -// } - -// const std::string silo_basename = "silo_venn_" + matset_type + "_modded_matnos"; -// const std::string silo_filename = silo_basename + ".root"; -// remove_path_if_exists(silo_filename); -// io::silo::save_mesh(save_mesh, silo_basename); - -// const std::string bp_basename = "bp_venn_" + matset_type + "_modded_matnos"; -// const std::string bp_filename = bp_basename + ".root"; -// remove_path_if_exists(bp_filename); -// io::blueprint::save_mesh(save_mesh, bp_basename, "hdf5"); +//----------------------------------------------------------------------------- +TEST(conduit_relay_io_silo, conduit_silo_cold_storage) +{ + uint32 a_val = 20; + uint32 b_val = 8; + uint32 c_val = 13; + + Node n; + n["a"] = a_val; + n["b"] = b_val; + n["c"] = c_val; + + EXPECT_EQ(n["a"].as_uint32(), a_val); + EXPECT_EQ(n["b"].as_uint32(), b_val); + EXPECT_EQ(n["c"].as_uint32(), c_val); + + n.print(); + + io::silo_write(n,"tout_cold_storage_test.silo:myobj"); + + Node n_load; + io::silo_read("tout_cold_storage_test.silo:myobj",n_load); + + std::cout << "round trip" << std::endl; + n_load.print(); + + EXPECT_EQ(n_load["a"].as_uint32(), a_val); + EXPECT_EQ(n_load["b"].as_uint32(), b_val); + EXPECT_EQ(n_load["c"].as_uint32(), c_val); +} + +//----------------------------------------------------------------------------- +TEST(conduit_relay_io_silo, conduit_silo_cold_storage_generic_iface) +{ + uint32 a_val = 20; + uint32 b_val = 8; + uint32 c_val = 13; + + Node n; + n["a"] = a_val; + n["b"] = b_val; + n["c"] = c_val; + + EXPECT_EQ(n["a"].as_uint32(), a_val); + EXPECT_EQ(n["b"].as_uint32(), b_val); + EXPECT_EQ(n["c"].as_uint32(), c_val); + + io::save(n, "tout_cold_storage_test_generic_iface.silo:myobj"); + + Node n_load; + io::load("tout_cold_storage_test_generic_iface.silo:myobj",n_load); + + EXPECT_EQ(n_load["a"].as_uint32(), a_val); + EXPECT_EQ(n_load["b"].as_uint32(), b_val); + EXPECT_EQ(n_load["c"].as_uint32(), c_val); +} + +//----------------------------------------------------------------------------- +// test reading in a handful of different overlink files +TEST(conduit_relay_io_silo, load_mesh_geometry) +{ + // TODO: all these files are in overlink symlink format. + // Symlinks may break on Windows (?) + // Could make them overlink format without the symlink. + // But would require modifying the files. + std::vector filename_vec = { + "box2d.silo", + "box3d.silo", + // "diamond.silo", <--- TODO this one fails because polytopal is not yet supported + // TODO: rename these files to be more descriptive. + // would also require modifying the paths stored within the files, + // and re-symlinking + "testDisk2D_a.silo", + // "donordiv.s2_materials2.silo", <--- TODO this one fails because polytopal is not yet supported + "donordiv.s2_materials3.silo" + }; + std::vector dims_vec = {2, 3, /*2,*/ 2, /*2,*/ 2}; + std::vector coordset_length_vec = {4, 8, /*36,*/ 1994, /*16,*/ 961}; + std::vector topology_length_vec = {1, 1, /*33,*/ 1920, /*9,*/ 900}; + for (int i = 0; i < filename_vec.size(); ++i) + { + Node mesh, info; + std::string path = utils::join_file_path("overlink", filename_vec.at(i)); + std::string input_file = relay_test_silo_data_path(path); + io::silo::load_mesh(input_file, mesh); + + EXPECT_TRUE(blueprint::mesh::verify(mesh, info)); + EXPECT_EQ(blueprint::mesh::number_of_domains(mesh), 1); + + const Node &domain = *blueprint::mesh::domains(mesh).front(); + EXPECT_TRUE(domain.has_child("coordsets")); + EXPECT_EQ(domain["coordsets"].number_of_children(), 1); + EXPECT_TRUE(domain.has_child("topologies")); + EXPECT_EQ(domain["topologies"].number_of_children(), 1); + + { // Coordset Validation // + const Node &cset = domain["coordsets"].child(0); + EXPECT_EQ(blueprint::mesh::coordset::dims(cset), dims_vec.at(i)); + EXPECT_EQ(blueprint::mesh::coordset::length(cset), coordset_length_vec.at(i)); + EXPECT_TRUE(blueprint::mesh::coordset::_explicit::verify(cset, info)); + } + + { // Topology Validation // + const Node &topo = domain["topologies"].child(0); + EXPECT_EQ(blueprint::mesh::topology::dims(topo), dims_vec.at(i)); + EXPECT_EQ(blueprint::mesh::topology::length(topo), topology_length_vec.at(i)); + EXPECT_TRUE(blueprint::mesh::topology::unstructured::verify(topo, info)); + } + } +} + +//----------------------------------------------------------------------------- +TEST(conduit_relay_io_silo, round_trip_basic) +{ + const std::vector> mesh_types = { + std::make_pair("uniform", "2"), std::make_pair("uniform", "3"), + std::make_pair("rectilinear", "2"), std::make_pair("rectilinear", "3"), + std::make_pair("structured", "2"), std::make_pair("structured", "3"), + std::make_pair("tris", "2"), + std::make_pair("quads", "2"), + std::make_pair("polygons", "2"), + std::make_pair("tets", "3"), + std::make_pair("hexs", "3"), + std::make_pair("wedges", "3"), + std::make_pair("pyramids", "3"), + // std::make_pair("polyhedra", "3") + }; + for (int i = 0; i < mesh_types.size(); ++i) + { + std::string dim = mesh_types[i].second; + index_t nx = 3; + index_t ny = 4; + index_t nz = (dim == "2" ? 0 : 2); + + const std::string mesh_type = mesh_types[i].first; + + Node save_mesh, load_mesh, info; + blueprint::mesh::examples::basic(mesh_type, nx, ny, nz, save_mesh); + + const std::string basename = "silo_basic_" + mesh_type + "_" + dim + "D"; + const std::string filename = basename + ".root"; + + remove_path_if_exists(filename); + io::silo::save_mesh(save_mesh, basename); + io::silo::load_mesh(filename, load_mesh); + EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + + // make changes to save mesh so the diff will pass + if (mesh_type == "uniform") + { + silo_uniform_to_rect_conversion("coords", "mesh", save_mesh); + } + silo_name_changer("mesh", save_mesh); + + // the loaded mesh will be in the multidomain format + // but the saved mesh is in the single domain format + EXPECT_EQ(load_mesh.number_of_children(), 1); + EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); + EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); + } +} + +//----------------------------------------------------------------------------- +// we are testing vector fields in this test +TEST(conduit_relay_io_silo, round_trip_braid) +{ + const std::vector> mesh_types = { + std::make_pair("uniform", "2"), std::make_pair("uniform", "3"), + std::make_pair("rectilinear", "2"), std::make_pair("rectilinear", "3"), + std::make_pair("structured", "2"), std::make_pair("structured", "3"), + std::make_pair("points", "2"), std::make_pair("points", "3"), + std::make_pair("points_implicit", "2"), std::make_pair("points_implicit", "3"), + std::make_pair("lines", "2"), std::make_pair("lines", "3"), + std::make_pair("tris", "2"), + std::make_pair("quads", "2"), + std::make_pair("tets", "3"), + std::make_pair("hexs", "3"), + std::make_pair("wedges", "3"), + std::make_pair("pyramids", "3"), + // std::make_pair("mixed_2d", "2"), + // std::make_pair("mixed", "3"), + }; + for (int i = 0; i < mesh_types.size(); ++i) + { + std::string dim = mesh_types[i].second; + index_t nx = 3; + index_t ny = 4; + index_t nz = (dim == "2" ? 0 : 2); + + std::string mesh_type = mesh_types[i].first; + + Node save_mesh, load_mesh, info; + blueprint::mesh::examples::braid(mesh_type, nx, ny, nz, save_mesh); + + const std::string basename = "silo_braid_" + mesh_type + "_" + dim + "D"; + const std::string filename = basename + ".cycle_000100.root"; + + // remove existing root file, directory and any output files + remove_path_if_exists(filename); + + io::silo::save_mesh(save_mesh, basename); + io::silo::load_mesh(filename, load_mesh); + EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + + // make changes to save mesh so the diff will pass + if (mesh_type == "uniform") + { + silo_uniform_to_rect_conversion("coords", "mesh", save_mesh); + } + if (mesh_type == "points") + { + // this is custom code for braid + // We know it is correct because the unstructured points version of braid + // uses every point in the coordset + save_mesh["topologies"].remove_child("mesh"); + save_mesh["topologies"]["mesh"]["type"] = "points"; + save_mesh["topologies"]["mesh"]["coordset"] = "coords"; + } + if (mesh_type == "points_implicit" || mesh_type == "points") + { + // the association doesn't matter for point meshes + // we choose vertex by convention + save_mesh["fields"]["radial"]["association"].reset(); + save_mesh["fields"]["radial"]["association"] = "vertex"; + } + silo_name_changer("mesh", save_mesh); + + // the loaded mesh will be in the multidomain format + // but the saved mesh is in the single domain format + EXPECT_EQ(load_mesh.number_of_children(), 1); + EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); + EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); + } +} + +//----------------------------------------------------------------------------- +// multidomain test +TEST(conduit_relay_io_silo, round_trip_spiral) +{ + for (int ndomains = 2; ndomains < 6; ndomains ++) + { + Node save_mesh, load_mesh, info; + blueprint::mesh::examples::spiral(ndomains, save_mesh); + + const std::string basename = "silo_spiral_" + std::to_string(ndomains) + "_domains"; + const std::string filename = basename + ".cycle_000000.root"; + + remove_path_if_exists(filename); + io::silo::save_mesh(save_mesh, basename); + io::silo::load_mesh(filename, load_mesh); + EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); + + // make changes to save mesh so the diff will pass + for (index_t child = 0; child < save_mesh.number_of_children(); child ++) + { + silo_name_changer("mesh", save_mesh[child]); + } + + EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); + NodeConstIterator l_itr = load_mesh.children(); + NodeConstIterator s_itr = save_mesh.children(); + while (l_itr.has_next()) + { + const Node &l_curr = l_itr.next(); + const Node &s_curr = s_itr.next(); + + EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); + } + } +} + +//----------------------------------------------------------------------------- +TEST(conduit_relay_io_silo, round_trip_julia) +{ + Node save_mesh, load_mesh, info; + blueprint::mesh::examples::julia(5, // nx + 5, // ny + 0, // x_min + 10, // x_max + 2, // y_min + 7, // y_max + 3, // c_re + 4, // c_im + save_mesh); + + const std::string basename = "silo_julia"; + const std::string filename = basename + ".root"; + + remove_path_if_exists(filename); + io::silo::save_mesh(save_mesh, basename); + io::silo::load_mesh(filename, load_mesh); + EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + + // make changes to save mesh so the diff will pass + silo_name_changer("mesh", save_mesh); + + // the loaded mesh will be in the multidomain format + // but the saved mesh is in the single domain format + EXPECT_EQ(load_mesh.number_of_children(), 1); + EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); + EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); +} + +//----------------------------------------------------------------------------- +// test material write and read +TEST(conduit_relay_io_silo, round_trip_venn) +{ + std::string matset_type = "sparse_by_element"; + for (int j = 0; j < 2; j ++) + { + Node save_mesh, sbe, load_mesh, info; + std::string size; + int nx, ny; + const double radius = 0.25; + if (j == 0) + { + size = "small"; + nx = ny = 4; + } + else + { + size = "large"; + nx = ny = 100; + } + blueprint::mesh::examples::venn(matset_type, nx, ny, radius, save_mesh); + + const std::string basename = "silo_venn_" + matset_type + "_" + size; + const std::string filename = basename + ".root"; + + remove_path_if_exists(filename); + io::silo::save_mesh(save_mesh, basename); + io::silo::load_mesh(filename, load_mesh); + EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + + // make changes to save mesh so the diff will pass + + // The field mat_check has values that are one type and matset_values + // that are another type. The silo writer converts both to double arrays + // in this case, so we follow suit. + Node mat_check_new_values, mat_check_new_matset_values; + save_mesh["fields"]["mat_check"]["values"].to_double_array(mat_check_new_values); + save_mesh["fields"]["mat_check"]["matset_values"].to_double_array(mat_check_new_matset_values); + save_mesh["fields"]["mat_check"]["values"].set_external(mat_check_new_values); + save_mesh["fields"]["mat_check"]["matset_values"].set_external(mat_check_new_matset_values); + + silo_name_changer("mesh", save_mesh); + + // the loaded mesh will be in the multidomain format + // but the saved mesh is in the single domain format + EXPECT_EQ(load_mesh.number_of_children(), 1); + EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); + EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); + } +} + +//----------------------------------------------------------------------------- +TEST(conduit_relay_io_silo, round_trip_venn_modded_matnos) +{ + const std::string matset_type = "sparse_by_element"; + Node save_mesh, load_mesh, info; + const int nx = 4; + const int ny = 4; + const double radius = 0.25; + blueprint::mesh::examples::venn(matset_type, nx, ny, radius, save_mesh); + + auto replace_matno = [](int matno) + { + return (matno == 1 ? 15 : + (matno == 2 ? 37 : + (matno == 3 ? 4 : + (matno == 0 ? 22 : + -1)))); + }; + + auto matmap_itr = save_mesh["matsets"]["matset"]["material_map"].children(); + while (matmap_itr.has_next()) + { + Node &mat = matmap_itr.next(); + mat.set(replace_matno(mat.as_int())); + } + + int_array matids = save_mesh["matsets"]["matset"]["material_ids"].value(); + for (int i = 0; i < save_mesh["matsets"]["matset"]["material_ids"].dtype().number_of_elements(); i ++) + { + matids[i] = replace_matno(matids[i]); + } + + const std::string silo_basename = "silo_venn_" + matset_type + "_modded_matnos"; + const std::string silo_filename = silo_basename + ".root"; + remove_path_if_exists(silo_filename); + io::silo::save_mesh(save_mesh, silo_basename); + + const std::string bp_basename = "bp_venn_" + matset_type + "_modded_matnos"; + const std::string bp_filename = bp_basename + ".root"; + remove_path_if_exists(bp_filename); + io::blueprint::save_mesh(save_mesh, bp_basename, "hdf5"); -// io::silo::load_mesh(silo_filename, load_mesh); -// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - -// // make changes to save mesh so the diff will pass - -// // The field mat_check has values that are one type and matset_values -// // that are another type. The silo writer converts both to double arrays -// // in this case, so we follow suit. -// Node mat_check_new_values, mat_check_new_matset_values; -// save_mesh["fields"]["mat_check"]["values"].to_double_array(mat_check_new_values); -// save_mesh["fields"]["mat_check"]["matset_values"].to_double_array(mat_check_new_matset_values); -// save_mesh["fields"]["mat_check"]["values"].set_external(mat_check_new_values); -// save_mesh["fields"]["mat_check"]["matset_values"].set_external(mat_check_new_matset_values); - -// // to_silo is going to reorder mixed materials least to greatest -// // so we must do the same -// int_array mat_ids = save_mesh["matsets"]["matset"]["material_ids"].value(); -// const auto mat_id10 = mat_ids[10]; -// const auto mat_id11 = mat_ids[11]; -// const auto mat_id12 = mat_ids[12]; -// mat_ids[10] = mat_id12; -// mat_ids[11] = mat_id10; -// mat_ids[12] = mat_id11; -// auto field_itr = save_mesh["fields"].children(); -// while (field_itr.has_next()) -// { -// const Node &n_field = field_itr.next(); -// if (n_field.has_child("matset")) -// { -// double_array matset_vals = n_field["matset_values"].value(); -// const auto matset_val10 = matset_vals[10]; -// const auto matset_val11 = matset_vals[11]; -// const auto matset_val12 = matset_vals[12]; -// matset_vals[10] = matset_val12; -// matset_vals[11] = matset_val10; -// matset_vals[12] = matset_val11; -// } -// } - -// silo_name_changer("mesh", save_mesh); - -// // the loaded mesh will be in the multidomain format -// // but the saved mesh is in the single domain format -// EXPECT_EQ(load_mesh.number_of_children(), 1); -// EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); - -// EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); -// } - -// //----------------------------------------------------------------------------- -// TEST(conduit_relay_io_silo, round_trip_spiral_multi_dom_materials) -// { -// Node save_mesh, load_mesh, info; -// const int ndomains = 4; -// blueprint::mesh::examples::spiral(ndomains, save_mesh); -// add_matset_to_spiral(save_mesh, ndomains); -// EXPECT_TRUE(blueprint::mesh::verify(save_mesh, info)); + io::silo::load_mesh(silo_filename, load_mesh); + EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + + // make changes to save mesh so the diff will pass + + // The field mat_check has values that are one type and matset_values + // that are another type. The silo writer converts both to double arrays + // in this case, so we follow suit. + Node mat_check_new_values, mat_check_new_matset_values; + save_mesh["fields"]["mat_check"]["values"].to_double_array(mat_check_new_values); + save_mesh["fields"]["mat_check"]["matset_values"].to_double_array(mat_check_new_matset_values); + save_mesh["fields"]["mat_check"]["values"].set_external(mat_check_new_values); + save_mesh["fields"]["mat_check"]["matset_values"].set_external(mat_check_new_matset_values); + + // to_silo is going to reorder mixed materials least to greatest + // so we must do the same + int_array mat_ids = save_mesh["matsets"]["matset"]["material_ids"].value(); + const auto mat_id10 = mat_ids[10]; + const auto mat_id11 = mat_ids[11]; + const auto mat_id12 = mat_ids[12]; + mat_ids[10] = mat_id12; + mat_ids[11] = mat_id10; + mat_ids[12] = mat_id11; + auto field_itr = save_mesh["fields"].children(); + while (field_itr.has_next()) + { + const Node &n_field = field_itr.next(); + if (n_field.has_child("matset")) + { + double_array matset_vals = n_field["matset_values"].value(); + const auto matset_val10 = matset_vals[10]; + const auto matset_val11 = matset_vals[11]; + const auto matset_val12 = matset_vals[12]; + matset_vals[10] = matset_val12; + matset_vals[11] = matset_val10; + matset_vals[12] = matset_val11; + } + } + + silo_name_changer("mesh", save_mesh); + + // the loaded mesh will be in the multidomain format + // but the saved mesh is in the single domain format + EXPECT_EQ(load_mesh.number_of_children(), 1); + EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); + + EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); +} + +//----------------------------------------------------------------------------- +TEST(conduit_relay_io_silo, round_trip_spiral_multi_dom_materials) +{ + Node save_mesh, load_mesh, info; + const int ndomains = 4; + blueprint::mesh::examples::spiral(ndomains, save_mesh); + add_matset_to_spiral(save_mesh, ndomains); + EXPECT_TRUE(blueprint::mesh::verify(save_mesh, info)); -// const std::string basename = "silo_multidom_materials_spiral"; -// const std::string filename = basename + ".cycle_000000.root"; - -// remove_path_if_exists(filename); -// io::silo::save_mesh(save_mesh, basename); -// io::silo::load_mesh(filename, load_mesh); -// EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); - -// // make changes to save mesh so the diff will pass -// for (index_t child = 0; child < save_mesh.number_of_children(); child ++) -// { -// // get the matset for this domain -// Node &n_matset = save_mesh[child]["matsets"]["matset"]; - -// // clean up volume fractions -// Node vf_arr; -// n_matset["volume_fractions"].to_float64_array(vf_arr); -// n_matset["volume_fractions"].reset(); -// n_matset["volume_fractions"].set(vf_arr); + const std::string basename = "silo_multidom_materials_spiral"; + const std::string filename = basename + ".cycle_000000.root"; + + remove_path_if_exists(filename); + io::silo::save_mesh(save_mesh, basename); + io::silo::load_mesh(filename, load_mesh); + EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); + + // make changes to save mesh so the diff will pass + for (index_t child = 0; child < save_mesh.number_of_children(); child ++) + { + // get the matset for this domain + Node &n_matset = save_mesh[child]["matsets"]["matset"]; + + // clean up volume fractions + Node vf_arr; + n_matset["volume_fractions"].to_float64_array(vf_arr); + n_matset["volume_fractions"].reset(); + n_matset["volume_fractions"].set(vf_arr); -// // cheat a little bit - we don't have these to start -// n_matset["sizes"].set_external(load_mesh[child]["matsets"]["mesh_matset"]["sizes"]); -// n_matset["offsets"].set_external(load_mesh[child]["matsets"]["mesh_matset"]["offsets"]); - -// silo_name_changer("mesh", save_mesh[child]); -// } - -// EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); -// NodeConstIterator l_itr = load_mesh.children(); -// NodeConstIterator s_itr = save_mesh.children(); -// while (l_itr.has_next()) -// { -// const Node &l_curr = l_itr.next(); -// const Node &s_curr = s_itr.next(); - -// EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); -// } -// } - -// //----------------------------------------------------------------------------- -// TEST(conduit_relay_io_silo, round_trip_grid_adjset) -// { -// Node save_mesh, load_mesh, info; -// blueprint::mesh::examples::grid("structured", 3, 3, 1, 2, 2, 1, save_mesh); - -// // we need a material in order for this to be valid overlink -// for (index_t child = 0; child < save_mesh.number_of_children(); child ++) -// { -// add_multi_buffer_full_matset(save_mesh[child], 4, "mesh"); -// } - -// const std::string basename = "silo_grid_adjset"; -// const std::string filename = basename + "/OvlTop.silo"; - -// Node write_opts; -// write_opts["file_style"] = "overlink"; -// write_opts["ovl_topo_name"] = "mesh"; - -// Node read_opts; -// read_opts["matset_style"] = "multi_buffer_full"; - -// remove_path_if_exists(filename); -// io::silo::save_mesh(save_mesh, basename, write_opts); -// io::blueprint::save_mesh(save_mesh, basename, "hdf5"); -// io::silo::load_mesh(filename, read_opts, load_mesh); -// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - -// for (index_t child = 0; child < save_mesh.number_of_children(); child ++) -// { -// // separate out vector fields -// Node &field_vel = save_mesh[child]["fields"]["vel"]; -// Node &field_vel_u = save_mesh[child]["fields"]["vel_u"]; -// Node &field_vel_v = save_mesh[child]["fields"]["vel_v"]; + // cheat a little bit - we don't have these to start + n_matset["sizes"].set_external(load_mesh[child]["matsets"]["mesh_matset"]["sizes"]); + n_matset["offsets"].set_external(load_mesh[child]["matsets"]["mesh_matset"]["offsets"]); + + silo_name_changer("mesh", save_mesh[child]); + } + + EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); + NodeConstIterator l_itr = load_mesh.children(); + NodeConstIterator s_itr = save_mesh.children(); + while (l_itr.has_next()) + { + const Node &l_curr = l_itr.next(); + const Node &s_curr = s_itr.next(); + + EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); + } +} + +//----------------------------------------------------------------------------- +TEST(conduit_relay_io_silo, round_trip_grid_adjset) +{ + Node save_mesh, load_mesh, info; + blueprint::mesh::examples::grid("structured", 3, 3, 1, 2, 2, 1, save_mesh); + + // we need a material in order for this to be valid overlink + for (index_t child = 0; child < save_mesh.number_of_children(); child ++) + { + add_multi_buffer_full_matset(save_mesh[child], 4, "mesh"); + } + + const std::string basename = "silo_grid_adjset"; + const std::string filename = basename + "/OvlTop.silo"; + + Node write_opts; + write_opts["file_style"] = "overlink"; + write_opts["ovl_topo_name"] = "mesh"; + + Node read_opts; + read_opts["matset_style"] = "multi_buffer_full"; + + remove_path_if_exists(filename); + io::silo::save_mesh(save_mesh, basename, write_opts); + io::blueprint::save_mesh(save_mesh, basename, "hdf5"); + io::silo::load_mesh(filename, read_opts, load_mesh); + EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + + for (index_t child = 0; child < save_mesh.number_of_children(); child ++) + { + // separate out vector fields + Node &field_vel = save_mesh[child]["fields"]["vel"]; + Node &field_vel_u = save_mesh[child]["fields"]["vel_u"]; + Node &field_vel_v = save_mesh[child]["fields"]["vel_v"]; -// field_vel_u["topology"].set(field_vel["topology"]); -// field_vel_u["association"].set(field_vel["association"]); -// field_vel_u["values"].set(field_vel["values/u"]); -// field_vel_v["topology"].set(field_vel["topology"]); -// field_vel_v["association"].set(field_vel["association"]); -// field_vel_v["values"].set(field_vel["values/v"]); - -// save_mesh[child]["fields"].remove_child("vel"); - -// // make adjset pairwise -// Node &pairwise_adjset = save_mesh[child]["adjsets"]["adjset"]; -// conduit::blueprint::mesh::adjset::to_pairwise(save_mesh[child]["adjsets"]["mesh_adj"], pairwise_adjset); -// save_mesh[child]["adjsets"].remove_child("mesh_adj"); - -// // make changes to save mesh so the diff will pass -// overlink_name_changer(save_mesh[child]); -// } - -// EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); -// NodeConstIterator l_itr = load_mesh.children(); -// NodeConstIterator s_itr = save_mesh.children(); -// while (l_itr.has_next()) -// { -// const Node &l_curr = l_itr.next(); -// const Node &s_curr = s_itr.next(); - -// EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); -// } -// } - -// //----------------------------------------------------------------------------- -// // -// // test read and write semantics -// // - -// //----------------------------------------------------------------------------- -// TEST(conduit_relay_io_silo, read_and_write_semantics) -// { -// for (int ndomains = 2; ndomains < 6; ndomains ++) -// { -// Node save_mesh, load_mesh, info; -// blueprint::mesh::examples::spiral(ndomains, save_mesh); - -// const std::string basename = "silo_spiral_" + std::to_string(ndomains) + "_domains"; -// const std::string filename = basename + ".cycle_000000.root"; - -// remove_path_if_exists(filename); -// io::silo::write_mesh(save_mesh, basename); -// io::silo::read_mesh(filename, load_mesh); -// EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); - -// // make changes to save mesh so the diff will pass -// for (index_t child = 0; child < save_mesh.number_of_children(); child ++) -// { -// silo_name_changer("mesh", save_mesh[child]); -// } - -// EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); -// NodeConstIterator l_itr = load_mesh.children(); -// NodeConstIterator s_itr = save_mesh.children(); -// while (l_itr.has_next()) -// { -// const Node &l_curr = l_itr.next(); -// const Node &s_curr = s_itr.next(); - -// EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); -// } -// } -// } - -// //----------------------------------------------------------------------------- -// // -// // special case tests -// // - -// //----------------------------------------------------------------------------- -// // var is not defined on a domain -// // -// // tests the silo "EMPTY" capability -// TEST(conduit_relay_io_silo, missing_domain_var) -// { -// Node save_mesh, load_mesh, info; -// const int ndomains = 4; -// blueprint::mesh::examples::spiral(ndomains, save_mesh); - -// // remove information for a particular domain -// save_mesh[2]["fields"].remove_child("dist"); - -// const std::string basename = "silo_missing_domain_var_spiral"; -// const std::string filename = basename + ".cycle_000000.root"; - -// remove_path_if_exists(filename); -// io::silo::save_mesh(save_mesh, basename); -// io::silo::load_mesh(filename, load_mesh); -// EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); - -// // make changes to save mesh so the diff will pass -// for (index_t child = 0; child < save_mesh.number_of_children(); child ++) -// { -// silo_name_changer("mesh", save_mesh[child]); -// } -// save_mesh[2].remove_child("fields"); - -// EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); -// NodeConstIterator l_itr = load_mesh.children(); -// NodeConstIterator s_itr = save_mesh.children(); -// while (l_itr.has_next()) -// { -// const Node &l_curr = l_itr.next(); -// const Node &s_curr = s_itr.next(); - -// EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); -// } -// } - -// //----------------------------------------------------------------------------- -// // matset is not defined on a domain -// // -// // tests the silo "EMPTY" capability -// TEST(conduit_relay_io_silo, missing_domain_matset) -// { -// Node save_mesh, load_mesh, info; -// const int ndomains = 4; -// blueprint::mesh::examples::spiral(ndomains, save_mesh); -// add_matset_to_spiral(save_mesh, ndomains); -// EXPECT_TRUE(blueprint::mesh::verify(save_mesh, info)); - -// // remove information for a particular domain -// save_mesh[2]["matsets"].remove_child("matset"); - -// const std::string basename = "silo_missing_domain_matset_spiral"; -// const std::string filename = basename + ".cycle_000000.root"; - -// remove_path_if_exists(filename); -// io::silo::save_mesh(save_mesh, basename); -// io::silo::load_mesh(filename, load_mesh); -// EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); - -// // make changes to save mesh so the diff will pass -// for (index_t child = 0; child < save_mesh.number_of_children(); child ++) -// { -// if (save_mesh[child].has_path("matsets/matset")) -// { -// // get the matset for this domain -// Node &n_matset = save_mesh[child]["matsets"]["matset"]; - -// // clean up volume fractions -// Node vf_arr; -// n_matset["volume_fractions"].to_float64_array(vf_arr); -// n_matset["volume_fractions"].reset(); -// n_matset["volume_fractions"].set(vf_arr); + field_vel_u["topology"].set(field_vel["topology"]); + field_vel_u["association"].set(field_vel["association"]); + field_vel_u["values"].set(field_vel["values/u"]); + field_vel_v["topology"].set(field_vel["topology"]); + field_vel_v["association"].set(field_vel["association"]); + field_vel_v["values"].set(field_vel["values/v"]); + + save_mesh[child]["fields"].remove_child("vel"); + + // make adjset pairwise + Node &pairwise_adjset = save_mesh[child]["adjsets"]["adjset"]; + conduit::blueprint::mesh::adjset::to_pairwise(save_mesh[child]["adjsets"]["mesh_adj"], pairwise_adjset); + save_mesh[child]["adjsets"].remove_child("mesh_adj"); + + // make changes to save mesh so the diff will pass + overlink_name_changer(save_mesh[child]); + } + + EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); + NodeConstIterator l_itr = load_mesh.children(); + NodeConstIterator s_itr = save_mesh.children(); + while (l_itr.has_next()) + { + const Node &l_curr = l_itr.next(); + const Node &s_curr = s_itr.next(); + + EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); + } +} + +//----------------------------------------------------------------------------- +// +// test read and write semantics +// + +//----------------------------------------------------------------------------- +TEST(conduit_relay_io_silo, read_and_write_semantics) +{ + for (int ndomains = 2; ndomains < 6; ndomains ++) + { + Node save_mesh, load_mesh, info; + blueprint::mesh::examples::spiral(ndomains, save_mesh); + + const std::string basename = "silo_spiral_" + std::to_string(ndomains) + "_domains"; + const std::string filename = basename + ".cycle_000000.root"; + + remove_path_if_exists(filename); + io::silo::write_mesh(save_mesh, basename); + io::silo::read_mesh(filename, load_mesh); + EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); + + // make changes to save mesh so the diff will pass + for (index_t child = 0; child < save_mesh.number_of_children(); child ++) + { + silo_name_changer("mesh", save_mesh[child]); + } + + EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); + NodeConstIterator l_itr = load_mesh.children(); + NodeConstIterator s_itr = save_mesh.children(); + while (l_itr.has_next()) + { + const Node &l_curr = l_itr.next(); + const Node &s_curr = s_itr.next(); + + EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); + } + } +} + +//----------------------------------------------------------------------------- +// +// special case tests +// + +//----------------------------------------------------------------------------- +// var is not defined on a domain +// +// tests the silo "EMPTY" capability +TEST(conduit_relay_io_silo, missing_domain_var) +{ + Node save_mesh, load_mesh, info; + const int ndomains = 4; + blueprint::mesh::examples::spiral(ndomains, save_mesh); + + // remove information for a particular domain + save_mesh[2]["fields"].remove_child("dist"); + + const std::string basename = "silo_missing_domain_var_spiral"; + const std::string filename = basename + ".cycle_000000.root"; + + remove_path_if_exists(filename); + io::silo::save_mesh(save_mesh, basename); + io::silo::load_mesh(filename, load_mesh); + EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); + + // make changes to save mesh so the diff will pass + for (index_t child = 0; child < save_mesh.number_of_children(); child ++) + { + silo_name_changer("mesh", save_mesh[child]); + } + save_mesh[2].remove_child("fields"); + + EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); + NodeConstIterator l_itr = load_mesh.children(); + NodeConstIterator s_itr = save_mesh.children(); + while (l_itr.has_next()) + { + const Node &l_curr = l_itr.next(); + const Node &s_curr = s_itr.next(); + + EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); + } +} + +//----------------------------------------------------------------------------- +// matset is not defined on a domain +// +// tests the silo "EMPTY" capability +TEST(conduit_relay_io_silo, missing_domain_matset) +{ + Node save_mesh, load_mesh, info; + const int ndomains = 4; + blueprint::mesh::examples::spiral(ndomains, save_mesh); + add_matset_to_spiral(save_mesh, ndomains); + EXPECT_TRUE(blueprint::mesh::verify(save_mesh, info)); + + // remove information for a particular domain + save_mesh[2]["matsets"].remove_child("matset"); + + const std::string basename = "silo_missing_domain_matset_spiral"; + const std::string filename = basename + ".cycle_000000.root"; + + remove_path_if_exists(filename); + io::silo::save_mesh(save_mesh, basename); + io::silo::load_mesh(filename, load_mesh); + EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); + + // make changes to save mesh so the diff will pass + for (index_t child = 0; child < save_mesh.number_of_children(); child ++) + { + if (save_mesh[child].has_path("matsets/matset")) + { + // get the matset for this domain + Node &n_matset = save_mesh[child]["matsets"]["matset"]; + + // clean up volume fractions + Node vf_arr; + n_matset["volume_fractions"].to_float64_array(vf_arr); + n_matset["volume_fractions"].reset(); + n_matset["volume_fractions"].set(vf_arr); -// // cheat a little bit - we don't have these to start -// n_matset["sizes"].set_external(load_mesh[child]["matsets"]["mesh_matset"]["sizes"]); -// n_matset["offsets"].set_external(load_mesh[child]["matsets"]["mesh_matset"]["offsets"]); -// } - -// silo_name_changer("mesh", save_mesh[child]); -// } -// save_mesh[2].remove_child("matsets"); - -// EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); -// NodeConstIterator l_itr = load_mesh.children(); -// NodeConstIterator s_itr = save_mesh.children(); -// while (l_itr.has_next()) -// { -// const Node &l_curr = l_itr.next(); -// const Node &s_curr = s_itr.next(); - -// EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); -// } -// } - -// //----------------------------------------------------------------------------- -// // mesh is not defined on a domain -// // -// // This case is much less interesting. -// // data passes through the clean mesh filter which -// // deletes domains that are missing topos. -// // They simply are not part of the mesh and so silo -// // doesn't have to deal with it. -// TEST(conduit_relay_io_silo, missing_domain_mesh_trivial) -// { -// Node save_mesh, load_mesh, info; -// const int ndomains = 4; -// blueprint::mesh::examples::spiral(ndomains, save_mesh); - -// // remove information for a particular domain -// save_mesh[2]["topologies"].remove_child("topo"); - -// const std::string basename = "silo_missing_domain_mesh_trivial_spiral"; -// const std::string filename = basename + ".cycle_000000.root"; - -// remove_path_if_exists(filename); -// io::silo::save_mesh(save_mesh, basename); -// io::silo::load_mesh(filename, load_mesh); - -// EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); - -// // make changes to save mesh so the diff will pass -// save_mesh.remove(2); -// save_mesh.rename_child("domain_000003", "domain_000002"); -// save_mesh[2]["state"]["domain_id"].reset(); -// save_mesh[2]["state"]["domain_id"] = 2; -// for (index_t child = 0; child < save_mesh.number_of_children(); child ++) -// { -// silo_name_changer("mesh", save_mesh[child]); -// } - -// EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); -// NodeConstIterator l_itr = load_mesh.children(); -// NodeConstIterator s_itr = save_mesh.children(); -// while (l_itr.has_next()) -// { -// const Node &l_curr = l_itr.next(); -// const Node &s_curr = s_itr.next(); - -// EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); -// } -// } - -// //----------------------------------------------------------------------------- -// // mesh is not defined on a domain but there are multiple meshes -// TEST(conduit_relay_io_silo, missing_domain_mesh) -// { -// Node save_mesh, save_mesh2, load_mesh, load_mesh2, info, opts; -// const int ndomains = 4; -// blueprint::mesh::examples::spiral(ndomains, save_mesh); -// blueprint::mesh::examples::spiral(ndomains, save_mesh2); - -// for (index_t child = 0; child < save_mesh.number_of_children(); child ++) -// { -// save_mesh[child]["coordsets"].rename_child("coords", "coords2"); -// save_mesh[child]["topologies"]["topo"]["coordset"].reset(); -// save_mesh[child]["topologies"]["topo"]["coordset"] = "coords2"; -// save_mesh[child]["topologies"].rename_child("topo", "topo2"); -// save_mesh[child]["fields"]["dist"]["topology"].reset(); -// save_mesh[child]["fields"]["dist"]["topology"] = "topo2"; -// save_mesh[child]["fields"].rename_child("dist", "dist2"); - -// save_mesh[child]["coordsets"]["coords"].set_external(save_mesh2[child]["coordsets"]["coords"]); -// save_mesh[child]["topologies"]["topo"].set_external(save_mesh2[child]["topologies"]["topo"]); -// save_mesh[child]["fields"]["dist"].set_external(save_mesh2[child]["fields"]["dist"]); -// } - -// // remove information for a particular domain -// save_mesh[2]["topologies"].remove_child("topo"); - -// const std::string basename = "silo_missing_domain_mesh_spiral"; -// const std::string filename = basename + ".cycle_000000.root"; - -// remove_path_if_exists(filename); -// io::silo::save_mesh(save_mesh, basename); -// opts["silo_names"]["multimesh_names"] = "mesh_topo2"; -// io::silo::load_mesh(filename, opts, load_mesh); -// opts["silo_names"]["multimesh_names"] = "mesh_topo"; -// io::silo::load_mesh(filename, opts, load_mesh2); -// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); -// EXPECT_TRUE(blueprint::mesh::verify(load_mesh2, info)); - -// // make changes to save mesh so the diff will pass -// save_mesh[2]["coordsets"].remove_child("coords"); -// save_mesh[2]["fields"].remove_child("dist"); -// for (index_t child = 0; child < save_mesh.number_of_children(); child ++) -// { -// silo_name_changer("mesh", save_mesh[child]); -// } - -// // we must merge the two meshes in load mesh -// // the indexing is tricky because one is missing a domain -// load_mesh[0]["coordsets"]["mesh_topo"].set_external(load_mesh2[0]["coordsets"]["mesh_topo"]); -// load_mesh[0]["topologies"]["mesh_topo"].set_external(load_mesh2[0]["topologies"]["mesh_topo"]); -// load_mesh[0]["fields"]["mesh_dist"].set_external(load_mesh2[0]["fields"]["mesh_dist"]); -// load_mesh[1]["coordsets"]["mesh_topo"].set_external(load_mesh2[1]["coordsets"]["mesh_topo"]); -// load_mesh[1]["topologies"]["mesh_topo"].set_external(load_mesh2[1]["topologies"]["mesh_topo"]); -// load_mesh[1]["fields"]["mesh_dist"].set_external(load_mesh2[1]["fields"]["mesh_dist"]); -// load_mesh[3]["coordsets"]["mesh_topo"].set_external(load_mesh2[2]["coordsets"]["mesh_topo"]); -// load_mesh[3]["topologies"]["mesh_topo"].set_external(load_mesh2[2]["topologies"]["mesh_topo"]); -// load_mesh[3]["fields"]["mesh_dist"].set_external(load_mesh2[2]["fields"]["mesh_dist"]); - -// EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); -// NodeConstIterator l_itr = load_mesh.children(); -// NodeConstIterator s_itr = save_mesh.children(); -// while (l_itr.has_next()) -// { -// const Node &l_curr = l_itr.next(); -// const Node &s_curr = s_itr.next(); - -// EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); -// } -// } - -// //----------------------------------------------------------------------------- -// // explicit points (unstructured mesh) do not use every coord -// TEST(conduit_relay_io_silo, unstructured_points) -// { -// Node save_mesh, load_mesh, info; -// blueprint::mesh::examples::braid("points", 2, 2, 2, save_mesh); - -// std::vector new_conn; -// std::vector new_field1; -// std::vector new_field2; -// std::vector new_xcoords, new_ycoords, new_zcoords; - -// int_accessor conn = save_mesh["topologies"]["mesh"]["elements"]["connectivity"].value(); - -// float_accessor field1 = save_mesh["fields"]["braid"]["values"].value(); -// float_accessor field2 = save_mesh["fields"]["radial"]["values"].value(); - -// float_accessor xcoords = save_mesh["coordsets"]["coords"]["values"]["x"].value(); -// float_accessor ycoords = save_mesh["coordsets"]["coords"]["values"]["y"].value(); -// float_accessor zcoords = save_mesh["coordsets"]["coords"]["values"]["z"].value(); - -// for (int i = 1; i < conn.number_of_elements(); i += 2) -// { -// new_conn.push_back(conn[i]); -// new_field1.push_back(field1[i]); -// new_field2.push_back(field2[i]); - -// new_xcoords.push_back(xcoords[conn[i]]); -// new_ycoords.push_back(ycoords[conn[i]]); -// new_zcoords.push_back(zcoords[conn[i]]); -// } -// save_mesh["topologies"]["mesh"]["elements"]["connectivity"].reset(); -// save_mesh["topologies"]["mesh"]["elements"]["connectivity"].set(new_conn); - -// save_mesh["fields"].remove_child("vel"); -// save_mesh["fields"]["braid"]["values"].reset(); -// save_mesh["fields"]["braid"]["values"].set(new_field1); -// save_mesh["fields"]["radial"]["values"].reset(); -// save_mesh["fields"]["radial"]["values"].set(new_field2); - -// // we have modified braid such that it only uses half of the points in the coordset - -// const std::string basename = "silo_unstructured_points_braid"; -// const std::string filename = basename + ".cycle_000100.root"; - -// // remove existing root file, directory and any output files -// remove_path_if_exists(filename); - -// io::silo::save_mesh(save_mesh, basename); -// io::silo::load_mesh(filename, load_mesh); -// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - -// // now we must remove the unused points and change to an implicit points topo so that the diff passes -// save_mesh["coordsets"]["coords"]["values"]["x"].reset(); -// save_mesh["coordsets"]["coords"]["values"]["x"].set(new_xcoords); -// save_mesh["coordsets"]["coords"]["values"]["y"].reset(); -// save_mesh["coordsets"]["coords"]["values"]["y"].set(new_ycoords); -// save_mesh["coordsets"]["coords"]["values"]["z"].reset(); -// save_mesh["coordsets"]["coords"]["values"]["z"].set(new_zcoords); - -// save_mesh["topologies"].remove_child("mesh"); -// save_mesh["topologies"]["mesh"]["type"] = "points"; -// save_mesh["topologies"]["mesh"]["coordset"] = "coords"; - -// // the association doesn't matter for point meshes -// // we choose vertex by convention -// save_mesh["fields"]["radial"]["association"].reset(); -// save_mesh["fields"]["radial"]["association"] = "vertex"; - -// silo_name_changer("mesh", save_mesh); - -// // the loaded mesh will be in the multidomain format -// // but the saved mesh is in the single domain format -// EXPECT_EQ(load_mesh.number_of_children(), 1); -// EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); - -// EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); -// } - -// //----------------------------------------------------------------------------- - -// // -// // save option tests -// // - -// // save options: -// /// opts: -// /// -// /// file_style: "default", "root_only", "multi_file", "overlink" -// /// when # of domains == 1, "default" ==> "root_only" -// /// else, "default" ==> "multi_file" -// /// -// /// silo_type: "default", "pdb", "hdf5", "unknown" -// /// when the file we are writing to exists, "default" ==> "unknown" -// /// else, "default" ==> "hdf5" -// /// note: these are additional silo_type options that we could add -// /// support for in the future: -// /// "hdf5_sec2", "hdf5_stdio", "hdf5_mpio", "hdf5_mpiposix", "taurus" -// /// -// /// suffix: "default", "cycle", "none" -// /// when cycle is present, "default" ==> "cycle" -// /// else, "default" ==> "none" -// /// -// /// root_file_ext: "default", "root", "silo" -// /// "default" ==> "root" -// /// if overlink, this parameter is unused. -// /// -// /// mesh_name: (used if present, default ==> "mesh") -// /// -// /// ovl_topo_name: (used if present, default ==> "") -// /// -// /// number_of_files: {# of files} -// /// when "multi_file" or "overlink": -// /// <= 0, use # of files == # of domains -// /// > 0, # of files == number_of_files - -// //----------------------------------------------------------------------------- -// TEST(conduit_relay_io_silo, round_trip_save_option_file_style) -// { -// // we will do overlink tests separately -// const std::vector file_styles = {"default", "root_only", "multi_file"}; -// for (int i = 0; i < file_styles.size(); i ++) -// { -// Node opts; -// opts["file_style"] = file_styles[i]; - -// const std::string basename = "silo_save_option_file_style_" + file_styles[i] + "_spiral"; -// const std::string filename = basename + ".cycle_000000.root"; - -// for (int ndomains = 1; ndomains < 5; ndomains += 3) -// { -// Node save_mesh, load_mesh, info; -// blueprint::mesh::examples::spiral(ndomains, save_mesh); -// remove_path_if_exists(filename); -// io::silo::save_mesh(save_mesh, basename, opts); -// io::silo::load_mesh(filename, load_mesh); -// EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); - -// // make changes to save mesh so the diff will pass -// for (index_t child = 0; child < save_mesh.number_of_children(); child ++) -// { -// silo_name_changer("mesh", save_mesh[child]); -// } - -// EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); -// NodeConstIterator l_itr = load_mesh.children(); -// NodeConstIterator s_itr = save_mesh.children(); -// while (l_itr.has_next()) -// { -// const Node &l_curr = l_itr.next(); -// const Node &s_curr = s_itr.next(); - -// EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); -// } -// } -// } -// } - -// //----------------------------------------------------------------------------- -// TEST(conduit_relay_io_silo, round_trip_save_option_number_of_files) -// { -// const std::vector number_of_files = {-1, 2}; -// for (int i = 0; i < number_of_files.size(); i ++) -// { -// Node opts; -// opts["file_style"] = "multi_file"; -// opts["number_of_files"] = number_of_files[i]; - -// const std::string basename = "silo_save_option_number_of_files_" + -// std::to_string(number_of_files[i]) + -// "_spiral"; -// const std::string filename = basename + ".cycle_000000.root"; - -// const int ndomains = 5; - -// Node save_mesh, load_mesh, info; -// blueprint::mesh::examples::spiral(ndomains, save_mesh); - -// remove_path_if_exists(filename); -// io::silo::save_mesh(save_mesh, basename, opts); -// io::silo::load_mesh(filename, load_mesh); -// EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); - -// // make changes to save mesh so the diff will pass -// for (index_t child = 0; child < save_mesh.number_of_children(); child ++) -// { -// silo_name_changer("mesh", save_mesh[child]); -// } - -// EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); -// NodeConstIterator l_itr = load_mesh.children(); -// NodeConstIterator s_itr = save_mesh.children(); -// while (l_itr.has_next()) -// { -// const Node &l_curr = l_itr.next(); -// const Node &s_curr = s_itr.next(); - -// EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); -// } -// } -// } - -// //----------------------------------------------------------------------------- -// TEST(conduit_relay_io_silo, round_trip_save_option_suffix) -// { -// const std::vector suffixes = {"default", "default", "cycle", "none"}; -// const std::vector file_suffixes = { -// "", // cycle is not present -// ".cycle_000005", // cycle is present -// ".cycle_000005", // cycle is turned on -// "", // cycle is turned off -// }; -// const std::vector include_cycle = {"no", "yes", "yes", "yes"}; -// for (int i = 0; i < suffixes.size(); i ++) -// { -// Node opts; -// opts["suffix"] = suffixes[i]; - -// const std::string basename = "silo_save_option_suffix_" + suffixes[i] + -// "_" + include_cycle[i] + "_basic"; -// const std::string filename = basename + file_suffixes[i] + ".root"; - -// Node save_mesh, load_mesh, info; -// blueprint::mesh::examples::basic("rectilinear", 3, 4, 0, save_mesh); - -// if (include_cycle[i] == "yes") -// { -// save_mesh["state/cycle"] = 5; -// } - -// remove_path_if_exists(filename); -// io::silo::save_mesh(save_mesh, basename, opts); -// io::silo::load_mesh(filename, load_mesh); -// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - -// silo_name_changer("mesh", save_mesh); - -// // the loaded mesh will be in the multidomain format -// // but the saved mesh is in the single domain format -// EXPECT_EQ(load_mesh.number_of_children(), 1); -// EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); - -// EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); -// } -// } - -// //----------------------------------------------------------------------------- -// TEST(conduit_relay_io_silo, round_trip_save_option_root_file_ext) -// { -// const std::vector root_file_exts = {"default", "root", "silo"}; - -// for (int i = 0; i < root_file_exts.size(); i ++) -// { -// Node opts; -// opts["root_file_ext"] = root_file_exts[i]; - -// std::string actual_file_ext = root_file_exts[i]; -// if (actual_file_ext == "default") -// { -// actual_file_ext = "root"; -// } - -// const std::string basename = "round_trip_save_option_root_file_ext_" + -// root_file_exts[i] + "_basic"; -// const std::string filename = basename + "." + actual_file_ext; - -// Node save_mesh, load_mesh, info; -// blueprint::mesh::examples::basic("rectilinear", 3, 4, 0, save_mesh); -// remove_path_if_exists(filename); -// io::silo::save_mesh(save_mesh, basename, opts); -// io::silo::load_mesh(filename, load_mesh); -// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - -// silo_name_changer("mesh", save_mesh); - -// // the loaded mesh will be in the multidomain format -// // but the saved mesh is in the single domain format -// EXPECT_EQ(load_mesh.number_of_children(), 1); -// EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); -// EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); -// } -// } - -// //----------------------------------------------------------------------------- -// TEST(conduit_relay_io_silo, round_trip_save_option_mesh_name) -// { -// const std::string basename = "silo_save_option_mesh_name_basic"; -// const std::string filename = basename + ".root"; - -// Node opts; -// opts["mesh_name"] = "mymesh"; - -// Node save_mesh, load_mesh, info; -// blueprint::mesh::examples::basic("rectilinear", 3, 4, 0, save_mesh); -// remove_path_if_exists(filename); -// io::silo::save_mesh(save_mesh, basename, opts); -// io::silo::load_mesh(filename, load_mesh); -// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - -// silo_name_changer("mymesh", save_mesh); - -// // the loaded mesh will be in the multidomain format -// // but the saved mesh is in the single domain format -// EXPECT_EQ(load_mesh.number_of_children(), 1); -// EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); -// EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); -// } - -// //----------------------------------------------------------------------------- -// TEST(conduit_relay_io_silo, round_trip_save_option_silo_type) -// { -// const std::vector silo_types = {"default", "pdb", "hdf5", "unknown"}; -// for (int i = 3; i < silo_types.size(); i ++) -// { -// Node opts; -// opts["silo_type"] = silo_types[i]; - -// Node save_mesh, load_mesh, info; -// blueprint::mesh::examples::basic("rectilinear", 3, 4, 0, save_mesh); - -// const std::string basename = "silo_save_option_silo_type_" + silo_types[i] + "_basic"; -// const std::string filename = basename + ".root"; - -// remove_path_if_exists(filename); -// io::silo::save_mesh(save_mesh, basename, opts); -// io::silo::load_mesh(filename, load_mesh); -// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + // cheat a little bit - we don't have these to start + n_matset["sizes"].set_external(load_mesh[child]["matsets"]["mesh_matset"]["sizes"]); + n_matset["offsets"].set_external(load_mesh[child]["matsets"]["mesh_matset"]["offsets"]); + } + + silo_name_changer("mesh", save_mesh[child]); + } + save_mesh[2].remove_child("matsets"); + + EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); + NodeConstIterator l_itr = load_mesh.children(); + NodeConstIterator s_itr = save_mesh.children(); + while (l_itr.has_next()) + { + const Node &l_curr = l_itr.next(); + const Node &s_curr = s_itr.next(); + + EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); + } +} + +//----------------------------------------------------------------------------- +// mesh is not defined on a domain +// +// This case is much less interesting. +// data passes through the clean mesh filter which +// deletes domains that are missing topos. +// They simply are not part of the mesh and so silo +// doesn't have to deal with it. +TEST(conduit_relay_io_silo, missing_domain_mesh_trivial) +{ + Node save_mesh, load_mesh, info; + const int ndomains = 4; + blueprint::mesh::examples::spiral(ndomains, save_mesh); + + // remove information for a particular domain + save_mesh[2]["topologies"].remove_child("topo"); + + const std::string basename = "silo_missing_domain_mesh_trivial_spiral"; + const std::string filename = basename + ".cycle_000000.root"; + + remove_path_if_exists(filename); + io::silo::save_mesh(save_mesh, basename); + io::silo::load_mesh(filename, load_mesh); + + EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); + + // make changes to save mesh so the diff will pass + save_mesh.remove(2); + save_mesh.rename_child("domain_000003", "domain_000002"); + save_mesh[2]["state"]["domain_id"].reset(); + save_mesh[2]["state"]["domain_id"] = 2; + for (index_t child = 0; child < save_mesh.number_of_children(); child ++) + { + silo_name_changer("mesh", save_mesh[child]); + } + + EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); + NodeConstIterator l_itr = load_mesh.children(); + NodeConstIterator s_itr = save_mesh.children(); + while (l_itr.has_next()) + { + const Node &l_curr = l_itr.next(); + const Node &s_curr = s_itr.next(); + + EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); + } +} + +//----------------------------------------------------------------------------- +// mesh is not defined on a domain but there are multiple meshes +TEST(conduit_relay_io_silo, missing_domain_mesh) +{ + Node save_mesh, save_mesh2, load_mesh, load_mesh2, info, opts; + const int ndomains = 4; + blueprint::mesh::examples::spiral(ndomains, save_mesh); + blueprint::mesh::examples::spiral(ndomains, save_mesh2); + + for (index_t child = 0; child < save_mesh.number_of_children(); child ++) + { + save_mesh[child]["coordsets"].rename_child("coords", "coords2"); + save_mesh[child]["topologies"]["topo"]["coordset"].reset(); + save_mesh[child]["topologies"]["topo"]["coordset"] = "coords2"; + save_mesh[child]["topologies"].rename_child("topo", "topo2"); + save_mesh[child]["fields"]["dist"]["topology"].reset(); + save_mesh[child]["fields"]["dist"]["topology"] = "topo2"; + save_mesh[child]["fields"].rename_child("dist", "dist2"); + + save_mesh[child]["coordsets"]["coords"].set_external(save_mesh2[child]["coordsets"]["coords"]); + save_mesh[child]["topologies"]["topo"].set_external(save_mesh2[child]["topologies"]["topo"]); + save_mesh[child]["fields"]["dist"].set_external(save_mesh2[child]["fields"]["dist"]); + } + + // remove information for a particular domain + save_mesh[2]["topologies"].remove_child("topo"); + + const std::string basename = "silo_missing_domain_mesh_spiral"; + const std::string filename = basename + ".cycle_000000.root"; + + remove_path_if_exists(filename); + io::silo::save_mesh(save_mesh, basename); + opts["silo_names"]["multimesh_names"] = "mesh_topo2"; + io::silo::load_mesh(filename, opts, load_mesh); + opts["silo_names"]["multimesh_names"] = "mesh_topo"; + io::silo::load_mesh(filename, opts, load_mesh2); + EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + EXPECT_TRUE(blueprint::mesh::verify(load_mesh2, info)); + + // make changes to save mesh so the diff will pass + save_mesh[2]["coordsets"].remove_child("coords"); + save_mesh[2]["fields"].remove_child("dist"); + for (index_t child = 0; child < save_mesh.number_of_children(); child ++) + { + silo_name_changer("mesh", save_mesh[child]); + } + + // we must merge the two meshes in load mesh + // the indexing is tricky because one is missing a domain + load_mesh[0]["coordsets"]["mesh_topo"].set_external(load_mesh2[0]["coordsets"]["mesh_topo"]); + load_mesh[0]["topologies"]["mesh_topo"].set_external(load_mesh2[0]["topologies"]["mesh_topo"]); + load_mesh[0]["fields"]["mesh_dist"].set_external(load_mesh2[0]["fields"]["mesh_dist"]); + load_mesh[1]["coordsets"]["mesh_topo"].set_external(load_mesh2[1]["coordsets"]["mesh_topo"]); + load_mesh[1]["topologies"]["mesh_topo"].set_external(load_mesh2[1]["topologies"]["mesh_topo"]); + load_mesh[1]["fields"]["mesh_dist"].set_external(load_mesh2[1]["fields"]["mesh_dist"]); + load_mesh[3]["coordsets"]["mesh_topo"].set_external(load_mesh2[2]["coordsets"]["mesh_topo"]); + load_mesh[3]["topologies"]["mesh_topo"].set_external(load_mesh2[2]["topologies"]["mesh_topo"]); + load_mesh[3]["fields"]["mesh_dist"].set_external(load_mesh2[2]["fields"]["mesh_dist"]); + + EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); + NodeConstIterator l_itr = load_mesh.children(); + NodeConstIterator s_itr = save_mesh.children(); + while (l_itr.has_next()) + { + const Node &l_curr = l_itr.next(); + const Node &s_curr = s_itr.next(); + + EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); + } +} + +//----------------------------------------------------------------------------- +// explicit points (unstructured mesh) do not use every coord +TEST(conduit_relay_io_silo, unstructured_points) +{ + Node save_mesh, load_mesh, info; + blueprint::mesh::examples::braid("points", 2, 2, 2, save_mesh); + + std::vector new_conn; + std::vector new_field1; + std::vector new_field2; + std::vector new_xcoords, new_ycoords, new_zcoords; + + int_accessor conn = save_mesh["topologies"]["mesh"]["elements"]["connectivity"].value(); + + float_accessor field1 = save_mesh["fields"]["braid"]["values"].value(); + float_accessor field2 = save_mesh["fields"]["radial"]["values"].value(); + + float_accessor xcoords = save_mesh["coordsets"]["coords"]["values"]["x"].value(); + float_accessor ycoords = save_mesh["coordsets"]["coords"]["values"]["y"].value(); + float_accessor zcoords = save_mesh["coordsets"]["coords"]["values"]["z"].value(); + + for (int i = 1; i < conn.number_of_elements(); i += 2) + { + new_conn.push_back(conn[i]); + new_field1.push_back(field1[i]); + new_field2.push_back(field2[i]); + + new_xcoords.push_back(xcoords[conn[i]]); + new_ycoords.push_back(ycoords[conn[i]]); + new_zcoords.push_back(zcoords[conn[i]]); + } + save_mesh["topologies"]["mesh"]["elements"]["connectivity"].reset(); + save_mesh["topologies"]["mesh"]["elements"]["connectivity"].set(new_conn); + + save_mesh["fields"].remove_child("vel"); + save_mesh["fields"]["braid"]["values"].reset(); + save_mesh["fields"]["braid"]["values"].set(new_field1); + save_mesh["fields"]["radial"]["values"].reset(); + save_mesh["fields"]["radial"]["values"].set(new_field2); + + // we have modified braid such that it only uses half of the points in the coordset + + const std::string basename = "silo_unstructured_points_braid"; + const std::string filename = basename + ".cycle_000100.root"; + + // remove existing root file, directory and any output files + remove_path_if_exists(filename); + + io::silo::save_mesh(save_mesh, basename); + io::silo::load_mesh(filename, load_mesh); + EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + + // now we must remove the unused points and change to an implicit points topo so that the diff passes + save_mesh["coordsets"]["coords"]["values"]["x"].reset(); + save_mesh["coordsets"]["coords"]["values"]["x"].set(new_xcoords); + save_mesh["coordsets"]["coords"]["values"]["y"].reset(); + save_mesh["coordsets"]["coords"]["values"]["y"].set(new_ycoords); + save_mesh["coordsets"]["coords"]["values"]["z"].reset(); + save_mesh["coordsets"]["coords"]["values"]["z"].set(new_zcoords); + + save_mesh["topologies"].remove_child("mesh"); + save_mesh["topologies"]["mesh"]["type"] = "points"; + save_mesh["topologies"]["mesh"]["coordset"] = "coords"; + + // the association doesn't matter for point meshes + // we choose vertex by convention + save_mesh["fields"]["radial"]["association"].reset(); + save_mesh["fields"]["radial"]["association"] = "vertex"; + + silo_name_changer("mesh", save_mesh); + + // the loaded mesh will be in the multidomain format + // but the saved mesh is in the single domain format + EXPECT_EQ(load_mesh.number_of_children(), 1); + EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); + + EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); +} + +//----------------------------------------------------------------------------- + +// +// save option tests +// + +// save options: +/// opts: +/// +/// file_style: "default", "root_only", "multi_file", "overlink" +/// when # of domains == 1, "default" ==> "root_only" +/// else, "default" ==> "multi_file" +/// +/// silo_type: "default", "pdb", "hdf5", "unknown" +/// when the file we are writing to exists, "default" ==> "unknown" +/// else, "default" ==> "hdf5" +/// note: these are additional silo_type options that we could add +/// support for in the future: +/// "hdf5_sec2", "hdf5_stdio", "hdf5_mpio", "hdf5_mpiposix", "taurus" +/// +/// suffix: "default", "cycle", "none" +/// when cycle is present, "default" ==> "cycle" +/// else, "default" ==> "none" +/// +/// root_file_ext: "default", "root", "silo" +/// "default" ==> "root" +/// if overlink, this parameter is unused. +/// +/// mesh_name: (used if present, default ==> "mesh") +/// +/// ovl_topo_name: (used if present, default ==> "") +/// +/// number_of_files: {# of files} +/// when "multi_file" or "overlink": +/// <= 0, use # of files == # of domains +/// > 0, # of files == number_of_files + +//----------------------------------------------------------------------------- +TEST(conduit_relay_io_silo, round_trip_save_option_file_style) +{ + // we will do overlink tests separately + const std::vector file_styles = {"default", "root_only", "multi_file"}; + for (int i = 0; i < file_styles.size(); i ++) + { + Node opts; + opts["file_style"] = file_styles[i]; + + const std::string basename = "silo_save_option_file_style_" + file_styles[i] + "_spiral"; + const std::string filename = basename + ".cycle_000000.root"; + + for (int ndomains = 1; ndomains < 5; ndomains += 3) + { + Node save_mesh, load_mesh, info; + blueprint::mesh::examples::spiral(ndomains, save_mesh); + remove_path_if_exists(filename); + io::silo::save_mesh(save_mesh, basename, opts); + io::silo::load_mesh(filename, load_mesh); + EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); + + // make changes to save mesh so the diff will pass + for (index_t child = 0; child < save_mesh.number_of_children(); child ++) + { + silo_name_changer("mesh", save_mesh[child]); + } + + EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); + NodeConstIterator l_itr = load_mesh.children(); + NodeConstIterator s_itr = save_mesh.children(); + while (l_itr.has_next()) + { + const Node &l_curr = l_itr.next(); + const Node &s_curr = s_itr.next(); + + EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); + } + } + } +} + +//----------------------------------------------------------------------------- +TEST(conduit_relay_io_silo, round_trip_save_option_number_of_files) +{ + const std::vector number_of_files = {-1, 2}; + for (int i = 0; i < number_of_files.size(); i ++) + { + Node opts; + opts["file_style"] = "multi_file"; + opts["number_of_files"] = number_of_files[i]; + + const std::string basename = "silo_save_option_number_of_files_" + + std::to_string(number_of_files[i]) + + "_spiral"; + const std::string filename = basename + ".cycle_000000.root"; + + const int ndomains = 5; + + Node save_mesh, load_mesh, info; + blueprint::mesh::examples::spiral(ndomains, save_mesh); + + remove_path_if_exists(filename); + io::silo::save_mesh(save_mesh, basename, opts); + io::silo::load_mesh(filename, load_mesh); + EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); + + // make changes to save mesh so the diff will pass + for (index_t child = 0; child < save_mesh.number_of_children(); child ++) + { + silo_name_changer("mesh", save_mesh[child]); + } + + EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); + NodeConstIterator l_itr = load_mesh.children(); + NodeConstIterator s_itr = save_mesh.children(); + while (l_itr.has_next()) + { + const Node &l_curr = l_itr.next(); + const Node &s_curr = s_itr.next(); + + EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); + } + } +} + +//----------------------------------------------------------------------------- +TEST(conduit_relay_io_silo, round_trip_save_option_suffix) +{ + const std::vector suffixes = {"default", "default", "cycle", "none"}; + const std::vector file_suffixes = { + "", // cycle is not present + ".cycle_000005", // cycle is present + ".cycle_000005", // cycle is turned on + "", // cycle is turned off + }; + const std::vector include_cycle = {"no", "yes", "yes", "yes"}; + for (int i = 0; i < suffixes.size(); i ++) + { + Node opts; + opts["suffix"] = suffixes[i]; + + const std::string basename = "silo_save_option_suffix_" + suffixes[i] + + "_" + include_cycle[i] + "_basic"; + const std::string filename = basename + file_suffixes[i] + ".root"; + + Node save_mesh, load_mesh, info; + blueprint::mesh::examples::basic("rectilinear", 3, 4, 0, save_mesh); + + if (include_cycle[i] == "yes") + { + save_mesh["state/cycle"] = 5; + } + + remove_path_if_exists(filename); + io::silo::save_mesh(save_mesh, basename, opts); + io::silo::load_mesh(filename, load_mesh); + EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + + silo_name_changer("mesh", save_mesh); + + // the loaded mesh will be in the multidomain format + // but the saved mesh is in the single domain format + EXPECT_EQ(load_mesh.number_of_children(), 1); + EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); + + EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); + } +} + +//----------------------------------------------------------------------------- +TEST(conduit_relay_io_silo, round_trip_save_option_root_file_ext) +{ + const std::vector root_file_exts = {"default", "root", "silo"}; + + for (int i = 0; i < root_file_exts.size(); i ++) + { + Node opts; + opts["root_file_ext"] = root_file_exts[i]; + + std::string actual_file_ext = root_file_exts[i]; + if (actual_file_ext == "default") + { + actual_file_ext = "root"; + } + + const std::string basename = "round_trip_save_option_root_file_ext_" + + root_file_exts[i] + "_basic"; + const std::string filename = basename + "." + actual_file_ext; + + Node save_mesh, load_mesh, info; + blueprint::mesh::examples::basic("rectilinear", 3, 4, 0, save_mesh); + remove_path_if_exists(filename); + io::silo::save_mesh(save_mesh, basename, opts); + io::silo::load_mesh(filename, load_mesh); + EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + + silo_name_changer("mesh", save_mesh); + + // the loaded mesh will be in the multidomain format + // but the saved mesh is in the single domain format + EXPECT_EQ(load_mesh.number_of_children(), 1); + EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); + EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); + } +} + +//----------------------------------------------------------------------------- +TEST(conduit_relay_io_silo, round_trip_save_option_mesh_name) +{ + const std::string basename = "silo_save_option_mesh_name_basic"; + const std::string filename = basename + ".root"; + + Node opts; + opts["mesh_name"] = "mymesh"; + + Node save_mesh, load_mesh, info; + blueprint::mesh::examples::basic("rectilinear", 3, 4, 0, save_mesh); + remove_path_if_exists(filename); + io::silo::save_mesh(save_mesh, basename, opts); + io::silo::load_mesh(filename, load_mesh); + EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + + silo_name_changer("mymesh", save_mesh); + + // the loaded mesh will be in the multidomain format + // but the saved mesh is in the single domain format + EXPECT_EQ(load_mesh.number_of_children(), 1); + EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); + EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); +} + +//----------------------------------------------------------------------------- +TEST(conduit_relay_io_silo, round_trip_save_option_silo_type) +{ + const std::vector silo_types = {"default", "pdb", "hdf5", "unknown"}; + for (int i = 3; i < silo_types.size(); i ++) + { + Node opts; + opts["silo_type"] = silo_types[i]; + + Node save_mesh, load_mesh, info; + blueprint::mesh::examples::basic("rectilinear", 3, 4, 0, save_mesh); + + const std::string basename = "silo_save_option_silo_type_" + silo_types[i] + "_basic"; + const std::string filename = basename + ".root"; + + remove_path_if_exists(filename); + io::silo::save_mesh(save_mesh, basename, opts); + io::silo::load_mesh(filename, load_mesh); + EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); -// silo_name_changer("mesh", save_mesh); - -// // the loaded mesh will be in the multidomain format -// // but the saved mesh is in the single domain format -// EXPECT_EQ(load_mesh.number_of_children(), 1); -// EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); - -// EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); -// } -// } - -// //----------------------------------------------------------------------------- -// TEST(conduit_relay_io_silo, round_trip_save_option_overlink1) -// { -// const std::vector ovl_topo_names = {"", "topo"}; -// for (int i = 0; i < ovl_topo_names.size(); i ++) -// { -// std::string basename; -// if (ovl_topo_names[i].empty()) -// { -// basename = "silo_save_option_overlink_spiral"; -// } -// else -// { -// basename = "silo_save_option_overlink_spiral_" + ovl_topo_names[i]; -// } -// const std::string filename = basename + "/OvlTop.silo"; - -// Node opts; -// opts["file_style"] = "overlink"; -// opts["ovl_topo_name"] = ovl_topo_names[i]; - -// int ndomains = 2; - -// Node save_mesh, load_mesh, info; -// blueprint::mesh::examples::spiral(ndomains, save_mesh); -// add_matset_to_spiral(save_mesh, ndomains); -// remove_path_if_exists(filename); -// io::silo::save_mesh(save_mesh, basename, opts); -// io::silo::load_mesh(filename, load_mesh); -// EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); - -// // make changes to save mesh so the diff will pass -// for (index_t child = 0; child < save_mesh.number_of_children(); child ++) -// { -// // get the matset for this domain -// Node &n_matset = save_mesh[child]["matsets"]["matset"]; - -// // clean up volume fractions -// Node vf_arr; -// n_matset["volume_fractions"].to_float64_array(vf_arr); -// n_matset["volume_fractions"].reset(); -// n_matset["volume_fractions"].set(vf_arr); + silo_name_changer("mesh", save_mesh); + + // the loaded mesh will be in the multidomain format + // but the saved mesh is in the single domain format + EXPECT_EQ(load_mesh.number_of_children(), 1); + EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); + + EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); + } +} + +//----------------------------------------------------------------------------- +TEST(conduit_relay_io_silo, round_trip_save_option_overlink1) +{ + const std::vector ovl_topo_names = {"", "topo"}; + for (int i = 0; i < ovl_topo_names.size(); i ++) + { + std::string basename; + if (ovl_topo_names[i].empty()) + { + basename = "silo_save_option_overlink_spiral"; + } + else + { + basename = "silo_save_option_overlink_spiral_" + ovl_topo_names[i]; + } + const std::string filename = basename + "/OvlTop.silo"; + + Node opts; + opts["file_style"] = "overlink"; + opts["ovl_topo_name"] = ovl_topo_names[i]; + + int ndomains = 2; + + Node save_mesh, load_mesh, info; + blueprint::mesh::examples::spiral(ndomains, save_mesh); + add_matset_to_spiral(save_mesh, ndomains); + remove_path_if_exists(filename); + io::silo::save_mesh(save_mesh, basename, opts); + io::silo::load_mesh(filename, load_mesh); + EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); + + // make changes to save mesh so the diff will pass + for (index_t child = 0; child < save_mesh.number_of_children(); child ++) + { + // get the matset for this domain + Node &n_matset = save_mesh[child]["matsets"]["matset"]; + + // clean up volume fractions + Node vf_arr; + n_matset["volume_fractions"].to_float64_array(vf_arr); + n_matset["volume_fractions"].reset(); + n_matset["volume_fractions"].set(vf_arr); -// // cheat a little bit - we don't have these to start -// n_matset["sizes"].set_external(load_mesh[child]["matsets"]["MMATERIAL"]["sizes"]); -// n_matset["offsets"].set_external(load_mesh[child]["matsets"]["MMATERIAL"]["offsets"]); - -// overlink_name_changer(save_mesh[child]); -// } - -// EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); -// NodeConstIterator l_itr = load_mesh.children(); -// NodeConstIterator s_itr = save_mesh.children(); -// while (l_itr.has_next()) -// { -// const Node &l_curr = l_itr.next(); -// const Node &s_curr = s_itr.next(); - -// EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); -// } -// } -// } - -// //----------------------------------------------------------------------------- -// // this tests var attributes and padding dimensions -// TEST(conduit_relay_io_silo, round_trip_save_option_overlink2) -// { -// const std::string basename = "silo_save_option_overlink_basic"; -// const std::string filename = basename + "/OvlTop.silo"; - -// Node write_opts, read_opts; -// write_opts["file_style"] = "overlink"; -// read_opts["matset_style"] = "multi_buffer_full"; - -// Node save_mesh, load_mesh, info; -// blueprint::mesh::examples::basic("structured", 3, 3, 1, save_mesh); - -// // add another field that is volume dependent -// Node &field2 = save_mesh["fields"]["field2"]; -// field2["association"] = "element"; -// field2["topology"] = "mesh"; -// field2["volume_dependent"] = "true"; -// field2["values"].set_external(save_mesh["fields"]["field"]["values"]); - -// // add a matset to make overlink happy -// add_multi_buffer_full_matset(save_mesh, 4, "mesh"); - -// remove_path_if_exists(filename); -// io::silo::save_mesh(save_mesh, basename, write_opts); -// io::silo::load_mesh(filename, read_opts, load_mesh); -// EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); - -// // make changes to save mesh so the diff will pass -// overlink_name_changer(save_mesh); - -// // the loaded mesh will be in the multidomain format -// // but the saved mesh is in the single domain format -// EXPECT_EQ(load_mesh.number_of_children(), 1); -// EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); - -// EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); - -// // open silo files and do some checks - -// DBfile *rootfile = DBOpen(filename.c_str(), DB_UNKNOWN, DB_READ); -// EXPECT_TRUE(DBInqVarExists(rootfile, "VAR_ATTRIBUTES")); -// EXPECT_TRUE(DBInqVarType(rootfile, "VAR_ATTRIBUTES") == DB_ARRAY); - -// DBcompoundarray *var_attr = DBGetCompoundarray(rootfile, "VAR_ATTRIBUTES"); - -// // fetch pointers to elements inside the compound array -// char **elemnames = var_attr->elemnames; -// int *elemlengths = var_attr->elemlengths; -// int nelems = var_attr->nelems; -// int *values = static_cast(var_attr->values); -// int nvalues = var_attr->nvalues; -// int datatype = var_attr->datatype; - -// EXPECT_EQ(std::string(elemnames[0]), "field"); -// EXPECT_EQ(std::string(elemnames[1]), "field2"); -// EXPECT_EQ(elemlengths[0], 5); -// EXPECT_EQ(elemlengths[1], 5); -// EXPECT_EQ(nelems, 2); -// // for first var -// EXPECT_EQ(values[0], 1); -// EXPECT_EQ(values[1], 0); -// EXPECT_EQ(values[2], 1); -// EXPECT_EQ(values[3], 0); -// EXPECT_EQ(values[4], 1); -// // for second var -// EXPECT_EQ(values[5], 1); -// EXPECT_EQ(values[6], 1); -// EXPECT_EQ(values[7], 1); -// EXPECT_EQ(values[8], 0); -// EXPECT_EQ(values[9], 1); -// EXPECT_EQ(nvalues, 10); -// EXPECT_EQ(datatype, DB_INT); - -// DBFreeCompoundarray(var_attr); - -// EXPECT_TRUE(DBInqVarExists(rootfile, "PAD_DIMS")); -// EXPECT_TRUE(DBInqVarType(rootfile, "PAD_DIMS") == DB_ARRAY); - -// DBcompoundarray *pad_dims = DBGetCompoundarray(rootfile, "PAD_DIMS"); - -// // fetch pointers to elements inside the compound array -// elemnames = pad_dims->elemnames; -// elemlengths = pad_dims->elemlengths; -// nelems = pad_dims->nelems; -// values = static_cast(pad_dims->values); -// nvalues = pad_dims->nvalues; -// datatype = pad_dims->datatype; - -// EXPECT_EQ(std::string(elemnames[0]), "paddims"); -// EXPECT_EQ(elemlengths[0], 6); -// EXPECT_EQ(nelems, 1); -// EXPECT_EQ(values[0], 0); -// EXPECT_EQ(values[1], 0); -// EXPECT_EQ(values[2], 0); -// EXPECT_EQ(values[3], 0); -// EXPECT_EQ(values[4], 0); -// EXPECT_EQ(values[5], 0); -// EXPECT_EQ(nvalues, 6); -// EXPECT_EQ(datatype, DB_INT); - -// DBFreeCompoundarray(pad_dims); - -// DBClose(rootfile); - -// // now check domain file - -// const std::string dom_filename = basename + "/domain0.silo"; -// DBfile *domfile = DBOpen(dom_filename.c_str(), DB_UNKNOWN, DB_READ); - -// EXPECT_TRUE(DBInqVarExists(domfile, "DOMAIN_NEIGHBOR_NUMS")); -// EXPECT_TRUE(DBInqVarType(domfile, "DOMAIN_NEIGHBOR_NUMS") == DB_ARRAY); - -// DBcompoundarray *dom_neighbor_nums = DBGetCompoundarray(domfile, "DOMAIN_NEIGHBOR_NUMS"); - -// // fetch pointers to elements inside the compound array -// elemnames = dom_neighbor_nums->elemnames; -// elemlengths = dom_neighbor_nums->elemlengths; -// nelems = dom_neighbor_nums->nelems; -// values = static_cast(dom_neighbor_nums->values); -// nvalues = dom_neighbor_nums->nvalues; -// datatype = dom_neighbor_nums->datatype; - -// EXPECT_EQ(std::string(elemnames[0]), "num_neighbors"); -// EXPECT_EQ(std::string(elemnames[1]), "neighbor_nums"); -// EXPECT_EQ(elemlengths[0], 1); -// EXPECT_EQ(elemlengths[1], 0); -// EXPECT_EQ(nelems, 2); -// EXPECT_EQ(values[0], 0); -// EXPECT_EQ(nvalues, 1); -// EXPECT_EQ(datatype, DB_INT); - -// DBFreeCompoundarray(dom_neighbor_nums); - -// DBClose(domfile); -// } - -// //----------------------------------------------------------------------------- -// // this tests material i/o -// TEST(conduit_relay_io_silo, round_trip_save_option_overlink3) -// { -// Node save_mesh, load_mesh, info; -// const int nx = 100, ny = 100; -// const double radius = 0.25; -// blueprint::mesh::examples::venn("sparse_by_element", nx, ny, radius, save_mesh); - -// const std::string basename = "silo_save_option_overlink_venn"; -// const std::string filename = basename + "/OvlTop.silo"; - -// Node opts; -// opts["file_style"] = "overlink"; - -// remove_path_if_exists(filename); -// io::silo::save_mesh(save_mesh, basename, opts); -// io::silo::load_mesh(filename, load_mesh); -// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - -// // make changes to save mesh so the diff will pass - -// // The field mat_check has values that are one type and matset_values -// // that are another type. The silo writer converts both to double arrays -// // in this case, so we follow suit. -// Node mat_check_new_values, mat_check_new_matset_values; -// save_mesh["fields"]["mat_check"]["values"].to_double_array(mat_check_new_values); -// save_mesh["fields"]["mat_check"]["matset_values"].to_double_array(mat_check_new_matset_values); -// save_mesh["fields"]["mat_check"]["values"].set_external(mat_check_new_values); -// save_mesh["fields"]["mat_check"]["matset_values"].set_external(mat_check_new_matset_values); - -// overlink_name_changer(save_mesh); - -// // the loaded mesh will be in the multidomain format -// // but the saved mesh is in the single domain format -// EXPECT_EQ(load_mesh.number_of_children(), 1); -// EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); - -// EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); -// } - -// //----------------------------------------------------------------------------- -// // we are testing vector fields get converted to scalars -// TEST(conduit_relay_io_silo, round_trip_save_option_overlink4) -// { -// const std::vector> mesh_types = { -// std::make_pair("rectilinear", "2"), std::make_pair("rectilinear", "3"), -// std::make_pair("structured", "2"), std::make_pair("structured", "3"), -// std::make_pair("quads", "2"), -// std::make_pair("hexs", "3"), -// }; -// for (int i = 0; i < mesh_types.size(); ++i) -// { -// std::string dim = mesh_types[i].second; -// index_t nx = 3; -// index_t ny = 4; -// index_t nz = (dim == "2" ? 0 : 2); - -// std::string mesh_type = mesh_types[i].first; - -// Node save_mesh, load_mesh, info; -// blueprint::mesh::examples::braid(mesh_type, nx, ny, nz, save_mesh); -// index_t nele_x = nx - 1; -// index_t nele_y = ny - 1; -// index_t nele_z = (dim == "2" ? 0 : nz - 1); - -// // provide a matset for braid -// braid_init_example_matset(nele_x, nele_y, nele_z, save_mesh["matsets"]["matset"]); - -// const std::string basename = "silo_save_option_overlink_braid_" + mesh_type + "_" + dim + "D"; -// const std::string filename = basename + "/OvlTop.silo"; - -// Node write_opts, read_opts; -// write_opts["file_style"] = "overlink"; -// read_opts["matset_style"] = "multi_buffer_full"; - -// // remove existing root file, directory and any output files -// remove_path_if_exists(filename); -// io::silo::save_mesh(save_mesh, basename, write_opts); -// io::silo::load_mesh(filename, read_opts, load_mesh); -// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - -// Node &field_vel = save_mesh["fields"]["vel"]; -// Node &field_vel_u = save_mesh["fields"]["vel_u"]; -// Node &field_vel_v = save_mesh["fields"]["vel_v"]; - -// field_vel_u["topology"].set(field_vel["topology"]); -// field_vel_u["association"].set(field_vel["association"]); -// field_vel_u["values"].set(field_vel["values/u"]); -// field_vel_v["topology"].set(field_vel["topology"]); -// field_vel_v["association"].set(field_vel["association"]); -// field_vel_v["values"].set(field_vel["values/v"]); - -// if (dim == "3") -// { -// Node &field_vel_w = save_mesh["fields"]["vel_w"]; -// field_vel_w["topology"].set(field_vel["topology"]); -// field_vel_w["association"].set(field_vel["association"]); -// field_vel_w["values"].set(field_vel["values/w"]); -// } - -// save_mesh["fields"].remove_child("vel"); - -// // make changes to save mesh so the diff will pass -// overlink_name_changer(save_mesh); - -// // the loaded mesh will be in the multidomain format -// // but the saved mesh is in the single domain format -// EXPECT_EQ(load_mesh.number_of_children(), 1); -// EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); - -// EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); -// } -// } - -// //----------------------------------------------------------------------------- -// // check that all the shape types work (specifically polytopal ones) -// TEST(conduit_relay_io_silo, round_trip_save_option_overlink5) -// { -// const std::vector> mesh_types = { -// std::make_pair("uniform", "2"), std::make_pair("uniform", "3"), -// std::make_pair("rectilinear", "2"), std::make_pair("rectilinear", "3"), -// std::make_pair("structured", "2"), std::make_pair("structured", "3"), -// std::make_pair("quads", "2"), -// std::make_pair("polygons", "2"), -// std::make_pair("hexs", "3"), -// // std::make_pair("polyhedra", "3") -// // Overlink does not support tris, wedges, pyramids, or tets -// }; -// for (int i = 0; i < mesh_types.size(); ++i) -// { -// const std::string dim = mesh_types[i].second; -// index_t nx = 3; -// index_t ny = 4; -// index_t nz = (dim == "2" ? 0 : 2); - -// const std::string mesh_type = mesh_types[i].first; - -// Node save_mesh, load_mesh, info; -// blueprint::mesh::examples::basic(mesh_type, nx, ny, nz, save_mesh); - -// const std::string basename = "silo_save_option_overlink_basic_" + mesh_type + "_" + dim + "D"; -// const std::string filename = basename + "/OvlTop.silo"; -// const std::string domfile = basename + "/domain0.silo"; - -// Node write_opts, read_opts; -// write_opts["file_style"] = "overlink"; -// read_opts["matset_style"] = "multi_buffer_full"; - -// // add a matset to make overlink happy -// int num_elems = (nx - 1) * (ny - 1); -// if (mesh_type == "tets") -// { -// num_elems *= 6; -// } -// add_multi_buffer_full_matset(save_mesh, num_elems, "mesh"); - -// remove_path_if_exists(filename); -// remove_path_if_exists(domfile); -// io::silo::save_mesh(save_mesh, basename, write_opts); -// io::silo::load_mesh(filename, read_opts, load_mesh); -// EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); - -// // make changes to save mesh so the diff will pass -// if (mesh_type == "uniform") -// { -// silo_uniform_to_rect_conversion("coords", "mesh", save_mesh); -// } -// overlink_name_changer(save_mesh); - -// // the loaded mesh will be in the multidomain format -// // but the saved mesh is in the single domain format -// EXPECT_EQ(load_mesh.number_of_children(), 1); -// EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); - -// EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); -// } -// } - -// //----------------------------------------------------------------------------- - -// // -// // read option tests -// // - -// // read options: -// /// opts: -// /// silo_names: -// /// multimesh_names: -// /// "{name1}" - multimeshes with this name will be read if they exist -// /// "{name2}" -// /// ... -// /// or -// /// "{all}" - all multimeshes will be read. -// /// or -// /// "{none}" - no multimeshes will be read. -// /// multivar_names: similar to multimesh_names. -// /// multimat_names: similar to multimesh_names. -// /// multimatspecies_names: similar to multimesh_names. TODO -// /// qmesh_names: similar to multimesh_names. -// /// qvar_names: similar to multimesh_names. -// /// ucdmesh_names: similar to multimesh_names. -// /// ucdvar_names: similar to multimesh_names. -// /// ptmesh_names: similar to multimesh_names. -// /// ptvar_names: similar to multimesh_names. -// /// mat_names: similar to multimesh_names. -// /// matspecies_names: similar to multimesh_names. TODO -// /// By default, everything in the file will be read unless manually turned off. -// /// -// /// matset_style: "default", "multi_buffer_full", "sparse_by_element", -// /// "multi_buffer_by_material" -// /// "default" ==> "sparse_by_element" -// /// -// /// mesh_name: legacy argument. This is interpreted as a multimesh name. -// /// It is added to the list of multimesh names to read, unless the -// /// user has specified "all" or "none", which will supersede this. -// /// TODO does it make sense to remove this? When? - -// //----------------------------------------------------------------------------- -// // TODO this is now a legacy feature. Should I remove eventually? -// TEST(conduit_relay_io_silo, round_trip_read_option_mesh_name) -// { -// Node load_mesh, info, opts; -// const std::string path = utils::join_file_path("silo", "multi_curv3d.silo"); -// const std::string input_file = relay_test_silo_data_path(path); - -// opts["mesh_name"] = "mesh1_dup"; - -// io::silo::load_mesh(input_file, opts, load_mesh); -// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - -// EXPECT_TRUE(load_mesh[0].has_path("topologies/mesh1_dup")); -// } - -// //----------------------------------------------------------------------------- -// TEST(conduit_relay_io_silo, round_trip_read_option_matset_style) -// { -// // the matset type and the type we are requesting on read -// const std::vector> matset_types = { -// std::make_pair("full", "full"), -// std::make_pair("sparse_by_material", "sparse_by_material"), -// std::make_pair("sparse_by_element", "sparse_by_element"), -// std::make_pair("sparse_by_element", "full"), -// std::make_pair("sparse_by_material", "sparse_by_element"), -// std::make_pair("sparse_by_material", "default"), -// }; - -// for (int i = 0; i < matset_types.size(); i ++) -// { -// std::string matset_type = matset_types[i].first; -// std::string matset_request = matset_types[i].second; - -// for (int j = 0; j < 2; j ++) -// { -// Node mesh_full, mesh_sbe, mesh_sbm, baseline_mesh, load_mesh, info; -// std::string size; -// int nx, ny; -// const double radius = 0.25; -// if (j == 0) -// { -// size = "small"; -// nx = ny = 4; -// } -// else -// { -// size = "large"; -// nx = ny = 100; -// } - -// blueprint::mesh::examples::venn("full", nx, ny, radius, mesh_full); -// blueprint::mesh::examples::venn("sparse_by_material", nx, ny, radius, mesh_sbm); -// blueprint::mesh::examples::venn("sparse_by_element", nx, ny, radius, mesh_sbe); - -// if (matset_type == "full") -// { -// baseline_mesh.set_external(mesh_full); -// } -// else if (matset_type == "sparse_by_material") -// { -// baseline_mesh.set_external(mesh_sbm); -// } -// else // (matset_type == "sparse_by_element") -// { -// baseline_mesh.set_external(mesh_sbe); -// } - -// Node opts; -// if (matset_request == "full") -// { -// opts["matset_style"] = "multi_buffer_full"; -// } -// else if (matset_request == "sparse_by_material") -// { -// opts["matset_style"] = "multi_buffer_by_material"; -// } -// else if (matset_request == "sparse_by_element") -// { -// opts["matset_style"] = "sparse_by_element"; -// } -// else -// { -// opts["matset_style"] = "default"; -// } - -// const std::string basename = "silo_venn2_" + matset_type + "_" + size; -// const std::string filename = basename + ".root"; - -// remove_path_if_exists(filename); -// io::silo::save_mesh(baseline_mesh, basename); -// io::silo::load_mesh(filename, opts, load_mesh); -// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - -// if (matset_request == "full") -// { -// baseline_mesh.set_external(mesh_full); -// } -// else if (matset_request == "sparse_by_material") -// { -// baseline_mesh.set_external(mesh_sbm); -// } -// else if (matset_request == "sparse_by_element") -// { -// baseline_mesh.set_external(mesh_sbe); -// } -// else -// { -// baseline_mesh.set_external(mesh_sbe); -// } - -// // make changes to save mesh so the diff will pass - -// // The field mat_check has values that are one type and matset_values -// // that are another type. The silo writer converts both to double arrays -// // in this case, so we follow suit. -// Node mat_check_new_values, mat_check_new_matset_values; -// baseline_mesh["fields"]["mat_check"]["values"].to_double_array(mat_check_new_values); -// if (baseline_mesh["fields"]["mat_check"]["matset_values"].dtype().is_object()) -// { -// auto mat_vals_itr = baseline_mesh["fields"]["mat_check"]["matset_values"].children(); -// while (mat_vals_itr.has_next()) -// { -// Node &mat_vals_for_mat = mat_vals_itr.next(); -// const std::string mat_name = mat_vals_itr.name(); -// mat_vals_for_mat.to_double_array(mat_check_new_matset_values[mat_name]); -// } -// } -// else -// { -// baseline_mesh["fields"]["mat_check"]["matset_values"].to_double_array(mat_check_new_matset_values); -// } -// baseline_mesh["fields"]["mat_check"]["values"].set_external(mat_check_new_values); -// baseline_mesh["fields"]["mat_check"]["matset_values"].set_external(mat_check_new_matset_values); - -// silo_name_changer("mesh", baseline_mesh); - -// // the loaded mesh will be in the multidomain format -// // but the saved mesh is in the single domain format -// EXPECT_EQ(load_mesh.number_of_children(), 1); -// EXPECT_EQ(load_mesh[0].number_of_children(), baseline_mesh.number_of_children()); -// EXPECT_FALSE(load_mesh[0].diff(baseline_mesh, info, CONDUIT_EPSILON, true)); -// } -// } -// } - -// //----------------------------------------------------------------------------- - -// // -// // read and write Silo and Overlink tests -// // - -// //----------------------------------------------------------------------------- -// // read normal silo files containing multimeshes, multivars, and multimats -// TEST(conduit_relay_io_silo, read_silo) -// { -// const std::vector> file_info = { -// {".", "multi_curv3d", ".silo"}, -// {".", "tire", ".silo"}, -// {".", "galaxy0000", ".silo"}, -// {".", "emptydomains", ".silo"}, -// {"multidir_test_data", "multidir0000", ".root"}, -// }; - -// // TODO what to do in the case where a multimesh points to no data? (mesh1_back) -// // fail silently, as we do now? - -// for (int i = 0; i < file_info.size(); i ++) -// { -// const std::string dirname = file_info[i][0]; -// const std::string basename = file_info[i][1]; -// const std::string fileext = file_info[i][2]; - -// Node load_mesh, info, write_opts; -// std::string filepath = utils::join_file_path(dirname, basename) + fileext; -// filepath = utils::join_file_path("silo", filepath); -// std::string input_file = relay_test_silo_data_path(filepath); - -// io::silo::load_mesh(input_file, load_mesh); -// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - -// const std::string out_name = "read_silo_" + basename; - -// // TODO are these remove paths doing anything? Don't they need filenames? -// remove_path_if_exists(out_name + "_write_blueprint"); -// io::blueprint::save_mesh(load_mesh, out_name + "_write_blueprint", "hdf5"); - -// remove_path_if_exists(out_name + "_write_silo"); -// io::silo::save_mesh(load_mesh, out_name + "_write_silo"); - -// // overlink requires matsets and does not support point meshes -// if (load_mesh[0].has_child("matsets") && basename != "galaxy0000") -// { -// remove_path_if_exists(out_name + "_write_overlink"); -// write_opts["file_style"] = "overlink"; -// write_opts["ovl_topo_name"] = "MMESH"; // TODO do I even need this -// io::silo::save_mesh(load_mesh, out_name + "_write_overlink", write_opts); -// } -// } -// } + // cheat a little bit - we don't have these to start + n_matset["sizes"].set_external(load_mesh[child]["matsets"]["MMATERIAL"]["sizes"]); + n_matset["offsets"].set_external(load_mesh[child]["matsets"]["MMATERIAL"]["offsets"]); + + overlink_name_changer(save_mesh[child]); + } + + EXPECT_EQ(load_mesh.number_of_children(), save_mesh.number_of_children()); + NodeConstIterator l_itr = load_mesh.children(); + NodeConstIterator s_itr = save_mesh.children(); + while (l_itr.has_next()) + { + const Node &l_curr = l_itr.next(); + const Node &s_curr = s_itr.next(); + + EXPECT_FALSE(l_curr.diff(s_curr, info, CONDUIT_EPSILON, true)); + } + } +} + +//----------------------------------------------------------------------------- +// this tests var attributes and padding dimensions +TEST(conduit_relay_io_silo, round_trip_save_option_overlink2) +{ + const std::string basename = "silo_save_option_overlink_basic"; + const std::string filename = basename + "/OvlTop.silo"; + + Node write_opts, read_opts; + write_opts["file_style"] = "overlink"; + read_opts["matset_style"] = "multi_buffer_full"; + + Node save_mesh, load_mesh, info; + blueprint::mesh::examples::basic("structured", 3, 3, 1, save_mesh); + + // add another field that is volume dependent + Node &field2 = save_mesh["fields"]["field2"]; + field2["association"] = "element"; + field2["topology"] = "mesh"; + field2["volume_dependent"] = "true"; + field2["values"].set_external(save_mesh["fields"]["field"]["values"]); + + // add a matset to make overlink happy + add_multi_buffer_full_matset(save_mesh, 4, "mesh"); + + remove_path_if_exists(filename); + io::silo::save_mesh(save_mesh, basename, write_opts); + io::silo::load_mesh(filename, read_opts, load_mesh); + EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); + + // make changes to save mesh so the diff will pass + overlink_name_changer(save_mesh); + + // the loaded mesh will be in the multidomain format + // but the saved mesh is in the single domain format + EXPECT_EQ(load_mesh.number_of_children(), 1); + EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); + + EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); + + // open silo files and do some checks + + DBfile *rootfile = DBOpen(filename.c_str(), DB_UNKNOWN, DB_READ); + EXPECT_TRUE(DBInqVarExists(rootfile, "VAR_ATTRIBUTES")); + EXPECT_TRUE(DBInqVarType(rootfile, "VAR_ATTRIBUTES") == DB_ARRAY); + + DBcompoundarray *var_attr = DBGetCompoundarray(rootfile, "VAR_ATTRIBUTES"); + + // fetch pointers to elements inside the compound array + char **elemnames = var_attr->elemnames; + int *elemlengths = var_attr->elemlengths; + int nelems = var_attr->nelems; + int *values = static_cast(var_attr->values); + int nvalues = var_attr->nvalues; + int datatype = var_attr->datatype; + + EXPECT_EQ(std::string(elemnames[0]), "field"); + EXPECT_EQ(std::string(elemnames[1]), "field2"); + EXPECT_EQ(elemlengths[0], 5); + EXPECT_EQ(elemlengths[1], 5); + EXPECT_EQ(nelems, 2); + // for first var + EXPECT_EQ(values[0], 1); + EXPECT_EQ(values[1], 0); + EXPECT_EQ(values[2], 1); + EXPECT_EQ(values[3], 0); + EXPECT_EQ(values[4], 1); + // for second var + EXPECT_EQ(values[5], 1); + EXPECT_EQ(values[6], 1); + EXPECT_EQ(values[7], 1); + EXPECT_EQ(values[8], 0); + EXPECT_EQ(values[9], 1); + EXPECT_EQ(nvalues, 10); + EXPECT_EQ(datatype, DB_INT); + + DBFreeCompoundarray(var_attr); + + EXPECT_TRUE(DBInqVarExists(rootfile, "PAD_DIMS")); + EXPECT_TRUE(DBInqVarType(rootfile, "PAD_DIMS") == DB_ARRAY); + + DBcompoundarray *pad_dims = DBGetCompoundarray(rootfile, "PAD_DIMS"); + + // fetch pointers to elements inside the compound array + elemnames = pad_dims->elemnames; + elemlengths = pad_dims->elemlengths; + nelems = pad_dims->nelems; + values = static_cast(pad_dims->values); + nvalues = pad_dims->nvalues; + datatype = pad_dims->datatype; + + EXPECT_EQ(std::string(elemnames[0]), "paddims"); + EXPECT_EQ(elemlengths[0], 6); + EXPECT_EQ(nelems, 1); + EXPECT_EQ(values[0], 0); + EXPECT_EQ(values[1], 0); + EXPECT_EQ(values[2], 0); + EXPECT_EQ(values[3], 0); + EXPECT_EQ(values[4], 0); + EXPECT_EQ(values[5], 0); + EXPECT_EQ(nvalues, 6); + EXPECT_EQ(datatype, DB_INT); + + DBFreeCompoundarray(pad_dims); + + DBClose(rootfile); + + // now check domain file + + const std::string dom_filename = basename + "/domain0.silo"; + DBfile *domfile = DBOpen(dom_filename.c_str(), DB_UNKNOWN, DB_READ); + + EXPECT_TRUE(DBInqVarExists(domfile, "DOMAIN_NEIGHBOR_NUMS")); + EXPECT_TRUE(DBInqVarType(domfile, "DOMAIN_NEIGHBOR_NUMS") == DB_ARRAY); + + DBcompoundarray *dom_neighbor_nums = DBGetCompoundarray(domfile, "DOMAIN_NEIGHBOR_NUMS"); + + // fetch pointers to elements inside the compound array + elemnames = dom_neighbor_nums->elemnames; + elemlengths = dom_neighbor_nums->elemlengths; + nelems = dom_neighbor_nums->nelems; + values = static_cast(dom_neighbor_nums->values); + nvalues = dom_neighbor_nums->nvalues; + datatype = dom_neighbor_nums->datatype; + + EXPECT_EQ(std::string(elemnames[0]), "num_neighbors"); + EXPECT_EQ(std::string(elemnames[1]), "neighbor_nums"); + EXPECT_EQ(elemlengths[0], 1); + EXPECT_EQ(elemlengths[1], 0); + EXPECT_EQ(nelems, 2); + EXPECT_EQ(values[0], 0); + EXPECT_EQ(nvalues, 1); + EXPECT_EQ(datatype, DB_INT); + + DBFreeCompoundarray(dom_neighbor_nums); + + DBClose(domfile); +} + +//----------------------------------------------------------------------------- +// this tests material i/o +TEST(conduit_relay_io_silo, round_trip_save_option_overlink3) +{ + Node save_mesh, load_mesh, info; + const int nx = 100, ny = 100; + const double radius = 0.25; + blueprint::mesh::examples::venn("sparse_by_element", nx, ny, radius, save_mesh); + + const std::string basename = "silo_save_option_overlink_venn"; + const std::string filename = basename + "/OvlTop.silo"; + + Node opts; + opts["file_style"] = "overlink"; + + remove_path_if_exists(filename); + io::silo::save_mesh(save_mesh, basename, opts); + io::silo::load_mesh(filename, load_mesh); + EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + + // make changes to save mesh so the diff will pass + + // The field mat_check has values that are one type and matset_values + // that are another type. The silo writer converts both to double arrays + // in this case, so we follow suit. + Node mat_check_new_values, mat_check_new_matset_values; + save_mesh["fields"]["mat_check"]["values"].to_double_array(mat_check_new_values); + save_mesh["fields"]["mat_check"]["matset_values"].to_double_array(mat_check_new_matset_values); + save_mesh["fields"]["mat_check"]["values"].set_external(mat_check_new_values); + save_mesh["fields"]["mat_check"]["matset_values"].set_external(mat_check_new_matset_values); + + overlink_name_changer(save_mesh); + + // the loaded mesh will be in the multidomain format + // but the saved mesh is in the single domain format + EXPECT_EQ(load_mesh.number_of_children(), 1); + EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); + + EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); +} + +//----------------------------------------------------------------------------- +// we are testing vector fields get converted to scalars +TEST(conduit_relay_io_silo, round_trip_save_option_overlink4) +{ + const std::vector> mesh_types = { + std::make_pair("rectilinear", "2"), std::make_pair("rectilinear", "3"), + std::make_pair("structured", "2"), std::make_pair("structured", "3"), + std::make_pair("quads", "2"), + std::make_pair("hexs", "3"), + }; + for (int i = 0; i < mesh_types.size(); ++i) + { + std::string dim = mesh_types[i].second; + index_t nx = 3; + index_t ny = 4; + index_t nz = (dim == "2" ? 0 : 2); + + std::string mesh_type = mesh_types[i].first; + + Node save_mesh, load_mesh, info; + blueprint::mesh::examples::braid(mesh_type, nx, ny, nz, save_mesh); + index_t nele_x = nx - 1; + index_t nele_y = ny - 1; + index_t nele_z = (dim == "2" ? 0 : nz - 1); + + // provide a matset for braid + braid_init_example_matset(nele_x, nele_y, nele_z, save_mesh["matsets"]["matset"]); + + const std::string basename = "silo_save_option_overlink_braid_" + mesh_type + "_" + dim + "D"; + const std::string filename = basename + "/OvlTop.silo"; + + Node write_opts, read_opts; + write_opts["file_style"] = "overlink"; + read_opts["matset_style"] = "multi_buffer_full"; + + // remove existing root file, directory and any output files + remove_path_if_exists(filename); + io::silo::save_mesh(save_mesh, basename, write_opts); + io::silo::load_mesh(filename, read_opts, load_mesh); + EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + + Node &field_vel = save_mesh["fields"]["vel"]; + Node &field_vel_u = save_mesh["fields"]["vel_u"]; + Node &field_vel_v = save_mesh["fields"]["vel_v"]; + + field_vel_u["topology"].set(field_vel["topology"]); + field_vel_u["association"].set(field_vel["association"]); + field_vel_u["values"].set(field_vel["values/u"]); + field_vel_v["topology"].set(field_vel["topology"]); + field_vel_v["association"].set(field_vel["association"]); + field_vel_v["values"].set(field_vel["values/v"]); + + if (dim == "3") + { + Node &field_vel_w = save_mesh["fields"]["vel_w"]; + field_vel_w["topology"].set(field_vel["topology"]); + field_vel_w["association"].set(field_vel["association"]); + field_vel_w["values"].set(field_vel["values/w"]); + } + + save_mesh["fields"].remove_child("vel"); + + // make changes to save mesh so the diff will pass + overlink_name_changer(save_mesh); + + // the loaded mesh will be in the multidomain format + // but the saved mesh is in the single domain format + EXPECT_EQ(load_mesh.number_of_children(), 1); + EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); + + EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); + } +} + +//----------------------------------------------------------------------------- +// check that all the shape types work (specifically polytopal ones) +TEST(conduit_relay_io_silo, round_trip_save_option_overlink5) +{ + const std::vector> mesh_types = { + std::make_pair("uniform", "2"), std::make_pair("uniform", "3"), + std::make_pair("rectilinear", "2"), std::make_pair("rectilinear", "3"), + std::make_pair("structured", "2"), std::make_pair("structured", "3"), + std::make_pair("quads", "2"), + std::make_pair("polygons", "2"), + std::make_pair("hexs", "3"), + // std::make_pair("polyhedra", "3") + // Overlink does not support tris, wedges, pyramids, or tets + }; + for (int i = 0; i < mesh_types.size(); ++i) + { + const std::string dim = mesh_types[i].second; + index_t nx = 3; + index_t ny = 4; + index_t nz = (dim == "2" ? 0 : 2); + + const std::string mesh_type = mesh_types[i].first; + + Node save_mesh, load_mesh, info; + blueprint::mesh::examples::basic(mesh_type, nx, ny, nz, save_mesh); + + const std::string basename = "silo_save_option_overlink_basic_" + mesh_type + "_" + dim + "D"; + const std::string filename = basename + "/OvlTop.silo"; + const std::string domfile = basename + "/domain0.silo"; + + Node write_opts, read_opts; + write_opts["file_style"] = "overlink"; + read_opts["matset_style"] = "multi_buffer_full"; + + // add a matset to make overlink happy + int num_elems = (nx - 1) * (ny - 1); + if (mesh_type == "tets") + { + num_elems *= 6; + } + add_multi_buffer_full_matset(save_mesh, num_elems, "mesh"); + + remove_path_if_exists(filename); + remove_path_if_exists(domfile); + io::silo::save_mesh(save_mesh, basename, write_opts); + io::silo::load_mesh(filename, read_opts, load_mesh); + EXPECT_TRUE(blueprint::mesh::verify(load_mesh,info)); + + // make changes to save mesh so the diff will pass + if (mesh_type == "uniform") + { + silo_uniform_to_rect_conversion("coords", "mesh", save_mesh); + } + overlink_name_changer(save_mesh); + + // the loaded mesh will be in the multidomain format + // but the saved mesh is in the single domain format + EXPECT_EQ(load_mesh.number_of_children(), 1); + EXPECT_EQ(load_mesh[0].number_of_children(), save_mesh.number_of_children()); + + EXPECT_FALSE(load_mesh[0].diff(save_mesh, info, CONDUIT_EPSILON, true)); + } +} + +//----------------------------------------------------------------------------- + +// +// read option tests +// + +// read options: +/// opts: +/// silo_names: +/// multimesh_names: +/// "{name1}" - multimeshes with this name will be read if they exist +/// "{name2}" +/// ... +/// or +/// "{all}" - all multimeshes will be read. +/// or +/// "{none}" - no multimeshes will be read. +/// multivar_names: similar to multimesh_names. +/// multimat_names: similar to multimesh_names. +/// multimatspecies_names: similar to multimesh_names. TODO +/// qmesh_names: similar to multimesh_names. +/// qvar_names: similar to multimesh_names. +/// ucdmesh_names: similar to multimesh_names. +/// ucdvar_names: similar to multimesh_names. +/// ptmesh_names: similar to multimesh_names. +/// ptvar_names: similar to multimesh_names. +/// mat_names: similar to multimesh_names. +/// matspecies_names: similar to multimesh_names. TODO +/// By default, everything in the file will be read unless manually turned off. +/// +/// matset_style: "default", "multi_buffer_full", "sparse_by_element", +/// "multi_buffer_by_material" +/// "default" ==> "sparse_by_element" +/// +/// mesh_name: legacy argument. This is interpreted as a multimesh name. +/// It is added to the list of multimesh names to read, unless the +/// user has specified "all" or "none", which will supersede this. +/// TODO does it make sense to remove this? When? + +//----------------------------------------------------------------------------- +// TODO this is now a legacy feature. Should I remove eventually? +TEST(conduit_relay_io_silo, round_trip_read_option_mesh_name) +{ + Node load_mesh, info, opts; + const std::string path = utils::join_file_path("silo", "multi_curv3d.silo"); + const std::string input_file = relay_test_silo_data_path(path); + + opts["mesh_name"] = "mesh1_dup"; + + io::silo::load_mesh(input_file, opts, load_mesh); + EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + + EXPECT_TRUE(load_mesh[0].has_path("topologies/mesh1_dup")); +} + +//----------------------------------------------------------------------------- +TEST(conduit_relay_io_silo, round_trip_read_option_matset_style) +{ + // the matset type and the type we are requesting on read + const std::vector> matset_types = { + std::make_pair("full", "full"), + std::make_pair("sparse_by_material", "sparse_by_material"), + std::make_pair("sparse_by_element", "sparse_by_element"), + std::make_pair("sparse_by_element", "full"), + std::make_pair("sparse_by_material", "sparse_by_element"), + std::make_pair("sparse_by_material", "default"), + }; + + for (int i = 0; i < matset_types.size(); i ++) + { + std::string matset_type = matset_types[i].first; + std::string matset_request = matset_types[i].second; + + for (int j = 0; j < 2; j ++) + { + Node mesh_full, mesh_sbe, mesh_sbm, baseline_mesh, load_mesh, info; + std::string size; + int nx, ny; + const double radius = 0.25; + if (j == 0) + { + size = "small"; + nx = ny = 4; + } + else + { + size = "large"; + nx = ny = 100; + } + + blueprint::mesh::examples::venn("full", nx, ny, radius, mesh_full); + blueprint::mesh::examples::venn("sparse_by_material", nx, ny, radius, mesh_sbm); + blueprint::mesh::examples::venn("sparse_by_element", nx, ny, radius, mesh_sbe); + + if (matset_type == "full") + { + baseline_mesh.set_external(mesh_full); + } + else if (matset_type == "sparse_by_material") + { + baseline_mesh.set_external(mesh_sbm); + } + else // (matset_type == "sparse_by_element") + { + baseline_mesh.set_external(mesh_sbe); + } + + Node opts; + if (matset_request == "full") + { + opts["matset_style"] = "multi_buffer_full"; + } + else if (matset_request == "sparse_by_material") + { + opts["matset_style"] = "multi_buffer_by_material"; + } + else if (matset_request == "sparse_by_element") + { + opts["matset_style"] = "sparse_by_element"; + } + else + { + opts["matset_style"] = "default"; + } + + const std::string basename = "silo_venn2_" + matset_type + "_" + size; + const std::string filename = basename + ".root"; + + remove_path_if_exists(filename); + io::silo::save_mesh(baseline_mesh, basename); + io::silo::load_mesh(filename, opts, load_mesh); + EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + + if (matset_request == "full") + { + baseline_mesh.set_external(mesh_full); + } + else if (matset_request == "sparse_by_material") + { + baseline_mesh.set_external(mesh_sbm); + } + else if (matset_request == "sparse_by_element") + { + baseline_mesh.set_external(mesh_sbe); + } + else + { + baseline_mesh.set_external(mesh_sbe); + } + + // make changes to save mesh so the diff will pass + + // The field mat_check has values that are one type and matset_values + // that are another type. The silo writer converts both to double arrays + // in this case, so we follow suit. + Node mat_check_new_values, mat_check_new_matset_values; + baseline_mesh["fields"]["mat_check"]["values"].to_double_array(mat_check_new_values); + if (baseline_mesh["fields"]["mat_check"]["matset_values"].dtype().is_object()) + { + auto mat_vals_itr = baseline_mesh["fields"]["mat_check"]["matset_values"].children(); + while (mat_vals_itr.has_next()) + { + Node &mat_vals_for_mat = mat_vals_itr.next(); + const std::string mat_name = mat_vals_itr.name(); + mat_vals_for_mat.to_double_array(mat_check_new_matset_values[mat_name]); + } + } + else + { + baseline_mesh["fields"]["mat_check"]["matset_values"].to_double_array(mat_check_new_matset_values); + } + baseline_mesh["fields"]["mat_check"]["values"].set_external(mat_check_new_values); + baseline_mesh["fields"]["mat_check"]["matset_values"].set_external(mat_check_new_matset_values); + + silo_name_changer("mesh", baseline_mesh); + + // the loaded mesh will be in the multidomain format + // but the saved mesh is in the single domain format + EXPECT_EQ(load_mesh.number_of_children(), 1); + EXPECT_EQ(load_mesh[0].number_of_children(), baseline_mesh.number_of_children()); + EXPECT_FALSE(load_mesh[0].diff(baseline_mesh, info, CONDUIT_EPSILON, true)); + } + } +} + +//----------------------------------------------------------------------------- + +// +// read and write Silo and Overlink tests +// + +//----------------------------------------------------------------------------- +// read normal silo files containing multimeshes, multivars, and multimats +TEST(conduit_relay_io_silo, read_silo) +{ + const std::vector> file_info = { + {".", "multi_curv3d", ".silo"}, + {".", "tire", ".silo"}, + {".", "galaxy0000", ".silo"}, + {".", "emptydomains", ".silo"}, + {"multidir_test_data", "multidir0000", ".root"}, + }; + + // TODO what to do in the case where a multimesh points to no data? (mesh1_back) + // fail silently, as we do now? + + for (int i = 0; i < file_info.size(); i ++) + { + const std::string dirname = file_info[i][0]; + const std::string basename = file_info[i][1]; + const std::string fileext = file_info[i][2]; + + Node load_mesh, info, write_opts; + std::string filepath = utils::join_file_path(dirname, basename) + fileext; + filepath = utils::join_file_path("silo", filepath); + std::string input_file = relay_test_silo_data_path(filepath); + + io::silo::load_mesh(input_file, load_mesh); + EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + + const std::string out_name = "read_silo_" + basename; + + // TODO are these remove paths doing anything? Don't they need filenames? + remove_path_if_exists(out_name + "_write_blueprint"); + io::blueprint::save_mesh(load_mesh, out_name + "_write_blueprint", "hdf5"); + + remove_path_if_exists(out_name + "_write_silo"); + io::silo::save_mesh(load_mesh, out_name + "_write_silo"); + + // overlink requires matsets and does not support point meshes + if (load_mesh[0].has_child("matsets") && basename != "galaxy0000") + { + remove_path_if_exists(out_name + "_write_overlink"); + write_opts["file_style"] = "overlink"; + write_opts["ovl_topo_name"] = "MMESH"; // TODO do I even need this + io::silo::save_mesh(load_mesh, out_name + "_write_overlink", write_opts); + } + } +} //----------------------------------------------------------------------------- // test that we can read silo without multimeshes, multivars, and multimats @@ -1851,230 +1851,141 @@ TEST(conduit_relay_io_silo, read_simple_silo) } //----------------------------------------------------------------------------- -TEST(conduit_relay_io_silo, bungus) +// test that we can read the fake overlink files from the visit test data +TEST(conduit_relay_io_silo, read_fake_overlink) +{ + const std::vector> file_info = { + // {"ev_0_0_100", "OvlTop", ".silo"}, + // uncomment once silo ucdmesh phzones are supported + {"hl18spec", "OvlTop", ".silo"}, + // {"regrovl_qh_1000_10001_4", "OvlTop", ".silo"}, + // uncomment once silo ucdmesh phzones are supported + {"utpyr4", "OvlTop", ".silo"}, + }; + + for (int i = 0; i < file_info.size(); i ++) + { + const std::string dirname = file_info[i][0]; + const std::string basename = file_info[i][1]; + const std::string fileext = file_info[i][2]; + + Node load_mesh, info, write_opts; + std::string filepath = utils::join_file_path(dirname, basename) + fileext; + filepath = utils::join_file_path("fake_overlink", filepath); + std::string input_file = relay_test_silo_data_path(filepath); + + io::silo::load_mesh(input_file, load_mesh); + EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + + const std::string out_name = "read_fake_overlink_" + dirname; + + remove_path_if_exists(out_name + "_write_blueprint"); + io::blueprint::save_mesh(load_mesh, out_name + "_write_blueprint", "hdf5"); + + remove_path_if_exists(out_name + "_write_silo"); + io::silo::save_mesh(load_mesh, out_name + "_write_silo"); + + remove_path_if_exists(out_name + "_write_overlink"); + write_opts["file_style"] = "overlink"; + write_opts["ovl_topo_name"] = "MMESH"; + io::silo::save_mesh(load_mesh, out_name + "_write_overlink", write_opts); + } +} + +//----------------------------------------------------------------------------- +// read overlink files in symlink format +// should be similar to reading raw silo +TEST(conduit_relay_io_silo, read_overlink_symlink_format) +{ + const std::vector> file_info = { + {".", "box2d", ".silo"}, + {".", "box3d", ".silo"}, + // {".", "diamond", ".silo"}, + // fails b/c polytopal not yet supported + {".", "testDisk2D_a", ".silo"}, + // {".", "donordiv.s2_materials2", ".silo"}, + // fails b/c polytopal not yet supported + {".", "donordiv.s2_materials3", ".silo"}, + }; + + for (int i = 0; i < file_info.size(); i ++) + { + const std::string dirname = file_info[i][0]; + const std::string basename = file_info[i][1]; + const std::string fileext = file_info[i][2]; + + Node load_mesh, info, write_opts; + std::string filepath = utils::join_file_path(dirname, basename) + fileext; + filepath = utils::join_file_path("overlink", filepath); + std::string input_file = relay_test_silo_data_path(filepath); + + io::silo::load_mesh(input_file, load_mesh); + EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + + const std::string out_name = "read_overlink_symlink_" + basename; + + remove_path_if_exists(out_name + "_write_blueprint"); + io::blueprint::save_mesh(load_mesh, out_name + "_write_blueprint", "hdf5"); + + remove_path_if_exists(out_name + "_write_silo"); + io::silo::save_mesh(load_mesh, out_name + "_write_silo"); + + remove_path_if_exists(out_name + "_write_overlink"); + write_opts["file_style"] = "overlink"; + write_opts["ovl_topo_name"] = "MMESH"; + io::silo::save_mesh(load_mesh, out_name + "_write_overlink", write_opts); + } +} + +//----------------------------------------------------------------------------- +// read overlink directly from ovltop.silo +// this case is tricky and involves messing with paths +TEST(conduit_relay_io_silo, read_overlink_directly) { - std::string yaml_text = "" - "coordsets:\n" - " coords:\n" - " type: \"explicit\"\n" - " values:\n" - " x: [-10.0, -6.6, -3.3, 0.0, 3.3, 6.6, 10.0,\n" - " -10.0, -6.6, -3.3, 0.0, 3.3, 6.6, 10.0,\n" - " -10.0, -6.6, -3.3, 0.0, 3.3, 6.6, 10.0,\n" - " -10.0, -6.6, -3.3, 0.0, 3.3, 6.6, 10.0,\n" - " -10.0, -6.6, -3.3, 0.0, 3.3, 6.6, 10.0,\n" - " -10.0, -6.6, -3.3, 0.0, 3.3, 6.6, 10.0]\n" - " y: [-10.0, -10.0, -10.0, -10.0, -10.0, -10.0, -10.0,\n" - " -6.0, -6.0, -6.0, -6.0, -6.0, -6.0, -6.0,\n" - " -2.0, -2.0, -2.0, -2.0, -2.0, -2.0, -2.0,\n" - " 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0,\n" - " 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0,\n" - " 10.0, 10.0, 10.0, 10.0, 10.0, 10.0, 10.0]\n" - " col_maj_coords:\n" - " type: \"explicit\"\n" - " values:\n" - " x: [-10.0, -10.0, -10.0, -10.0, -10.0, -10.0,\n" - " -6.6, -6.6, -6.6, -6.6, -6.6, -6.6,\n" - " -3.3, -3.3, -3.3, -3.3, -3.3, -3.3,\n" - " 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\n" - " 3.3, 3.3, 3.3, 3.3, 3.3, 3.3,\n" - " 6.6, 6.6, 6.6, 6.6, 6.6, 6.6,\n" - " 10.0, 10.0, 10.0, 10.0, 10.0, 10.0]\n" - " y: [-10.0, -6.0, -2.0, 2.0, 6.0, 10.0,\n" - " -10.0, -6.0, -2.0, 2.0, 6.0, 10.0,\n" - " -10.0, -6.0, -2.0, 2.0, 6.0, 10.0,\n" - " -10.0, -6.0, -2.0, 2.0, 6.0, 10.0,\n" - " -10.0, -6.0, -2.0, 2.0, 6.0, 10.0,\n" - " -10.0, -6.0, -2.0, 2.0, 6.0, 10.0,\n" - " -10.0, -6.0, -2.0, 2.0, 6.0, 10.0]\n" - "topologies:\n" - " mesh:\n" - " type: \"structured\"\n" - " coordset: \"coords\"\n" - " elements:\n" - " dims:\n" - " i: 3\n" - " j: 2\n" - " offsets: [2, 2]\n" - " strides: [1, 7]\n" - " full_mesh:\n" - " type: \"structured\"\n" - " coordset: \"coords\"\n" - " elements:\n" - " dims:\n" - " i: 6\n" - " j: 5\n" - " offsets: [0, 0]\n" - " strides: [1, 7]\n" - " mesh_col_maj:\n" - " type: \"structured\"\n" - " coordset: \"col_maj_coords\"\n" - " elements:\n" - " dims:\n" - " i: 3\n" - " j: 2\n" - " offsets: [2, 2]\n" - " strides: [6, 1]\n" - " full_mesh_col_maj:\n" - " type: \"structured\"\n" - " coordset: \"col_maj_coords\"\n" - " elements:\n" - " dims:\n" - " i: 6\n" - " j: 5\n" - // " offsets: [0, 0]\n" - " strides: [6, 1]\n" - ""; - - // TODO justin add a new test case to t_blueprint_mesh_examples - // also might want to add as a visit test - // just take the yaml and save for conduit test - - Node my_structured_strided_mesh; - my_structured_strided_mesh.parse(yaml_text, "yaml"); - - std::cout << my_structured_strided_mesh.to_yaml() << std::endl; - - remove_path_if_exists("bungus"); - io::blueprint::save_mesh(my_structured_strided_mesh, "bungus", "hdf5"); + const std::vector> file_info = { + {"box2d", "OvlTop", ".silo"}, + {"box3d", "OvlTop", ".silo"}, + // {"diamond", "OvlTop", ".silo"}, + {"testDisk2D_a", "OvlTop", ".silo"}, + // {"donordiv.s2_materials2", "OvlTop", ".silo"}, + {"donordiv.s2_materials3", "OvlTop", ".silo"}, + }; + + for (int i = 0; i < file_info.size(); i ++) + { + const std::string dirname = file_info[i][0]; + const std::string basename = file_info[i][1]; + const std::string fileext = file_info[i][2]; + + Node load_mesh, info, write_opts; + + std::string filepath = utils::join_file_path(dirname, basename) + fileext; + filepath = utils::join_file_path("overlink", filepath); + std::string input_file = relay_test_silo_data_path(filepath); + + io::silo::load_mesh(input_file, load_mesh); + EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + + const std::string out_name = "read_overlink_direct_" + dirname; + + remove_path_if_exists(out_name + "_write_blueprint"); + io::blueprint::save_mesh(load_mesh, out_name + "_write_blueprint", "hdf5"); + + remove_path_if_exists(out_name + "_write_silo"); + io::silo::save_mesh(load_mesh, out_name + "_write_silo"); + + remove_path_if_exists(out_name + "_write_overlink"); + write_opts["file_style"] = "overlink"; + write_opts["ovl_topo_name"] = "MMESH"; + io::silo::save_mesh(load_mesh, out_name + "_write_overlink", write_opts); + } } -// //----------------------------------------------------------------------------- -// // test that we can read the fake overlink files from the visit test data -// TEST(conduit_relay_io_silo, read_fake_overlink) -// { -// const std::vector> file_info = { -// // {"ev_0_0_100", "OvlTop", ".silo"}, -// // uncomment once silo ucdmesh phzones are supported -// {"hl18spec", "OvlTop", ".silo"}, -// // {"regrovl_qh_1000_10001_4", "OvlTop", ".silo"}, -// // uncomment once silo ucdmesh phzones are supported -// {"utpyr4", "OvlTop", ".silo"}, -// }; - -// for (int i = 0; i < file_info.size(); i ++) -// { -// const std::string dirname = file_info[i][0]; -// const std::string basename = file_info[i][1]; -// const std::string fileext = file_info[i][2]; - -// Node load_mesh, info, write_opts; -// std::string filepath = utils::join_file_path(dirname, basename) + fileext; -// filepath = utils::join_file_path("fake_overlink", filepath); -// std::string input_file = relay_test_silo_data_path(filepath); - -// io::silo::load_mesh(input_file, load_mesh); -// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - -// const std::string out_name = "read_fake_overlink_" + dirname; - -// remove_path_if_exists(out_name + "_write_blueprint"); -// io::blueprint::save_mesh(load_mesh, out_name + "_write_blueprint", "hdf5"); - -// remove_path_if_exists(out_name + "_write_silo"); -// io::silo::save_mesh(load_mesh, out_name + "_write_silo"); - -// remove_path_if_exists(out_name + "_write_overlink"); -// write_opts["file_style"] = "overlink"; -// write_opts["ovl_topo_name"] = "MMESH"; -// io::silo::save_mesh(load_mesh, out_name + "_write_overlink", write_opts); -// } -// } - -// //----------------------------------------------------------------------------- -// // read overlink files in symlink format -// // should be similar to reading raw silo -// TEST(conduit_relay_io_silo, read_overlink_symlink_format) -// { -// const std::vector> file_info = { -// {".", "box2d", ".silo"}, -// {".", "box3d", ".silo"}, -// // {".", "diamond", ".silo"}, -// // fails b/c polytopal not yet supported -// {".", "testDisk2D_a", ".silo"}, -// // {".", "donordiv.s2_materials2", ".silo"}, -// // fails b/c polytopal not yet supported -// {".", "donordiv.s2_materials3", ".silo"}, -// }; - -// for (int i = 0; i < file_info.size(); i ++) -// { -// const std::string dirname = file_info[i][0]; -// const std::string basename = file_info[i][1]; -// const std::string fileext = file_info[i][2]; - -// Node load_mesh, info, write_opts; -// std::string filepath = utils::join_file_path(dirname, basename) + fileext; -// filepath = utils::join_file_path("overlink", filepath); -// std::string input_file = relay_test_silo_data_path(filepath); - -// io::silo::load_mesh(input_file, load_mesh); -// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - -// const std::string out_name = "read_overlink_symlink_" + basename; - -// remove_path_if_exists(out_name + "_write_blueprint"); -// io::blueprint::save_mesh(load_mesh, out_name + "_write_blueprint", "hdf5"); - -// remove_path_if_exists(out_name + "_write_silo"); -// io::silo::save_mesh(load_mesh, out_name + "_write_silo"); - -// remove_path_if_exists(out_name + "_write_overlink"); -// write_opts["file_style"] = "overlink"; -// write_opts["ovl_topo_name"] = "MMESH"; -// io::silo::save_mesh(load_mesh, out_name + "_write_overlink", write_opts); -// } -// } - -// //----------------------------------------------------------------------------- -// // read overlink directly from ovltop.silo -// // this case is tricky and involves messing with paths -// TEST(conduit_relay_io_silo, read_overlink_directly) -// { -// const std::vector> file_info = { -// {"box2d", "OvlTop", ".silo"}, -// {"box3d", "OvlTop", ".silo"}, -// // {"diamond", "OvlTop", ".silo"}, -// {"testDisk2D_a", "OvlTop", ".silo"}, -// // {"donordiv.s2_materials2", "OvlTop", ".silo"}, -// {"donordiv.s2_materials3", "OvlTop", ".silo"}, -// }; - -// for (int i = 0; i < file_info.size(); i ++) -// { -// const std::string dirname = file_info[i][0]; -// const std::string basename = file_info[i][1]; -// const std::string fileext = file_info[i][2]; - -// Node load_mesh, info, write_opts; - -// std::string filepath = utils::join_file_path(dirname, basename) + fileext; -// filepath = utils::join_file_path("overlink", filepath); -// std::string input_file = relay_test_silo_data_path(filepath); - -// io::silo::load_mesh(input_file, load_mesh); -// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - -// const std::string out_name = "read_overlink_direct_" + dirname; - -// remove_path_if_exists(out_name + "_write_blueprint"); -// io::blueprint::save_mesh(load_mesh, out_name + "_write_blueprint", "hdf5"); - -// remove_path_if_exists(out_name + "_write_silo"); -// io::silo::save_mesh(load_mesh, out_name + "_write_silo"); - -// remove_path_if_exists(out_name + "_write_overlink"); -// write_opts["file_style"] = "overlink"; -// write_opts["ovl_topo_name"] = "MMESH"; -// io::silo::save_mesh(load_mesh, out_name + "_write_overlink", write_opts); -// } -// } - -// // TODO add tests for... -// // - polytopal meshes once they are supported -// // - units once they are supported -// // - etc. - -// // TODO add tetra8 and c36_m5 to all the overlink i/o tests - -// // TODO somewhere I need to error on overlink when there are different var or mesh types across domains +// TODO add tests for... +// - polytopal meshes once they are supported +// - units once they are supported +// - etc. + +// TODO add tetra8 and c36_m5 to all the overlink i/o tests + +// TODO somewhere I need to error on overlink when there are different var or mesh types across domains From 4b654a23659e2b7d7adecbce3b0cef383f74319a Mon Sep 17 00:00:00 2001 From: Justin Privitera Date: Thu, 11 Apr 2024 14:22:12 -0700 Subject: [PATCH 34/56] clean up --- src/libs/relay/conduit_relay_io_silo.cpp | 74 +++++++++++++++--------- src/tests/relay/t_relay_io_silo.cpp | 12 ++-- 2 files changed, 52 insertions(+), 34 deletions(-) diff --git a/src/libs/relay/conduit_relay_io_silo.cpp b/src/libs/relay/conduit_relay_io_silo.cpp index 2e7e64c95..6881968f2 100644 --- a/src/libs/relay/conduit_relay_io_silo.cpp +++ b/src/libs/relay/conduit_relay_io_silo.cpp @@ -1117,6 +1117,41 @@ read_quadmesh_domain(DBquadmesh *quadmesh_ptr, Node &topo_out = mesh_domain["topologies"][multimesh_name]; Node &coords_out = mesh_domain["coordsets"][multimesh_name]; + auto colmajor_regular_striding = [&](int strides[]) + { + // we can only succeed here if the data is regularly strided + if (1 == ndims) + { + CONDUIT_ASSERT(quadmesh_ptr->stride[0] == 1, + "Structured (noncollinear) column major quadmesh " << + multimesh_name << " has irregular striding, which " + "makes it impossible to correctly convert to Blueprint."); + strides[0] = 1; + } + else if (2 == ndims) + { + CONDUIT_ASSERT(quadmesh_ptr->stride[0] == 1 && + quadmesh_ptr->stride[1] == quadmesh_ptr->dims[0], + "Structured (noncollinear) column major quadmesh " << + multimesh_name << " has irregular striding, which " + "makes it impossible to correctly convert to Blueprint."); + strides[0] = quadmesh_ptr->dims[1]; + strides[1] = 1; + } + else // (3 == ndims) + { + CONDUIT_ASSERT(quadmesh_ptr->stride[0] == 1 && + quadmesh_ptr->stride[1] == quadmesh_ptr->dims[0] && + quadmesh_ptr->stride[2] == quadmesh_ptr->dims[0] * quadmesh_ptr->dims[1], + "Structured (noncollinear) column major quadmesh " << + multimesh_name << " has irregular striding, which " + "makes it impossible to correctly convert to Blueprint."); + strides[0] = quadmesh_ptr->dims[1] * quadmesh_ptr->dims[2]; + strides[1] = quadmesh_ptr->dims[2]; + strides[2] = 1; + } + }; + if (coordtype == DB_COLLINEAR) { coords_out["type"] = "rectilinear"; @@ -1158,8 +1193,13 @@ read_quadmesh_domain(DBquadmesh *quadmesh_ptr, if (quadmesh_ptr->major_order == DB_COLMAJOR) { - CONDUIT_ERROR("TODO what to do in this case"); - // resort to strided structured? + // resort to strided structured + int strides[] = {0,0,0}; + + // we can only succeed here if the data is regularly strided + colmajor_regular_striding(strides); + + topo_out["elements/dims/strides"].set(strides, ndims); } } else @@ -1197,30 +1237,7 @@ read_quadmesh_domain(DBquadmesh *quadmesh_ptr, int actual_strides[] = {0,0,0}; // we can only succeed here if the data is regularly strided - if (1 == ndims) - { - CONDUIT_ASSERT(quadmesh_ptr->stride[0] == 1, - "TODO BAD"); - actual_strides[0] = 1; - } - else if (2 == ndims) - { - CONDUIT_ASSERT(quadmesh_ptr->stride[0] == 1 && - quadmesh_ptr->stride[1] == quadmesh_ptr->dims[0], - "TODO BAD"); - actual_strides[0] = quadmesh_ptr->dims[1]; - actual_strides[1] = 1; - } - else // (3 == ndims) - { - CONDUIT_ASSERT(quadmesh_ptr->stride[0] == 1 && - quadmesh_ptr->stride[1] == quadmesh_ptr->dims[0] && - quadmesh_ptr->stride[2] == quadmesh_ptr->dims[0] * quadmesh_ptr->dims[1], - "TODO BAD"); - actual_strides[0] = quadmesh_ptr->dims[1] * quadmesh_ptr->dims[2]; - actual_strides[1] = quadmesh_ptr->dims[2]; - actual_strides[2] = 1; - } + colmajor_regular_striding(actual_strides); topo_out["elements/dims/strides"].set(actual_strides, ndims); } @@ -4429,9 +4446,12 @@ void silo_write_structured_mesh(DBfile *dbfile, index_t num_elems; // check for strided structured case - if (n_topo.has_path("elements/dims/offsets") && + if (n_topo.has_path("elements/dims/offsets") || n_topo.has_path("elements/dims/strides")) { + // I could potentially support either case where only offsets or only strides + // is present. Would need to think more about that. But certainly if both + // are present we have to give up, or completely take apart the mesh. CONDUIT_ERROR("Strided Structured Blueprint case does not have a general " "analog in Silo."); } diff --git a/src/tests/relay/t_relay_io_silo.cpp b/src/tests/relay/t_relay_io_silo.cpp index 0c577d685..8da39ad9b 100644 --- a/src/tests/relay/t_relay_io_silo.cpp +++ b/src/tests/relay/t_relay_io_silo.cpp @@ -1805,11 +1805,11 @@ TEST(conduit_relay_io_silo, read_silo) TEST(conduit_relay_io_silo, read_simple_silo) { const std::vector> file_info = { - // {"curv2d", ".silo", "no"}, - {"curv2d_colmajor", ".silo", "no"}, // TODO look at strided structured to handle this case - // {"curv3d", ".silo", "yes"}, - // {"curv3d_colmajor", ".silo", "no"}, // TODO - // {"globe", ".silo", "yes"}, // TODO need to fix a known bug for this one to work + {"curv2d", ".silo", "no"}, + {"curv2d_colmajor", ".silo", "no"}, + {"curv3d", ".silo", "yes"}, + {"curv3d_colmajor", ".silo", "no"}, + // {"globe", ".silo", "yes"}, // TODO need to add support for mixed shape topos }; for (int i = 0; i < file_info.size(); i ++) { @@ -1825,8 +1825,6 @@ TEST(conduit_relay_io_silo, read_simple_silo) io::silo::load_mesh(input_file, load_mesh); EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - load_mesh.print(); - const std::string out_name = "read_silo_" + basename; // TODO are these remove paths doing anything? Don't they need filenames? From 45fdd680ff75fd5337ab76f1d00fe177e2d2eddc Mon Sep 17 00:00:00 2001 From: Justin Privitera Date: Thu, 11 Apr 2024 15:48:36 -0700 Subject: [PATCH 35/56] I survived --- src/libs/relay/conduit_relay_io_silo.cpp | 215 +++++++++++++++++------ src/tests/relay/t_relay_io_silo.cpp | 4 +- 2 files changed, 159 insertions(+), 60 deletions(-) diff --git a/src/libs/relay/conduit_relay_io_silo.cpp b/src/libs/relay/conduit_relay_io_silo.cpp index 6881968f2..e670ef17c 100644 --- a/src/libs/relay/conduit_relay_io_silo.cpp +++ b/src/libs/relay/conduit_relay_io_silo.cpp @@ -314,12 +314,26 @@ class SiloTreePathGenerator //----------------------------------------------------------------------------- -class SiloReadBookkeeping +class SiloReadOptions { public: bool read_all = true; bool read_none = false; +}; + +//----------------------------------------------------------------------------- + +class SiloReadBookkeeping +{ +public: + int num_obj_in_toc = 0; + char **toc_names = nullptr; + int var_type; std::vector names_to_read; + + SiloReadBookkeeping(int num_obj_in_toc_, char **toc_names_, int var_type_) : + num_obj_in_toc(num_obj_in_toc_), toc_names(toc_names_), var_type(var_type_) {} + SiloReadBookkeeping(){} }; //----------------------------------------------------------------------------- @@ -2710,13 +2724,13 @@ read_root_silo_index(const std::string &root_file_path, real_opts.set_external(opts); } - std::map reading_info; + std::map reading_options; // read all is turned on, and read none is turned off - reading_info["mesh_names"] = detail::SiloReadBookkeeping(); - reading_info["var_names"] = detail::SiloReadBookkeeping(); - reading_info["mat_names"] = detail::SiloReadBookkeeping(); - // reading_info["matspecies_names"] = detail::SiloReadBookkeeping(); + reading_options["mesh_names"] = detail::SiloReadOptions(); + reading_options["var_names"] = detail::SiloReadOptions(); + reading_options["mat_names"] = detail::SiloReadOptions(); + // reading_options["matspecies_names"] = detail::SiloReadOptions(); if (real_opts.has_child("silo_names")) { @@ -2726,7 +2740,7 @@ read_root_silo_index(const std::string &root_file_path, const Node &silo_object_type = silo_names_itr.next(); const std::string silo_object_type_name = silo_names_itr.name(); - if (reading_info.count(silo_object_type_name) == 0) + if (reading_options.count(silo_object_type_name) == 0) { error_oss << "TODO unknown option"; return false; @@ -2743,8 +2757,8 @@ read_root_silo_index(const std::string &root_file_path, error_oss << "TODO this is bad"; return false; } - reading_info[silo_object_type_name].read_all = true; - reading_info[silo_object_type_name].read_none = false; + reading_options[silo_object_type_name].read_all = true; + reading_options[silo_object_type_name].read_none = false; } else if (silo_object_type.has_child("none")) { @@ -2753,21 +2767,21 @@ read_root_silo_index(const std::string &root_file_path, error_oss << "TODO this is bad"; return false; } - reading_info[silo_object_type_name].read_all = false; - reading_info[silo_object_type_name].read_none = true; + reading_options[silo_object_type_name].read_all = false; + reading_options[silo_object_type_name].read_none = true; } else { // we must have named some specific items we want to read - reading_info[silo_object_type_name].read_all = false; - reading_info[silo_object_type_name].read_none = false; + reading_options[silo_object_type_name].read_all = false; + reading_options[silo_object_type_name].read_none = false; } } else { // no children were specified so we want to read everything of this kind - reading_info[silo_object_type_name].read_all = true; - reading_info[silo_object_type_name].read_none = false; + reading_options[silo_object_type_name].read_all = true; + reading_options[silo_object_type_name].read_none = false; } } else @@ -2775,65 +2789,140 @@ read_root_silo_index(const std::string &root_file_path, const std::string silo_name_value = silo_object_type.as_string(); if ("all" == silo_name_value) { - reading_info[silo_object_type_name].read_all = true; - reading_info[silo_object_type_name].read_none = false; + reading_options[silo_object_type_name].read_all = true; + reading_options[silo_object_type_name].read_none = false; } else if ("none" == silo_name_value) { - reading_info[silo_object_type_name].read_all = false; - reading_info[silo_object_type_name].read_none = true; + reading_options[silo_object_type_name].read_all = false; + reading_options[silo_object_type_name].read_none = true; } else { // we must have named some specific items we want to read - reading_info[silo_object_type_name].read_all = false; - reading_info[silo_object_type_name].read_none = false; + reading_options[silo_object_type_name].read_all = false; + reading_options[silo_object_type_name].read_none = false; } } } } - // names to read get stored in reading_info["mesh_names"].names_to_read - auto generate_read_list = [&](const std::string silo_obj_name, // e.g. "mesh_names" - const std::string obj_name, // e.g. "mesh" - just for errors - const int num_silo_objects_in_toc, - char** toc_names) - { - // if we are *not* reading *no* meshes --> we are reading meshes - if (! reading_info[silo_obj_name].read_none) - { - if (reading_info[silo_obj_name].read_all) + std::map reading_info; + reading_info["multimesh_names"] = detail::SiloReadBookkeeping(toc->nmultimesh, + toc->multimesh_names, + DB_MULTIMESH); + reading_info["multivar_names"] = detail::SiloReadBookkeeping(toc->nmultivar, + toc->multivar_names, + DB_MULTIVAR); + reading_info["multimat_names"] = detail::SiloReadBookkeeping(toc->nmultimat, + toc->multimat_names, + DB_MULTIMAT); + // reading_info["multimatspecies_names"] = detail::SiloReadBookkeeping(toc->nmultimatspecies, + // toc->multimatspecies_names, + // DB_MULTIMATSPECIES); + reading_info["qmesh_names"] = detail::SiloReadBookkeeping(toc->nqmesh, + toc->qmesh_names, + DB_QUADMESH); + reading_info["qvar_names"] = detail::SiloReadBookkeeping(toc->nqvar, + toc->qvar_names, + DB_QUADVAR); + reading_info["ucdmesh_names"] = detail::SiloReadBookkeeping(toc->nucdmesh, + toc->ucdmesh_names, + DB_UCDMESH); + reading_info["ucdvar_names"] = detail::SiloReadBookkeeping(toc->nucdvar, + toc->ucdvar_names, + DB_UCDVAR); + reading_info["ptmesh_names"] = detail::SiloReadBookkeeping(toc->nptmesh, + toc->ptmesh_names, + DB_POINTMESH); + reading_info["ptvar_names"] = detail::SiloReadBookkeeping(toc->nptvar, + toc->ptvar_names, + DB_POINTVAR); + reading_info["mat_names"] = detail::SiloReadBookkeeping(toc->nmat, + toc->mat_names, + DB_MATERIAL); + // reading_info["matspecies_names"] = detail::SiloReadBookkeeping(toc->nmatspecies, + // toc->matspecies_names, + // DB_MATSPECIES); + + const std::vector mesh_name_types = + {"multimesh_names", "qmesh_names", "ucdmesh_names", "ptmesh_names"}; + const std::vector var_name_types = + {"multivar_names", "qvar_names", "ucdvar_names", "ptvar_names"}; + const std::vector mat_name_types = + {"multimat_names", "mat_names"}; + // std::vector mat_species_name_types = + // {"multimatspecies_names", "matspecies_names"}; + + auto generate_read_list = [&](DBfile *dbfile, + std::ostringstream &error_oss, + const std::string &what_are_we_reading, + const std::vector &type_names, + const std::string &error_msg) + { + // if we are *not* reading *no* objects of this type --> we are reading objects of this type + if (! reading_options[what_are_we_reading].read_none) + { + if (reading_options[what_are_we_reading].read_all) { - for (int toc_id = 0; toc_id < num_silo_objects_in_toc; toc_id ++) + for (const std::string &type_name : type_names) { - reading_info[silo_obj_name].names_to_read.push_back(toc_names[toc_id]); + for (int toc_id = 0; toc_id < reading_info[type_name].num_obj_in_toc; toc_id ++) + { + reading_info[type_name].names_to_read.push_back( + reading_info[type_name].toc_names[toc_id]); + } } } else { - if (real_opts["silo_names"][silo_obj_name].dtype().is_object()) + auto check_silo_name = [&](const std::string &silo_name) { - reading_info[silo_obj_name].names_to_read = real_opts["silo_names"][silo_obj_name].child_names(); - } - else - { - reading_info[silo_obj_name].names_to_read.push_back(real_opts["silo_names"][silo_obj_name].as_string()); - } + if (! DBInqVarExists(dbfile, silo_name.c_str())) + { + error_oss << "No silo var found matching " << silo_name; + return false; + } + + const int silo_var_type = DBInqVarType(dbfile, silo_name.c_str()); - for (size_t list_id = 0; list_id < reading_info[silo_obj_name].names_to_read.size(); list_id ++) - { bool found = false; - for (int toc_id = 0; toc_id < num_silo_objects_in_toc; toc_id ++) + for (const std::string &type_name : type_names) { - if (toc_names[toc_id] == reading_info[silo_obj_name].names_to_read[list_id]) + if (silo_var_type == reading_info[type_name].var_type) { + reading_info[type_name].names_to_read.push_back(silo_name); found = true; break; } } - if (!found) + if (! found) + { + error_oss << "Requested silo object " << silo_name << error_msg << silo_var_type << "."; + return false; + } + + return true; + }; + + if (real_opts["silo_names"][what_are_we_reading].dtype().is_object()) + { + for (const std::string silo_name : real_opts["silo_names"][what_are_we_reading].child_names()) + { + if (! check_silo_name(silo_name)) + { + // error_oss is already populated + return false; + } + } + } + else + { + const std::string silo_name = real_opts["silo_names"][what_are_we_reading].as_string(); + + if (! check_silo_name(silo_name)) { - error_oss << "No " << obj_name << " found matching " << reading_info["multimesh_names"].names_to_read[list_id]; + // error_oss is already populated return false; } } @@ -2845,18 +2934,28 @@ read_root_silo_index(const std::string &root_file_path, // Silo doesn't let us have any names that are the same in the same directory, // even if they are different types. So we don't have to worry about name collisions. - if (! (generate_read_list("multimesh_names", "multimesh", toc->nmultimesh, toc->multimesh_names) && - generate_read_list("multivar_names", "multivar", toc->nmultivar, toc->multivar_names) && - generate_read_list("multimat_names", "multimat", toc->nmultimat, toc->multimat_names) && - // generate_read_list("multimatspecies_names", "multimatspecies", toc->nmultimatspecies, toc->multimatspecies_names) && - generate_read_list("qmesh_names", "qmesh", toc->nqmesh, toc->qmesh_names) && - generate_read_list("qvar_names", "qvar", toc->nqvar, toc->qvar_names) && - generate_read_list("ucdmesh_names", "ucdmesh", toc->nucdmesh, toc->ucdmesh_names) && - generate_read_list("ucdvar_names", "ucdvar", toc->nucdvar, toc->ucdvar_names) && - generate_read_list("ptmesh_names", "ptmesh", toc->nptmesh, toc->ptmesh_names) && - generate_read_list("ptvar_names", "ptvar", toc->nptvar, toc->ptvar_names) && - generate_read_list("mat_names", "mat", toc->nmat, toc->mat_names))) - // generate_read_list("matspecies_names", "matspecies", toc->nmatspecies, toc->matspecies_names) + + if (! (generate_read_list(dbfile.getSiloObject(), + error_oss, + "mesh_names", + mesh_name_types, + " was found in file but it is not one of the supported " + "mesh types: DB_MULTIMESH, DB_QUADMESH, DB_UCDMESH, or " + "DB_POINTMESH. Instead it had silo var type ") && + generate_read_list(dbfile.getSiloObject(), + error_oss, + "var_names", + var_name_types, + " was found in file but it is not one of the supported " + "var types: DB_MULTIVAR, DB_QUADVAR, DB_UCDVAR, or " + "DB_POINTVAR. Instead it had silo var type ") && + generate_read_list(dbfile.getSiloObject(), + error_oss, + "mat_names", + mat_name_types, + " was found in file but it is not one of the supported " + "mat types: DB_MULTIMAT or DB_MATERIAL. Instead it had " + "silo var type "))) { // error msg should already be populated return false; diff --git a/src/tests/relay/t_relay_io_silo.cpp b/src/tests/relay/t_relay_io_silo.cpp index 8da39ad9b..79a2e335a 100644 --- a/src/tests/relay/t_relay_io_silo.cpp +++ b/src/tests/relay/t_relay_io_silo.cpp @@ -802,9 +802,9 @@ TEST(conduit_relay_io_silo, missing_domain_mesh) remove_path_if_exists(filename); io::silo::save_mesh(save_mesh, basename); - opts["silo_names"]["multimesh_names"] = "mesh_topo2"; + opts["silo_names"]["mesh_names"] = "mesh_topo2"; io::silo::load_mesh(filename, opts, load_mesh); - opts["silo_names"]["multimesh_names"] = "mesh_topo"; + opts["silo_names"]["mesh_names"] = "mesh_topo"; io::silo::load_mesh(filename, opts, load_mesh2); EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); EXPECT_TRUE(blueprint::mesh::verify(load_mesh2, info)); From 684451ba685dea82d013d31090635c98851fc02c Mon Sep 17 00:00:00 2001 From: Justin Privitera Date: Thu, 11 Apr 2024 16:14:02 -0700 Subject: [PATCH 36/56] thinking about life' --- src/libs/relay/conduit_relay_io_silo.cpp | 42 +++++++++++++++++++----- 1 file changed, 33 insertions(+), 9 deletions(-) diff --git a/src/libs/relay/conduit_relay_io_silo.cpp b/src/libs/relay/conduit_relay_io_silo.cpp index e670ef17c..7b451da98 100644 --- a/src/libs/relay/conduit_relay_io_silo.cpp +++ b/src/libs/relay/conduit_relay_io_silo.cpp @@ -2652,10 +2652,11 @@ read_root_silo_index(const std::string &root_file_path, return false; } - // TODO unify lists of meshes and vars - // then be smart about reading - - // handle legacy mesh_name argument + // Handle legacy mesh_name argument: + // The best way to do this is to put this mesh name into the opts before we + // start looking at the opts for real. However, we can't modify the opts the + // user passed in. Thus we need to create a new opts node and have it point + // at the original opts node for everything except the mesh names. Node real_opts; if (opts.has_child("mesh_name")) { @@ -2669,6 +2670,7 @@ read_root_silo_index(const std::string &root_file_path, { const Node &opts_item = opts_itr.next(); const std::string opt_name = opts_itr.name(); + // if you're not the silo_names, then we can point to original opts if (opt_name != "silo_names") { real_opts[opt_name].set_external(opts_item); @@ -2681,6 +2683,7 @@ read_root_silo_index(const std::string &root_file_path, { const Node &silo_names_entry = silo_names_itr.next(); const std::string silo_names_name = silo_names_itr.name(); + // if you're not the mesh_names, then you can point to original opts if (silo_names_name != "mesh_names") { real_opts["silo_names"][silo_names_name].set_external(silo_names_entry); @@ -2689,23 +2692,36 @@ read_root_silo_index(const std::string &root_file_path, } } + // now handle mesh names if (opts.has_path("silo_names/mesh_names")) { if (opts["silo_names"]["mesh_names"].has_child("all") || opts["silo_names"]["mesh_names"].has_child("none") || opts["silo_names"]["mesh_names"].has_child(opts_mesh_name)) { + // if we have already set things up such that the provided mesh name is superfluous + // then we can point to original opts real_opts["silo_names"]["mesh_names"].set_external(opts["silo_names"]["mesh_names"]); } else { - auto mesh_names_itr = opts["silo_names"]["mesh_names"].children(); - while (mesh_names_itr.has_next()) + // otherwise we must grab the mesh names that are already present and point to them + if (opts["silo_names"]["mesh_names"].dtype().is_object()) { - const Node &mesh_names_entry = mesh_names_itr.next(); - const std::string mesh_name = mesh_names_itr.name(); - real_opts["silo_names"]["mesh_names"][mesh_name].set_external(mesh_names_entry); + auto mesh_names_itr = opts["silo_names"]["mesh_names"].children(); + while (mesh_names_itr.has_next()) + { + const Node &mesh_names_entry = mesh_names_itr.next(); + const std::string mesh_name = mesh_names_itr.name(); + real_opts["silo_names"]["mesh_names"][mesh_name].set_external(mesh_names_entry); + } } + else + { + // I am letting users abuse me + real_opts["silo_names"]["mesh_names"][opts["silo_names"]["mesh_names"].as_string()]; + } + // and then add our extra mesh name to the list real_opts["silo_names"]["mesh_names"][opts_mesh_name]; } } @@ -2724,6 +2740,9 @@ read_root_silo_index(const std::string &root_file_path, real_opts.set_external(opts); } + // Reading options will hold information about what to read for each general + // kind of silo object. For each of the cases, we have the option to read all, + // read none, or read some of that type. std::map reading_options; // read all is turned on, and read none is turned off @@ -2732,6 +2751,11 @@ read_root_silo_index(const std::string &root_file_path, reading_options["mat_names"] = detail::SiloReadOptions(); // reading_options["matspecies_names"] = detail::SiloReadOptions(); + // TODO is there any way I could skip the following step or combine it with the next step? + // this is awfully complicated + + // Now we need to see what things we are supposed to read based on + // what was passed in to the options. if (real_opts.has_child("silo_names")) { auto silo_names_itr = real_opts["silo_names"].children(); From dba97e0eff6feba5c8163441cea5242cfcb76b56 Mon Sep 17 00:00:00 2001 From: Justin Privitera Date: Wed, 17 Apr 2024 11:24:41 -0700 Subject: [PATCH 37/56] symlinks and comments --- src/libs/relay/conduit_relay_io_silo.cpp | 54 ++++++++---------------- 1 file changed, 18 insertions(+), 36 deletions(-) diff --git a/src/libs/relay/conduit_relay_io_silo.cpp b/src/libs/relay/conduit_relay_io_silo.cpp index 7b451da98..ec1d56845 100644 --- a/src/libs/relay/conduit_relay_io_silo.cpp +++ b/src/libs/relay/conduit_relay_io_silo.cpp @@ -3153,35 +3153,26 @@ read_root_silo_index(const std::string &root_file_path, /// /// opts: /// silo_names: -/// multimesh_names: -/// "{name1}" - multimeshes with this name will be read if they exist +/// mesh_names: +/// "{name1}" - meshes with this name will be read if they exist /// "{name2}" /// ... /// or -/// "{all}" - all multimeshes will be read. +/// "{all}" - all meshes will be read. /// or -/// "{none}" - no multimeshes will be read. -/// multivar_names: similar to multimesh_names. -/// multimat_names: similar to multimesh_names. -/// multimatspecies_names: similar to multimesh_names. TODO -/// qmesh_names: similar to multimesh_names. -/// qvar_names: similar to multimesh_names. -/// ucdmesh_names: similar to multimesh_names. -/// ucdvar_names: similar to multimesh_names. -/// ptmesh_names: similar to multimesh_names. -/// ptvar_names: similar to multimesh_names. -/// mat_names: similar to multimesh_names. -/// matspecies_names: similar to multimesh_names. TODO +/// "{none}" - no meshes will be read. +/// var_names: similar to mesh_names. +/// mat_names: similar to mesh_names. +/// matspecies_names: similar to mesh_names. TODO /// By default, everything in the file will be read unless manually turned off. /// /// matset_style: "default", "multi_buffer_full", "sparse_by_element", /// "multi_buffer_by_material" /// "default" ==> "sparse_by_element" /// -/// mesh_name: legacy argument. This is interpreted as a multimesh name. -/// It is added to the list of multimesh names to read, unless the +/// mesh_name: legacy argument. This is interpreted as a mesh name. +/// It is added to the list of mesh names to read, unless the /// user has specified "all" or "none", which will supersede this. -/// TODO does it make sense to remove this? When? //----------------------------------------------------------------------------- void CONDUIT_RELAY_API read_mesh(const std::string &root_file_path, @@ -3543,35 +3534,26 @@ void load_mesh(const std::string &root_file_path, //----------------------------------------------------------------------------- /// opts: /// silo_names: -/// multimesh_names: -/// "{name1}" - multimeshes with this name will be read if they exist +/// mesh_names: +/// "{name1}" - meshes with this name will be read if they exist /// "{name2}" /// ... /// or -/// "{all}" - all multimeshes will be read. +/// "{all}" - all meshes will be read. /// or -/// "{none}" - no multimeshes will be read. -/// multivar_names: similar to multimesh_names. -/// multimat_names: similar to multimesh_names. -/// multimatspecies_names: similar to multimesh_names. TODO -/// qmesh_names: similar to multimesh_names. -/// qvar_names: similar to multimesh_names. -/// ucdmesh_names: similar to multimesh_names. -/// ucdvar_names: similar to multimesh_names. -/// ptmesh_names: similar to multimesh_names. -/// ptvar_names: similar to multimesh_names. -/// mat_names: similar to multimesh_names. -/// matspecies_names: similar to multimesh_names. TODO +/// "{none}" - no meshes will be read. +/// var_names: similar to mesh_names. +/// mat_names: similar to mesh_names. +/// matspecies_names: similar to mesh_names. TODO /// By default, everything in the file will be read unless manually turned off. /// /// matset_style: "default", "multi_buffer_full", "sparse_by_element", /// "multi_buffer_by_material" /// "default" ==> "sparse_by_element" /// -/// mesh_name: legacy argument. This is interpreted as a multimesh name. -/// It is added to the list of multimesh names to read, unless the +/// mesh_name: legacy argument. This is interpreted as a mesh name. +/// It is added to the list of mesh names to read, unless the /// user has specified "all" or "none", which will supersede this. -/// TODO does it make sense to remove this? When? //----------------------------------------------------------------------------- void load_mesh(const std::string &root_file_path, const Node &opts, From 7ae27744bc593031f23029eb4de5dbd87009f431 Mon Sep 17 00:00:00 2001 From: Justin Privitera Date: Wed, 17 Apr 2024 13:04:10 -0700 Subject: [PATCH 38/56] colmajor fields --- src/libs/relay/conduit_relay_io_silo.cpp | 216 ++++++++++++++--------- 1 file changed, 135 insertions(+), 81 deletions(-) diff --git a/src/libs/relay/conduit_relay_io_silo.cpp b/src/libs/relay/conduit_relay_io_silo.cpp index ec1d56845..f4883dd79 100644 --- a/src/libs/relay/conduit_relay_io_silo.cpp +++ b/src/libs/relay/conduit_relay_io_silo.cpp @@ -633,6 +633,41 @@ check_using_whole_coordset(const int *dims, return dim0_ok && dim1_ok && dim2_ok; }; +//----------------------------------------------------------------------------- +void +colmajor_regular_striding(int *strides_out, + const int ndims, + const std::string &error_msg, + const int *silo_strides, + const int *silo_dims) +{ + // we can only succeed here if the data is regularly strided + if (1 == ndims) + { + CONDUIT_ASSERT(silo_strides[0] == 1, + error_msg); + strides_out[0] = 1; + } + else if (2 == ndims) + { + CONDUIT_ASSERT(silo_strides[0] == 1 && + silo_strides[1] == silo_dims[0], + error_msg); + strides_out[0] = silo_dims[1]; + strides_out[1] = 1; + } + else // (3 == ndims) + { + CONDUIT_ASSERT(silo_strides[0] == 1 && + silo_strides[1] == silo_dims[0] && + silo_strides[2] == silo_dims[0] * silo_dims[1], + error_msg); + strides_out[0] = silo_dims[1] * silo_dims[2]; + strides_out[1] = silo_dims[2]; + strides_out[2] = 1; + } +} + //----------------------------------------------------------------------------- void copy_point_coords(const int datatype, @@ -1131,41 +1166,6 @@ read_quadmesh_domain(DBquadmesh *quadmesh_ptr, Node &topo_out = mesh_domain["topologies"][multimesh_name]; Node &coords_out = mesh_domain["coordsets"][multimesh_name]; - auto colmajor_regular_striding = [&](int strides[]) - { - // we can only succeed here if the data is regularly strided - if (1 == ndims) - { - CONDUIT_ASSERT(quadmesh_ptr->stride[0] == 1, - "Structured (noncollinear) column major quadmesh " << - multimesh_name << " has irregular striding, which " - "makes it impossible to correctly convert to Blueprint."); - strides[0] = 1; - } - else if (2 == ndims) - { - CONDUIT_ASSERT(quadmesh_ptr->stride[0] == 1 && - quadmesh_ptr->stride[1] == quadmesh_ptr->dims[0], - "Structured (noncollinear) column major quadmesh " << - multimesh_name << " has irregular striding, which " - "makes it impossible to correctly convert to Blueprint."); - strides[0] = quadmesh_ptr->dims[1]; - strides[1] = 1; - } - else // (3 == ndims) - { - CONDUIT_ASSERT(quadmesh_ptr->stride[0] == 1 && - quadmesh_ptr->stride[1] == quadmesh_ptr->dims[0] && - quadmesh_ptr->stride[2] == quadmesh_ptr->dims[0] * quadmesh_ptr->dims[1], - "Structured (noncollinear) column major quadmesh " << - multimesh_name << " has irregular striding, which " - "makes it impossible to correctly convert to Blueprint."); - strides[0] = quadmesh_ptr->dims[1] * quadmesh_ptr->dims[2]; - strides[1] = quadmesh_ptr->dims[2]; - strides[2] = 1; - } - }; - if (coordtype == DB_COLLINEAR) { coords_out["type"] = "rectilinear"; @@ -1189,6 +1189,10 @@ read_quadmesh_domain(DBquadmesh *quadmesh_ptr, coords_out["type"] = "explicit"; topo_out["type"] = "structured"; + const std::string irregular_striding_err_msg = "Structured (noncollinear)" + " column major quadmesh " + multimesh_name + " has irregular striding," + " which makes it impossible to correctly convert to Blueprint."; + if (detail::check_using_whole_coordset(quadmesh_ptr->dims, quadmesh_ptr->min_index, quadmesh_ptr->max_index, @@ -1205,54 +1209,47 @@ read_quadmesh_domain(DBquadmesh *quadmesh_ptr, topo_out["elements/dims/k"] = quadmesh_ptr->dims[2] - 1; } + // row major case requires nothing else if (quadmesh_ptr->major_order == DB_COLMAJOR) { // resort to strided structured int strides[] = {0,0,0}; - - // we can only succeed here if the data is regularly strided - colmajor_regular_striding(strides); - + detail::colmajor_regular_striding(strides, + ndims, + irregular_striding_err_msg, + quadmesh_ptr->stride, + quadmesh_ptr->dims); topo_out["elements/dims/strides"].set(strides, ndims); } } else { // strided structured case - if (quadmesh_ptr->major_order == DB_ROWMAJOR) + + topo_out["elements/dims/i"] = quadmesh_ptr->max_index[0] - quadmesh_ptr->min_index[0]; + if (ndims > 1) { - topo_out["elements/dims/i"] = quadmesh_ptr->max_index[0] - quadmesh_ptr->min_index[0]; - if (ndims > 1) - { - topo_out["elements/dims/j"] = quadmesh_ptr->max_index[1] - quadmesh_ptr->min_index[1]; - } - if (ndims > 2) - { - topo_out["elements/dims/k"] = quadmesh_ptr->max_index[2] - quadmesh_ptr->min_index[2]; - } + topo_out["elements/dims/j"] = quadmesh_ptr->max_index[1] - quadmesh_ptr->min_index[1]; + } + if (ndims > 2) + { + topo_out["elements/dims/k"] = quadmesh_ptr->max_index[2] - quadmesh_ptr->min_index[2]; + } - topo_out["elements/dims/offsets"].set(quadmesh_ptr->min_index, ndims); + topo_out["elements/dims/offsets"].set(quadmesh_ptr->min_index, ndims); + + if (quadmesh_ptr->major_order == DB_ROWMAJOR) + { topo_out["elements/dims/strides"].set(quadmesh_ptr->stride, ndims); } else // colmajor { - topo_out["elements/dims/i"] = quadmesh_ptr->max_index[0] - quadmesh_ptr->min_index[0]; - if (ndims > 1) - { - topo_out["elements/dims/j"] = quadmesh_ptr->max_index[1] - quadmesh_ptr->min_index[1]; - } - if (ndims > 2) - { - topo_out["elements/dims/k"] = quadmesh_ptr->max_index[2] - quadmesh_ptr->min_index[2]; - } - - topo_out["elements/dims/offsets"].set(quadmesh_ptr->min_index, ndims); - int actual_strides[] = {0,0,0}; - - // we can only succeed here if the data is regularly strided - colmajor_regular_striding(actual_strides); - + detail::colmajor_regular_striding(actual_strides, + ndims, + irregular_striding_err_msg, + quadmesh_ptr->stride, + quadmesh_ptr->dims); topo_out["elements/dims/strides"].set(actual_strides, ndims); } } @@ -1544,6 +1541,7 @@ read_variable_domain_helper(const T *var_ptr, const int vartype, const std::string &bottom_level_mesh_name, const std::string &volume_dependent, + const Node &mesh_out, Node &intermediate_field) { // If we cannot fetch this var we will skip @@ -1581,7 +1579,9 @@ read_variable_domain_helper(const T *var_ptr, intermediate_field["topology"] = multimesh_name; - // handle association + const std::string &topo_type = mesh_out["topologies"][multimesh_name]["type"].as_string(); + + // handle association and strided structured if (vartype == DB_UCDVAR) { intermediate_field["association"] = var_ptr->centering == DB_ZONECENT ? "element" : "vertex"; @@ -1590,17 +1590,69 @@ read_variable_domain_helper(const T *var_ptr, { intermediate_field["association"] = var_ptr->centering == DB_NODECENT ? "vertex" : "element"; + // put me in jail const DBquadvar* quadvar_ptr = reinterpret_cast(var_ptr); - // TODO what do we do if we aren't structured? Same q for dealing with meshes - // handle strided structured fields, if appropriate - if (! detail::check_using_whole_coordset(quadvar_ptr->dims, - quadvar_ptr->min_index, - quadvar_ptr->max_index, - quadvar_ptr->ndims)) + if (topo_type == "structured") + { + const std::string irregular_striding_err_msg = "Structured (noncollinear)" + " column major quadmesh " + multimesh_name + " field " + var_name + + " has irregular striding, which makes it impossible to correctly" + " convert to Blueprint."; + + if (detail::check_using_whole_coordset(quadvar_ptr->dims, + quadvar_ptr->min_index, + quadvar_ptr->max_index, + quadvar_ptr->ndims)) + { + // row major case requires nothing else + if (quadvar_ptr->major_order == DB_COLMAJOR) + { + // resort to strided structured + int strides[] = {0,0,0}; + detail::colmajor_regular_striding(strides, + quadvar_ptr->ndims, + irregular_striding_err_msg, + quadvar_ptr->stride, + quadvar_ptr->dims); + intermediate_field["strides"].set(strides, quadvar_ptr->ndims); + } + } + else + { + // strided structured case + + intermediate_field["offsets"].set(quadvar_ptr->min_index, quadvar_ptr->ndims); + + if (quadvar_ptr->major_order == DB_ROWMAJOR) + { + intermediate_field["strides"].set(quadvar_ptr->stride, quadvar_ptr->ndims); + } + else // colmajor + { + int actual_strides[] = {0,0,0}; + detail::colmajor_regular_striding(actual_strides, + quadvar_ptr->ndims, + irregular_striding_err_msg, + quadvar_ptr->stride, + quadvar_ptr->dims); + intermediate_field["strides"].set(actual_strides, quadvar_ptr->ndims); + } + } + } + else // rectilinear { - intermediate_field["offsets"].set(quadvar_ptr->min_index, quadvar_ptr->ndims); - intermediate_field["strides"].set(quadvar_ptr->stride, quadvar_ptr->ndims); + CONDUIT_ASSERT(detail::check_using_whole_coordset(quadvar_ptr->dims, + quadvar_ptr->min_index, + quadvar_ptr->max_index, + quadvar_ptr->ndims), + "Field " << var_name << " for Rectilinear grid (collinear quadmesh) " << + multimesh_name << " is using a subset of the provided field values. We " + "do not support this case."); + + CONDUIT_ASSERT(quadvar_ptr->major_order == DB_ROWMAJOR, + "Field " << var_name << " for Rectilinear grid (collinear quadmesh) " << + multimesh_name << " is column major in silo. We do not support this case."); } } else // if (vartype == DB_POINTVAR) @@ -1608,6 +1660,8 @@ read_variable_domain_helper(const T *var_ptr, intermediate_field["association"] = "vertex"; // TODO what to do about check_using_whole_coordset case? + + // TODO what to do about colmajor case? } // if we have volume dependence we can track it @@ -1663,8 +1717,8 @@ read_variable_domain(const int vartype, &DBFreeUcdvar}; if (!read_variable_domain_helper( - ucdvar.getSiloObject(), var_name, multimesh_name, - vartype, bottom_level_mesh_name, volume_dependent, intermediate_field)) + ucdvar.getSiloObject(), var_name, multimesh_name, vartype, + bottom_level_mesh_name, volume_dependent, mesh_out, intermediate_field)) { return false; // we hit a case where we want to skip this var } @@ -1681,8 +1735,8 @@ read_variable_domain(const int vartype, &DBFreeQuadvar}; if (!read_variable_domain_helper( - quadvar.getSiloObject(), var_name, multimesh_name, - vartype, bottom_level_mesh_name, volume_dependent, intermediate_field)) + quadvar.getSiloObject(), var_name, multimesh_name, vartype, + bottom_level_mesh_name, volume_dependent, mesh_out, intermediate_field)) { return false; // we hit a case where we want to skip this var } @@ -1699,8 +1753,8 @@ read_variable_domain(const int vartype, &DBFreeMeshvar}; if (!read_variable_domain_helper( - meshvar.getSiloObject(), var_name, multimesh_name, - vartype, bottom_level_mesh_name, volume_dependent, intermediate_field)) + meshvar.getSiloObject(), var_name, multimesh_name, vartype, + bottom_level_mesh_name, volume_dependent, mesh_out, intermediate_field)) { return false; // we hit a case where we want to skip this var } From 592736e5039647450b966506075ece00065605ac Mon Sep 17 00:00:00 2001 From: Justin Privitera Date: Wed, 17 Apr 2024 15:21:00 -0700 Subject: [PATCH 39/56] structured strided gets no mats from silo --- src/libs/relay/conduit_relay_io_silo.cpp | 26 ++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/src/libs/relay/conduit_relay_io_silo.cpp b/src/libs/relay/conduit_relay_io_silo.cpp index f4883dd79..f5acea0ab 100644 --- a/src/libs/relay/conduit_relay_io_silo.cpp +++ b/src/libs/relay/conduit_relay_io_silo.cpp @@ -541,7 +541,7 @@ get_coordset_axis_labels(const int sys) } else if (sys == DB_OTHER) { - CONDUIT_INFO("Encountered DB_OTHER, we will default to a cartesian coordinate system."); + CONDUIT_INFO("Encountered DB_OTHER; defaulting to a cartesian coordinate system."); coordnames.push_back(conduit::blueprint::mesh::utils::CARTESIAN_AXES[0].c_str()); coordnames.push_back(conduit::blueprint::mesh::utils::CARTESIAN_AXES[1].c_str()); coordnames.push_back(conduit::blueprint::mesh::utils::CARTESIAN_AXES[2].c_str()); @@ -1599,7 +1599,7 @@ read_variable_domain_helper(const T *var_ptr, " column major quadmesh " + multimesh_name + " field " + var_name + " has irregular striding, which makes it impossible to correctly" " convert to Blueprint."; - + if (detail::check_using_whole_coordset(quadvar_ptr->dims, quadvar_ptr->min_index, quadvar_ptr->max_index, @@ -1851,6 +1851,26 @@ read_matset_domain(DBfile* matset_domain_file_to_use, return false; } + // Check for structured strided case: + const Node &topo_out = mesh_out["topologies"][multimesh_name]; + if (topo_out["type"].as_string() == "structured" && + (topo_out.has_path("elements/dims/strides") || + topo_out.has_path("elements/dims/strides"))) + { + CONDUIT_INFO("DBmaterial " + matset_name + " is associated with mesh " + + bottom_level_mesh_name + ", which is a Structured (noncollinear) " + "quadmesh. It uses a subset of the coordinates to define the mesh. " + "We map meshes like this to Structured Strided meshes in Blueprint, " + "which expect associated matsets to only provide data for mesh zones " + "that are actually being used by the topology. Silo materials provide " + "data for all mesh zones, whether or not they are being used by the " + "quadmesh. We have opted to not support this case, as to do so would " + "require us to manually remove material set data that corresponds to " + "unused zones. If there is a need for this feature, please contact a " + "Conduit developer. In the meantime, we will skip reading this DBmaterial."); + return false; + } + CONDUIT_ASSERT(matset_ptr->allowmat0 == 0, "Material " << matset_name << " for multimesh " << multimesh_name << " may contain zones with no materials defined on them." << @@ -3484,8 +3504,6 @@ read_mesh(const std::string &root_file_path, // In silo, for each mesh, there can only be one matset, because otherwise // it would be ambiguous. In Blueprint, we can allow multiple matsets per // topo, because the fields explicitly link to the matset they use. - // Right now, we only ever read one mesh, meaning that there can only - // be one matset in our newly created blueprint mesh. if (read_matset_domain(matset_domain_file_to_use, n_matset, matset_name, mesh_index_name, multimat_name, bottom_level_mesh_name, opts_matset_style, matset_field_reconstruction, mesh_out)) From f12e1297eb90000f6933f83e7e85183f506069b5 Mon Sep 17 00:00:00 2001 From: Justin Privitera Date: Wed, 17 Apr 2024 16:00:00 -0700 Subject: [PATCH 40/56] notes on what is needed --- src/libs/relay/conduit_relay_io_silo.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/libs/relay/conduit_relay_io_silo.cpp b/src/libs/relay/conduit_relay_io_silo.cpp index f5acea0ab..6d3d4d9da 100644 --- a/src/libs/relay/conduit_relay_io_silo.cpp +++ b/src/libs/relay/conduit_relay_io_silo.cpp @@ -734,6 +734,8 @@ void add_shape_info(DBzonelist *zonelist_ptr, Node &n_elements) { + // TODO handle min and max index case (check_using_whole_coordset case) + for (int i = 0; i < zonelist_ptr->nshapes; ++i) { CONDUIT_ASSERT(zonelist_ptr->shapetype[0] == zonelist_ptr->shapetype[i], @@ -1659,8 +1661,8 @@ read_variable_domain_helper(const T *var_ptr, { intermediate_field["association"] = "vertex"; - // TODO what to do about check_using_whole_coordset case? - + // TODO handle min and max index case (check_using_whole_coordset case) + // TODO what to do about colmajor case? } @@ -2031,7 +2033,7 @@ read_matset_domain(DBfile* matset_domain_file_to_use, } } - // TODO find colmajor data to test this + // TODO still need to find colmajor data to test this // TODO are there other places where I'm reading where things could be rowmajor or colmajor intermediate_matset["material_ids"].set(material_ids.data(), material_ids.size()); From 0a688cbcba7246bff5bfa70e062a4e75068b9210 Mon Sep 17 00:00:00 2001 From: Justin Privitera Date: Wed, 17 Apr 2024 16:22:28 -0700 Subject: [PATCH 41/56] give up on irregularly strided matsets --- src/libs/relay/conduit_relay_io_silo.cpp | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/libs/relay/conduit_relay_io_silo.cpp b/src/libs/relay/conduit_relay_io_silo.cpp index 6d3d4d9da..f8e229f7a 100644 --- a/src/libs/relay/conduit_relay_io_silo.cpp +++ b/src/libs/relay/conduit_relay_io_silo.cpp @@ -1873,6 +1873,29 @@ read_matset_domain(DBfile* matset_domain_file_to_use, return false; } + // we can only succeed here if the data is regularly strided + const std::string irregular_striding_err_msg = "DBmaterial " + matset_name + + " has irregular striding, which makes it impossible to correctly convert" + " to Blueprint."; + if (1 == matset_ptr->ndims) + { + CONDUIT_ASSERT(matset_ptr->stride[0] == 1, + irregular_striding_err_msg); + } + else if (2 == matset_ptr->ndims) + { + CONDUIT_ASSERT(matset_ptr->stride[0] == 1 && + matset_ptr->stride[1] == matset_ptr->dims[0], + irregular_striding_err_msg); + } + else // (3 == matset_ptr->ndims) + { + CONDUIT_ASSERT(matset_ptr->stride[0] == 1 && + matset_ptr->stride[1] == matset_ptr->dims[0] && + matset_ptr->stride[2] == matset_ptr->dims[0] * matset_ptr->dims[1], + irregular_striding_err_msg); + } + CONDUIT_ASSERT(matset_ptr->allowmat0 == 0, "Material " << matset_name << " for multimesh " << multimesh_name << " may contain zones with no materials defined on them." << From 0e662f596d6e9d4cf5053bf5eac73edcf499dcde Mon Sep 17 00:00:00 2001 From: Justin Privitera Date: Wed, 17 Apr 2024 16:27:24 -0700 Subject: [PATCH 42/56] notes --- src/libs/relay/conduit_relay_io_silo.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/libs/relay/conduit_relay_io_silo.cpp b/src/libs/relay/conduit_relay_io_silo.cpp index f8e229f7a..e11dd4c39 100644 --- a/src/libs/relay/conduit_relay_io_silo.cpp +++ b/src/libs/relay/conduit_relay_io_silo.cpp @@ -2044,6 +2044,14 @@ read_matset_domain(DBfile* matset_domain_file_to_use, } else // COLMAJOR { + // I'm not convinced it is ever possible to hit this case. + // If you have column major mesh data, you hit the strided structured + // case, which (for now) forces an early return at the beginning of + // this function. We may reenable that case later, which requires + // filtering the matset down and is potentially quite challenging. + // The only way you can get here I think is if you have column major + // material data and row major mesh data. I've never seen an example + // file like that so far. for (int x = 0; x < nx; x ++) { for (int y = 0; y < ny; y ++) From c7eb704644cbfa1fa116dc487b816c4571ba9818 Mon Sep 17 00:00:00 2001 From: Justin Privitera Date: Wed, 17 Apr 2024 16:59:35 -0700 Subject: [PATCH 43/56] here are symlinks for visit --- .../relay/data/silo/overlink/c36_m5.silo | 1 + .../relay/data/silo/overlink/tetra8.silo | 1 + src/tests/relay/data/silo/silo/curv2d.silo | Bin 0 -> 60924 bytes .../relay/data/silo/silo/curv2d_colmajor.silo | Bin 0 -> 61028 bytes src/tests/relay/data/silo/silo/curv3d.silo | Bin 0 -> 1553200 bytes .../relay/data/silo/silo/curv3d_colmajor.silo | Bin 0 -> 1553632 bytes src/tests/relay/data/silo/silo/globe.silo | Bin 0 -> 171996 bytes 7 files changed, 2 insertions(+) create mode 120000 src/tests/relay/data/silo/overlink/c36_m5.silo create mode 120000 src/tests/relay/data/silo/overlink/tetra8.silo create mode 100644 src/tests/relay/data/silo/silo/curv2d.silo create mode 100644 src/tests/relay/data/silo/silo/curv2d_colmajor.silo create mode 100644 src/tests/relay/data/silo/silo/curv3d.silo create mode 100644 src/tests/relay/data/silo/silo/curv3d_colmajor.silo create mode 100644 src/tests/relay/data/silo/silo/globe.silo diff --git a/src/tests/relay/data/silo/overlink/c36_m5.silo b/src/tests/relay/data/silo/overlink/c36_m5.silo new file mode 120000 index 000000000..2cc79621e --- /dev/null +++ b/src/tests/relay/data/silo/overlink/c36_m5.silo @@ -0,0 +1 @@ +c36_m5/OvlTop.silo \ No newline at end of file diff --git a/src/tests/relay/data/silo/overlink/tetra8.silo b/src/tests/relay/data/silo/overlink/tetra8.silo new file mode 120000 index 000000000..97767bf9e --- /dev/null +++ b/src/tests/relay/data/silo/overlink/tetra8.silo @@ -0,0 +1 @@ +tetra8/OvlTop.silo \ No newline at end of file diff --git a/src/tests/relay/data/silo/silo/curv2d.silo b/src/tests/relay/data/silo/silo/curv2d.silo new file mode 100644 index 0000000000000000000000000000000000000000..8f7a2d53d3b9404fdd12145ec9f350f106ce0325 GIT binary patch literal 60924 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 literal 0 HcmV?d00001 diff --git a/src/tests/relay/data/silo/silo/curv2d_colmajor.silo b/src/tests/relay/data/silo/silo/curv2d_colmajor.silo new file mode 100644 index 0000000000000000000000000000000000000000..e34430e48dea918d4c3fa3be6696a0118e4c9498 GIT binary patch literal 61028 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 T__4Ocxu5^*xnXfWfPVfLd^^C{ literal 0 HcmV?d00001 diff --git a/src/tests/relay/data/silo/silo/curv3d.silo b/src/tests/relay/data/silo/silo/curv3d.silo new file mode 100644 index 0000000000000000000000000000000000000000..9b2571a53f80bc1f1049b67dcd1b20e497fc0f65 GIT binary patch literal 1553200 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%0D8XT3L?X9=$z!d-V3`?a|w#w?}V}-rgVaT;k_g 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≷~T+(%CU)5*JhMDhv4mVpmMViA4 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%0DMX5=w08euPpsooiemFE)xHl<4il2&H(B1VYLD zWF(>VPZ~ogmwp*XC{Hb#OenqUJV+=P@0ms@kr^`xW&M}42<7L9IfT-4@;pLW^V$MJ z`LXn3LbzLZdYIKGTfx`nSKl;a8K70Rb4{0QaXiV}pfyL(we*?!?>LU}&3 z5}~XaT#ZncR;W!V55EygD3iw5CzO%(8xu;*M-hY)IlmR5bm@32p|t+aB$Nhgx)4f@ zyYC>Bn~L`&6yxPSgmQX#e?mD?eGspQjd3V@Frn;yFpf}mL?jZ*#^XtZvhLBbgtDyL zL_(Q!ZVI7v?3YR?ZJwV_C=n%R5lX`$vk4{imAQmcGiU*!R35pAP|ClVMku9fEhQAc z3CjrO{Js^0a;APdp?sUVico%8c}bz91r;NdHHS(O%9hnP5z3o!L4C5% z`FU#yp_F|fj8JNJX+$VZOE)7F^^Sv3`YdZpD8mMIB$RP=I}^&RFS-%Rip;wRWkX6N zq5QL5KSIgAd@rFK*%?D9-_4ID6r=BOLJ0~^B9y=7-bW~{)=wamZX>1;O5@;ELTNQ+ zI-zvTok=L&I?X1O-s|TQ%7Bs!2xaJqMTC-=lSU|GBbE}%l%>lEW#&&S2xY;&>4dWE z)m4PDy3XT-Vi*RYI8b-RS z505nrr?WqgH4KMi5RWws!x+M24I?Kzj>j5Cx+{^#8ivz3n#UT3!;!*c4Z|=d@mRyi z$)3t%4JDOhV>lc$_}?2N-8GAU#xM+H4v#eqr*j_vd}HKfFW|9;;czVGv4)ZETEb%u z!!RD@v4-JvF6Xg^k(0fW#~Oyi@edwr80oIZc&uRO=ldv;t@&z>C@)w5^EMfL32aZx>cc3f1?o*fs}vuDRe z_3YVkQ9XNhTvX4V9T(NJXU9eL?AdWqJ$rUsRL`Cr7uB<8$3^w**>O=ldv;t@&z>C@ z)w5^EMfL32aZx>cc3f1?o*fs}vuDRe_3YVkQ9XNhTvX4V9T(NJXU9eL?AdWqJ$rUs zRL`Cr7uB<8$3^w**>O=ldv;t@&z>C@wbq^;7q!-&9T&CMo*fsp)}9>~wbq^;7q!-& z9T&CMo*fsp)}9>~wbq^;7q!-&9T&CMo*fsp)}9>~wbq^;7q!-&9T&CMo*fsp)}9>~ zwbq^;7q!-&9T&CMo*fsp)}9>~wbq^;7q!-&9T&CMo*fsp)}9>~wbq^;7q!-&9T&CM zo*fsp)}9>~wbq^;7q!-&9T&CMo*fsp)}9>~wbq^;7q!-&9T&CMo*fsp)}9>~wbq^; z7q!-&9T&CMo*fsp)}9>~wbq^;7j=zXJ1*)PxprLCHFE8^sB7ffaZ%UEwd10$k!#0A zT_e|yi@HXx9T#KeIrT+}sk?YO9G<1&OY z*1VZe0@hR{l%)Z|gmR0sCZW8zD}+$GhKCW#frSkT#d)d;q5Rah1)SBb3WiCJ{FP~2+op&uH zlzBfdCY1LZJwhlSZaJb*nicz8q0EjtsZfrt|4E^={qd4Q*|j!+P|oZrNhl$w%MwbD z8i9l|sY@k7SwAwEP~KfylTa>h4ZvlXEf%WOv| z5eGUFO7t(C2xU%KH$r)_=beO-H@+94lv~}GP};rKpHSjY4kVN%RR$Bv4l|ZezH%lI zO6A2P38mA^$%K-0G=)%HB_|Qe&Sp~yf>2JUmLZg& z*z$zZylW7l^sZlpP)3)oPACh%sYNKy<##zZ_VqD^@^QJZ6iTaRrxZ$BpR)?(>y%3hC3LSpq4bU}K`2wd zElnuTJa`kKe0XyOLMgVU5}`D25lkpCZ`2@^`B8NUW!s5RLiv0`JwmBisu7`(s|leb zHEK>M>DyZp%G*8K63Uq)?Fl6`SrJNaqYI%-UDAzEo({c}P!4YCNhkqb`w&X=gHePM zlQ4i#=ARoxDBI=^CX^G^WBFQK7(vfD2}N}rK`2T4k_aWN$FmB>wQZ9^DIc~?p-f+r zrBKfP@}@!=HDa$q$vgO-Lh0A#kV4u0LastFYo1Ujo9BF^Q0o8qy+TT# zbbidAP#*Z8IH9~;x-_ADugVch;|Vttiu1W3LdiH@nNSV~S0j|ly=xLm?^$&SW$yM+ zLV5H1FhaRhzagQtjcH6MV;^lsD4Ba&5Xu)uYeK2prX8U~Cv_l{rN;SP3=J_uWas3C}+OxODIk2^e2?y$;%bWx>~CgN|V>uD3oo^rxZ%(vY84c z=Y`D*<({Z*3g!5@9SUXCV{a&wvz>M;lxd&uQz+%;?^h_xn;ljtb@v}tD9=thp-@`a z`bMGX*G?&vyPQ8MlmlfhDwK^M8iexW(*cBX>(mm2lG>{@q3jGPM<``ZmnW3oJ1Y>% zvK5sG<#1wELaEcCI-$gsuSF=&2`LO3PVcgfjV_283cY3@4Og7n=}D_k9tB zvM9qrDEr5@CX{Mj+Yw4k)eeMW7^w;+CwrzsNq5avC{E`hh2n55RVaqBlE)fGPWCDu zYZ&RSH9XcZoX#hCtYJ7D&+=HqFpP~n)-ZCiU*xfdk?wkh#~OyynZ;ub!{K;?#~OxV z?BcP8k&~UxV+|wS^&XEk45#xTk2MU3<6|Cc7>1F@V+|uG`!gPE80oIBc&uSKohNy$ zVK^M8d8}a=#yK8q7&+M&d90xrl#`9&a0C!ax~n*$7)B{VaXQNoN=|k;9%~p5$IU#} zFw$KWc&uR{+c)4s2ShP)56+R465;J)=+_AO5^TX%o3wp}gB>t3nxG=bsA2uVj`& zNk93TLTPbur$X7gW4A(yeIiGpTwL&;LV0xD0fo{e`Xhz%R+l3RB_{lsLOC0BLZK`^ z|D{4{aQt5iMepWw9YF5gbVi~4u=1QjnU{J|p;TyT5K3-+e?qyxO>KoTsNO9Kr9
    q0H>qSD}n;-(R5&ZZ}Y&^lTHOP;PBCOrbPzI2B6e<|7nJK(o;b@a%upy4I7^`vuh`#239TMuqNut~6D7LAXcJ{} zvk4~3^41TUC?D0HVWJEVoo%B06gJ;PnbmNyiBhr2QWIru#0nE7qUAqK6syhSCQ8rt z>r9kS%%@D05uKkkQGV|Ff{8M_`xXHSSXr<;{s-m?(Xxd}E>RK#qVM0XYJ4 z1mpTgfum2DXuHW18(^@3KQIkA2BbG5aR5<(SJ@lb^8&em@`<$%e2oES1T7i@c|9 ziKIgq)5%nf92Aaetm8_3*1<_0o1khy`(4PTmK<4fWSJYtIhtd5 zekgMTnH$L5K;{NAH;}o3%nf92Aaetm8_3*1<_0o1khQ=vcz!5z1DPAh+(70AGB=R9 zfy@nLZXk05nH$L5K;{NAH;}bFck%pC<_0o1khy`(4PTmbn4<*YW%)k_};! zBTJ4fIkM!)k|RrwEIG2|$dV&Vjx0H{9U%l*hhgk|RrwEIG2|$dV&Vjx0H{sVswOmdB)?D)m&!6(v`cTv2jG$rUA6lw6T>BECsIRdPkik)@s* zA@x*QGl$2(940xkTmR^fIo+B9iJ0XyTFv%4qSCm{)*3*WE@i_RB zNzNoWljMq$D@v{?b;Tk4`(O-{Tv5(-*udjJaz&{t$~hjABg^03rJky}rkuwN{=Fc% zqU4H_D@r|8{`M|8vgY3h^0#;S+xsaV2lh>JMadNTmK<5;2B|zhn#0nVxdeh69lpl zCb^>Iijpfzj=X{23pTQCOmaoZ6(v`cTv2jG$rUA6lpHydbAlmk43k_@az)7%B}aaj z-wO`0JSMrK#yNrX97sJ?^ElYWBv+IiSIijpfzt|+;pT(ebr4kSmGIx^=(e3Nw!_!y8}?_F|a=}#rDok)%>IkM!)k|Rrw zEIG2|$oVO< zUditT8EgZSTv5(tI>h5Zaz)7%C0CSOQF2B3+dJ{^gGeU1qU4H_D@u;6`Mp5S_m>>` z6psV@Cg(CqpQa}1)0BFu)Keu#mK<4fWXX{wN0xPCzvTQ_a%7$$aBfH}m1Qu^@|eNL zM{*{~nIvbDoJn#f$(bZ)lAK9$CaI?q=Z2{)gK3t>Bv+L4zax3RAi1LCijpfzt|+;p zX@hrdb}7Tv2jG$rbtMg7f7$ER9LdBsr7hOp-H6&LlY#agLPAGMHw0 zOmZg4nIvbDoJn#;IS+ap&leTmK<4fWXX{wN0uDfAXzgfgO7pa$dV&Vjx6WBr}7+0&Sm1it?_Z# zz&0|;6(v`cTv2jG$rYvNAdhopgMTLkvJfUYljKaPJPsshlAK9$MY&$hA$~8ATv2jG z-{*=?we=^2Hv>u%!l(Pn62fIyAR*L_uS^K-BdQa^y%%Z|!o;`!MhK5CX+Q{@qni-I zJM~)-!pR?66GFMH_Jq)IE)hbvsIG(%SLaSbnErJyLU{b8y9r@O>HtFc_^!Vb!nrEL z2%+k6Cm}d8M-oEcNn;2hxyyJ$SWy0cLU{J02MHnW{b_`7y3I^NnEu$qgb)}omk=Hw zKA#Yp?psI*I~-|*(9`t@AsoK+C?O=qtssP-ce@B-PP0{nP<7dALO|56?3g>beuZ$H z>sJUydLSXx;`$W=qIPA+MAWYAm=m~ug@CAC*)b8dD?8@9T)#s2mg`ptWx0NZ(17b# z2wk~;g@CAC*)b8dD?28lc4fyz)UNE9hq-=*fT&&BF%h*ZJ0_xbWyeI+uI!kI+Lav> zQMsJV~xqgLEnd?^wsQT3#T)#p<)UNE9h}xAM6VZER$CUb&Z|Yg7`qd_`B_SYs zuk4tJ+Lav>QMQ^nfmW0rUYe@*G z`qe_NB_TY+wIl>o{R+{0WyeI+uI!kI-YYvMs(yv&y|QB>davx5h}xAM6H&XeV7}t^zQ1vTB@0A_1sMW6!=MLI2i(36^AJ>u) zidy{&(R*dbl=_u#>RGF~7YZSZd!Z0M;r=TGMD5CsS%rI{5D>L1J0_xbWyeI-uNHCr z3Zba=U!m$(h;s++n26phJ7!U?0*RK!|YhT$hQRfdLYFBnl zsbBeKJuF1;l^qk&du7KgYW-JIzw*s`SkiyxoAU{aTK^T|+(A1gs{abH_LUt|>Q}y5 z4@>&5e6yZAYW*uz{R-88g{og6*1oc17PbB>Ie$>ip)>c4uD*HTA7oj-``zd~Gl(2gnn zSH9`LLag0x$CUb&Z`N~{`jv04M_AO(A1rG9S5m+7&GiUT{Z~@I`fK-JN&QOdSKc_s zFoV}pN09!jzjptX)UTv|<&FB5)UN``7d5S-R=*m+*8)V4`c)I3T$51FAC&V4y;0*r zt>2ESUuE$6R|tr;uk4sbt^cYrpUa1Us$Ze5KRAZ31&Dy^zmoF@rGE8Ss$U_lJ!r?w z;5X#(J=rI!|LQQWMTJn**1tm4ujKqe-&~JS`mdyZ<&FAPQCq*=8|Mak zvQJd~s;I4hCF{5Q=6Zy({*`amzw$+|)zxAYwf-wv|Ehsc&MDj!-GmUNekJuQ>A&hl z*W0({Nv+8n+o0-KMXmoz&L14(lWP*9u0JUKSJQm5rn_8!aIR0TNhtNJH1eeWbiLaY zwfa?2JAY8tZ}<1fn(jLTN)iI9|4PnN zXvakKUfD5=TK|&M`fJy(Q0uoBwetsOH}lPU zS69YCoj+LA`mc`dD@zDh>y7foW72=+oAukhQJeB)pQ!p3qW8*aShDLY+TY)cUWktSkBIb5#Ax z8*8(BvQJ;Eb#=8Eh}xAMQ~Iy|+Wl8WZT%}${YuUs^iBOr*1wYeD{u5)6}9sRf37l& z5UTR}?Fdr8@=g6}LHYX$LDp}-{@Ro$=Oj*h>|sI(446v@sQMLZ{r2lU5Aa%FU)0VY zENcB%sPhL=^{b-ReND?28lc4fybYW1sE`TBzhQomY3zNl%P z+wCF*U-esE8As|@a{WPXoKILgzA_=8u0M#XUlq0ftId2aAA&b(Q=aVeB-gJH5Vb2i zrqr)|Q@^_2b=a@<_1>sWd9u%<)_;Xs{|Z&VLS27Q>Q{38L2uN!Jn5D4#x_?wx6c=k zdE?wbPxgtbUp3vgkPxo5f9iT)*VFX~A;|fIF7m}S2~qu5RhO+Mgre4e^(mjrhv1Fc zlqdT{)UNE9MXi42$y!z3*ao%!6{2=!$3(4vg{WQGG0Sl+2|?;tQor(~9`$Fpk@}Ty z*1wYa)fngzmn?@dgB~JS^rAbzw$Lz|g|fI5HBlU^xrY=i2*Lal#=ICs#FDd!K$`GZ%U`*$Ngugv{d2&n$6Hv&o$ zf;VbYp6nAjUfb7|J6e7g+h?^+hzTBZ}e!P>Q_ar|4QmtzNugN;#|S2#X$96$@*8m zS^r9|KPcQ|`#t8UzXg>YqGl&?NV)vs2+bT=WO z)^GphuD=rks(w|4*HTB2`jv0`uNHCr3c-_JDQ|4!jovCx_UXyFe%{#TYUlR(;xTXZ zR(Z0|qE^3>{;NmH7i(e_wetsWL@(8~>_h5TzNue%(ktbSZBYGJwRkNm1X=&;Z{$h+ z>3X+Wwxj_e6t(rQq<%%@i<%aqc4f!(RlVxUIM;g~;I+Q~Mx77%XZQ1J>HtFcnCn*v zsP)?swJSTO)USL~ze4n0*)gU6$~XO2zNl4QErutxCU0z0)aqB)x_;I5zE1kDeA9o0 zxb~nO6V-o(Si9Yhi8_DqO+J4R!5g(HPxdMGtH1X8SJzvIy4KgD`ma#+D{riA<;gya zTK!7SAN0-ngV$Pry586Q*}Bt>+8%ZOAfk3<$1G~~E7bZ|h;s++m__aUL2vX{d9qJW zdZoOvjW>F$JlW^fYD>O&%p0{SPxdMGt2Cdi>5i&j$@=ZSx&Gj_^iJJ~*P!~Zo^0z+ z2#DI19n+IqlQ*{URppIsq<+17uBJxE|OZu;R z(e>7*JbC?%sz?3V{Yd@FH`gCT^aX2@o{8{2rJHs#4aJ*hQ$V;f)8s;(Bp zlUkEEw#gVipAcmIt4DmYrn@iB6}(yuIe%~!-H7_sAMHcxSH9`T@oj)k`E2&?3Qjhwx+erP&H}xz}YE9nQrl{4guD6fs zT3;{ySJHoV<=Ry@;&asc?SHm!>PBsk>c2Y7=kg)Q`GX_L7d0(8f6zDABlJdZl_&eW z-a6E^zTT5slQ*{UMXl;;F+8a?d1D(nfAFt;{@~SmqkQq$m31XweeR1|)zxDBQJ<8z zo=4TMWc{nMJ~^lG&(@u8)b>TKe&vm83wpB8YhAzUdS8dCUu}wRLI~Gdf4bh+N&QOd zS2v%)hIoHn{+uW#n)SulCs(w|}`mf~t!7=2EnpRO;|4PT0h?)vr+N zx6Ap1a{iz<&M~~QuH>uFi(3B`;@X3DOhoUM9kZzQU&-|cW&JBp`lH}=KR59 z`^pl+jq0cRv-^?$tJ>sA|J3zvbG6!%FCIhHuMoXgc1)>XN&QOdSH0+Z>t27hZ|X*E zFY90ZwbyT#`jynL{%mc^lkKm!4t1@s_oUY3jct5UtGZeYPijrx*hcDCf9?9!)q10R z@mNvozxty(leeBn)vplO9<*bku0M#VUD+|E|LU*Zf8~v}tvuOhQLA5F?|FdN`g%Ek zP|hFpMo-rD)}gNT^*5>>^=J1})XpFLxymp?sLJcNBS`(qH}$Ip|sI(446v@sQMLZ{q}1;|L=NVSJciQys|IKSD*W$*Xn99Q0EV#>Q}PzjJjm2te$Tjj|%T&+e}$@F`QloGSBoL_E4lvQ_13Dc_Igih zP2Sk%YUlR(;xTWW8|cYCQT40qU5EWzUw^%QRM-0Yo~}m-!58NWUM)sZJAV+>e}!7V z-OmUx{0zeg3E=-v>_-fvA}h-#3>`H-KBC}b`>~tZ=(xnB6uz&{ejQ^pZ)`ta`^v}T^B*=s zuXsFZ=!oR8gOkU`#gFA@`0Kdh`Pj*^qvH9oMpryOE-}e|*l5Zsgw*-p@3Cqrqv}Ka zSe$!<7WvOBgAW7W$Y@#CXnC2B(fE}3!6{?nW0RcuPZ+oI-LJoo*!a=o;>RYAPAGWV zFt2#t{p}l_`EmPeE642P?>_whwy*n>N5|X8q!;_6ZIcp54^A8%7e9%&wQmF2AHHVj zq4E*+>0q+g|OM@}~LUqkSJce(VH$tT^I~W=-uu)a-8&_N`_9 z5wMR$#)5*adEgl7E*ClSZxt!{;^oWskh4cf1NVI`zOT>h`(NL#cwF&wZsm-&`5XEf zW%$?^jN`%MCXdM<6#UGu|1Iqe?XSqb{pkL{&nQvwnEUhMybt?Be}Dh{M*@uC{LlaM zad5{EDA4kl#P-+qR_zUtQt*%H6$s-)0}XN$%}FD|~_b^DFPt+z*sm zRIr~bpD+A;jQfKHRSF-u`n`5Pe6hsUpD%oLrTc?B9{K&a+uYmj|AYM%?)OIB%lmV| z8@RkbU;TH6x89#y718(SI_@9bhVJ{q&kOIbeEk33pVRi*IgnwjPxxg-#gs*E#3F6-1n{B_ifzwZQb|n3htXnxbK@6 z{LbYdk%fV7@cqfv7fggY8-FknYQ%hPB1F25n+PFMADalzOy6%Jq*UK$B6JMeWg>h$ z^mP-#@ZVt~H2c@fCc>lDx0nb&&3@iQSl{d!6Jg*@PnrmwYppdAjtp9DA_TmZZX#q4 zSYaY8tMI6aaOT@I6JgUg3r&PKO3gPB?(8zhL|EvWWg?s|J;OxsTbgPjyzHE6BHaJM zL=)kM#1s?Z`})ZyLPFV*Cc=F|2_{1K_OT{Hx0!=YgxqfinFuS#3@{O@Rp@6T)X45* zA|z(?G7%89D?4Uwu3sV47XOD}*Clzd~5S^(%xLT)%?&ANJk^tg5Q*|3?MN229X2=OOVpp{8gK>~+@O z2M5f718R<-fq!p+U3qffTHq9Q@L5wJUZ?{mPm7 zSg87yqWTpARQ;+1)she(=3cQAQM+R2WvX8xc#CRD2&8`HOg#%#zuHB$Bm`2wa%Mgj zs(#gpYDoxC^{YIpB_Tl7uMl&u*ol~X#ZE-+ik;`EeuZE#)she(YFF$;)UMdsf$CQX z5Vb3IB5GIcM9jTnCt~guJ0GF?6#`WK3bA&Ooza zy<%r^t6w#tS`q@p+Cg?AYFF%(`B%=&$3o1#VrOxyU%fj_cytH-F8gx~_zk`N%~Ua=EVyJ9C|?I1f*^((~OD|RB*4zd%mc95N@`c)OGB_Tl6 zuGo2&*76}h)vt=%{Ht12OF~fG)(;}~zGCN>w3ZJ6YW<+hzj9_imejALepM7TE?Gb5 z%zjwKZT+Csubi2WRov!ZA!=9bMAWX>iI{uE&f-?TLhOCTPE`G>xXr(+M024Kpypp8 z_P%0gahrct-0D}T`BzfEa%MmG;x_*ZRlh23^REzV2ib{QKZw}7ot?#P{*~0Pq<&Qt zYYe4+CH1SKn4=~2D`(adO8v^2{oE022iYm}ubkP>9WnQcov8U&=V<;F0-1m1%>4)v zbFbK0-0D}de$biyu%v$V&z^tPi}q4SP~7TQC222p1c<$_*eUy8$^KVGQR9;JgR*|G zC~91&`B$=j(3$%Y%KR&5_H&o@gU+lcMAfelYX{khs$W&5{jU(9_HUQ^)#r}flThkc z|Lpn|YX5e`y$9J@+}00D{mPkomejADnSZsK?gfaTDCSlb<~57k{Ra_yx3jY(-3t%_ z;@*SoMBICjol?JYWQ_av z#t?DuL3S3m`B%lQeubEO#ZFCY`4FJi4Q_>~ zTL1sK?j`$QtuUO}6HE5LTH?r_SVgfndtqKv_P>()Rbl2+-Rg6SVr^hyUQ_B<&eX4p zVsER$yk=o)O-1n>sb4u$&nj;9E7bm1QonL${#7N~iwXg1|8~^+!D{d283>%HRTbnw z)vsj!mDI0_qJAa!A6#QNv8J%N&A&p`uMl&u*om5dRhU{+Q9K7#zgkfCB?H05pPZ>z z<@d<^E17>)6l)CS{(}>ZqNrEpe@#^V3U&WM#M~=(7PtCUaa%vQaP-3lg8S(Hg9wV- z`oZEh|Ee18e}w>5ze3HwN`G>nfnZ!EXX;h?J+l9m?0;1h^{mUZ7Zrlyw*QsXuU;{V zqF$B%HD&#vGxeewO)HvkWKZ zWTEO;MX@%pFt3TKU!nGIN7Sy^iKtz%6IH)Ltsg9I^()l;E7bm1sQOi9t|cL${jU%; zEY$r6ov2k6Q}{W|0~432ib|LUlq6YgNWJ{J5l?$7gSqv;xSbHsxs|=g#b1G z3Q@aaXHnFq3iFyy)vNM*5Oc5CSrl`t3iFz%`c-49B_Tl7uYRVrdQ|`sgOAf%J_M-sgT-zBm8>6(Gn`mch?;*T>j#~wUkzW{#X#W1T&sc{h`q1a zSx{}siN~aVwa<|`Sx(if@_P__x3jY_bES&nIjH@w5Oc5CSroOY!n~&JeRlh>*e}&k)ot?$4ekJ?2 zKWsQr(~|w$yBJRFiB(W-$%)5M>j$gR`auL;Up{IeK+V5Oe{!FJAb(xSsh^A6{3}%b zstMJS5TMo%qUu-0ZT+C^-~P|uzx}1MFBu5(&x>;E=i)a1>hG#Y-RkQ|{pz1xzj}=3 zUm>_adr=`k&A&p`uTcA6q3%EE#9XU_9Ei1p>@05U2dmItR0y1?RTbnoyW}?m0jhrG z#9XU_9H{wMwP-IY1VynnurRObRJ|&{2etpzmjgaA5IC_`upo!bzk0`TqNXMFt2YfN zYFb4xx2iC&>C}3`{GP(B^(%_!6lSeoQ9Q?~dR2ap)URe4w_1z(-#lOHS5m(!iux9! zcE!%(HvbA$zbfi|uKw!v3#u(S@mO(NKUj(GJ%~W|Z~x8sC(pkssJ7(9W2pKSYW`JW zYE4D)ocwhqr+!A&uZm)AU}0VpHUA1#zbbC?uik3B!a&gbZfDjD=Jz1(J;=_&)S8Oo zIr-~KPW@aIwW-3qW>M6p3iF!z>q<`jEcGj?U)}1x1q<_h)c#kuy6*4qdVW#VrV8_# zQokB*I58)yxYe&v>j!22Rd>UQHHC<|SL`fkZj=*`oum0z2xR`%QNxLv7GmxdJBy+= zRhZXws$P}fBkKn@8cx)-q<;0!o`2Pg>Q@MgTm7o!r>__YWdAGK|LX7F`-)%he=~RL z@A!XY{*}zXDoj1Q_#z6)eb6P;JSH$E1EG^{c|H2fWqiI5F3%AV+bV ze^uP-SBSY+>~vzUtAZRwu{N+UuZfs@#ZIYTIa9xq^@Fm0urTWZZ}mB-^@FJT)veB( z`n#TA-0D|D=w5&boS17>kfW&QS^d@PxAVmr2xR`1Gxe+Dwtmowy{-y!pypo{R9kZ5 zF(+zO1vzB@E2&@IYHg}8&o8L9g6<9I#AB%XRZ*-BEX->bxA|92)vNM*3Yr_`#AAh-D^(QF$zNA;>gS@U zO%>)f7nFU;K#;$#=rq)yx&&gj`a_Z;8%#|vN=M=@-z{0$yQ}eCz zdu0CAKYRYwh0-$&1jVg>H6?qZf#4$5uMnW-Uk#?YPzX@_w@05U2N83x*jdor zC?_71`B%RgPRzfmLF)$*6tuR_iN~C%RTbnw&A(bf^REyTX0B9GJST$cR|x*Ddep7H zPC>OLCmuu8uGsl^)uV3pbqcC2Iq{g(uikWIPxqpzO%>)fovK&m_sIM!nSXVwbE^vT ze5dAH<@d<^tLcUlH7(Ts?TFeHI}x=jcB1N6sP%)zt$yXiT&sc{sQs@{^{dKdyBiQ_ z|0_fd%ZYmp7UXcER#lKg=3gCkWKLF%*slx(h}so9i=sAFnAa?B>j$0K>#86Js($rS zr8NcuCu&s%IS^|H+3D1J!TcW7{_WrNSz;i_UsrPK=c1TfRhZW-%v`CWcuxL#QBM6_ z+~!~9uPZtAv(&GgsbAgdzUzP2^HKFH#M(i2qSgQ@ErP36R6f3-hVQ9q8j_aHk_ z^{e8xeh^W+Vkctnc6R#tO}9}Tc`eyS@%39%Y!verjJHv|T5E)jBJ4_njbh*GzBY>1 zn`3Ph@sm5-DCz{Zw^2Or(>98z5)n3v>C;=;DEv#dv{3~4!fg~6{KIV&sQ1iX)Mw3J z)G^Lp)Va-GRGY?LR9nto)HRH~sB1TSQGFKnqWWs=MfFkHi|U)R7d0kfFKR5tUep*A z?fvbEWE;iP#uOXHecQ&_DDM7xxQ!y-7-XYZ@A=;y{KcHy{L1Wy{I;gy{NXFy{Ky#dr{YJ z_M-YM>_zp}*o*3;vKQ4iXD@0@!d}!^jJ?QlDAN7e)MOjQ?yxB~ifgIkY!qvk54TbD z`*om=;Rf zv{4}2wG9)KZ4_53OtDdnnKaHuu`PbMjpE9}fi?=grI(FD#YNjFlHc!Wqe!}D+9)P< zh_F$lz5bw$0$Fca{pbTWihgIB*eKjdO>7jX_sm|@XU$&ZagI8-*^6q^*o$h**^9b{ zu@`miW-qGG!d_HgjlHNoDtl3VbM~UfBtbKd3jpCsNV{H__ zTpwnmxV$mmMzMK%PaDNoTf5jO#(&+;MltuCvQZ%0wfa4iZ4?(5PqtCaoIch@QT5O; z8%6pH@ivP29edg+);`t6MiEBqnNVy_RW^z-J6hQ&kadUYJ)7Dnx_%U9qZkm@$VO3b zMME0}>OHd;^;xqQb&RtYd48kXH1?v}a`vLGVeCa+yV;BCv#=M{S7R@#kIG(D-<-Xu zF$sH7V=?w3$Dv5~oh_4X6v3gBZ4{%Q8)KtrKV+zlqU7TKHi}NCVr>*UyOWIqwI-Im z6=$@Cfo#{d*G;xjwEt<6jlxr9jE!RW`XM%o+%^4d6npl@*eKS`i?UH1ra8eV5`x>< zD3Igtk=2{nD602uXrqW)Q_n_`SJh>s7`U*mjRN(a*^BzD*^4^H*^4^2*^6w~P;EJT zQP(i`qORTSMfF+Oi|VVf7u82)FRE|OUeuU`y{NGmdy(T%q`R7bvW+5e*d!Z8TF7V{ zMR2XbHj1RheQXp#-J)$2i!$5WC>k|aHi}c(Eo~IYcI~6@rrIdRo}Xx=I8|qqjRLtp zvYXa)0rEx@y}fqMxa0qnKT) zri}vip4p4~tl5h?#@UNHx7mwo)7Xn_&r#Pf_M)!c>_zoi*o*3`u@~7-MfJ_uiyD)# z7c~}RFLE3Tb$>|j9faLyqK%^CwGlQ7)V(a&i&_)Q-f1Hqf`Qs|j=f(;w}R2@?WQ&g zWV_bjiK#Y85-ql1lNqhZ)6kmE7rdcj}Uh1w{P^_EVrSG7^Z z9jaubu-o2eqZqTgqKyLep4p4~tl5h?#@UNJzfo-(dy(xq@;ZjPcC#1NXJIe0zlQ9m zBKzm4F$sH7V=?w3$Dv4fjp(U1ind=qYNJ5i%YwbAHL>g+L-&hCarAhkjbb9ry+`pd z)z?tO(;5>L$aZbbna6Du$ot&CJ8YAt9Z4}*tJKHGk?(VixAjc(j9%^8t*hTYe zP&AlX#YT}BS;0mzze|XXqEpH}Hi{A_f@~D4`UTl2Q16+&sLz_csAHVHsB@dWs5Xtg zsJ5KFsB0K|k=JijpM|}sz8ZT`eN^_M`sVCKj!RHuG4>+Ip-A_aYaX{zTuU8iqo{Of zu#KYn?1ya>HN8jiibFA@Z4{_`#j^MK#jY@3p!=qxK+Roa@4n%6U_9QuvW;TF zfnXa2vR!L5e~OI)Iqx31k4a;??;8qaKNYz@6LQ@ga$XN|{O#xGNBu<%jrU&4MA1~A z$waZM^vO&VLBAc(L=kr7SSAY8duA`{vt}>q7-ui)+-5JTO=B;zJx5;0kk@ZyzXjP} zL-kSFi|n5x$0ew-7<-Z9P^5cz*c2NDa$PKP-#O%cQ<3x6?(bI1MsdgD@-_-j)SWhp zC8bK)D3I+Ma^KjRd4p^e%W^x}D3JX%fxJ>T#GKX24A&R*2H&0bWS#$Hrg&R*0t zjJ>F9H+xZi7WN|hYsh{os&CF-)R=_5$nh9*9Ex$GiYYCZWTM#B_LWQ&aiq7-ui)+-5JTO=B;rEoU$48pdAKwVS=DJ_~zMeKqzX`>DwOIdWWr8jGwp4TUwcJWuu-7qaItsYh#xXh%njd@iK5Pw%uE!4$KK0Cfo#{1_qj)2 zzx`+*aumpQRIoWc)ISf{xB>=p{M{pOb|#8b-zI0Gn0M!>OcYHc24$icx2kt0irwGG zW}=9m6P<}-#h7lHC{XX2y{ONcy{KcHy{L1Wy{I;gy{NXFy{Ky#dy&^~WWNR3Uqkj& zQGIjvBF81j@fdO(igfq;b)by`Szkk~k!0`H4o5Rl+)s5T6k{IzS0;++V=rc+$m={a z6GdeBgiI93b`5zQL#{9RhJOE!;xzS>QFv)Qh@x6lvrH7>cU8+ok+(D`6UE6Er7}@e z9(HLXifMaJY($Zed3Ym=x=RjjL=g~@vk^tmkNY;FK)q-7qCRW(qKkTted_JLKCW=Srmd-@cy~5RvD3I;iiu4}upcwFF?n)FJThCpI;z&l{ zl_4#LtjR!ude7`deb($n9pmgpo!jh1wQ1}{wdL$ZUBlRmx^}Y{)n{QZs;|ag zR3DYSsJ=OSQDYMJqQ+wEMUF#}?&yeAl_)N+j$MgjXkeq2C_);C ztVA&-s>DhZCH9@jK=H$&{TV2zT~jFN_e2T>{eH|qLBF3fP|)us4HWeISOW$99@;=b zzkfGS(7Xc!1m z-kw51^XCi{G;hy9LG$Mf6f|$oKtXf%3=}kH&p<(Q_6!s>XDpK7i6HIeUc0mv_F)Ag7(ESP|$v01`65-%|Jo> zzZocKuRQ|=>OHd;^;xqQb&RtYb#Aj4)uyo*)t0jtbq!-L>e|g-RG)>tsJpwP&C} z)sWeXY}e@iND2krk4m9HUcc%7V+sY`|4gBv`@R_{=zezw3cAmqfr9RDXrQ3`IvObG zewGFbx{s%Ug6>~xpg@kl=^nWT3gr7neb($n9pmgpo!jh1wQ1}{wdL$ZUBlRmx^}Y{ z)n{QZs;|agR3DYS$o@HMOu}B&Sd6{MaVXMF_sCT!=pMNW1+uP4_sCT!=zg^d1>Gap zKtcD&HBit!at#!8k6Z%I+R=-qLq3Cp1kV26-tb{@laQuvc0=4Fyy~uX0(flb2#o_oQg~Gk1mqO8a zVLOEa*4=MA4MyTcAL$qY9$^f>4BA zIcA_hy=V3!KX24A&R*2H&0bWS#$Hrg&R*0tjJ>F9H+xZi7WN|hYp6addr^IJ_M*ll z>_v^m*oz#8BHg>grYIDrgN7>fB~8s!d}rsx4

    Kevg)U}(v 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

    Kevg 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

    <9g%6A_u{Aj_s z7s*F&a4ejCkb~YX7TgC5C+^%C@~v4coPCgk?yeNv2MZ_e+}ZN2SuC7=kb_RI66k}E zPOlb-;iJ=Q1Y-F8glh$2_~`UHffznIohcB*N2jv{V)#BjF*G?xm@8PLjQ@fZx3*~~ z&-t{IM<3d*7pz$L z&0^u?*q<-on#ID&vAqeJH|lRqJGES!B0x^7a`jS8l-^VA0CSMU=6|7Ol|LqjFwrMBN z`LvTqAKG3MtXV9axc!y#(IXrSC&&Kl@^4HW3n$0^8}hALESwzsZ_2l3v2b$iuaa-g zV&UZ2e@nhKi-nV8|0DU{z5Dh&7A#?@h=-cm90{oW5}4&h_u;;Aq#s z7a<3o@*5BO;G!H{3e5#|Nbez!64?pN5t`o z{ZW5Eq^0)7X8X(E%UHly47IJQk+GS7|Ezy6xpiKjt>b?;fP_1DDt`lz~e(`~Qx+Gle48$#>- z9iu(gr+cnX_gbIsUAn*j^zW|hSgNl7%6%>K_s=%Z-9G6iH>Y-$oZ4rV9-aPmO1*OB zGH2-XwEgA&cJ=j{F6*oB*Y~&4Y0v*N=8wA1`}}Qx8Hbtw#c>$>KQjQob5#GKt&DAv zld-!eyGOEnCc9U%dnem6*}RNx-YnVX$)^1^Py1`0_SZb^uX);E^R&O_X@AZ0q`%DO zH%;D?h-FmC@31p11BPZYk4znYFYl9inS^fX`tWdEsPI(b0%3|US(qf8FZlbA=gB`; zI7c{J7%z+y#tLT%X9{NsrwbK?@#={2z6?uTfY8nCFBV$1@2yZ3402A2)hd{ zgyupsVK?-Uc>?|}A{^-|to%VUHaE)-aaFuYSFhjUPxLmkQxKx-fTp~;p zE*35lE)=E;7YI{?KYBhUYoC*Z^M#4R1mQg4T;UwyY+<}GP8ch2e?C(PTgyUMR4@cG$2LV^6o!tN4Ku5gU}Lg8}x=Lmh}cMyJ* z|ABCt{C+}LiLSlCb;|W+&b%@oOB9<6dqUGQlY2(BZUcS8zDR_ zf3fhU+MW5+WN^KK`*W^DU@cq110`tW9E>zpq0^`j1xcxYtV;iAZxJlSv=TJ>J zTNom|FR<1=RDD?^ct00w-+=3#Yk=?TRT4UBA6v^VlWihfPkr~7Jy*7)?1!@bWe?N7 zxc6Yu==JjVmF+G*OSVY-DA|$Xcgj8~-dpy3@keFf5=T4Nh%@K#19i-sh3Y!SP=RLx z{9h5|OjA4H9>sOb_XRf+PEh_fviHh1m94Kn2gsf$d#o($*uTpju6=O7#ByETAb&sE z9^$iQ?-oB=c9i&CvQLTkk)0&|nC#o)=>1x8bP7L6$DAi@s$&cjcxJ)hmL=yBwS%vf z%e{v0GgcN(RQ|TI%Vl?yZJ<7_WhclUC(AneWZ5IM5AN|;?gcl>FOcmiK1a4#{219& z#FxlEE#6mlviRe&tHrrSuM_9mf*-77&KEY*F@_5~Q(-+#&UCedZ zJJ}N1X0i>{r;Y4H+2dtDksTm=r1mj3S?*sq$uE@crTARgd&E1)juu}k`;2%$*(u^r z$i5@aePyQjs>Ivsmrcr=Rd5~$+=Yd;9KQ#Pv`rd8w)2X|8KH<|Dd_-4(fBD z?D?{tWIvT1D0`IlF)mr|xi`z-U$(d6*UR#K?T)fz#EWE~74I*5f%ucM?}~FToF)EN z;_YT{6nB-s;WKa(9K zd$jg3K3U!;C@T(-?W6b&vV5QXSlLs>@0NW|{O__;#h;RWPn`SrZ1J}fKSam8Rj8?B zj1qVz#}KSqukvDz7M;J&{g@{%kq7V-DP)DpMzy5%bqCvx$I!sW3-R6ljXf_ zzWmm*eHFh^cDeX*vZsj`%RVoDvh0Q8Ps_e9ewOSU@zseRs$(t`w$L$75qL&dpmuVu zP(E0z+-Aa0@~a8ml%Ffh_eu7UZKOVJWv9q?mi2CQA)*i~Iv+`zwC4?0w>$WX}*^Ci|lJ zK-r7MpOyVke4Om{;_oJYn2ve7u$7K6M&KFd{%R-ZO67wel-ol1r~FNY9?GvT`;hEj zvb(6yA+l3tyU2beJ52Ui?c=;;se5jfe~|3o6`v=&Li_~TGsW+f{fGD<*=gd>$$lh0 zUiJoY>InGZI_4chEgj=jfoG}*sGXdv)DC`B?(V`m`I`wnmES=2VcET9cU7N5WiOQN zD$8@r;j+hR9}|+jT>TfyKUnr;#plc3FMguzS>nrOUlJcIdx`k-vLB0|EqkN*`-vZ+ zV=fYE>lmjAJmYPxc5<#(KKMzwdk8tot0CS?wxKNFXKE?iSbf^dUL@O1mglA;WRKTA zCMJ7@y5Ans1Vxf+Xak{Wd zXrp#=u2DYtS-E=(<&?L%cyHMqWcj{To@^8KIZXCq+3vF6%8r!nq+ zfr{TE`=Izqvg5?>lYK>esO+WUFUo!@ey;4z;vXh{l#Y3)u(ghHhQKrM1JzE>waN$o zRPJ6vdF9my{{G-H=)-mrAw$U-p6nF-IklM+)PWfP+a`zT0C~ph# zzOp;X^8K`Y+1=FV2-!IZ`G?95R(zrC!{S|K&lbO5_BHY0 zvX_g$B>TDe1ljrGA18i{j=4nGR>wF?SS=i^c5-GaALIxvWhyFfOZoj|8_Dv0xqW1t zsn3zJ(`9?fu9Y1v+gbaVlI+#$zgT{I*&&MGCi{qZH`#NU-$JLs58h3#~VvBEn-TeXujOZlLjkf;1g%G*kQf7zX7`M%!1vdz`!DA`M8d&_<= zJ4W^-?c;)EuTlRy};a#Df+R2%%d{AC!rTmSQS4;liWp|N%N_Icl7V2}f>}9fjWPgx7Rkn-vF*Vt1 z)&DN}hszFA{0`a2#Cyn|C;pJ^8{(s6uM~e(_ABv8vbTzVmiVzc=G{VF9b>%ko^XiT z$@%xsz5E=oApJ$}!kj%e$=PF__Q4jP19Ba++5YnUYf5FkG;NLQmzwm?U++2KA2l9} zk*~Ht_jvg8o3RBw@Zm@sh?Z59jTS=Lu z?a!^N-~aq;wC+^gS1PG?|K;?D^FP{ESjw&I}M`C6E zOnvFI&9eP^4jw$LSJ&ZVc&}xv0ko6M?JYNF$bjyHx~p*5GQ|518$5DIS3V)iY;{8W zi2eh6)3TlLTc54)y2||59yz(n+d|-W%c+!YYfz?bVE;i~dygK`d(eo?E8bCUy9gW~ zJ*3;Uj`{2R_`vR?*Xz6A|3%-@aneP9toraeHBC9CF)fxj(e+|2GPQtXYA$_kSY99R z8Mg_&Z6I$?LYlo?Vp*8 zPUn;P9OC%Q?xY)(qqXQYrLr#l%_jUmJr3)mKXNNP>b>tqjGkQFs?sJ4N@kr{wC?ax zhyWRijv`< zzcuaKg3hOW-C;uK?+c#2Z1~Q%&if(D-R^w%-(lfX&2vxtqM-W``9JO1X~YY?H_fY><(d6dDs;a4A9TQpPhG!sZNZAyI~9LAzH;Hk&*%j-tx-7Z zkSFW*tF%R7=Jjux#hcJ%5?_&uKpu3m=_7 zyxJpmt7PZNJhPvQh0b^X^G|Dh#s}lq6f~ICSX6B^LpxJ+nw+J{}}$rdY z^FGSX@As1B)+5RldR}}znY-QBvHNGPpKl5>*HeXbeO1lQo3AI&Q-)Vf^H?G2^qcH` z!Poaz57ipK-H6Sz^Wy8t^XPWx`@WL7FXR+X>^GwF?Ta=me7ix9LmEE2Rd(KdJ$ato z?tJ&poL9xH-ZHOyo9w*!dh$HFef|4N#lo{E^nY_@*XIj%I`oz`+YWp?J8v1CeVV;b zntPs{@BSIPx?sP8DK+X2{3<(dnSMWK?~~@9C+EBWfrr*T;_=NsEqJ}lb9HW+^J8}2 zeEnEw=AKvQyZ^D*&A;X4u3x9uQ$BlrXO3Szd!ID-JY`-_`9k;a^Ldf@VNVW@qNzkPv&lSzWcXaHhInJQ6Cm~+Wez3}jhH|6!abAF$FR@_lk zbLy>qs~>Px(dE|`_I`J_%Zo1V_x$2UeQznQ_0`?=J6=6Ke_3vizDrvz&0q7;4msB^ zn3+Fq?x{`Bn_5ylt==_{H0f|z(a*i#x~IZ+3yL0WdilZ&kDZx!`ny9;d*<=U+1E2| z%*RK)F=1N%!LLtT{$;D;{Q0XdymG~l*W_Pt;>Nu$dT?3(;Ax9axVq)ddDZ3|P;+db z8AbgL+HOgw5ySGf+O$*Mou=Gal)LH0&91!pj^bJ)51MiOXVZ%(eA>Ik`}f_NKcUfq zcUSm)X7MqTW|up=;)D75`+w5?%Uv(Zd++owx9QPtX7>0e7IdFfb(a}MZEjvW_v9Te z$<}f2Z+qqi-&}BE@w?B=Drmgq(&EprDmeO^T}!g#=JESaZMr^|r|V^Tx_*|Y>uGtq zzOvly&UgQGeWd-YUY@R>)bI1<>H5lYx2NlCx%;QmS@iEo8{?xTAr@2EO)!} z-9KF)%hUC;JY7G_)Ah7GU0)^UZg;->r|V;Rx?Yy2>!&1LPbKO4%5t|m-~H3|u{>Qb z>AWhIr0c09U0+$AIj@Q(&Ub&0dwM@wp5C99XYT8tX2;ju?ap_9k9&GQTAtpY(tdu< zj#FkoKWE#W@BSY5^nSEFy+4(t_p9vrn7iHi?(cC=??)x+{i!6qUzMb~JcNs!A7;7Ro$vm>UOW$}{wqoKU`eVEv)t{@cYj|mo`+Qbm85#GB-Mvm?sn(9 zzwdj#emqZUy_LNV%-!yM_fK_1_ImO>rS(=;-{x-j_2&M*@A>-iJf-zkR+r{(cfR}k zKIiMn^OV+G>Gf<~V(xb5yT9*yzMec!X}wjF*3Bj6Zg;->`@ZMv$@7%fTP11TTw?Ba z=ez&`M&4t$@7%C zei~-y&F#*2e_tQIUVQ!ddh$G(yWRQj@A=Ebe@^$$ib=m!vh%dz`YL_Ck-hKvdh)!P z|K9cG_aDC>`TfcB;_J!W?ap_9zyElC{r;5cr#h(gxn!jc?_Zhcpy_>ho9w*#dh$H| z-usv9JJ)-jhxMpRzbt~>nxedzU*$KBV9?{j{CGIzW4-QVjckGroo-zWY4WbSt7yT8{@9(UJ! zt|R>ZWbSt7yT8{@o;SY_TQ`1xGIzW4-QVjc*X6GFTu1o*$=vPEcYm*+T$j7vv;O@4 zWbSt7yMHF0eZG;Go^Rx(=Nox$PtP~9>-hA1BR@UgC{E8eiqi9qy!3n{FFoJLOV2m* z)ANnu^n9Z@J>Mu!&o}bZ^Nqapd?PPC-zZAYH?prMJ>Mu!&o_$G^Nr&4e4{u$-zZAY zH}cZ+jiU5?BQHJQ$WPBV^3(H;;`DqYKRw?lPR}=r(({e%@ze8-y!3n{`}`t3-^fqT zH}cc-jqJF2JpWUhu8$4R57Xy->G~>2*H=NhKK?Y%57T{Z!}Iy{xoUd9`_n$3Pwz+B z&rwqSx8d`zbiJkbqwMD>{(Q@ycl~#N{*~&8f>aM?=P9kXvY%s_r@B6y@BXQdNbwDy zFZ=VbREHF%I-<~@Z>75Z|N8Ugz5n*fk=IpRlwb7bkjwVD@3Eru2drD$vFf_Q~70Q-yYiwtb*@ z&Ibz`w7h?A-sXK*J-g@EH~i}TwZStv6*um^D6iQIch7I$V_M#lb3eFst4)gX>b%pb zPpkH4=3RbEZeEY_cNDi=d1J3lH(r*Xf5o16cDVI|;wsx7-1w8+d-G51uut{XyS<+O z+L_a8Z$EcwUjHvw4ZXYd!n~5*H|a5X=-j*()o#1z?cHw78#wja30)r3Jn4Wji!H;b5>gf4-_4hh$;7%(q$nUc6MX#LS`@;O!_Uk=+SC{ACxopO?Z+0D${k&$Q{AJ*qitA5*PtMc-cL zzt5OH2TeNKI{Q6Uw>v*Qzbx?e;p@fMkFTfy?(1vA``2IV{cFQ>FTU?yJ$v8&vp@IR z@LV~w&hY#6pMCw+?4u7)fBeuFv+MUwzW$)cx~Fc>yRhlnv*vz&N8a2nrwpC(%!2%< zJ3K!0|Fm~DaB_|J9zP+72$lvLf}xQvA_l>(jh=(hRoafX+Tu2YBsN6Mjf4%s@tT!H zj7Xb}mwQKV)JEtX60&2Ho)a&%*INg{(zu3*?nRhrE?W}0GjqPbgVk(OMU(59&nNlr zInVR_pJ)E(`Jd-`{`0)-n|ZN%+05FlXaD~3mdzJUT(SJBw(88|Lr?hkpSSV5O=H#7 zJ5HT3EqeD!J1Jb7vOyGgfQUzqyZ#NSk3xJ{q@M~6LG?cTp;41Q*>6u;Zd$c$)OJbuq5w^#q{jQNlD zpE5t3m)hCy@WYj3THZaPsp`7<*MF2beOBfH|M1Jl-aWC(JFR`dxC1|%x2tse-A31D zUTSGwlbM$qtgA5dQcLSXHt~5chVzFt&s6gl=`Wgjsiham%5eU0Fu$c|r+KJmUMl{L zaK5nS$!Y$Lt~DP^@$rvznjdcF&B^{4zlHPFHP2M@tu#+=edo=6ao&!Zmulvvme$>x zd8xs=?{MA3SG6zAgVX#)%{$e5&&*3LuE)2+^)FgCfnVxdUsr*D!_z!dJ-08mPi9`~ zhSEROyiz?k*_-+iUu@rA3)c-QzS0N3$1XPO0!#a2SpGLPFZIsa`f1niUKXy~$1lTr z#h&^d#1F>ny&b4^B-$^-*M1jT7ZSwlNBn;Nx_*!8eO+pg-RAwk{$l<3a{oua&*=9W z`O)=z&iU_*j_!SUA6xU5HBXrMVLQsYns6Tx{XWG!Wqb6NjTgVveLGw8mNmau>u$6z zCn!&!a32x+D~{$H^mbpG-gw8Bw&pFXKcjUwq}Mu{a32w^muH@`bLEU(+}@MB+nTqm zbv9aeqjfp-GsAsEv|gTh%FffXS_@nC3_4u%mbK1C>jczKHP5TGPfWNzz&x*DA0K01 z!M;4$OSo^3v9HoTOuS#s`;hqtUs_jc+ON{SS)t$3v|qu#W~RRp>?_CkJDlI6`8uY5 z80;&jdH<$=Slo|rGt-|p{rSQ^lBU05`WwMMoMyaS+E-Qkgc$!uu&-*kkB{+h7=Ki8 z-(llFEWIzo-*b%r(D-|c`+A%4S7~4Ha34D3pEtj!6!&2_{@&8Q`{6#1=Jzl2du8#Q z1LKb>t!Kc`Z~Rf__vPaF`~ROmYTf5ym6gs0`uv$o#W^6I&zmcr7gYR+pPe_?y?7jC z;p1;#0{?yaygB#t!T+?szb<{C4$i46oDE$#0jj8nO}nkyJos|Kc|`i68>&8nbLV=L z&Y&)q^{L`PTU~s7!UpZoMWjMdE5S=P4{FESVkZa~Ry;tfi%*iUlZ%HfcJXN-Y%xz^ zZBf$%yjwiDw2MzYVb>H7{p{ki_H)+qrBuap)>4I9a^c8Pr8CR=7s^*z2wCenYv-R? zJkIk==d2AId4B2OvEn&v#dF7c75VjlQ5Db??vJlU9o7g%#_1(e8`)ivNmo5BTD#UY#xv{L1HUZ66%pRmf-a zQeMG-h4V-Yx+~_r$tJ}UKa1y>eO>26K3Y5`xbWGq&+*gwkTs>U7S2YNVz};%7&lf1 z>gpUEUhDj{?xO=OgaMuYh;!Zhg0ACrN@gzq$r>m=tqu8K@%>`{86B89@VDzgu{kvV zj1K-;b)eYyGyjYZOdT+Fz|;X#2TUC>b->gCQwK~PFm>SDr~_s{e6t^ZVF>!AkJ*oL z5@Z27q$T1A*8VBlPetn^v|gQaj2&GoT=A7&`={vpv0sX# z{Z@hoxOaH1aK%@8?VqCkRM>xm{QGF%nebZSim&uq52p39ny;vR)wOSpt`)BMO0WGL zw4aLR18cvP;CuCRbggj3S9)V##r^Ec9kU<4wY1OJZcgz2>>jR1(lzHCJF;8OUA2RH zw$cvlX;aqs^>lPjf$Zsv*FCYfO&z7zdqMWB^IG&=WKX(QxZ*4QhIUS!=^t)f{X;YU z+PKDFWD^} z{Z4~@3^>ol?1%3M``yq#^ywdJU(L`SWl!`ES+8gI!w>Y*-DmveY5!pS7oPUNr@tLu zD_rrFUhg^l8|Jjcsq-#Al#aDX0=Qv+1Jhvd&H-3BV9t>~*$=;Pp!dMC@O%KZ zZ|m9*cHXixCe68f=kU2{|55E9tMu#I4_5o}YJUg(8{z(YO24lCU<3Q<=WG8I{2RXZ zk5zivM^LYpoAV~hOXD$rL$xpM&#isc&3^bnzvGMhV4MB$)h==#bht0O`tiXz$e)Dg z@Th%KJD_XzOB7$}b>5Nsd-$1rwNHBQ>0056uk_j{-Ry_|z(=$1emi_lX8hIAYff{I zupL%^aLhqV+&^x++S_FQdvDF2`kuG;^_QjP6NNQUn_11>K3jjeV_^a1eg$C{ojGdU z@Ms{ltN)AZCrfK3SyNg3yQ`1ZTWWphI+n|RVcq8FLZJ%FF|9sbWl#wF{94b^!%Io5 zUH^_WXAd7ecKDc)qt7AD9)CyLaTg9BdSPPt+2@Z467BVOq#Zl*{Ncsu#r39LeYT72 z&id+zwNnpkrz;APi)-16pZy0GSJLkP{b=EHz(FiYEiNJ|MmV}KGih{z<*U}LDWLf2 zRa$LZ4DMO@JE#Xk$Bs{w3Q`FFye^CJmCqXf`1aPL4wxHcxnZwIE$XQF88{HKuzBc+ zk)wu>96h46$hiJ}4IgpQnPV=rE*TZb z9|2W&&K`gEsN%B=)*_eIFdtBiIWCB9T?8+NrNz{RzlX7)d)W9OVAv%A^}LhB|7XLz z&%VR&vq9^ApM87pqmy6Q7~V(S3N1ykoaGp9nN?Q$YtX8o^l|nTSesotC%pCH*!s!U zhi$giJ^mf1SFQQ!MOQw5-u*4_t@%;0g?r0%DS6tUQIU2vH-_E>` z@Ek{_m)~CFQ|5Jq{wwBvgx3mJe5IFPL;d~E=i~(C?j8EG6t4J6FTaNR`|^7P{rx^V zcgXlfN`3`%4(B)JcQk&H(!52Vc^{$u>%GFf4|7i7#iesAJmVJ`a@{Fs>=^ok<@aaa zpC9;pw(>RJ);WLry;Jrk|FPO9T`OGim0o@g{m!i4o%Q>Q+9zErT=A9O{4O2r?-{hy z-NWZ3zli!Bt0#>)HG0@$`c41H2dl>2JZ7&$j-Ohk@O>7}t9kaodDXgp?&%pv z)ts8H-equRr$tqM%Po8DvH0;9nlg0{%o#EG;rpw$pZdFVwm$OKsu}<03}3eYjH;XJ zo}X87&b`$;e(=zqr`$HZWntv3bD9rpZu#i#y{tR$NwrLyb;-c%rux;>_PD)uzmtDm zwR-sL3pVY0Pt{`sXZ+`lKTl;QEK6LPeR6VGo@wLXIpM!2PHXwms}0_Y!&+MAEWPoT zhd;T!<%ZL@9F}}+Vao;6=AL$2GqkEdhRo`YRK%4S?BF_bJ&mj^DdX)u=0i*tCwZ(um{ZlRrUL~ z+9$5u*AMk3{e4xMc~u+Nyei&nHuFAgevb*~;q(3quNAKNO3!=EraiJXe>Cu$ueRy8 zBWHCOuJ}riU*5(~AI>+$kMGcLcXX}!sEV)j_~j|jCXVKh;>UM1Ut8A-$FJ`wJ$`)~ zzrNE@H+GBr=5Fg~UcKgP>ssN8uk^G(e6Ov-dFYyluld@#ru}kgzZ|8fJqn)_znj_2 z7YOI!>ssM@Zc0yk6g~&d`(?g>nHOAo0d^0cQ!sAa+2Q=}a9?g+D_qYlczP%=>Vd_YvBkS=UmmX=YuE>_zq?douR*+$VpSv1w>8 zvLD%#)*tCw;cCymZu?UEqxMMclb##?!mvG4xZ*3lX}_kNmASKz^Tzkj`f zS{I}CN9~c?C$(2vC#GwKE56dJf2aPQo`dYo^be2PY4M>Q8%+PujK4On@mFAHmWzKQ zgMTA~eGWa(#_%|~6H(Ky-RO8>M#=lXGf1?HeMh5>z z2LDDC{*ACa_&2KYZ&c&osK&ohjenyG|3(J?Miu^z4E~K4{2ML!H>&Y(wBX;U#=lX8 zeGhn4L#Jn zx4y>wTD!w~8-=;zoWO@IH!qUx}Z+w+8PQZ27fezqi)!eURt;D9Z`r_kd-vH!O#J zVFd08`@#OO0`3C`z=1Fd4}b^4gWw=|Fgyev3M*k1JPcOD!SHbSLuf-MpbM=dxQ5n| zTtn+9uAw!AYiJ$KHMEZ58d^W%8d^16L+e-VB&^n%LX#Iq1X#JFHX#I?9 zXvKnS2fEOQ)(M0|7y8gTk#OijA6h374qfO&>tw>A3w>yvLO67x53QdQ4qfO&>r}#_ z3w>zS5)NJHL+clWLl^qcI*oAXLLXYE6AoSIL+cE}p$mOz{R`pHg+8=?NjP+&53Qku zLl^qciU;8l=)gF1VG8;%2dy(n4;>hXE=)lm=Ad;J>7fJT(1j`J!yL5EcC6wj0v#BK zE=)lm=Abo<^w5EE=)x5AVGdfuNe>+uhb~M(ALgJng7naVap=Mn^kEKK=a3#cFb-Xq zfL69JJ0OJ#=6kx-bQO zn1j|R(nANvp$k*chdF4SPkQLUICNnO`Y;Er(WHkCj6)ZupbvAQ!ovEn1wl*ht@dqhY=Ws4vfJ#Oh6YVVG5?953?`_^U%78{9yz} zp#x(u4inIYNtl9Z=))|`!927sCVv=#QRu)JjKc(UVG^cb8u~B`b1)CBdh&-67=;du z!8lAn7bal}rlAkBFbDI{8c+T(0;AA@F&Kvl=)xpS!8G(?7Up0cT9=SNjKC;#U<}4# z0=h5>Q!ovEn1wl*ht{R!4k9IR5g3IIjKMfeKo=%q3Z|hCvoHtq&~k(Pd%!YS4kNGvMqwp%U=57H zS{R3QFahhK3mae(Ho_Eaf@#zEx{B`)%V0T-zzP_JmC%7TFa~R3 z9M-`EtcNaafJxX0Q?Lo9VKek$E6l<+n1k&w4?CcBwPO`OJzyCuhY?r-qp%V>um;9p zEsVoDn1J=rg$*zX8(|7I!8B}!K5T_q*ama39p+&Nw65X%!!lS7Bd`KSVI_264UEBB z7>9K*0qda)8(zEx}NV3%V0T-zzP_JmC%7TFa~R39M-`E ztcNaafJxX0Q?Lo9VKek$E6l<+n1k&w4?CbWiSG}~U^$Gy3K)fz(1A5D25Vs)*1-g< zhc0Y@N!SQeunDGNGxT9A%)&O9gY7U6JD@e0?+?phIgG#x7=@M4fi*A&YhfJL!33;_ zE^L5F*a%aw38rB)^kFN^!Zw(L?Jy5Jpf!c>56fUVjKB&Qg_Y2OH82KiVI0=M1gwWH zY=BAF2ve{LreQPmVJpnSHkgC$Fb_MRbpziYmceouffX0yv9KOafG)fSHoz$`38%wGcpFT?S+EJ-1JiIGY=#S< z4(Cwuoj*H<8T2?s+54uLiBco>5x!&-O-jKg8D4vvBeI2P8!3DAYtzy>%4CgF702ycTaI14tx zdte&QgUxWkG$)u(x`kC|1)l|Dy7AZFMuOYN-`jP(dg3j)uj_cO;{CPjcn!px{%P0o zlEj-;u?c_uZ8UPb_kgbB{q59WzH8=_euw9i9=h)Nq+jW3aa`w^+fQ_c)HRU?2PGqQ`xYcpm9I3H#Zx-ud6KJql&{L6a*4XHcuFTZlB;}_ugdXF zmG6Sti50JYFKh3*;)GEjbbH#~sZY5%71uJTd7Du>E7{oY-cJo4l#_Q4;|d8KARn|-*`^S-V3f8L(+%IWh5 z{-)L5>+<`5w*0SY(c`{HJnzsTop*B}$3vcnyfB{9ADGP!W$~gn z?3IW8@v*1g$=S;=)qU-6U< zd7k7dALXlZs9ak=Ic3%pySCeHb3a}^ZqYLPiC;e#`QWJ6?NfH%t$pggFWOtpT{`pb zslOLJ?t8@ZNarCZr0y%8(jm{2T;-#DRSuQwsE?Pw5zix?HzbhbAuqhIcuI#nPjZ!y@>MyIXR99e zT~GTsG_ad>+6VH&cx$r{;pNTD7!(Pr*xg|L-};J50z_Te*Ctt z&wkFn<&szTjJ}w)BXeu_-#7DwUFm+m|4%9w*@yOB;pOkVOZ2$!?H0uINarCZr0ydx zOou#Aa+MGA!g8owpX4Wg+`QeZ_FsL!mz6y=;P!Lxw_#W= z;(4U=J`Ut~$P4c)p1zOdNUrixzA6Xu>eIaLmo>+%z>`t-;vUbjzN{``u@n_jbrPuX_vjICd`=l6g0 z*R4Bb?YiVucP00IQ1rO(5zjj*NarCZB=W*|N{2j8a+QzrMV_s4wQlX*I(qNr_JW>! z+p~ZAwmtg%*T>J!y=mvZ_s+?GedZP0dh?ns|6}~aqQ`xYcpmAz#{)SY^1}Oyr*z2k zBv<(;UzG!S_T)!q4t;v$O8eQH&iM4Gb3d>TOix`p@v!A~3fg9VLvc=gISN4fraLi@P?S-ejbjK|xE*3rRd&Kid z=OM=zbzkw64tbvBDj(&ma;RKy&AaQ(+njgoBN~Ra-uJ6^d*aU5_M3avQhOM7^6a$7 zpa0;fMK%oUJMlaxNarmJoE|6y@F% z>_l=TSN5oUk!P!1YxY?E;kZP1XUr)tUwm|86DPas+-=H+thO7mlYwtkAMnnymu={A z-+LyA=lyGt&bRg~$?=irBhN=(m`-veSNR~%MxKK_XLw@zg2O)P;heV51wX31eskvs zeX6#)^o?$g>g$zz%sKt=yI!+lXczs1cs}WT3HV#t#ZRxyI^*@&_t=_^>{nUH>jm;-qUVcK(hTgeBI}_Rk z>HO(|93OeU?#q7E&Pa~rA}_QHl>>Rs@&~tidfX-3Iz4~6a)(*NdxiB=@9`;j+*Wt$ zilsIT+Y8nIt%7tuazY|6v>&x6k|VjwNA`)luv}+d@$%v0d;G;#ebjqf{hR)uR=oYx zuB|o<`%h{oWEW=#ay;aPcA|Dg{V&OpT;-#7OyxkHUFh{)`9$*?TXv%MME$P)AMU;E zx$?y}4Es@PFJupk134b@LOW4B^}8fTa@CJgzA6Xu?4x_!c*xX2n>(_TcW6)4?|OLT zqRGGCbBPVZeiZRM*~2k`93Oe1ohY9AU6LcY>PIPGl>>Rs6^ll+7-)*@Y;(`KtDza;YAw zUbfNSTAO{So~z!g-H=_#PGmR83&$m$?L+yE-P^bQt`Z?c7nXHf2DDV(sk}%DWCQ2Uuhhoeox`; zUFu)Kuz#iT#Wg{{ENm}iFX~?@p88jkBe}{)`64fD&r~1PuUS|BO8q4Do8AukWnuqH zcCo(wEA``)FY=tu{VVlrPG=mVevkS=>KCb>w66Y@`d!HLl}>UbSNW{1f2Dp+=kdA5 z%^F8*TrIzq#@!lUsGU&1OZOE|=_E&Tm5;{fDu?=cdT+}v)^~iaakuP5?TIM6P(1mu z);2y@zOoaQOYd#fPmPlgz>oC-<8!#y@wwUwwJWjMZG5hF?9)!;bB&WV zZq_(j<7$o1|H$}U<8ZYT$P35k8pkW0;@^eq?cY^nAoRfm69^>uCjUHNHM-_@STud99(@y03UjCpnUGu)T6ZS^ literal 0 HcmV?d00001 From ac801549633f671d8a4e6fde87a256bfcd2b802f Mon Sep 17 00:00:00 2001 From: Justin Privitera Date: Wed, 17 Apr 2024 17:00:12 -0700 Subject: [PATCH 44/56] I put this in another PR --- .../blueprint/t_blueprint_mesh_examples.cpp | 85 ------------------- 1 file changed, 85 deletions(-) diff --git a/src/tests/blueprint/t_blueprint_mesh_examples.cpp b/src/tests/blueprint/t_blueprint_mesh_examples.cpp index e49047316..ed096de85 100644 --- a/src/tests/blueprint/t_blueprint_mesh_examples.cpp +++ b/src/tests/blueprint/t_blueprint_mesh_examples.cpp @@ -579,91 +579,6 @@ TEST(conduit_blueprint_mesh_examples, strided_structured_3d) } -//----------------------------------------------------------------------------- -TEST(conduit_blueprint_mesh_examples, strided_structured_colmajor) -{ - Node res; - - const std::string yaml_text = "" - "coordsets:\n" - " coords:\n" - " type: \"explicit\"\n" - " values:\n" - " x: [-10.0, -6.6, -3.3, 0.0, 3.3, 6.6, 10.0,\n" - " -10.0, -6.6, -3.3, 0.0, 3.3, 6.6, 10.0,\n" - " -10.0, -6.6, -3.3, 0.0, 3.3, 6.6, 10.0,\n" - " -10.0, -6.6, -3.3, 0.0, 3.3, 6.6, 10.0,\n" - " -10.0, -6.6, -3.3, 0.0, 3.3, 6.6, 10.0,\n" - " -10.0, -6.6, -3.3, 0.0, 3.3, 6.6, 10.0]\n" - " y: [-10.0, -10.0, -10.0, -10.0, -10.0, -10.0, -10.0,\n" - " -6.0, -6.0, -6.0, -6.0, -6.0, -6.0, -6.0,\n" - " -2.0, -2.0, -2.0, -2.0, -2.0, -2.0, -2.0,\n" - " 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0,\n" - " 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0,\n" - " 10.0, 10.0, 10.0, 10.0, 10.0, 10.0, 10.0]\n" - " col_maj_coords:\n" - " type: \"explicit\"\n" - " values:\n" - " x: [-10.0, -10.0, -10.0, -10.0, -10.0, -10.0,\n" - " -6.6, -6.6, -6.6, -6.6, -6.6, -6.6,\n" - " -3.3, -3.3, -3.3, -3.3, -3.3, -3.3,\n" - " 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\n" - " 3.3, 3.3, 3.3, 3.3, 3.3, 3.3,\n" - " 6.6, 6.6, 6.6, 6.6, 6.6, 6.6,\n" - " 10.0, 10.0, 10.0, 10.0, 10.0, 10.0]\n" - " y: [-10.0, -6.0, -2.0, 2.0, 6.0, 10.0,\n" - " -10.0, -6.0, -2.0, 2.0, 6.0, 10.0,\n" - " -10.0, -6.0, -2.0, 2.0, 6.0, 10.0,\n" - " -10.0, -6.0, -2.0, 2.0, 6.0, 10.0,\n" - " -10.0, -6.0, -2.0, 2.0, 6.0, 10.0,\n" - " -10.0, -6.0, -2.0, 2.0, 6.0, 10.0,\n" - " -10.0, -6.0, -2.0, 2.0, 6.0, 10.0]\n" - "topologies:\n" - " mesh:\n" - " type: \"structured\"\n" - " coordset: \"coords\"\n" - " elements:\n" - " dims:\n" - " i: 3\n" - " j: 2\n" - " offsets: [2, 2]\n" - " strides: [1, 7]\n" - " full_mesh:\n" - " type: \"structured\"\n" - " coordset: \"coords\"\n" - " elements:\n" - " dims:\n" - " i: 6\n" - " j: 5\n" - " offsets: [0, 0]\n" - " strides: [1, 7]\n" - " mesh_col_maj:\n" - " type: \"structured\"\n" - " coordset: \"col_maj_coords\"\n" - " elements:\n" - " dims:\n" - " i: 3\n" - " j: 2\n" - " offsets: [2, 2]\n" - " strides: [6, 1]\n" - " full_mesh_col_maj:\n" - " type: \"structured\"\n" - " coordset: \"col_maj_coords\"\n" - " elements:\n" - " dims:\n" - " i: 6\n" - " j: 5\n" - " strides: [6, 1]\n"; - - res.parse(yaml_text, "yaml"); - - Node info; - EXPECT_TRUE(blueprint::mesh::verify(res, info)); - CONDUIT_INFO(info.to_yaml()); - - test_save_mesh_helper(res, "strided_structured_colmajor"); -} - //----------------------------------------------------------------------------- TEST(conduit_blueprint_mesh_examples, julia) From 2ad23c647424f9f6faf05336c3ad7e40a57179bb Mon Sep 17 00:00:00 2001 From: Justin Privitera Date: Wed, 17 Apr 2024 17:03:04 -0700 Subject: [PATCH 45/56] fiox options comment --- src/tests/relay/t_relay_io_silo.cpp | 27 +++++++++------------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/src/tests/relay/t_relay_io_silo.cpp b/src/tests/relay/t_relay_io_silo.cpp index 79a2e335a..90db4deeb 100644 --- a/src/tests/relay/t_relay_io_silo.cpp +++ b/src/tests/relay/t_relay_io_silo.cpp @@ -1569,35 +1569,26 @@ TEST(conduit_relay_io_silo, round_trip_save_option_overlink5) // read options: /// opts: /// silo_names: -/// multimesh_names: -/// "{name1}" - multimeshes with this name will be read if they exist +/// mesh_names: +/// "{name1}" - meshes with this name will be read if they exist /// "{name2}" /// ... /// or -/// "{all}" - all multimeshes will be read. +/// "{all}" - all meshes will be read. /// or -/// "{none}" - no multimeshes will be read. -/// multivar_names: similar to multimesh_names. -/// multimat_names: similar to multimesh_names. -/// multimatspecies_names: similar to multimesh_names. TODO -/// qmesh_names: similar to multimesh_names. -/// qvar_names: similar to multimesh_names. -/// ucdmesh_names: similar to multimesh_names. -/// ucdvar_names: similar to multimesh_names. -/// ptmesh_names: similar to multimesh_names. -/// ptvar_names: similar to multimesh_names. -/// mat_names: similar to multimesh_names. -/// matspecies_names: similar to multimesh_names. TODO +/// "{none}" - no meshes will be read. +/// var_names: similar to mesh_names. +/// mat_names: similar to mesh_names. +/// matspecies_names: similar to mesh_names. TODO /// By default, everything in the file will be read unless manually turned off. /// /// matset_style: "default", "multi_buffer_full", "sparse_by_element", /// "multi_buffer_by_material" /// "default" ==> "sparse_by_element" /// -/// mesh_name: legacy argument. This is interpreted as a multimesh name. -/// It is added to the list of multimesh names to read, unless the +/// mesh_name: legacy argument. This is interpreted as a mesh name. +/// It is added to the list of mesh names to read, unless the /// user has specified "all" or "none", which will supersede this. -/// TODO does it make sense to remove this? When? //----------------------------------------------------------------------------- // TODO this is now a legacy feature. Should I remove eventually? From 43a0bebb83dfb9109a4d6c26a67abf6e67e6c739 Mon Sep 17 00:00:00 2001 From: Justin Privitera Date: Wed, 17 Apr 2024 17:10:32 -0700 Subject: [PATCH 46/56] oops --- src/libs/relay/conduit_relay_io_silo.cpp | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/libs/relay/conduit_relay_io_silo.cpp b/src/libs/relay/conduit_relay_io_silo.cpp index e11dd4c39..42f316609 100644 --- a/src/libs/relay/conduit_relay_io_silo.cpp +++ b/src/libs/relay/conduit_relay_io_silo.cpp @@ -4650,11 +4650,6 @@ void silo_write_structured_mesh(DBfile *dbfile, const bool write_overlink, Node &n_mesh_info) { - // declare pointers out here to prevent losing them to scope - int pts_dims[3]; - int ele_dims[3]; - index_t num_elems; - // check for strided structured case if (n_topo.has_path("elements/dims/offsets") || n_topo.has_path("elements/dims/strides")) @@ -4665,11 +4660,12 @@ void silo_write_structured_mesh(DBfile *dbfile, CONDUIT_ERROR("Strided Structured Blueprint case does not have a general " "analog in Silo."); } + int ele_dims[3]; ele_dims[0] = n_topo["elements/dims/i"].to_value(); ele_dims[1] = n_topo["elements/dims/j"].to_value(); ele_dims[2] = 0; - num_elems = ele_dims[0] * ele_dims[1]; + index_t num_elems = ele_dims[0] * ele_dims[1]; if (ndims == 3) { @@ -4678,6 +4674,7 @@ void silo_write_structured_mesh(DBfile *dbfile, } // silo needs the node dims to define a structured grid + int pts_dims[3]; pts_dims[0] = ele_dims[0] + 1; pts_dims[1] = ele_dims[1] + 1; pts_dims[2] = 1; From 1fc9605e0524f7e8941f05fa8057808bbb8362ac Mon Sep 17 00:00:00 2001 From: Justin Privitera Date: Thu, 18 Apr 2024 11:33:40 -0700 Subject: [PATCH 47/56] limit meshes --- src/tests/relay/t_relay_io_silo.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/tests/relay/t_relay_io_silo.cpp b/src/tests/relay/t_relay_io_silo.cpp index 90db4deeb..776b6e2e6 100644 --- a/src/tests/relay/t_relay_io_silo.cpp +++ b/src/tests/relay/t_relay_io_silo.cpp @@ -1591,7 +1591,6 @@ TEST(conduit_relay_io_silo, round_trip_save_option_overlink5) /// user has specified "all" or "none", which will supersede this. //----------------------------------------------------------------------------- -// TODO this is now a legacy feature. Should I remove eventually? TEST(conduit_relay_io_silo, round_trip_read_option_mesh_name) { Node load_mesh, info, opts; @@ -1604,6 +1603,8 @@ TEST(conduit_relay_io_silo, round_trip_read_option_mesh_name) EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); EXPECT_TRUE(load_mesh[0].has_path("topologies/mesh1_dup")); + + EXPECT_TRUE(load_mesh[0]["topologies"].number_of_children() == 1); } //----------------------------------------------------------------------------- From d14a60bd4373d5a373495254fa31bf4d7ebdde8b Mon Sep 17 00:00:00 2001 From: Justin Privitera Date: Fri, 19 Apr 2024 17:33:26 -0700 Subject: [PATCH 48/56] I think I restored the status quo --- src/libs/relay/conduit_relay_io_silo.cpp | 999 ++++++++--------------- src/tests/relay/t_relay_io_silo.cpp | 818 ++++++++++--------- 2 files changed, 796 insertions(+), 1021 deletions(-) diff --git a/src/libs/relay/conduit_relay_io_silo.cpp b/src/libs/relay/conduit_relay_io_silo.cpp index 42f316609..4caa9488a 100644 --- a/src/libs/relay/conduit_relay_io_silo.cpp +++ b/src/libs/relay/conduit_relay_io_silo.cpp @@ -312,30 +312,6 @@ class SiloTreePathGenerator } }; -//----------------------------------------------------------------------------- - -class SiloReadOptions -{ -public: - bool read_all = true; - bool read_none = false; -}; - -//----------------------------------------------------------------------------- - -class SiloReadBookkeeping -{ -public: - int num_obj_in_toc = 0; - char **toc_names = nullptr; - int var_type; - std::vector names_to_read; - - SiloReadBookkeeping(int num_obj_in_toc_, char **toc_names_, int var_type_) : - num_obj_in_toc(num_obj_in_toc_), toc_names(toc_names_), var_type(var_type_) {} - SiloReadBookkeeping(){} -}; - //----------------------------------------------------------------------------- std::string sanitize_silo_varname(const std::string &varname) { @@ -2724,10 +2700,12 @@ bool read_root_silo_index(const std::string &root_file_path, const Node &opts, Node &root_node, // output + std::string &mesh_name_to_read, // output std::ostringstream &error_oss) // output { // clear output vars root_node.reset(); + mesh_name_to_read = ""; error_oss.str(""); // first, make sure we can open the root file @@ -2759,344 +2737,82 @@ read_root_silo_index(const std::string &root_file_path, return false; } - // Handle legacy mesh_name argument: - // The best way to do this is to put this mesh name into the opts before we - // start looking at the opts for real. However, we can't modify the opts the - // user passed in. Thus we need to create a new opts node and have it point - // at the original opts node for everything except the mesh names. - Node real_opts; - if (opts.has_child("mesh_name")) + // decide what multimesh to extract + if (opts.has_child("mesh_name") && opts["mesh_name"].dtype().is_string()) { - const std::string opts_mesh_name = opts["mesh_name"].as_string(); - if (! opts_mesh_name.empty()) - { - // we could manually go through the other opts, but I think this approach - // is cleaner in case we add more down the road - auto opts_itr = opts.children(); - while (opts_itr.has_next()) - { - const Node &opts_item = opts_itr.next(); - const std::string opt_name = opts_itr.name(); - // if you're not the silo_names, then we can point to original opts - if (opt_name != "silo_names") - { - real_opts[opt_name].set_external(opts_item); - } - else - { - // silo names case - auto silo_names_itr = opts["silo_names"].children(); - while (silo_names_itr.has_next()) - { - const Node &silo_names_entry = silo_names_itr.next(); - const std::string silo_names_name = silo_names_itr.name(); - // if you're not the mesh_names, then you can point to original opts - if (silo_names_name != "mesh_names") - { - real_opts["silo_names"][silo_names_name].set_external(silo_names_entry); - } - } - } - } + mesh_name_to_read = opts["mesh_name"].as_string(); + } - // now handle mesh names - if (opts.has_path("silo_names/mesh_names")) - { - if (opts["silo_names"]["mesh_names"].has_child("all") || - opts["silo_names"]["mesh_names"].has_child("none") || - opts["silo_names"]["mesh_names"].has_child(opts_mesh_name)) - { - // if we have already set things up such that the provided mesh name is superfluous - // then we can point to original opts - real_opts["silo_names"]["mesh_names"].set_external(opts["silo_names"]["mesh_names"]); - } - else - { - // otherwise we must grab the mesh names that are already present and point to them - if (opts["silo_names"]["mesh_names"].dtype().is_object()) - { - auto mesh_names_itr = opts["silo_names"]["mesh_names"].children(); - while (mesh_names_itr.has_next()) - { - const Node &mesh_names_entry = mesh_names_itr.next(); - const std::string mesh_name = mesh_names_itr.name(); - real_opts["silo_names"]["mesh_names"][mesh_name].set_external(mesh_names_entry); - } - } - else - { - // I am letting users abuse me - real_opts["silo_names"]["mesh_names"][opts["silo_names"]["mesh_names"].as_string()]; - } - // and then add our extra mesh name to the list - real_opts["silo_names"]["mesh_names"][opts_mesh_name]; - } - } - else - { - real_opts["silo_names"]["mesh_names"].set(opts_mesh_name); - } + // we want to know the mesh type so it is easy to read later + int mesh_type = -1; + + // check mesh name + if (mesh_name_to_read.empty()) + { + // if not specified, we will default to first multimesh we find + if (toc->nmultimesh > 0) + { + mesh_name_to_read = toc->multimesh_names[0]; + mesh_type = DB_MULTIMESH; + } + // otherwise we will try quadmeshes + else if (toc->nqmesh > 0) + { + mesh_name_to_read = toc->qmesh_names[0]; + mesh_type = DB_QUADMESH; + } + // otherwise we will try ucdmeshes + else if (toc->nucdmesh > 0) + { + mesh_name_to_read = toc->ucdmesh_names[0]; + mesh_type = DB_UCDMESH; + } + // otherwise we will try ptmeshes + else if (toc->nptmesh > 0) + { + mesh_name_to_read = toc->ptmesh_names[0]; + mesh_type = DB_POINTMESH; } else { - real_opts.set_external(opts); + error_oss << "No meshes found in file: " << root_file_path; + return false; } } else { - real_opts.set_external(opts); - } - - // Reading options will hold information about what to read for each general - // kind of silo object. For each of the cases, we have the option to read all, - // read none, or read some of that type. - std::map reading_options; - - // read all is turned on, and read none is turned off - reading_options["mesh_names"] = detail::SiloReadOptions(); - reading_options["var_names"] = detail::SiloReadOptions(); - reading_options["mat_names"] = detail::SiloReadOptions(); - // reading_options["matspecies_names"] = detail::SiloReadOptions(); - - // TODO is there any way I could skip the following step or combine it with the next step? - // this is awfully complicated - - // Now we need to see what things we are supposed to read based on - // what was passed in to the options. - if (real_opts.has_child("silo_names")) - { - auto silo_names_itr = real_opts["silo_names"].children(); - while (silo_names_itr.has_next()) + bool found = false; + auto check_mesh_name_in_file = [&](const int nmeshes, + char **mesh_names, + const int curr_mesh_type) { - const Node &silo_object_type = silo_names_itr.next(); - const std::string silo_object_type_name = silo_names_itr.name(); - - if (reading_options.count(silo_object_type_name) == 0) - { - error_oss << "TODO unknown option"; - return false; - } - - if (silo_object_type.dtype().is_object()) - { - if (silo_object_type.number_of_children() > 0) - { - if (silo_object_type.has_child("all")) - { - if (silo_object_type.number_of_children() > 1) - { - error_oss << "TODO this is bad"; - return false; - } - reading_options[silo_object_type_name].read_all = true; - reading_options[silo_object_type_name].read_none = false; - } - else if (silo_object_type.has_child("none")) - { - if (silo_object_type.number_of_children() > 1) - { - error_oss << "TODO this is bad"; - return false; - } - reading_options[silo_object_type_name].read_all = false; - reading_options[silo_object_type_name].read_none = true; - } - else - { - // we must have named some specific items we want to read - reading_options[silo_object_type_name].read_all = false; - reading_options[silo_object_type_name].read_none = false; - } - } - else - { - // no children were specified so we want to read everything of this kind - reading_options[silo_object_type_name].read_all = true; - reading_options[silo_object_type_name].read_none = false; - } - } - else - { - const std::string silo_name_value = silo_object_type.as_string(); - if ("all" == silo_name_value) - { - reading_options[silo_object_type_name].read_all = true; - reading_options[silo_object_type_name].read_none = false; - } - else if ("none" == silo_name_value) - { - reading_options[silo_object_type_name].read_all = false; - reading_options[silo_object_type_name].read_none = true; - } - else - { - // we must have named some specific items we want to read - reading_options[silo_object_type_name].read_all = false; - reading_options[silo_object_type_name].read_none = false; - } - } - } - } - - std::map reading_info; - reading_info["multimesh_names"] = detail::SiloReadBookkeeping(toc->nmultimesh, - toc->multimesh_names, - DB_MULTIMESH); - reading_info["multivar_names"] = detail::SiloReadBookkeeping(toc->nmultivar, - toc->multivar_names, - DB_MULTIVAR); - reading_info["multimat_names"] = detail::SiloReadBookkeeping(toc->nmultimat, - toc->multimat_names, - DB_MULTIMAT); - // reading_info["multimatspecies_names"] = detail::SiloReadBookkeeping(toc->nmultimatspecies, - // toc->multimatspecies_names, - // DB_MULTIMATSPECIES); - reading_info["qmesh_names"] = detail::SiloReadBookkeeping(toc->nqmesh, - toc->qmesh_names, - DB_QUADMESH); - reading_info["qvar_names"] = detail::SiloReadBookkeeping(toc->nqvar, - toc->qvar_names, - DB_QUADVAR); - reading_info["ucdmesh_names"] = detail::SiloReadBookkeeping(toc->nucdmesh, - toc->ucdmesh_names, - DB_UCDMESH); - reading_info["ucdvar_names"] = detail::SiloReadBookkeeping(toc->nucdvar, - toc->ucdvar_names, - DB_UCDVAR); - reading_info["ptmesh_names"] = detail::SiloReadBookkeeping(toc->nptmesh, - toc->ptmesh_names, - DB_POINTMESH); - reading_info["ptvar_names"] = detail::SiloReadBookkeeping(toc->nptvar, - toc->ptvar_names, - DB_POINTVAR); - reading_info["mat_names"] = detail::SiloReadBookkeeping(toc->nmat, - toc->mat_names, - DB_MATERIAL); - // reading_info["matspecies_names"] = detail::SiloReadBookkeeping(toc->nmatspecies, - // toc->matspecies_names, - // DB_MATSPECIES); - - const std::vector mesh_name_types = - {"multimesh_names", "qmesh_names", "ucdmesh_names", "ptmesh_names"}; - const std::vector var_name_types = - {"multivar_names", "qvar_names", "ucdvar_names", "ptvar_names"}; - const std::vector mat_name_types = - {"multimat_names", "mat_names"}; - // std::vector mat_species_name_types = - // {"multimatspecies_names", "matspecies_names"}; - - auto generate_read_list = [&](DBfile *dbfile, - std::ostringstream &error_oss, - const std::string &what_are_we_reading, - const std::vector &type_names, - const std::string &error_msg) - { - // if we are *not* reading *no* objects of this type --> we are reading objects of this type - if (! reading_options[what_are_we_reading].read_none) - { - if (reading_options[what_are_we_reading].read_all) - { - for (const std::string &type_name : type_names) - { - for (int toc_id = 0; toc_id < reading_info[type_name].num_obj_in_toc; toc_id ++) - { - reading_info[type_name].names_to_read.push_back( - reading_info[type_name].toc_names[toc_id]); - } - } - } - else + for (int i = 0; i < nmeshes; i ++) { - auto check_silo_name = [&](const std::string &silo_name) - { - if (! DBInqVarExists(dbfile, silo_name.c_str())) - { - error_oss << "No silo var found matching " << silo_name; - return false; - } - - const int silo_var_type = DBInqVarType(dbfile, silo_name.c_str()); - - bool found = false; - for (const std::string &type_name : type_names) - { - if (silo_var_type == reading_info[type_name].var_type) - { - reading_info[type_name].names_to_read.push_back(silo_name); - found = true; - break; - } - } - if (! found) - { - error_oss << "Requested silo object " << silo_name << error_msg << silo_var_type << "."; - return false; - } - - return true; - }; - - if (real_opts["silo_names"][what_are_we_reading].dtype().is_object()) + if (mesh_names[i] == mesh_name_to_read) { - for (const std::string silo_name : real_opts["silo_names"][what_are_we_reading].child_names()) - { - if (! check_silo_name(silo_name)) - { - // error_oss is already populated - return false; - } - } - } - else - { - const std::string silo_name = real_opts["silo_names"][what_are_we_reading].as_string(); - - if (! check_silo_name(silo_name)) - { - // error_oss is already populated - return false; - } + found = true; + mesh_type = curr_mesh_type; + break; } } + }; + check_mesh_name_in_file(toc->nmultimesh, toc->multimesh_names, DB_MULTIMESH); + check_mesh_name_in_file(toc->nqmesh, toc->qmesh_names, DB_QUADMESH); + check_mesh_name_in_file(toc->nucdmesh, toc->ucdmesh_names, DB_UCDMESH); + check_mesh_name_in_file(toc->nptmesh, toc->ptmesh_names, DB_POINTMESH); + + if (!found) + { + error_oss << "No mesh found matching " << mesh_name_to_read; + return false; } - - return true; - }; - - // Silo doesn't let us have any names that are the same in the same directory, - // even if they are different types. So we don't have to worry about name collisions. - - if (! (generate_read_list(dbfile.getSiloObject(), - error_oss, - "mesh_names", - mesh_name_types, - " was found in file but it is not one of the supported " - "mesh types: DB_MULTIMESH, DB_QUADMESH, DB_UCDMESH, or " - "DB_POINTMESH. Instead it had silo var type ") && - generate_read_list(dbfile.getSiloObject(), - error_oss, - "var_names", - var_name_types, - " was found in file but it is not one of the supported " - "var types: DB_MULTIVAR, DB_QUADVAR, DB_UCDVAR, or " - "DB_POINTVAR. Instead it had silo var type ") && - generate_read_list(dbfile.getSiloObject(), - error_oss, - "mat_names", - mat_name_types, - " was found in file but it is not one of the supported " - "mat types: DB_MULTIMAT or DB_MATERIAL. Instead it had " - "silo var type "))) - { - // error msg should already be populated - return false; } // Get the selected matset flavor std::string opts_matset_style = ""; - if (real_opts.has_child("matset_style") && real_opts["matset_style"].dtype().is_string()) + if (opts.has_child("matset_style") && opts["matset_style"].dtype().is_string()) { - opts_matset_style = real_opts["matset_style"].as_string(); + opts_matset_style = opts["matset_style"].as_string(); if (opts_matset_style != "default" && opts_matset_style != "multi_buffer_full" && opts_matset_style != "sparse_by_element" && @@ -3110,12 +2826,48 @@ read_root_silo_index(const std::string &root_file_path, } } - // start with multimeshes, multivars, and multimats (and someday multimatspecies) - for (const std::string &multimesh_name : reading_info["multimesh_names"].names_to_read) + auto prep_simple_silo_obj_metadata = [&](const int num_vars, + char **var_names, + const int var_type, + const int num_mats, + char **mat_names) + { + root_node[mesh_name_to_read]["nblocks"] = 1; + root_node[mesh_name_to_read]["nameschemes"] = "no"; + root_node[mesh_name_to_read]["mesh_types"].set(mesh_type); + root_node[mesh_name_to_read]["mesh_paths"].append().set(mesh_name_to_read); + + for (int i = 0; i < num_vars; i ++) + { + const std::string var_name = var_names[i]; + Node &var = root_node[mesh_name_to_read]["vars"][var_name]; + var["nameschemes"] = "no"; + var["var_types"].set(var_type); + var["var_paths"].append().set(var_name); + } + // TODO should we check here if materials are associated with this mesh? + // we have logic to get the right one later, but it could be quick to check now + for (int i = 0; i < num_mats; i ++) + { + const std::string mat_name = mat_names[i]; + Node &material = root_node[mesh_name_to_read]["matsets"][mat_name]; + material["nameschemes"] = "no"; + material["matset_paths"].append().set(mat_name); + } + + read_state(dbfile.getSiloObject(), root_node, mesh_name_to_read); + + if (! opts_matset_style.empty()) + { + root_node[mesh_name_to_read]["matset_style"] = opts_matset_style; + } + }; + + if (DB_MULTIMESH == mesh_type) { int nblocks; if (! read_multimesh(dbfile.getSiloObject(), - multimesh_name, + mesh_name_to_read, nblocks, root_node, error_oss)) @@ -3124,7 +2876,7 @@ read_root_silo_index(const std::string &root_file_path, } if (! read_multivars(toc, dbfile.getSiloObject(), - multimesh_name, + mesh_name_to_read, nblocks, root_node, error_oss)) @@ -3133,7 +2885,7 @@ read_root_silo_index(const std::string &root_file_path, } if (! read_multimats(toc, dbfile.getSiloObject(), - multimesh_name, + mesh_name_to_read, nblocks, root_node, error_oss)) @@ -3141,75 +2893,38 @@ read_root_silo_index(const std::string &root_file_path, return false; } - read_state(dbfile.getSiloObject(), root_node, multimesh_name); + read_state(dbfile.getSiloObject(), root_node, mesh_name_to_read); // overlink-specific read_var_attributes(dbfile.getSiloObject(), - multimesh_name, + mesh_name_to_read, root_node); if (! opts_matset_style.empty()) { - root_node[multimesh_name]["matset_style"] = opts_matset_style; + root_node[mesh_name_to_read]["matset_style"] = opts_matset_style; } } - - auto prep_simple_silo_obj_metadata = [&](const std::vector &mesh_names, - const std::vector &var_names, - const std::vector &mat_names, - const int mesh_type, - const int var_type) + else if (DB_QUADMESH == mesh_type) { - for (const std::string &mesh_name : mesh_names) - { - root_node[mesh_name]["nblocks"] = 1; - root_node[mesh_name]["nameschemes"] = "no"; - root_node[mesh_name]["mesh_types"].set(mesh_type); - root_node[mesh_name]["mesh_paths"].append().set(mesh_name); - - // at this stage we assume that all vars could be associated with this mesh - // TODO should I do a check here then? - for (const std::string &var_name : var_names) - { - Node &var = root_node[mesh_name]["vars"][var_name]; - var["nameschemes"] = "no"; - var["var_types"].set(var_type); - var["var_paths"].append().set(var_name); - } - // same is true for materials - // TODO ugh I don't like this - for (const std::string &mat_name : mat_names) - { - Node &material = root_node[mesh_name]["matsets"][mat_name]; - material["nameschemes"] = "no"; - material["matset_paths"].append().set(mat_name); - } - - // TODO I love rereading state for every mesh. This is so silly - read_state(dbfile.getSiloObject(), root_node, mesh_name); - - if (! opts_matset_style.empty()) - { - root_node[mesh_name]["matset_style"] = opts_matset_style; - } - } - }; - - prep_simple_silo_obj_metadata(reading_info["qmesh_names"].names_to_read, - reading_info["qvar_names"].names_to_read, - reading_info["mat_names"].names_to_read, - DB_QUADMESH, - DB_QUADVAR); - prep_simple_silo_obj_metadata(reading_info["ucdmesh_names"].names_to_read, - reading_info["ucdvar_names"].names_to_read, - reading_info["mat_names"].names_to_read, - DB_UCDMESH, - DB_UCDVAR); - prep_simple_silo_obj_metadata(reading_info["ptmesh_names"].names_to_read, - reading_info["ptvar_names"].names_to_read, - reading_info["mat_names"].names_to_read, - DB_POINTMESH, - DB_POINTVAR); + prep_simple_silo_obj_metadata(toc->nqvar, toc->qvar_names, DB_QUADVAR, + toc->nmat, toc->mat_names); + } + else if (DB_UCDMESH == mesh_type) + { + prep_simple_silo_obj_metadata(toc->nucdvar, toc->ucdvar_names, DB_UCDVAR, + toc->nmat, toc->mat_names); + } + else if (DB_POINTMESH == mesh_type) + { + prep_simple_silo_obj_metadata(toc->nptvar, toc->ptvar_names, DB_POINTVAR, + toc->nmat, toc->mat_names); + } + else + { + error_oss << "Unknown mesh type for mesh " << mesh_name_to_read; + return false; + } // our silo index should look like this: @@ -3259,27 +2974,15 @@ read_root_silo_index(const std::string &root_file_path, //----------------------------------------------------------------------------- /// /// opts: -/// silo_names: -/// mesh_names: -/// "{name1}" - meshes with this name will be read if they exist -/// "{name2}" -/// ... -/// or -/// "{all}" - all meshes will be read. -/// or -/// "{none}" - no meshes will be read. -/// var_names: similar to mesh_names. -/// mat_names: similar to mesh_names. -/// matspecies_names: similar to mesh_names. TODO -/// By default, everything in the file will be read unless manually turned off. +/// mesh_name: "{name}" +/// provide explicit mesh name, for cases where silo data includes +/// more than one mesh. +/// We only allow reading of a single mesh to keep these options on +/// par with the relay io blueprint options. /// /// matset_style: "default", "multi_buffer_full", "sparse_by_element", /// "multi_buffer_by_material" /// "default" ==> "sparse_by_element" -/// -/// mesh_name: legacy argument. This is interpreted as a mesh name. -/// It is added to the list of mesh names to read, unless the -/// user has specified "all" or "none", which will supersede this. //----------------------------------------------------------------------------- void CONDUIT_RELAY_API read_mesh(const std::string &root_file_path, @@ -3295,6 +2998,7 @@ read_mesh(const std::string &root_file_path, int error = 0; std::ostringstream error_oss; + std::string mesh_name_to_read; Node root_node; // only read bp index on rank 0 @@ -3303,6 +3007,7 @@ read_mesh(const std::string &root_file_path, if (!read_root_silo_index(root_file_path, opts, root_node, + mesh_name_to_read, error_oss)) { error = 1; @@ -3331,8 +3036,13 @@ read_mesh(const std::string &root_file_path, } else { - // broadcast the root node + // broadcast the mesh name and the root node // from rank 0 to all ranks + n_global.set(mesh_name_to_read); + conduit::relay::mpi::broadcast_using_schema(n_global, + 0, + mpi_comm); + mesh_name_to_read = n_global.as_string(); conduit::relay::mpi::broadcast_using_schema(root_node, 0, mpi_comm); @@ -3345,271 +3055,264 @@ read_mesh(const std::string &root_file_path, } #endif - auto root_itr = root_node.children(); - while (root_itr.has_next()) - { - const Node &mesh_index = root_itr.next(); - const std::string mesh_index_name = root_itr.name(); + const Node &mesh_index = root_node[mesh_name_to_read]; - // read all domains for given mesh - int num_domains = mesh_index["nblocks"].to_int(); + // read all domains for given mesh + int num_domains = mesh_index["nblocks"].to_int(); - std::ostringstream oss; - int domain_start = 0; - int domain_end = num_domains; + std::ostringstream oss; + int domain_start = 0; + int domain_end = num_domains; #if CONDUIT_RELAY_IO_MPI_ENABLED - int read_size = num_domains / par_size; - int rem = num_domains % par_size; - if(par_rank < rem) - { - read_size++; - } + int read_size = num_domains / par_size; + int rem = num_domains % par_size; + if(par_rank < rem) + { + read_size++; + } - Node n_read_size; - Node n_doms_per_rank; + Node n_read_size; + Node n_doms_per_rank; - n_read_size.set_int32(read_size); + n_read_size.set_int32(read_size); - relay::mpi::all_gather_using_schema(n_read_size, - n_doms_per_rank, - mpi_comm); - int *counts = (int*)n_doms_per_rank.data_ptr(); + relay::mpi::all_gather_using_schema(n_read_size, + n_doms_per_rank, + mpi_comm); + int *counts = (int*)n_doms_per_rank.data_ptr(); - int rank_offset = 0; - for(int i = 0; i < par_rank; ++i) - { - rank_offset += counts[i]; - } + int rank_offset = 0; + for(int i = 0; i < par_rank; ++i) + { + rank_offset += counts[i]; + } - domain_start = rank_offset; - domain_end = rank_offset + read_size; + domain_start = rank_offset; + domain_end = rank_offset + read_size; #endif - // TODO there's no reason for this to live inside each mesh index - // but I don't know where else to stash it - const std::string opts_matset_style = (mesh_index.has_child("matset_style") ? - mesh_index["matset_style"].as_string() : "default"); + const std::string opts_matset_style = (mesh_index.has_child("matset_style") ? + mesh_index["matset_style"].as_string() : "default"); - bool mesh_nameschemes = false; - if (mesh_index.has_child("nameschemes") && - mesh_index["nameschemes"].as_string() == "yes") - { - mesh_nameschemes = true; - CONDUIT_ERROR("TODO no support for nameschemes yet"); - } - detail::SiloTreePathGenerator mesh_path_gen{mesh_nameschemes}; + bool mesh_nameschemes = false; + if (mesh_index.has_child("nameschemes") && + mesh_index["nameschemes"].as_string() == "yes") + { + mesh_nameschemes = true; + CONDUIT_ERROR("TODO no support for nameschemes yet"); + } + detail::SiloTreePathGenerator mesh_path_gen{mesh_nameschemes}; - std::string root_file_name, relative_dir; - utils::rsplit_file_path(root_file_path, root_file_name, relative_dir); + std::string root_file_name, relative_dir; + utils::rsplit_file_path(root_file_path, root_file_name, relative_dir); - // If the root file is named OvlTop.silo, then there is a very good chance that - // this file is valid overlink. Therefore, we must modify the paths we get from - // the root node to reflect this. - bool ovltop_case = (root_file_name == "OvlTop.silo"); + // If the root file is named OvlTop.silo, then there is a very good chance that + // this file is valid overlink. Therefore, we must modify the paths we get from + // the root node to reflect this. + bool ovltop_case = (root_file_name == "OvlTop.silo"); - for (int domain_id = domain_start; domain_id < domain_end; domain_id ++) - { - // - // Read Mesh - // + for (int domain_id = domain_start; domain_id < domain_end; domain_id ++) + { + // + // Read Mesh + // - const std::string silo_mesh_path = mesh_index["mesh_paths"][domain_id].as_string(); - const int_accessor meshtypes = mesh_index["mesh_types"].value(); - const int meshtype = meshtypes[domain_id]; + const std::string silo_mesh_path = mesh_index["mesh_paths"][domain_id].as_string(); + const int_accessor meshtypes = mesh_index["mesh_types"].value(); + const int meshtype = meshtypes[domain_id]; - std::string mesh_name, mesh_domain_filename; - mesh_path_gen.GeneratePaths(silo_mesh_path, relative_dir, mesh_domain_filename, mesh_name); + std::string mesh_name, mesh_domain_filename; + mesh_path_gen.GeneratePaths(silo_mesh_path, relative_dir, mesh_domain_filename, mesh_name); - if (mesh_name == "EMPTY") - { - continue; // skip this domain - } + if (mesh_name == "EMPTY") + { + continue; // skip this domain + } - std::string bottom_level_mesh_name, tmp; - conduit::utils::rsplit_file_path(mesh_name, "/", bottom_level_mesh_name, tmp); + std::string bottom_level_mesh_name, tmp; + conduit::utils::rsplit_file_path(mesh_name, "/", bottom_level_mesh_name, tmp); - // root only case - if (mesh_domain_filename.empty()) - { - mesh_domain_filename = root_file_path; - // we are in the root file only case so overlink is not possible - ovltop_case = false; - } + // root only case + if (mesh_domain_filename.empty()) + { + mesh_domain_filename = root_file_path; + // we are in the root file only case so overlink is not possible + ovltop_case = false; + } - detail::SiloObjectWrapperCheckError mesh_domain_file{ - nullptr, &DBClose}; - DBfile *mesh_domain_file_to_use = open_or_reuse_file(ovltop_case, - mesh_domain_filename, "", nullptr, mesh_domain_file); + detail::SiloObjectWrapperCheckError mesh_domain_file{ + nullptr, &DBClose}; + DBfile *mesh_domain_file_to_use = open_or_reuse_file(ovltop_case, + mesh_domain_filename, "", nullptr, mesh_domain_file); - // this is for the blueprint mesh output - std::string domain_path = conduit_fmt::format("domain_{:06d}", domain_id); + // this is for the blueprint mesh output + std::string domain_path = conduit_fmt::format("domain_{:06d}", domain_id); - if (! read_mesh_domain(meshtype, mesh_domain_file_to_use, mesh_name, - mesh_index_name, domain_path, mesh)) - { - continue; // we hit a case where we want to skip this mesh domain - } + if (! read_mesh_domain(meshtype, mesh_domain_file_to_use, mesh_name, + mesh_name_to_read, domain_path, mesh)) + { + continue; // we hit a case where we want to skip this mesh domain + } - // we know we were for sure successful (we didn't skip ahead to the next domain) - // so we create the mesh_out now for good - Node &mesh_out = mesh[domain_path]; + // we know we were for sure successful (we didn't skip ahead to the next domain) + // so we create the mesh_out now for good + Node &mesh_out = mesh[domain_path]; - // - // Read State - // + // + // Read State + // - mesh_out["state"]["domain_id"] = static_cast(domain_id); - if (mesh_index.has_path("state/time")) - { - mesh_out["state"]["time"] = mesh_index["state"]["time"].as_double(); - } - if (mesh_index.has_path("state/cycle")) - { - mesh_out["state"]["cycle"] = (index_t) mesh_index["state"]["cycle"].as_int(); - } + mesh_out["state"]["domain_id"] = static_cast(domain_id); + if (mesh_index.has_path("state/time")) + { + mesh_out["state"]["time"] = mesh_index["state"]["time"].as_double(); + } + if (mesh_index.has_path("state/cycle")) + { + mesh_out["state"]["cycle"] = (index_t) mesh_index["state"]["cycle"].as_int(); + } - // - // Read Adjset (overlink only) - // + // + // Read Adjset (overlink only) + // - read_adjset(mesh_domain_file_to_use, mesh_index_name, domain_id, mesh_out); + read_adjset(mesh_domain_file_to_use, mesh_name_to_read, domain_id, mesh_out); - // - // Read Materials - // + // + // Read Materials + // - // This node will house the recipe for reconstructing matset_values - // from silo mixvals. - Node matset_field_reconstruction; + // This node will house the recipe for reconstructing matset_values + // from silo mixvals. + Node matset_field_reconstruction; - // for each mesh domain, we would like to iterate through all the materials - // and extract the same domain from them. - if (mesh_index.has_child("matsets")) + // for each mesh domain, we would like to iterate through all the materials + // and extract the same domain from them. + if (mesh_index.has_child("matsets")) + { + auto matset_itr = mesh_index["matsets"].children(); + while (matset_itr.has_next()) { - auto matset_itr = mesh_index["matsets"].children(); - while (matset_itr.has_next()) - { - const Node &n_matset = matset_itr.next(); - std::string multimat_name = matset_itr.name(); + const Node &n_matset = matset_itr.next(); + std::string multimat_name = matset_itr.name(); - bool matset_nameschemes = false; - if (n_matset.has_child("nameschemes") && - n_matset["nameschemes"].as_string() == "yes") - { - matset_nameschemes = true; - CONDUIT_ERROR("TODO no support for nameschemes yet"); - } - detail::SiloTreePathGenerator matset_path_gen{matset_nameschemes}; + bool matset_nameschemes = false; + if (n_matset.has_child("nameschemes") && + n_matset["nameschemes"].as_string() == "yes") + { + matset_nameschemes = true; + CONDUIT_ERROR("TODO no support for nameschemes yet"); + } + detail::SiloTreePathGenerator matset_path_gen{matset_nameschemes}; - std::string silo_matset_path = n_matset["matset_paths"][domain_id].as_string(); + std::string silo_matset_path = n_matset["matset_paths"][domain_id].as_string(); - std::string matset_name, matset_domain_filename; - matset_path_gen.GeneratePaths(silo_matset_path, relative_dir, matset_domain_filename, matset_name); + std::string matset_name, matset_domain_filename; + matset_path_gen.GeneratePaths(silo_matset_path, relative_dir, matset_domain_filename, matset_name); - if (matset_name == "EMPTY") - { - // we choose not to write anything to blueprint - continue; - } + if (matset_name == "EMPTY") + { + // we choose not to write anything to blueprint + continue; + } - // root only case - if (matset_domain_filename.empty()) - { - matset_domain_filename = root_file_path; - // we are in the root file only case so overlink is not possible - ovltop_case = false; - } + // root only case + if (matset_domain_filename.empty()) + { + matset_domain_filename = root_file_path; + // we are in the root file only case so overlink is not possible + ovltop_case = false; + } - detail::SiloObjectWrapperCheckError matset_domain_file{ - nullptr, &DBClose}; - DBfile *matset_domain_file_to_use = open_or_reuse_file( - ovltop_case, matset_domain_filename, mesh_domain_filename, - mesh_domain_file.getSiloObject(), matset_domain_file); - - // If this completes successfully, it means we have found a matset - // associated with this mesh. Thus we can break iteration here, - // since there can only be one matset. This is the earliest we can - // break iteration because the silo index may have multiple matsets, - // and we have no way of knowing until now which one is associated - // with our mesh. - // In silo, for each mesh, there can only be one matset, because otherwise - // it would be ambiguous. In Blueprint, we can allow multiple matsets per - // topo, because the fields explicitly link to the matset they use. - if (read_matset_domain(matset_domain_file_to_use, n_matset, matset_name, - mesh_index_name, multimat_name, bottom_level_mesh_name, - opts_matset_style, matset_field_reconstruction, mesh_out)) - { - break; - } + detail::SiloObjectWrapperCheckError matset_domain_file{ + nullptr, &DBClose}; + DBfile *matset_domain_file_to_use = open_or_reuse_file( + ovltop_case, matset_domain_filename, mesh_domain_filename, + mesh_domain_file.getSiloObject(), matset_domain_file); + + // If this completes successfully, it means we have found a matset + // associated with this mesh. Thus we can break iteration here, + // since there can only be one matset. This is the earliest we can + // break iteration because the silo index may have multiple matsets, + // and we have no way of knowing until now which one is associated + // with our mesh. + // In silo, for each mesh, there can only be one matset, because otherwise + // it would be ambiguous. In Blueprint, we can allow multiple matsets per + // topo, because the fields explicitly link to the matset they use. + if (read_matset_domain(matset_domain_file_to_use, n_matset, matset_name, + mesh_name_to_read, multimat_name, bottom_level_mesh_name, + opts_matset_style, matset_field_reconstruction, mesh_out)) + { + break; } } + } - // - // Read Fields - // + // + // Read Fields + // - // for each mesh domain, we would like to iterate through all the variables - // and extract the same domain from them. - if (mesh_index.has_child("vars")) + // for each mesh domain, we would like to iterate through all the variables + // and extract the same domain from them. + if (mesh_index.has_child("vars")) + { + auto var_itr = mesh_index["vars"].children(); + while (var_itr.has_next()) { - auto var_itr = mesh_index["vars"].children(); - while (var_itr.has_next()) - { - const Node &n_var = var_itr.next(); - std::string multivar_name = var_itr.name(); + const Node &n_var = var_itr.next(); + std::string multivar_name = var_itr.name(); - bool var_nameschemes = false; - if (n_var.has_child("nameschemes") && - n_var["nameschemes"].as_string() == "yes") - { - var_nameschemes = true; - CONDUIT_ERROR("TODO no support for nameschemes yet"); - } - detail::SiloTreePathGenerator var_path_gen{var_nameschemes}; - - std::string silo_var_path = n_var["var_paths"][domain_id].as_string(); - int_accessor vartypes = n_var["var_types"].value(); - int vartype = vartypes[domain_id]; + bool var_nameschemes = false; + if (n_var.has_child("nameschemes") && + n_var["nameschemes"].as_string() == "yes") + { + var_nameschemes = true; + CONDUIT_ERROR("TODO no support for nameschemes yet"); + } + detail::SiloTreePathGenerator var_path_gen{var_nameschemes}; - std::string var_name, var_domain_filename; - var_path_gen.GeneratePaths(silo_var_path, relative_dir, var_domain_filename, var_name); + std::string silo_var_path = n_var["var_paths"][domain_id].as_string(); + int_accessor vartypes = n_var["var_types"].value(); + int vartype = vartypes[domain_id]; - if (var_name == "EMPTY") - { - // we choose not to write anything to blueprint - continue; - } + std::string var_name, var_domain_filename; + var_path_gen.GeneratePaths(silo_var_path, relative_dir, var_domain_filename, var_name); - // this info can be tracked with overlink VAR_ATTRIBUTES - std::string volume_dependent = ""; - if (n_var.has_child("volume_dependent")) - { - volume_dependent = n_var["volume_dependent"].as_string(); - } + if (var_name == "EMPTY") + { + // we choose not to write anything to blueprint + continue; + } - // root only case - if (var_domain_filename.empty()) - { - var_domain_filename = root_file_path; - // we are in the root file only case so overlink is not possible - ovltop_case = false; - } + // this info can be tracked with overlink VAR_ATTRIBUTES + std::string volume_dependent = ""; + if (n_var.has_child("volume_dependent")) + { + volume_dependent = n_var["volume_dependent"].as_string(); + } - detail::SiloObjectWrapperCheckError var_domain_file{ - nullptr, &DBClose}; - DBfile *var_domain_file_to_use = open_or_reuse_file( - ovltop_case, var_domain_filename, mesh_domain_filename, - mesh_domain_file.getSiloObject(), var_domain_file); - - // we don't care if this skips the var or not since this is the - // last thing in the loop iteration - read_variable_domain(vartype, var_domain_file_to_use, var_name, - mesh_index_name, multivar_name, bottom_level_mesh_name, - volume_dependent, opts_matset_style, - matset_field_reconstruction, mesh_out); + // root only case + if (var_domain_filename.empty()) + { + var_domain_filename = root_file_path; + // we are in the root file only case so overlink is not possible + ovltop_case = false; } + + detail::SiloObjectWrapperCheckError var_domain_file{ + nullptr, &DBClose}; + DBfile *var_domain_file_to_use = open_or_reuse_file( + ovltop_case, var_domain_filename, mesh_domain_filename, + mesh_domain_file.getSiloObject(), var_domain_file); + + // we don't care if this skips the var or not since this is the + // last thing in the loop iteration + read_variable_domain(vartype, var_domain_file_to_use, var_name, + mesh_name_to_read, multivar_name, bottom_level_mesh_name, + volume_dependent, opts_matset_style, + matset_field_reconstruction, mesh_out); } } } @@ -3638,27 +3341,13 @@ void load_mesh(const std::string &root_file_path, //----------------------------------------------------------------------------- /// opts: -/// silo_names: -/// mesh_names: -/// "{name1}" - meshes with this name will be read if they exist -/// "{name2}" -/// ... -/// or -/// "{all}" - all meshes will be read. -/// or -/// "{none}" - no meshes will be read. -/// var_names: similar to mesh_names. -/// mat_names: similar to mesh_names. -/// matspecies_names: similar to mesh_names. TODO -/// By default, everything in the file will be read unless manually turned off. +/// mesh_name: "{name}" +/// provide explicit mesh name, for cases where silo data includes +/// more than one mesh. /// /// matset_style: "default", "multi_buffer_full", "sparse_by_element", /// "multi_buffer_by_material" /// "default" ==> "sparse_by_element" -/// -/// mesh_name: legacy argument. This is interpreted as a mesh name. -/// It is added to the list of mesh names to read, unless the -/// user has specified "all" or "none", which will supersede this. //----------------------------------------------------------------------------- void load_mesh(const std::string &root_file_path, const Node &opts, diff --git a/src/tests/relay/t_relay_io_silo.cpp b/src/tests/relay/t_relay_io_silo.cpp index 776b6e2e6..be322d6a5 100644 --- a/src/tests/relay/t_relay_io_silo.cpp +++ b/src/tests/relay/t_relay_io_silo.cpp @@ -1568,29 +1568,16 @@ TEST(conduit_relay_io_silo, round_trip_save_option_overlink5) // read options: /// opts: -/// silo_names: -/// mesh_names: -/// "{name1}" - meshes with this name will be read if they exist -/// "{name2}" -/// ... -/// or -/// "{all}" - all meshes will be read. -/// or -/// "{none}" - no meshes will be read. -/// var_names: similar to mesh_names. -/// mat_names: similar to mesh_names. -/// matspecies_names: similar to mesh_names. TODO -/// By default, everything in the file will be read unless manually turned off. +/// mesh_name: "{name}" +/// provide explicit mesh name, for cases where silo data includes +/// more than one mesh. /// /// matset_style: "default", "multi_buffer_full", "sparse_by_element", /// "multi_buffer_by_material" /// "default" ==> "sparse_by_element" -/// -/// mesh_name: legacy argument. This is interpreted as a mesh name. -/// It is added to the list of mesh names to read, unless the -/// user has specified "all" or "none", which will supersede this. //----------------------------------------------------------------------------- +// test legacy mesh name option TEST(conduit_relay_io_silo, round_trip_read_option_mesh_name) { Node load_mesh, info, opts; @@ -1608,374 +1595,473 @@ TEST(conduit_relay_io_silo, round_trip_read_option_mesh_name) } //----------------------------------------------------------------------------- -TEST(conduit_relay_io_silo, round_trip_read_option_matset_style) -{ - // the matset type and the type we are requesting on read - const std::vector> matset_types = { - std::make_pair("full", "full"), - std::make_pair("sparse_by_material", "sparse_by_material"), - std::make_pair("sparse_by_element", "sparse_by_element"), - std::make_pair("sparse_by_element", "full"), - std::make_pair("sparse_by_material", "sparse_by_element"), - std::make_pair("sparse_by_material", "default"), - }; - - for (int i = 0; i < matset_types.size(); i ++) - { - std::string matset_type = matset_types[i].first; - std::string matset_request = matset_types[i].second; - - for (int j = 0; j < 2; j ++) - { - Node mesh_full, mesh_sbe, mesh_sbm, baseline_mesh, load_mesh, info; - std::string size; - int nx, ny; - const double radius = 0.25; - if (j == 0) - { - size = "small"; - nx = ny = 4; - } - else - { - size = "large"; - nx = ny = 100; - } - - blueprint::mesh::examples::venn("full", nx, ny, radius, mesh_full); - blueprint::mesh::examples::venn("sparse_by_material", nx, ny, radius, mesh_sbm); - blueprint::mesh::examples::venn("sparse_by_element", nx, ny, radius, mesh_sbe); - - if (matset_type == "full") - { - baseline_mesh.set_external(mesh_full); - } - else if (matset_type == "sparse_by_material") - { - baseline_mesh.set_external(mesh_sbm); - } - else // (matset_type == "sparse_by_element") - { - baseline_mesh.set_external(mesh_sbe); - } - - Node opts; - if (matset_request == "full") - { - opts["matset_style"] = "multi_buffer_full"; - } - else if (matset_request == "sparse_by_material") - { - opts["matset_style"] = "multi_buffer_by_material"; - } - else if (matset_request == "sparse_by_element") - { - opts["matset_style"] = "sparse_by_element"; - } - else - { - opts["matset_style"] = "default"; - } - - const std::string basename = "silo_venn2_" + matset_type + "_" + size; - const std::string filename = basename + ".root"; - - remove_path_if_exists(filename); - io::silo::save_mesh(baseline_mesh, basename); - io::silo::load_mesh(filename, opts, load_mesh); - EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - - if (matset_request == "full") - { - baseline_mesh.set_external(mesh_full); - } - else if (matset_request == "sparse_by_material") - { - baseline_mesh.set_external(mesh_sbm); - } - else if (matset_request == "sparse_by_element") - { - baseline_mesh.set_external(mesh_sbe); - } - else - { - baseline_mesh.set_external(mesh_sbe); - } - - // make changes to save mesh so the diff will pass - - // The field mat_check has values that are one type and matset_values - // that are another type. The silo writer converts both to double arrays - // in this case, so we follow suit. - Node mat_check_new_values, mat_check_new_matset_values; - baseline_mesh["fields"]["mat_check"]["values"].to_double_array(mat_check_new_values); - if (baseline_mesh["fields"]["mat_check"]["matset_values"].dtype().is_object()) - { - auto mat_vals_itr = baseline_mesh["fields"]["mat_check"]["matset_values"].children(); - while (mat_vals_itr.has_next()) - { - Node &mat_vals_for_mat = mat_vals_itr.next(); - const std::string mat_name = mat_vals_itr.name(); - mat_vals_for_mat.to_double_array(mat_check_new_matset_values[mat_name]); - } - } - else - { - baseline_mesh["fields"]["mat_check"]["matset_values"].to_double_array(mat_check_new_matset_values); - } - baseline_mesh["fields"]["mat_check"]["values"].set_external(mat_check_new_values); - baseline_mesh["fields"]["mat_check"]["matset_values"].set_external(mat_check_new_matset_values); - - silo_name_changer("mesh", baseline_mesh); - - // the loaded mesh will be in the multidomain format - // but the saved mesh is in the single domain format - EXPECT_EQ(load_mesh.number_of_children(), 1); - EXPECT_EQ(load_mesh[0].number_of_children(), baseline_mesh.number_of_children()); - EXPECT_FALSE(load_mesh[0].diff(baseline_mesh, info, CONDUIT_EPSILON, true)); - } - } -} - -//----------------------------------------------------------------------------- - -// -// read and write Silo and Overlink tests -// - -//----------------------------------------------------------------------------- -// read normal silo files containing multimeshes, multivars, and multimats -TEST(conduit_relay_io_silo, read_silo) +TEST(conduit_relay_io_silo, round_trip_read_option_silo_names) { - const std::vector> file_info = { - {".", "multi_curv3d", ".silo"}, - {".", "tire", ".silo"}, - {".", "galaxy0000", ".silo"}, - {".", "emptydomains", ".silo"}, - {"multidir_test_data", "multidir0000", ".root"}, - }; - - // TODO what to do in the case where a multimesh points to no data? (mesh1_back) - // fail silently, as we do now? + Node load_mesh, info; + const std::string path = utils::join_file_path("silo", "multi_curv3d.silo"); + const std::string input_file = relay_test_silo_data_path(path); - for (int i = 0; i < file_info.size(); i ++) + auto check_read_all_case = [&]() { - const std::string dirname = file_info[i][0]; - const std::string basename = file_info[i][1]; - const std::string fileext = file_info[i][2]; - - Node load_mesh, info, write_opts; - std::string filepath = utils::join_file_path(dirname, basename) + fileext; - filepath = utils::join_file_path("silo", filepath); - std::string input_file = relay_test_silo_data_path(filepath); - - io::silo::load_mesh(input_file, load_mesh); EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - - const std::string out_name = "read_silo_" + basename; - - // TODO are these remove paths doing anything? Don't they need filenames? - remove_path_if_exists(out_name + "_write_blueprint"); - io::blueprint::save_mesh(load_mesh, out_name + "_write_blueprint", "hdf5"); - - remove_path_if_exists(out_name + "_write_silo"); - io::silo::save_mesh(load_mesh, out_name + "_write_silo"); - - // overlink requires matsets and does not support point meshes - if (load_mesh[0].has_child("matsets") && basename != "galaxy0000") - { - remove_path_if_exists(out_name + "_write_overlink"); - write_opts["file_style"] = "overlink"; - write_opts["ovl_topo_name"] = "MMESH"; // TODO do I even need this - io::silo::save_mesh(load_mesh, out_name + "_write_overlink", write_opts); - } - } -} - -//----------------------------------------------------------------------------- -// test that we can read silo without multimeshes, multivars, and multimats -TEST(conduit_relay_io_silo, read_simple_silo) -{ - const std::vector> file_info = { - {"curv2d", ".silo", "no"}, - {"curv2d_colmajor", ".silo", "no"}, - {"curv3d", ".silo", "yes"}, - {"curv3d_colmajor", ".silo", "no"}, - // {"globe", ".silo", "yes"}, // TODO need to add support for mixed shape topos + + // check meshes + EXPECT_TRUE(load_mesh[0].has_path("topologies/mesh1")); + EXPECT_TRUE(load_mesh[0].has_path("topologies/mesh1_dup")); + EXPECT_TRUE(load_mesh[0].has_path("topologies/mesh1_hidden")); + EXPECT_TRUE(load_mesh[0]["topologies"].number_of_children() == 3); + + // check vars + EXPECT_TRUE(load_mesh[0].has_path("fields/d")); + EXPECT_TRUE(load_mesh[0].has_path("fields/d_dup")); + EXPECT_TRUE(load_mesh[0].has_path("fields/p")); + EXPECT_TRUE(load_mesh[0].has_path("fields/u")); + EXPECT_TRUE(load_mesh[0].has_path("fields/u_dup")); + EXPECT_TRUE(load_mesh[0].has_path("fields/v")); + EXPECT_TRUE(load_mesh[0].has_path("fields/v_dup")); + EXPECT_TRUE(load_mesh[0].has_path("fields/v_dup_hidden")); + EXPECT_TRUE(load_mesh[0].has_path("fields/w")); + EXPECT_TRUE(load_mesh[0].has_path("fields/w_dup")); + EXPECT_TRUE(load_mesh[0]["fields"].number_of_children() == 10); + + // check mats + EXPECT_TRUE(load_mesh[0].has_path("matsets/mat1")); + EXPECT_TRUE(load_mesh[0]["matsets"].number_of_children() == 1); }; - for (int i = 0; i < file_info.size(); i ++) - { - const std::string basename = file_info[i][0]; - const std::string fileext = file_info[i][1]; - const std::string round_trip = file_info[i][2]; - - Node load_mesh, info, write_opts; - std::string filepath = basename + fileext; - filepath = utils::join_file_path("silo", filepath); - std::string input_file = relay_test_silo_data_path(filepath); + { // read all by default io::silo::load_mesh(input_file, load_mesh); - EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - - const std::string out_name = "read_silo_" + basename; - - // TODO are these remove paths doing anything? Don't they need filenames? - remove_path_if_exists(out_name + "_write_blueprint"); - io::blueprint::save_mesh(load_mesh, out_name + "_write_blueprint", "hdf5"); - - if (round_trip == "yes") - { - remove_path_if_exists(out_name + "_write_silo"); - io::silo::save_mesh(load_mesh, out_name + "_write_silo"); - - // overlink requires matsets - if (load_mesh[0].has_child("matsets")) - { - remove_path_if_exists(out_name + "_write_overlink"); - write_opts["file_style"] = "overlink"; - write_opts["ovl_topo_name"] = "MMESH"; // TODO do I even need this - io::silo::save_mesh(load_mesh, out_name + "_write_overlink", write_opts); - } - } + check_read_all_case(); + load_mesh.reset(); } -} - -//----------------------------------------------------------------------------- -// test that we can read the fake overlink files from the visit test data -TEST(conduit_relay_io_silo, read_fake_overlink) -{ - const std::vector> file_info = { - // {"ev_0_0_100", "OvlTop", ".silo"}, - // uncomment once silo ucdmesh phzones are supported - {"hl18spec", "OvlTop", ".silo"}, - // {"regrovl_qh_1000_10001_4", "OvlTop", ".silo"}, - // uncomment once silo ucdmesh phzones are supported - {"utpyr4", "OvlTop", ".silo"}, - }; - - for (int i = 0; i < file_info.size(); i ++) - { - const std::string dirname = file_info[i][0]; - const std::string basename = file_info[i][1]; - const std::string fileext = file_info[i][2]; - Node load_mesh, info, write_opts; - std::string filepath = utils::join_file_path(dirname, basename) + fileext; - filepath = utils::join_file_path("fake_overlink", filepath); - std::string input_file = relay_test_silo_data_path(filepath); - - io::silo::load_mesh(input_file, load_mesh); - EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - - const std::string out_name = "read_fake_overlink_" + dirname; - - remove_path_if_exists(out_name + "_write_blueprint"); - io::blueprint::save_mesh(load_mesh, out_name + "_write_blueprint", "hdf5"); - - remove_path_if_exists(out_name + "_write_silo"); - io::silo::save_mesh(load_mesh, out_name + "_write_silo"); - - remove_path_if_exists(out_name + "_write_overlink"); - write_opts["file_style"] = "overlink"; - write_opts["ovl_topo_name"] = "MMESH"; - io::silo::save_mesh(load_mesh, out_name + "_write_overlink", write_opts); + { // read all with string + Node opts; + opts["silo_names"]["mesh_names"] = "all"; + opts["silo_names"]["var_names"] = "all"; + opts["silo_names"]["mat_names"] = "all"; + io::silo::load_mesh(input_file, opts, load_mesh); + check_read_all_case(); + load_mesh.reset(); } -} - -//----------------------------------------------------------------------------- -// read overlink files in symlink format -// should be similar to reading raw silo -TEST(conduit_relay_io_silo, read_overlink_symlink_format) -{ - const std::vector> file_info = { - {".", "box2d", ".silo"}, - {".", "box3d", ".silo"}, - // {".", "diamond", ".silo"}, - // fails b/c polytopal not yet supported - {".", "testDisk2D_a", ".silo"}, - // {".", "donordiv.s2_materials2", ".silo"}, - // fails b/c polytopal not yet supported - {".", "donordiv.s2_materials3", ".silo"}, - }; - - for (int i = 0; i < file_info.size(); i ++) - { - const std::string dirname = file_info[i][0]; - const std::string basename = file_info[i][1]; - const std::string fileext = file_info[i][2]; - - Node load_mesh, info, write_opts; - std::string filepath = utils::join_file_path(dirname, basename) + fileext; - filepath = utils::join_file_path("overlink", filepath); - std::string input_file = relay_test_silo_data_path(filepath); - io::silo::load_mesh(input_file, load_mesh); - EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - - const std::string out_name = "read_overlink_symlink_" + basename; - - remove_path_if_exists(out_name + "_write_blueprint"); - io::blueprint::save_mesh(load_mesh, out_name + "_write_blueprint", "hdf5"); - - remove_path_if_exists(out_name + "_write_silo"); - io::silo::save_mesh(load_mesh, out_name + "_write_silo"); - - remove_path_if_exists(out_name + "_write_overlink"); - write_opts["file_style"] = "overlink"; - write_opts["ovl_topo_name"] = "MMESH"; - io::silo::save_mesh(load_mesh, out_name + "_write_overlink", write_opts); + { // read all with child + Node opts; + opts["silo_names"]["mesh_names"]["all"]; + opts["silo_names"]["var_names"]["all"]; + opts["silo_names"]["mat_names"]["all"]; + io::silo::load_mesh(input_file, opts, load_mesh); + check_read_all_case(); + load_mesh.reset(); } -} - -//----------------------------------------------------------------------------- -// read overlink directly from ovltop.silo -// this case is tricky and involves messing with paths -TEST(conduit_relay_io_silo, read_overlink_directly) -{ - const std::vector> file_info = { - {"box2d", "OvlTop", ".silo"}, - {"box3d", "OvlTop", ".silo"}, - // {"diamond", "OvlTop", ".silo"}, - {"testDisk2D_a", "OvlTop", ".silo"}, - // {"donordiv.s2_materials2", "OvlTop", ".silo"}, - {"donordiv.s2_materials3", "OvlTop", ".silo"}, - }; - for (int i = 0; i < file_info.size(); i ++) + // we will still read all meshes so there is something to read + auto check_read_none_case = [&]() { - const std::string dirname = file_info[i][0]; - const std::string basename = file_info[i][1]; - const std::string fileext = file_info[i][2]; - - Node load_mesh, info, write_opts; - - std::string filepath = utils::join_file_path(dirname, basename) + fileext; - filepath = utils::join_file_path("overlink", filepath); - std::string input_file = relay_test_silo_data_path(filepath); - - io::silo::load_mesh(input_file, load_mesh); EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + + // check meshes + EXPECT_TRUE(load_mesh[0].has_path("topologies/mesh1")); + EXPECT_TRUE(load_mesh[0].has_path("topologies/mesh1_dup")); + EXPECT_TRUE(load_mesh[0].has_path("topologies/mesh1_hidden")); + EXPECT_TRUE(load_mesh[0]["topologies"].number_of_children() == 3); - const std::string out_name = "read_overlink_direct_" + dirname; + // check vars + EXPECT_FALSE(load_mesh[0].has_child("fields")); - remove_path_if_exists(out_name + "_write_blueprint"); - io::blueprint::save_mesh(load_mesh, out_name + "_write_blueprint", "hdf5"); + // check mats + EXPECT_FALSE(load_mesh[0].has_child("matsets")); + }; - remove_path_if_exists(out_name + "_write_silo"); - io::silo::save_mesh(load_mesh, out_name + "_write_silo"); + { // read none with string + Node opts; + opts["silo_names"]["var_names"] = "none"; + opts["silo_names"]["mat_names"] = "none"; + io::silo::load_mesh(input_file, opts, load_mesh); + check_read_none_case(); + load_mesh.reset(); + } - remove_path_if_exists(out_name + "_write_overlink"); - write_opts["file_style"] = "overlink"; - write_opts["ovl_topo_name"] = "MMESH"; - io::silo::save_mesh(load_mesh, out_name + "_write_overlink", write_opts); + { // read none with child + Node opts; + opts["silo_names"]["var_names"]["none"]; + opts["silo_names"]["mat_names"]["none"]; + io::silo::load_mesh(input_file, opts, load_mesh); + load_mesh.print(); + check_read_none_case(); + load_mesh.reset(); } } -// TODO add tests for... -// - polytopal meshes once they are supported -// - units once they are supported -// - etc. - -// TODO add tetra8 and c36_m5 to all the overlink i/o tests - -// TODO somewhere I need to error on overlink when there are different var or mesh types across domains +// //----------------------------------------------------------------------------- +// TEST(conduit_relay_io_silo, round_trip_read_option_matset_style) +// { +// // the matset type and the type we are requesting on read +// const std::vector> matset_types = { +// std::make_pair("full", "full"), +// std::make_pair("sparse_by_material", "sparse_by_material"), +// std::make_pair("sparse_by_element", "sparse_by_element"), +// std::make_pair("sparse_by_element", "full"), +// std::make_pair("sparse_by_material", "sparse_by_element"), +// std::make_pair("sparse_by_material", "default"), +// }; + +// for (int i = 0; i < matset_types.size(); i ++) +// { +// std::string matset_type = matset_types[i].first; +// std::string matset_request = matset_types[i].second; + +// for (int j = 0; j < 2; j ++) +// { +// Node mesh_full, mesh_sbe, mesh_sbm, baseline_mesh, load_mesh, info; +// std::string size; +// int nx, ny; +// const double radius = 0.25; +// if (j == 0) +// { +// size = "small"; +// nx = ny = 4; +// } +// else +// { +// size = "large"; +// nx = ny = 100; +// } + +// blueprint::mesh::examples::venn("full", nx, ny, radius, mesh_full); +// blueprint::mesh::examples::venn("sparse_by_material", nx, ny, radius, mesh_sbm); +// blueprint::mesh::examples::venn("sparse_by_element", nx, ny, radius, mesh_sbe); + +// if (matset_type == "full") +// { +// baseline_mesh.set_external(mesh_full); +// } +// else if (matset_type == "sparse_by_material") +// { +// baseline_mesh.set_external(mesh_sbm); +// } +// else // (matset_type == "sparse_by_element") +// { +// baseline_mesh.set_external(mesh_sbe); +// } + +// Node opts; +// if (matset_request == "full") +// { +// opts["matset_style"] = "multi_buffer_full"; +// } +// else if (matset_request == "sparse_by_material") +// { +// opts["matset_style"] = "multi_buffer_by_material"; +// } +// else if (matset_request == "sparse_by_element") +// { +// opts["matset_style"] = "sparse_by_element"; +// } +// else +// { +// opts["matset_style"] = "default"; +// } + +// const std::string basename = "silo_venn2_" + matset_type + "_" + size; +// const std::string filename = basename + ".root"; + +// remove_path_if_exists(filename); +// io::silo::save_mesh(baseline_mesh, basename); +// io::silo::load_mesh(filename, opts, load_mesh); +// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + +// if (matset_request == "full") +// { +// baseline_mesh.set_external(mesh_full); +// } +// else if (matset_request == "sparse_by_material") +// { +// baseline_mesh.set_external(mesh_sbm); +// } +// else if (matset_request == "sparse_by_element") +// { +// baseline_mesh.set_external(mesh_sbe); +// } +// else +// { +// baseline_mesh.set_external(mesh_sbe); +// } + +// // make changes to save mesh so the diff will pass + +// // The field mat_check has values that are one type and matset_values +// // that are another type. The silo writer converts both to double arrays +// // in this case, so we follow suit. +// Node mat_check_new_values, mat_check_new_matset_values; +// baseline_mesh["fields"]["mat_check"]["values"].to_double_array(mat_check_new_values); +// if (baseline_mesh["fields"]["mat_check"]["matset_values"].dtype().is_object()) +// { +// auto mat_vals_itr = baseline_mesh["fields"]["mat_check"]["matset_values"].children(); +// while (mat_vals_itr.has_next()) +// { +// Node &mat_vals_for_mat = mat_vals_itr.next(); +// const std::string mat_name = mat_vals_itr.name(); +// mat_vals_for_mat.to_double_array(mat_check_new_matset_values[mat_name]); +// } +// } +// else +// { +// baseline_mesh["fields"]["mat_check"]["matset_values"].to_double_array(mat_check_new_matset_values); +// } +// baseline_mesh["fields"]["mat_check"]["values"].set_external(mat_check_new_values); +// baseline_mesh["fields"]["mat_check"]["matset_values"].set_external(mat_check_new_matset_values); + +// silo_name_changer("mesh", baseline_mesh); + +// // the loaded mesh will be in the multidomain format +// // but the saved mesh is in the single domain format +// EXPECT_EQ(load_mesh.number_of_children(), 1); +// EXPECT_EQ(load_mesh[0].number_of_children(), baseline_mesh.number_of_children()); +// EXPECT_FALSE(load_mesh[0].diff(baseline_mesh, info, CONDUIT_EPSILON, true)); +// } +// } +// } + +// //----------------------------------------------------------------------------- + +// // +// // read and write Silo and Overlink tests +// // + +// //----------------------------------------------------------------------------- +// // read normal silo files containing multimeshes, multivars, and multimats +// TEST(conduit_relay_io_silo, read_silo) +// { +// const std::vector> file_info = { +// {".", "multi_curv3d", ".silo"}, +// {".", "tire", ".silo"}, +// {".", "galaxy0000", ".silo"}, +// {".", "emptydomains", ".silo"}, +// {"multidir_test_data", "multidir0000", ".root"}, +// }; + +// // TODO what to do in the case where a multimesh points to no data? (mesh1_back) +// // fail silently, as we do now? + +// for (int i = 0; i < file_info.size(); i ++) +// { +// const std::string dirname = file_info[i][0]; +// const std::string basename = file_info[i][1]; +// const std::string fileext = file_info[i][2]; + +// Node load_mesh, info, write_opts; +// std::string filepath = utils::join_file_path(dirname, basename) + fileext; +// filepath = utils::join_file_path("silo", filepath); +// std::string input_file = relay_test_silo_data_path(filepath); + +// io::silo::load_mesh(input_file, load_mesh); +// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + +// const std::string out_name = "read_silo_" + basename; + +// // TODO are these remove paths doing anything? Don't they need filenames? +// remove_path_if_exists(out_name + "_write_blueprint"); +// io::blueprint::save_mesh(load_mesh, out_name + "_write_blueprint", "hdf5"); + +// remove_path_if_exists(out_name + "_write_silo"); +// io::silo::save_mesh(load_mesh, out_name + "_write_silo"); + +// // overlink requires matsets and does not support point meshes +// if (load_mesh[0].has_child("matsets") && basename != "galaxy0000") +// { +// remove_path_if_exists(out_name + "_write_overlink"); +// write_opts["file_style"] = "overlink"; +// write_opts["ovl_topo_name"] = "MMESH"; // TODO do I even need this +// io::silo::save_mesh(load_mesh, out_name + "_write_overlink", write_opts); +// } +// } +// } + +// //----------------------------------------------------------------------------- +// // test that we can read silo without multimeshes, multivars, and multimats +// TEST(conduit_relay_io_silo, read_simple_silo) +// { +// const std::vector> file_info = { +// {"curv2d", ".silo", "no"}, +// {"curv2d_colmajor", ".silo", "no"}, +// {"curv3d", ".silo", "yes"}, +// {"curv3d_colmajor", ".silo", "no"}, +// // {"globe", ".silo", "yes"}, // TODO need to add support for mixed shape topos +// }; +// for (int i = 0; i < file_info.size(); i ++) +// { +// const std::string basename = file_info[i][0]; +// const std::string fileext = file_info[i][1]; +// const std::string round_trip = file_info[i][2]; + +// Node load_mesh, info, write_opts; +// std::string filepath = basename + fileext; +// filepath = utils::join_file_path("silo", filepath); +// std::string input_file = relay_test_silo_data_path(filepath); + +// io::silo::load_mesh(input_file, load_mesh); +// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + +// const std::string out_name = "read_silo_" + basename; + +// // TODO are these remove paths doing anything? Don't they need filenames? +// remove_path_if_exists(out_name + "_write_blueprint"); +// io::blueprint::save_mesh(load_mesh, out_name + "_write_blueprint", "hdf5"); + +// if (round_trip == "yes") +// { +// remove_path_if_exists(out_name + "_write_silo"); +// io::silo::save_mesh(load_mesh, out_name + "_write_silo"); + +// // overlink requires matsets +// if (load_mesh[0].has_child("matsets")) +// { +// remove_path_if_exists(out_name + "_write_overlink"); +// write_opts["file_style"] = "overlink"; +// write_opts["ovl_topo_name"] = "MMESH"; // TODO do I even need this +// io::silo::save_mesh(load_mesh, out_name + "_write_overlink", write_opts); +// } +// } +// } +// } + +// //----------------------------------------------------------------------------- +// // test that we can read the fake overlink files from the visit test data +// TEST(conduit_relay_io_silo, read_fake_overlink) +// { +// const std::vector> file_info = { +// // {"ev_0_0_100", "OvlTop", ".silo"}, +// // uncomment once silo ucdmesh phzones are supported +// {"hl18spec", "OvlTop", ".silo"}, +// // {"regrovl_qh_1000_10001_4", "OvlTop", ".silo"}, +// // uncomment once silo ucdmesh phzones are supported +// {"utpyr4", "OvlTop", ".silo"}, +// }; + +// for (int i = 0; i < file_info.size(); i ++) +// { +// const std::string dirname = file_info[i][0]; +// const std::string basename = file_info[i][1]; +// const std::string fileext = file_info[i][2]; + +// Node load_mesh, info, write_opts; +// std::string filepath = utils::join_file_path(dirname, basename) + fileext; +// filepath = utils::join_file_path("fake_overlink", filepath); +// std::string input_file = relay_test_silo_data_path(filepath); + +// io::silo::load_mesh(input_file, load_mesh); +// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + +// const std::string out_name = "read_fake_overlink_" + dirname; + +// remove_path_if_exists(out_name + "_write_blueprint"); +// io::blueprint::save_mesh(load_mesh, out_name + "_write_blueprint", "hdf5"); + +// remove_path_if_exists(out_name + "_write_silo"); +// io::silo::save_mesh(load_mesh, out_name + "_write_silo"); + +// remove_path_if_exists(out_name + "_write_overlink"); +// write_opts["file_style"] = "overlink"; +// write_opts["ovl_topo_name"] = "MMESH"; +// io::silo::save_mesh(load_mesh, out_name + "_write_overlink", write_opts); +// } +// } + +// //----------------------------------------------------------------------------- +// // read overlink files in symlink format +// // should be similar to reading raw silo +// TEST(conduit_relay_io_silo, read_overlink_symlink_format) +// { +// const std::vector> file_info = { +// {".", "box2d", ".silo"}, +// {".", "box3d", ".silo"}, +// // {".", "diamond", ".silo"}, +// // fails b/c polytopal not yet supported +// {".", "testDisk2D_a", ".silo"}, +// // {".", "donordiv.s2_materials2", ".silo"}, +// // fails b/c polytopal not yet supported +// {".", "donordiv.s2_materials3", ".silo"}, +// }; + +// for (int i = 0; i < file_info.size(); i ++) +// { +// const std::string dirname = file_info[i][0]; +// const std::string basename = file_info[i][1]; +// const std::string fileext = file_info[i][2]; + +// Node load_mesh, info, write_opts; +// std::string filepath = utils::join_file_path(dirname, basename) + fileext; +// filepath = utils::join_file_path("overlink", filepath); +// std::string input_file = relay_test_silo_data_path(filepath); + +// io::silo::load_mesh(input_file, load_mesh); +// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + +// const std::string out_name = "read_overlink_symlink_" + basename; + +// remove_path_if_exists(out_name + "_write_blueprint"); +// io::blueprint::save_mesh(load_mesh, out_name + "_write_blueprint", "hdf5"); + +// remove_path_if_exists(out_name + "_write_silo"); +// io::silo::save_mesh(load_mesh, out_name + "_write_silo"); + +// remove_path_if_exists(out_name + "_write_overlink"); +// write_opts["file_style"] = "overlink"; +// write_opts["ovl_topo_name"] = "MMESH"; +// io::silo::save_mesh(load_mesh, out_name + "_write_overlink", write_opts); +// } +// } + +// //----------------------------------------------------------------------------- +// // read overlink directly from ovltop.silo +// // this case is tricky and involves messing with paths +// TEST(conduit_relay_io_silo, read_overlink_directly) +// { +// const std::vector> file_info = { +// {"box2d", "OvlTop", ".silo"}, +// {"box3d", "OvlTop", ".silo"}, +// // {"diamond", "OvlTop", ".silo"}, +// {"testDisk2D_a", "OvlTop", ".silo"}, +// // {"donordiv.s2_materials2", "OvlTop", ".silo"}, +// {"donordiv.s2_materials3", "OvlTop", ".silo"}, +// }; + +// for (int i = 0; i < file_info.size(); i ++) +// { +// const std::string dirname = file_info[i][0]; +// const std::string basename = file_info[i][1]; +// const std::string fileext = file_info[i][2]; + +// Node load_mesh, info, write_opts; + +// std::string filepath = utils::join_file_path(dirname, basename) + fileext; +// filepath = utils::join_file_path("overlink", filepath); +// std::string input_file = relay_test_silo_data_path(filepath); + +// io::silo::load_mesh(input_file, load_mesh); +// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + +// const std::string out_name = "read_overlink_direct_" + dirname; + +// remove_path_if_exists(out_name + "_write_blueprint"); +// io::blueprint::save_mesh(load_mesh, out_name + "_write_blueprint", "hdf5"); + +// remove_path_if_exists(out_name + "_write_silo"); +// io::silo::save_mesh(load_mesh, out_name + "_write_silo"); + +// remove_path_if_exists(out_name + "_write_overlink"); +// write_opts["file_style"] = "overlink"; +// write_opts["ovl_topo_name"] = "MMESH"; +// io::silo::save_mesh(load_mesh, out_name + "_write_overlink", write_opts); +// } +// } + +// // TODO add tests for... +// // - polytopal meshes once they are supported +// // - units once they are supported +// // - etc. + +// // TODO add tetra8 and c36_m5 to all the overlink i/o tests + +// // TODO somewhere I need to error on overlink when there are different var or mesh types across domains From bb56d77cab4f7c926b3742bf74fa622c61623cbb Mon Sep 17 00:00:00 2001 From: Justin Privitera Date: Tue, 23 Apr 2024 08:33:59 -0700 Subject: [PATCH 49/56] remove bad test --- src/tests/relay/t_relay_io_silo.cpp | 797 ++++++++++++---------------- 1 file changed, 349 insertions(+), 448 deletions(-) diff --git a/src/tests/relay/t_relay_io_silo.cpp b/src/tests/relay/t_relay_io_silo.cpp index be322d6a5..1c635d84b 100644 --- a/src/tests/relay/t_relay_io_silo.cpp +++ b/src/tests/relay/t_relay_io_silo.cpp @@ -1594,474 +1594,375 @@ TEST(conduit_relay_io_silo, round_trip_read_option_mesh_name) EXPECT_TRUE(load_mesh[0]["topologies"].number_of_children() == 1); } +TEST(conduit_relay_io_silo, round_trip_read_option_matset_style) //----------------------------------------------------------------------------- -TEST(conduit_relay_io_silo, round_trip_read_option_silo_names) { - Node load_mesh, info; - const std::string path = utils::join_file_path("silo", "multi_curv3d.silo"); - const std::string input_file = relay_test_silo_data_path(path); + // the matset type and the type we are requesting on read + const std::vector> matset_types = { + std::make_pair("full", "full"), + std::make_pair("sparse_by_material", "sparse_by_material"), + std::make_pair("sparse_by_element", "sparse_by_element"), + std::make_pair("sparse_by_element", "full"), + std::make_pair("sparse_by_material", "sparse_by_element"), + std::make_pair("sparse_by_material", "default"), + }; - auto check_read_all_case = [&]() + for (int i = 0; i < matset_types.size(); i ++) { - EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - - // check meshes - EXPECT_TRUE(load_mesh[0].has_path("topologies/mesh1")); - EXPECT_TRUE(load_mesh[0].has_path("topologies/mesh1_dup")); - EXPECT_TRUE(load_mesh[0].has_path("topologies/mesh1_hidden")); - EXPECT_TRUE(load_mesh[0]["topologies"].number_of_children() == 3); - - // check vars - EXPECT_TRUE(load_mesh[0].has_path("fields/d")); - EXPECT_TRUE(load_mesh[0].has_path("fields/d_dup")); - EXPECT_TRUE(load_mesh[0].has_path("fields/p")); - EXPECT_TRUE(load_mesh[0].has_path("fields/u")); - EXPECT_TRUE(load_mesh[0].has_path("fields/u_dup")); - EXPECT_TRUE(load_mesh[0].has_path("fields/v")); - EXPECT_TRUE(load_mesh[0].has_path("fields/v_dup")); - EXPECT_TRUE(load_mesh[0].has_path("fields/v_dup_hidden")); - EXPECT_TRUE(load_mesh[0].has_path("fields/w")); - EXPECT_TRUE(load_mesh[0].has_path("fields/w_dup")); - EXPECT_TRUE(load_mesh[0]["fields"].number_of_children() == 10); - - // check mats - EXPECT_TRUE(load_mesh[0].has_path("matsets/mat1")); - EXPECT_TRUE(load_mesh[0]["matsets"].number_of_children() == 1); + std::string matset_type = matset_types[i].first; + std::string matset_request = matset_types[i].second; + + for (int j = 0; j < 2; j ++) + { + Node mesh_full, mesh_sbe, mesh_sbm, baseline_mesh, load_mesh, info; + std::string size; + int nx, ny; + const double radius = 0.25; + if (j == 0) + { + size = "small"; + nx = ny = 4; + } + else + { + size = "large"; + nx = ny = 100; + } + + blueprint::mesh::examples::venn("full", nx, ny, radius, mesh_full); + blueprint::mesh::examples::venn("sparse_by_material", nx, ny, radius, mesh_sbm); + blueprint::mesh::examples::venn("sparse_by_element", nx, ny, radius, mesh_sbe); + + if (matset_type == "full") + { + baseline_mesh.set_external(mesh_full); + } + else if (matset_type == "sparse_by_material") + { + baseline_mesh.set_external(mesh_sbm); + } + else // (matset_type == "sparse_by_element") + { + baseline_mesh.set_external(mesh_sbe); + } + + Node opts; + if (matset_request == "full") + { + opts["matset_style"] = "multi_buffer_full"; + } + else if (matset_request == "sparse_by_material") + { + opts["matset_style"] = "multi_buffer_by_material"; + } + else if (matset_request == "sparse_by_element") + { + opts["matset_style"] = "sparse_by_element"; + } + else + { + opts["matset_style"] = "default"; + } + + const std::string basename = "silo_venn2_" + matset_type + "_" + size; + const std::string filename = basename + ".root"; + + remove_path_if_exists(filename); + io::silo::save_mesh(baseline_mesh, basename); + io::silo::load_mesh(filename, opts, load_mesh); + EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + + if (matset_request == "full") + { + baseline_mesh.set_external(mesh_full); + } + else if (matset_request == "sparse_by_material") + { + baseline_mesh.set_external(mesh_sbm); + } + else if (matset_request == "sparse_by_element") + { + baseline_mesh.set_external(mesh_sbe); + } + else + { + baseline_mesh.set_external(mesh_sbe); + } + + // make changes to save mesh so the diff will pass + + // The field mat_check has values that are one type and matset_values + // that are another type. The silo writer converts both to double arrays + // in this case, so we follow suit. + Node mat_check_new_values, mat_check_new_matset_values; + baseline_mesh["fields"]["mat_check"]["values"].to_double_array(mat_check_new_values); + if (baseline_mesh["fields"]["mat_check"]["matset_values"].dtype().is_object()) + { + auto mat_vals_itr = baseline_mesh["fields"]["mat_check"]["matset_values"].children(); + while (mat_vals_itr.has_next()) + { + Node &mat_vals_for_mat = mat_vals_itr.next(); + const std::string mat_name = mat_vals_itr.name(); + mat_vals_for_mat.to_double_array(mat_check_new_matset_values[mat_name]); + } + } + else + { + baseline_mesh["fields"]["mat_check"]["matset_values"].to_double_array(mat_check_new_matset_values); + } + baseline_mesh["fields"]["mat_check"]["values"].set_external(mat_check_new_values); + baseline_mesh["fields"]["mat_check"]["matset_values"].set_external(mat_check_new_matset_values); + + silo_name_changer("mesh", baseline_mesh); + + // the loaded mesh will be in the multidomain format + // but the saved mesh is in the single domain format + EXPECT_EQ(load_mesh.number_of_children(), 1); + EXPECT_EQ(load_mesh[0].number_of_children(), baseline_mesh.number_of_children()); + EXPECT_FALSE(load_mesh[0].diff(baseline_mesh, info, CONDUIT_EPSILON, true)); + } + } +} + +//----------------------------------------------------------------------------- + +// +// read and write Silo and Overlink tests +// + +//----------------------------------------------------------------------------- +// read normal silo files containing multimeshes, multivars, and multimats +TEST(conduit_relay_io_silo, read_silo) +{ + const std::vector> file_info = { + {".", "multi_curv3d", ".silo"}, + {".", "tire", ".silo"}, + {".", "galaxy0000", ".silo"}, + {".", "emptydomains", ".silo"}, + {"multidir_test_data", "multidir0000", ".root"}, }; - { // read all by default + // TODO what to do in the case where a multimesh points to no data? (mesh1_back) + // fail silently, as we do now? + + for (int i = 0; i < file_info.size(); i ++) + { + const std::string dirname = file_info[i][0]; + const std::string basename = file_info[i][1]; + const std::string fileext = file_info[i][2]; + + Node load_mesh, info, write_opts; + std::string filepath = utils::join_file_path(dirname, basename) + fileext; + filepath = utils::join_file_path("silo", filepath); + std::string input_file = relay_test_silo_data_path(filepath); + io::silo::load_mesh(input_file, load_mesh); - check_read_all_case(); - load_mesh.reset(); - } + EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - { // read all with string - Node opts; - opts["silo_names"]["mesh_names"] = "all"; - opts["silo_names"]["var_names"] = "all"; - opts["silo_names"]["mat_names"] = "all"; - io::silo::load_mesh(input_file, opts, load_mesh); - check_read_all_case(); - load_mesh.reset(); + const std::string out_name = "read_silo_" + basename; + + // TODO are these remove paths doing anything? Don't they need filenames? + remove_path_if_exists(out_name + "_write_blueprint"); + io::blueprint::save_mesh(load_mesh, out_name + "_write_blueprint", "hdf5"); + + remove_path_if_exists(out_name + "_write_silo"); + io::silo::save_mesh(load_mesh, out_name + "_write_silo"); + + // overlink requires matsets and does not support point meshes + if (load_mesh[0].has_child("matsets") && basename != "galaxy0000") + { + remove_path_if_exists(out_name + "_write_overlink"); + write_opts["file_style"] = "overlink"; + write_opts["ovl_topo_name"] = "MMESH"; // TODO do I even need this + io::silo::save_mesh(load_mesh, out_name + "_write_overlink", write_opts); + } } +} - { // read all with child - Node opts; - opts["silo_names"]["mesh_names"]["all"]; - opts["silo_names"]["var_names"]["all"]; - opts["silo_names"]["mat_names"]["all"]; - io::silo::load_mesh(input_file, opts, load_mesh); - check_read_all_case(); - load_mesh.reset(); +//----------------------------------------------------------------------------- +// test that we can read silo without multimeshes, multivars, and multimats +TEST(conduit_relay_io_silo, read_simple_silo) +{ + const std::vector> file_info = { + {"curv2d", ".silo", "no"}, + {"curv2d_colmajor", ".silo", "no"}, + {"curv3d", ".silo", "yes"}, + {"curv3d_colmajor", ".silo", "no"}, + // {"globe", ".silo", "yes"}, // TODO need to add support for mixed shape topos + }; + for (int i = 0; i < file_info.size(); i ++) + { + const std::string basename = file_info[i][0]; + const std::string fileext = file_info[i][1]; + const std::string round_trip = file_info[i][2]; + + Node load_mesh, info, write_opts; + std::string filepath = basename + fileext; + filepath = utils::join_file_path("silo", filepath); + std::string input_file = relay_test_silo_data_path(filepath); + + io::silo::load_mesh(input_file, load_mesh); + EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + + const std::string out_name = "read_silo_" + basename; + + // TODO are these remove paths doing anything? Don't they need filenames? + remove_path_if_exists(out_name + "_write_blueprint"); + io::blueprint::save_mesh(load_mesh, out_name + "_write_blueprint", "hdf5"); + + if (round_trip == "yes") + { + remove_path_if_exists(out_name + "_write_silo"); + io::silo::save_mesh(load_mesh, out_name + "_write_silo"); + + // overlink requires matsets + if (load_mesh[0].has_child("matsets")) + { + remove_path_if_exists(out_name + "_write_overlink"); + write_opts["file_style"] = "overlink"; + write_opts["ovl_topo_name"] = "MMESH"; // TODO do I even need this + io::silo::save_mesh(load_mesh, out_name + "_write_overlink", write_opts); + } + } } +} - // we will still read all meshes so there is something to read - auto check_read_none_case = [&]() +//----------------------------------------------------------------------------- +// test that we can read the fake overlink files from the visit test data +TEST(conduit_relay_io_silo, read_fake_overlink) +{ + const std::vector> file_info = { + // {"ev_0_0_100", "OvlTop", ".silo"}, + // uncomment once silo ucdmesh phzones are supported + {"hl18spec", "OvlTop", ".silo"}, + // {"regrovl_qh_1000_10001_4", "OvlTop", ".silo"}, + // uncomment once silo ucdmesh phzones are supported + {"utpyr4", "OvlTop", ".silo"}, + }; + + for (int i = 0; i < file_info.size(); i ++) { + const std::string dirname = file_info[i][0]; + const std::string basename = file_info[i][1]; + const std::string fileext = file_info[i][2]; + + Node load_mesh, info, write_opts; + std::string filepath = utils::join_file_path(dirname, basename) + fileext; + filepath = utils::join_file_path("fake_overlink", filepath); + std::string input_file = relay_test_silo_data_path(filepath); + + io::silo::load_mesh(input_file, load_mesh); EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - - // check meshes - EXPECT_TRUE(load_mesh[0].has_path("topologies/mesh1")); - EXPECT_TRUE(load_mesh[0].has_path("topologies/mesh1_dup")); - EXPECT_TRUE(load_mesh[0].has_path("topologies/mesh1_hidden")); - EXPECT_TRUE(load_mesh[0]["topologies"].number_of_children() == 3); - // check vars - EXPECT_FALSE(load_mesh[0].has_child("fields")); + const std::string out_name = "read_fake_overlink_" + dirname; + + remove_path_if_exists(out_name + "_write_blueprint"); + io::blueprint::save_mesh(load_mesh, out_name + "_write_blueprint", "hdf5"); - // check mats - EXPECT_FALSE(load_mesh[0].has_child("matsets")); + remove_path_if_exists(out_name + "_write_silo"); + io::silo::save_mesh(load_mesh, out_name + "_write_silo"); + + remove_path_if_exists(out_name + "_write_overlink"); + write_opts["file_style"] = "overlink"; + write_opts["ovl_topo_name"] = "MMESH"; + io::silo::save_mesh(load_mesh, out_name + "_write_overlink", write_opts); + } +} + +//----------------------------------------------------------------------------- +// read overlink files in symlink format +// should be similar to reading raw silo +TEST(conduit_relay_io_silo, read_overlink_symlink_format) +{ + const std::vector> file_info = { + {".", "box2d", ".silo"}, + {".", "box3d", ".silo"}, + // {".", "diamond", ".silo"}, + // fails b/c polytopal not yet supported + {".", "testDisk2D_a", ".silo"}, + // {".", "donordiv.s2_materials2", ".silo"}, + // fails b/c polytopal not yet supported + {".", "donordiv.s2_materials3", ".silo"}, }; - { // read none with string - Node opts; - opts["silo_names"]["var_names"] = "none"; - opts["silo_names"]["mat_names"] = "none"; - io::silo::load_mesh(input_file, opts, load_mesh); - check_read_none_case(); - load_mesh.reset(); + for (int i = 0; i < file_info.size(); i ++) + { + const std::string dirname = file_info[i][0]; + const std::string basename = file_info[i][1]; + const std::string fileext = file_info[i][2]; + + Node load_mesh, info, write_opts; + std::string filepath = utils::join_file_path(dirname, basename) + fileext; + filepath = utils::join_file_path("overlink", filepath); + std::string input_file = relay_test_silo_data_path(filepath); + + io::silo::load_mesh(input_file, load_mesh); + EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + + const std::string out_name = "read_overlink_symlink_" + basename; + + remove_path_if_exists(out_name + "_write_blueprint"); + io::blueprint::save_mesh(load_mesh, out_name + "_write_blueprint", "hdf5"); + + remove_path_if_exists(out_name + "_write_silo"); + io::silo::save_mesh(load_mesh, out_name + "_write_silo"); + + remove_path_if_exists(out_name + "_write_overlink"); + write_opts["file_style"] = "overlink"; + write_opts["ovl_topo_name"] = "MMESH"; + io::silo::save_mesh(load_mesh, out_name + "_write_overlink", write_opts); } +} - { // read none with child - Node opts; - opts["silo_names"]["var_names"]["none"]; - opts["silo_names"]["mat_names"]["none"]; - io::silo::load_mesh(input_file, opts, load_mesh); - load_mesh.print(); - check_read_none_case(); - load_mesh.reset(); +//----------------------------------------------------------------------------- +// read overlink directly from ovltop.silo +// this case is tricky and involves messing with paths +TEST(conduit_relay_io_silo, read_overlink_directly) +{ + const std::vector> file_info = { + {"box2d", "OvlTop", ".silo"}, + {"box3d", "OvlTop", ".silo"}, + // {"diamond", "OvlTop", ".silo"}, + {"testDisk2D_a", "OvlTop", ".silo"}, + // {"donordiv.s2_materials2", "OvlTop", ".silo"}, + {"donordiv.s2_materials3", "OvlTop", ".silo"}, + }; + + for (int i = 0; i < file_info.size(); i ++) + { + const std::string dirname = file_info[i][0]; + const std::string basename = file_info[i][1]; + const std::string fileext = file_info[i][2]; + + Node load_mesh, info, write_opts; + + std::string filepath = utils::join_file_path(dirname, basename) + fileext; + filepath = utils::join_file_path("overlink", filepath); + std::string input_file = relay_test_silo_data_path(filepath); + + io::silo::load_mesh(input_file, load_mesh); + EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + + const std::string out_name = "read_overlink_direct_" + dirname; + + remove_path_if_exists(out_name + "_write_blueprint"); + io::blueprint::save_mesh(load_mesh, out_name + "_write_blueprint", "hdf5"); + + remove_path_if_exists(out_name + "_write_silo"); + io::silo::save_mesh(load_mesh, out_name + "_write_silo"); + + remove_path_if_exists(out_name + "_write_overlink"); + write_opts["file_style"] = "overlink"; + write_opts["ovl_topo_name"] = "MMESH"; + io::silo::save_mesh(load_mesh, out_name + "_write_overlink", write_opts); } } -// //----------------------------------------------------------------------------- -// TEST(conduit_relay_io_silo, round_trip_read_option_matset_style) -// { -// // the matset type and the type we are requesting on read -// const std::vector> matset_types = { -// std::make_pair("full", "full"), -// std::make_pair("sparse_by_material", "sparse_by_material"), -// std::make_pair("sparse_by_element", "sparse_by_element"), -// std::make_pair("sparse_by_element", "full"), -// std::make_pair("sparse_by_material", "sparse_by_element"), -// std::make_pair("sparse_by_material", "default"), -// }; - -// for (int i = 0; i < matset_types.size(); i ++) -// { -// std::string matset_type = matset_types[i].first; -// std::string matset_request = matset_types[i].second; - -// for (int j = 0; j < 2; j ++) -// { -// Node mesh_full, mesh_sbe, mesh_sbm, baseline_mesh, load_mesh, info; -// std::string size; -// int nx, ny; -// const double radius = 0.25; -// if (j == 0) -// { -// size = "small"; -// nx = ny = 4; -// } -// else -// { -// size = "large"; -// nx = ny = 100; -// } - -// blueprint::mesh::examples::venn("full", nx, ny, radius, mesh_full); -// blueprint::mesh::examples::venn("sparse_by_material", nx, ny, radius, mesh_sbm); -// blueprint::mesh::examples::venn("sparse_by_element", nx, ny, radius, mesh_sbe); - -// if (matset_type == "full") -// { -// baseline_mesh.set_external(mesh_full); -// } -// else if (matset_type == "sparse_by_material") -// { -// baseline_mesh.set_external(mesh_sbm); -// } -// else // (matset_type == "sparse_by_element") -// { -// baseline_mesh.set_external(mesh_sbe); -// } - -// Node opts; -// if (matset_request == "full") -// { -// opts["matset_style"] = "multi_buffer_full"; -// } -// else if (matset_request == "sparse_by_material") -// { -// opts["matset_style"] = "multi_buffer_by_material"; -// } -// else if (matset_request == "sparse_by_element") -// { -// opts["matset_style"] = "sparse_by_element"; -// } -// else -// { -// opts["matset_style"] = "default"; -// } - -// const std::string basename = "silo_venn2_" + matset_type + "_" + size; -// const std::string filename = basename + ".root"; - -// remove_path_if_exists(filename); -// io::silo::save_mesh(baseline_mesh, basename); -// io::silo::load_mesh(filename, opts, load_mesh); -// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - -// if (matset_request == "full") -// { -// baseline_mesh.set_external(mesh_full); -// } -// else if (matset_request == "sparse_by_material") -// { -// baseline_mesh.set_external(mesh_sbm); -// } -// else if (matset_request == "sparse_by_element") -// { -// baseline_mesh.set_external(mesh_sbe); -// } -// else -// { -// baseline_mesh.set_external(mesh_sbe); -// } - -// // make changes to save mesh so the diff will pass - -// // The field mat_check has values that are one type and matset_values -// // that are another type. The silo writer converts both to double arrays -// // in this case, so we follow suit. -// Node mat_check_new_values, mat_check_new_matset_values; -// baseline_mesh["fields"]["mat_check"]["values"].to_double_array(mat_check_new_values); -// if (baseline_mesh["fields"]["mat_check"]["matset_values"].dtype().is_object()) -// { -// auto mat_vals_itr = baseline_mesh["fields"]["mat_check"]["matset_values"].children(); -// while (mat_vals_itr.has_next()) -// { -// Node &mat_vals_for_mat = mat_vals_itr.next(); -// const std::string mat_name = mat_vals_itr.name(); -// mat_vals_for_mat.to_double_array(mat_check_new_matset_values[mat_name]); -// } -// } -// else -// { -// baseline_mesh["fields"]["mat_check"]["matset_values"].to_double_array(mat_check_new_matset_values); -// } -// baseline_mesh["fields"]["mat_check"]["values"].set_external(mat_check_new_values); -// baseline_mesh["fields"]["mat_check"]["matset_values"].set_external(mat_check_new_matset_values); - -// silo_name_changer("mesh", baseline_mesh); - -// // the loaded mesh will be in the multidomain format -// // but the saved mesh is in the single domain format -// EXPECT_EQ(load_mesh.number_of_children(), 1); -// EXPECT_EQ(load_mesh[0].number_of_children(), baseline_mesh.number_of_children()); -// EXPECT_FALSE(load_mesh[0].diff(baseline_mesh, info, CONDUIT_EPSILON, true)); -// } -// } -// } - -// //----------------------------------------------------------------------------- - -// // -// // read and write Silo and Overlink tests -// // - -// //----------------------------------------------------------------------------- -// // read normal silo files containing multimeshes, multivars, and multimats -// TEST(conduit_relay_io_silo, read_silo) -// { -// const std::vector> file_info = { -// {".", "multi_curv3d", ".silo"}, -// {".", "tire", ".silo"}, -// {".", "galaxy0000", ".silo"}, -// {".", "emptydomains", ".silo"}, -// {"multidir_test_data", "multidir0000", ".root"}, -// }; - -// // TODO what to do in the case where a multimesh points to no data? (mesh1_back) -// // fail silently, as we do now? - -// for (int i = 0; i < file_info.size(); i ++) -// { -// const std::string dirname = file_info[i][0]; -// const std::string basename = file_info[i][1]; -// const std::string fileext = file_info[i][2]; - -// Node load_mesh, info, write_opts; -// std::string filepath = utils::join_file_path(dirname, basename) + fileext; -// filepath = utils::join_file_path("silo", filepath); -// std::string input_file = relay_test_silo_data_path(filepath); - -// io::silo::load_mesh(input_file, load_mesh); -// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - -// const std::string out_name = "read_silo_" + basename; - -// // TODO are these remove paths doing anything? Don't they need filenames? -// remove_path_if_exists(out_name + "_write_blueprint"); -// io::blueprint::save_mesh(load_mesh, out_name + "_write_blueprint", "hdf5"); - -// remove_path_if_exists(out_name + "_write_silo"); -// io::silo::save_mesh(load_mesh, out_name + "_write_silo"); - -// // overlink requires matsets and does not support point meshes -// if (load_mesh[0].has_child("matsets") && basename != "galaxy0000") -// { -// remove_path_if_exists(out_name + "_write_overlink"); -// write_opts["file_style"] = "overlink"; -// write_opts["ovl_topo_name"] = "MMESH"; // TODO do I even need this -// io::silo::save_mesh(load_mesh, out_name + "_write_overlink", write_opts); -// } -// } -// } - -// //----------------------------------------------------------------------------- -// // test that we can read silo without multimeshes, multivars, and multimats -// TEST(conduit_relay_io_silo, read_simple_silo) -// { -// const std::vector> file_info = { -// {"curv2d", ".silo", "no"}, -// {"curv2d_colmajor", ".silo", "no"}, -// {"curv3d", ".silo", "yes"}, -// {"curv3d_colmajor", ".silo", "no"}, -// // {"globe", ".silo", "yes"}, // TODO need to add support for mixed shape topos -// }; -// for (int i = 0; i < file_info.size(); i ++) -// { -// const std::string basename = file_info[i][0]; -// const std::string fileext = file_info[i][1]; -// const std::string round_trip = file_info[i][2]; - -// Node load_mesh, info, write_opts; -// std::string filepath = basename + fileext; -// filepath = utils::join_file_path("silo", filepath); -// std::string input_file = relay_test_silo_data_path(filepath); - -// io::silo::load_mesh(input_file, load_mesh); -// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - -// const std::string out_name = "read_silo_" + basename; - -// // TODO are these remove paths doing anything? Don't they need filenames? -// remove_path_if_exists(out_name + "_write_blueprint"); -// io::blueprint::save_mesh(load_mesh, out_name + "_write_blueprint", "hdf5"); - -// if (round_trip == "yes") -// { -// remove_path_if_exists(out_name + "_write_silo"); -// io::silo::save_mesh(load_mesh, out_name + "_write_silo"); - -// // overlink requires matsets -// if (load_mesh[0].has_child("matsets")) -// { -// remove_path_if_exists(out_name + "_write_overlink"); -// write_opts["file_style"] = "overlink"; -// write_opts["ovl_topo_name"] = "MMESH"; // TODO do I even need this -// io::silo::save_mesh(load_mesh, out_name + "_write_overlink", write_opts); -// } -// } -// } -// } - -// //----------------------------------------------------------------------------- -// // test that we can read the fake overlink files from the visit test data -// TEST(conduit_relay_io_silo, read_fake_overlink) -// { -// const std::vector> file_info = { -// // {"ev_0_0_100", "OvlTop", ".silo"}, -// // uncomment once silo ucdmesh phzones are supported -// {"hl18spec", "OvlTop", ".silo"}, -// // {"regrovl_qh_1000_10001_4", "OvlTop", ".silo"}, -// // uncomment once silo ucdmesh phzones are supported -// {"utpyr4", "OvlTop", ".silo"}, -// }; - -// for (int i = 0; i < file_info.size(); i ++) -// { -// const std::string dirname = file_info[i][0]; -// const std::string basename = file_info[i][1]; -// const std::string fileext = file_info[i][2]; - -// Node load_mesh, info, write_opts; -// std::string filepath = utils::join_file_path(dirname, basename) + fileext; -// filepath = utils::join_file_path("fake_overlink", filepath); -// std::string input_file = relay_test_silo_data_path(filepath); - -// io::silo::load_mesh(input_file, load_mesh); -// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - -// const std::string out_name = "read_fake_overlink_" + dirname; - -// remove_path_if_exists(out_name + "_write_blueprint"); -// io::blueprint::save_mesh(load_mesh, out_name + "_write_blueprint", "hdf5"); - -// remove_path_if_exists(out_name + "_write_silo"); -// io::silo::save_mesh(load_mesh, out_name + "_write_silo"); - -// remove_path_if_exists(out_name + "_write_overlink"); -// write_opts["file_style"] = "overlink"; -// write_opts["ovl_topo_name"] = "MMESH"; -// io::silo::save_mesh(load_mesh, out_name + "_write_overlink", write_opts); -// } -// } - -// //----------------------------------------------------------------------------- -// // read overlink files in symlink format -// // should be similar to reading raw silo -// TEST(conduit_relay_io_silo, read_overlink_symlink_format) -// { -// const std::vector> file_info = { -// {".", "box2d", ".silo"}, -// {".", "box3d", ".silo"}, -// // {".", "diamond", ".silo"}, -// // fails b/c polytopal not yet supported -// {".", "testDisk2D_a", ".silo"}, -// // {".", "donordiv.s2_materials2", ".silo"}, -// // fails b/c polytopal not yet supported -// {".", "donordiv.s2_materials3", ".silo"}, -// }; - -// for (int i = 0; i < file_info.size(); i ++) -// { -// const std::string dirname = file_info[i][0]; -// const std::string basename = file_info[i][1]; -// const std::string fileext = file_info[i][2]; - -// Node load_mesh, info, write_opts; -// std::string filepath = utils::join_file_path(dirname, basename) + fileext; -// filepath = utils::join_file_path("overlink", filepath); -// std::string input_file = relay_test_silo_data_path(filepath); - -// io::silo::load_mesh(input_file, load_mesh); -// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - -// const std::string out_name = "read_overlink_symlink_" + basename; - -// remove_path_if_exists(out_name + "_write_blueprint"); -// io::blueprint::save_mesh(load_mesh, out_name + "_write_blueprint", "hdf5"); - -// remove_path_if_exists(out_name + "_write_silo"); -// io::silo::save_mesh(load_mesh, out_name + "_write_silo"); - -// remove_path_if_exists(out_name + "_write_overlink"); -// write_opts["file_style"] = "overlink"; -// write_opts["ovl_topo_name"] = "MMESH"; -// io::silo::save_mesh(load_mesh, out_name + "_write_overlink", write_opts); -// } -// } - -// //----------------------------------------------------------------------------- -// // read overlink directly from ovltop.silo -// // this case is tricky and involves messing with paths -// TEST(conduit_relay_io_silo, read_overlink_directly) -// { -// const std::vector> file_info = { -// {"box2d", "OvlTop", ".silo"}, -// {"box3d", "OvlTop", ".silo"}, -// // {"diamond", "OvlTop", ".silo"}, -// {"testDisk2D_a", "OvlTop", ".silo"}, -// // {"donordiv.s2_materials2", "OvlTop", ".silo"}, -// {"donordiv.s2_materials3", "OvlTop", ".silo"}, -// }; - -// for (int i = 0; i < file_info.size(); i ++) -// { -// const std::string dirname = file_info[i][0]; -// const std::string basename = file_info[i][1]; -// const std::string fileext = file_info[i][2]; - -// Node load_mesh, info, write_opts; - -// std::string filepath = utils::join_file_path(dirname, basename) + fileext; -// filepath = utils::join_file_path("overlink", filepath); -// std::string input_file = relay_test_silo_data_path(filepath); - -// io::silo::load_mesh(input_file, load_mesh); -// EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - -// const std::string out_name = "read_overlink_direct_" + dirname; - -// remove_path_if_exists(out_name + "_write_blueprint"); -// io::blueprint::save_mesh(load_mesh, out_name + "_write_blueprint", "hdf5"); - -// remove_path_if_exists(out_name + "_write_silo"); -// io::silo::save_mesh(load_mesh, out_name + "_write_silo"); - -// remove_path_if_exists(out_name + "_write_overlink"); -// write_opts["file_style"] = "overlink"; -// write_opts["ovl_topo_name"] = "MMESH"; -// io::silo::save_mesh(load_mesh, out_name + "_write_overlink", write_opts); -// } -// } - -// // TODO add tests for... -// // - polytopal meshes once they are supported -// // - units once they are supported -// // - etc. - -// // TODO add tetra8 and c36_m5 to all the overlink i/o tests - -// // TODO somewhere I need to error on overlink when there are different var or mesh types across domains +// TODO add tests for... +// - polytopal meshes once they are supported +// - units once they are supported +// - etc. + +// TODO add tetra8 and c36_m5 to all the overlink i/o tests + +// TODO somewhere I need to error on overlink when there are different var or mesh types across domains From dc68424d83977d5ec4464c4ebee474a0738f4790 Mon Sep 17 00:00:00 2001 From: Justin Privitera Date: Tue, 23 Apr 2024 08:35:59 -0700 Subject: [PATCH 50/56] fixed failing test --- src/tests/relay/t_relay_io_silo.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tests/relay/t_relay_io_silo.cpp b/src/tests/relay/t_relay_io_silo.cpp index 1c635d84b..e243899e8 100644 --- a/src/tests/relay/t_relay_io_silo.cpp +++ b/src/tests/relay/t_relay_io_silo.cpp @@ -802,9 +802,9 @@ TEST(conduit_relay_io_silo, missing_domain_mesh) remove_path_if_exists(filename); io::silo::save_mesh(save_mesh, basename); - opts["silo_names"]["mesh_names"] = "mesh_topo2"; + opts["mesh_name"] = "mesh_topo2"; io::silo::load_mesh(filename, opts, load_mesh); - opts["silo_names"]["mesh_names"] = "mesh_topo"; + opts["mesh_name"] = "mesh_topo"; io::silo::load_mesh(filename, opts, load_mesh2); EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); EXPECT_TRUE(blueprint::mesh::verify(load_mesh2, info)); From ddd0eb6998e928bbfd7ba67a626d30aa2734da27 Mon Sep 17 00:00:00 2001 From: Justin Privitera Date: Tue, 23 Apr 2024 08:40:59 -0700 Subject: [PATCH 51/56] smaller PR is better --- src/tests/relay/t_relay_io_silo.cpp | 34 +++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/src/tests/relay/t_relay_io_silo.cpp b/src/tests/relay/t_relay_io_silo.cpp index e243899e8..11e2babe4 100644 --- a/src/tests/relay/t_relay_io_silo.cpp +++ b/src/tests/relay/t_relay_io_silo.cpp @@ -802,6 +802,7 @@ TEST(conduit_relay_io_silo, missing_domain_mesh) remove_path_if_exists(filename); io::silo::save_mesh(save_mesh, basename); + opts["mesh_name"] = "mesh_topo2"; io::silo::load_mesh(filename, opts, load_mesh); opts["mesh_name"] = "mesh_topo"; @@ -1735,11 +1736,20 @@ TEST(conduit_relay_io_silo, round_trip_read_option_matset_style) TEST(conduit_relay_io_silo, read_silo) { const std::vector> file_info = { - {".", "multi_curv3d", ".silo"}, - {".", "tire", ".silo"}, - {".", "galaxy0000", ".silo"}, - {".", "emptydomains", ".silo"}, - {"multidir_test_data", "multidir0000", ".root"}, + {".", "multi_curv3d", ".silo", "" }, // test default case + {".", "multi_curv3d", ".silo", "mesh1" }, + // {".", "multi_curv3d", ".silo", "mesh1_back" }, // this multimesh points to paths that do not exist + {".", "multi_curv3d", ".silo", "mesh1_dup" }, + // {".", "multi_curv3d", ".silo", "mesh1_front" }, // same here + {".", "multi_curv3d", ".silo", "mesh1_hidden"}, + {".", "tire", ".silo", "" }, // test default case + {".", "tire", ".silo", "tire" }, + {".", "galaxy0000", ".silo", "" }, // test default case + {".", "galaxy0000", ".silo", "StarMesh" }, + {".", "emptydomains", ".silo", "" }, // test default case + {".", "emptydomains", ".silo", "mesh" }, + {"multidir_test_data", "multidir0000", ".root", "" }, // test default case + {"multidir_test_data", "multidir0000", ".root", "Mesh" }, }; // TODO what to do in the case where a multimesh points to no data? (mesh1_back) @@ -1750,16 +1760,22 @@ TEST(conduit_relay_io_silo, read_silo) const std::string dirname = file_info[i][0]; const std::string basename = file_info[i][1]; const std::string fileext = file_info[i][2]; + const std::string meshname = file_info[i][3]; - Node load_mesh, info, write_opts; + Node load_mesh, info, read_opts, write_opts; std::string filepath = utils::join_file_path(dirname, basename) + fileext; filepath = utils::join_file_path("silo", filepath); std::string input_file = relay_test_silo_data_path(filepath); - io::silo::load_mesh(input_file, load_mesh); + read_opts["mesh_name"] = meshname; + io::silo::load_mesh(input_file, read_opts, load_mesh); EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - const std::string out_name = "read_silo_" + basename; + std::string out_name = "read_silo_" + basename; + if (!meshname.empty()) + { + out_name += "_" + meshname; + } // TODO are these remove paths doing anything? Don't they need filenames? remove_path_if_exists(out_name + "_write_blueprint"); @@ -1773,7 +1789,7 @@ TEST(conduit_relay_io_silo, read_silo) { remove_path_if_exists(out_name + "_write_overlink"); write_opts["file_style"] = "overlink"; - write_opts["ovl_topo_name"] = "MMESH"; // TODO do I even need this + write_opts["ovl_topo_name"] = meshname; // TODO do I even need this io::silo::save_mesh(load_mesh, out_name + "_write_overlink", write_opts); } } From f20b5adef99b92aa64bcb810429cc9852dd1b774 Mon Sep 17 00:00:00 2001 From: Justin Privitera Date: Tue, 23 Apr 2024 10:29:34 -0700 Subject: [PATCH 52/56] restore old tests --- src/tests/relay/t_relay_io_silo.cpp | 84 ++++++++++++++++++++--------- 1 file changed, 59 insertions(+), 25 deletions(-) diff --git a/src/tests/relay/t_relay_io_silo.cpp b/src/tests/relay/t_relay_io_silo.cpp index 11e2babe4..2ed5447a5 100644 --- a/src/tests/relay/t_relay_io_silo.cpp +++ b/src/tests/relay/t_relay_io_silo.cpp @@ -1848,12 +1848,16 @@ TEST(conduit_relay_io_silo, read_simple_silo) TEST(conduit_relay_io_silo, read_fake_overlink) { const std::vector> file_info = { - // {"ev_0_0_100", "OvlTop", ".silo"}, + // {"ev_0_0_100", "OvlTop", ".silo", "" }, // test default case + // {"ev_0_0_100", "OvlTop", ".silo", "MMESH"}, // uncomment once silo ucdmesh phzones are supported - {"hl18spec", "OvlTop", ".silo"}, - // {"regrovl_qh_1000_10001_4", "OvlTop", ".silo"}, + {"hl18spec", "OvlTop", ".silo", "" }, // test default case + {"hl18spec", "OvlTop", ".silo", "MMESH"}, + // {"regrovl_qh_1000_10001_4", "OvlTop", ".silo", "" }, // test default case + // {"regrovl_qh_1000_10001_4", "OvlTop", ".silo", "MMESH"}, // uncomment once silo ucdmesh phzones are supported - {"utpyr4", "OvlTop", ".silo"}, + {"utpyr4", "OvlTop", ".silo", "" }, // test default case + {"utpyr4", "OvlTop", ".silo", "MMESH"}, }; for (int i = 0; i < file_info.size(); i ++) @@ -1861,16 +1865,22 @@ TEST(conduit_relay_io_silo, read_fake_overlink) const std::string dirname = file_info[i][0]; const std::string basename = file_info[i][1]; const std::string fileext = file_info[i][2]; + const std::string meshname = file_info[i][3]; - Node load_mesh, info, write_opts; + Node load_mesh, info, read_opts, write_opts; std::string filepath = utils::join_file_path(dirname, basename) + fileext; filepath = utils::join_file_path("fake_overlink", filepath); std::string input_file = relay_test_silo_data_path(filepath); - io::silo::load_mesh(input_file, load_mesh); + read_opts["mesh_name"] = meshname; + io::silo::load_mesh(input_file, read_opts, load_mesh); EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - const std::string out_name = "read_fake_overlink_" + dirname; + std::string out_name = "read_fake_overlink_" + dirname; + if (!meshname.empty()) + { + out_name += "_" + meshname; + } remove_path_if_exists(out_name + "_write_blueprint"); io::blueprint::save_mesh(load_mesh, out_name + "_write_blueprint", "hdf5"); @@ -1891,14 +1901,20 @@ TEST(conduit_relay_io_silo, read_fake_overlink) TEST(conduit_relay_io_silo, read_overlink_symlink_format) { const std::vector> file_info = { - {".", "box2d", ".silo"}, - {".", "box3d", ".silo"}, - // {".", "diamond", ".silo"}, + {".", "box2d", ".silo", "" }, // test default case + {".", "box2d", ".silo", "MMESH"}, + {".", "box3d", ".silo", "" }, // test default case + {".", "box3d", ".silo", "MMESH"}, + // {".", "diamond", ".silo", "" }, // test default case + // {".", "diamond", ".silo", "MMESH"}, // fails b/c polytopal not yet supported - {".", "testDisk2D_a", ".silo"}, - // {".", "donordiv.s2_materials2", ".silo"}, + {".", "testDisk2D_a", ".silo", "" }, // test default case + {".", "testDisk2D_a", ".silo", "MMESH"}, + // {".", "donordiv.s2_materials2", ".silo", "" }, // test default case + // {".", "donordiv.s2_materials2", ".silo", "MMESH"}, // fails b/c polytopal not yet supported - {".", "donordiv.s2_materials3", ".silo"}, + {".", "donordiv.s2_materials3", ".silo", "" }, // test default case + {".", "donordiv.s2_materials3", ".silo", "MMESH"}, }; for (int i = 0; i < file_info.size(); i ++) @@ -1906,16 +1922,22 @@ TEST(conduit_relay_io_silo, read_overlink_symlink_format) const std::string dirname = file_info[i][0]; const std::string basename = file_info[i][1]; const std::string fileext = file_info[i][2]; + const std::string meshname = file_info[i][3]; - Node load_mesh, info, write_opts; + Node load_mesh, info, read_opts, write_opts; std::string filepath = utils::join_file_path(dirname, basename) + fileext; filepath = utils::join_file_path("overlink", filepath); std::string input_file = relay_test_silo_data_path(filepath); - io::silo::load_mesh(input_file, load_mesh); + read_opts["mesh_name"] = meshname; + io::silo::load_mesh(input_file, read_opts, load_mesh); EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - const std::string out_name = "read_overlink_symlink_" + basename; + std::string out_name = "read_overlink_symlink_" + basename; + if (!meshname.empty()) + { + out_name += "_" + meshname; + } remove_path_if_exists(out_name + "_write_blueprint"); io::blueprint::save_mesh(load_mesh, out_name + "_write_blueprint", "hdf5"); @@ -1936,12 +1958,18 @@ TEST(conduit_relay_io_silo, read_overlink_symlink_format) TEST(conduit_relay_io_silo, read_overlink_directly) { const std::vector> file_info = { - {"box2d", "OvlTop", ".silo"}, - {"box3d", "OvlTop", ".silo"}, - // {"diamond", "OvlTop", ".silo"}, - {"testDisk2D_a", "OvlTop", ".silo"}, - // {"donordiv.s2_materials2", "OvlTop", ".silo"}, - {"donordiv.s2_materials3", "OvlTop", ".silo"}, + {"box2d", "OvlTop", ".silo", "" }, // test default case + {"box2d", "OvlTop", ".silo", "MMESH"}, + {"box3d", "OvlTop", ".silo", "" }, // test default case + {"box3d", "OvlTop", ".silo", "MMESH"}, + // {"diamond", "OvlTop", ".silo", "" }, // test default case + // {"diamond", "OvlTop", ".silo", "MMESH"}, + {"testDisk2D_a", "OvlTop", ".silo", "" }, // test default case + {"testDisk2D_a", "OvlTop", ".silo", "MMESH"}, + // {"donordiv.s2_materials2", "OvlTop", ".silo", "" }, // test default case + // {"donordiv.s2_materials2", "OvlTop", ".silo", "MMESH"}, + {"donordiv.s2_materials3", "OvlTop", ".silo", "" }, // test default case + {"donordiv.s2_materials3", "OvlTop", ".silo", "MMESH"}, }; for (int i = 0; i < file_info.size(); i ++) @@ -1949,17 +1977,23 @@ TEST(conduit_relay_io_silo, read_overlink_directly) const std::string dirname = file_info[i][0]; const std::string basename = file_info[i][1]; const std::string fileext = file_info[i][2]; + const std::string meshname = file_info[i][3]; - Node load_mesh, info, write_opts; + Node load_mesh, info, read_opts, write_opts; std::string filepath = utils::join_file_path(dirname, basename) + fileext; filepath = utils::join_file_path("overlink", filepath); std::string input_file = relay_test_silo_data_path(filepath); - io::silo::load_mesh(input_file, load_mesh); + read_opts["mesh_name"] = meshname; + io::silo::load_mesh(input_file, read_opts, load_mesh); EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - const std::string out_name = "read_overlink_direct_" + dirname; + std::string out_name = "read_overlink_direct_" + dirname; + if (!meshname.empty()) + { + out_name += "_" + meshname; + } remove_path_if_exists(out_name + "_write_blueprint"); io::blueprint::save_mesh(load_mesh, out_name + "_write_blueprint", "hdf5"); From 7bb66bc0ea65faca693e75d4687f5adf88c60c4d Mon Sep 17 00:00:00 2001 From: Justin Privitera Date: Tue, 23 Apr 2024 10:34:51 -0700 Subject: [PATCH 53/56] I like const --- src/tests/relay/t_relay_io_silo.cpp | 28 ++++++++-------------------- 1 file changed, 8 insertions(+), 20 deletions(-) diff --git a/src/tests/relay/t_relay_io_silo.cpp b/src/tests/relay/t_relay_io_silo.cpp index 2ed5447a5..dc9ca95b8 100644 --- a/src/tests/relay/t_relay_io_silo.cpp +++ b/src/tests/relay/t_relay_io_silo.cpp @@ -1771,11 +1771,8 @@ TEST(conduit_relay_io_silo, read_silo) io::silo::load_mesh(input_file, read_opts, load_mesh); EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - std::string out_name = "read_silo_" + basename; - if (!meshname.empty()) - { - out_name += "_" + meshname; - } + const std::string out_name = "read_silo_" + basename + + (meshname.empty() ? "" : "_" + meshname); // TODO are these remove paths doing anything? Don't they need filenames? remove_path_if_exists(out_name + "_write_blueprint"); @@ -1876,11 +1873,8 @@ TEST(conduit_relay_io_silo, read_fake_overlink) io::silo::load_mesh(input_file, read_opts, load_mesh); EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - std::string out_name = "read_fake_overlink_" + dirname; - if (!meshname.empty()) - { - out_name += "_" + meshname; - } + const std::string out_name = "read_fake_overlink_" + dirname + + (meshname.empty() ? "" : "_" + meshname); remove_path_if_exists(out_name + "_write_blueprint"); io::blueprint::save_mesh(load_mesh, out_name + "_write_blueprint", "hdf5"); @@ -1933,11 +1927,8 @@ TEST(conduit_relay_io_silo, read_overlink_symlink_format) io::silo::load_mesh(input_file, read_opts, load_mesh); EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - std::string out_name = "read_overlink_symlink_" + basename; - if (!meshname.empty()) - { - out_name += "_" + meshname; - } + const std::string out_name = "read_overlink_symlink_" + basename + + (meshname.empty() ? "" : "_" + meshname); remove_path_if_exists(out_name + "_write_blueprint"); io::blueprint::save_mesh(load_mesh, out_name + "_write_blueprint", "hdf5"); @@ -1989,11 +1980,8 @@ TEST(conduit_relay_io_silo, read_overlink_directly) io::silo::load_mesh(input_file, read_opts, load_mesh); EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); - std::string out_name = "read_overlink_direct_" + dirname; - if (!meshname.empty()) - { - out_name += "_" + meshname; - } + const std::string out_name = "read_overlink_direct_" + dirname + + (meshname.empty() ? "" : "_" + meshname); remove_path_if_exists(out_name + "_write_blueprint"); io::blueprint::save_mesh(load_mesh, out_name + "_write_blueprint", "hdf5"); From 7b77c6c7b744861039758437da37e37e84e6b70f Mon Sep 17 00:00:00 2001 From: Justin Privitera Date: Tue, 23 Apr 2024 10:42:34 -0700 Subject: [PATCH 54/56] have a comment --- src/libs/relay/conduit_relay_io_silo.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/libs/relay/conduit_relay_io_silo.cpp b/src/libs/relay/conduit_relay_io_silo.cpp index 4caa9488a..e5c132f60 100644 --- a/src/libs/relay/conduit_relay_io_silo.cpp +++ b/src/libs/relay/conduit_relay_io_silo.cpp @@ -1637,9 +1637,12 @@ read_variable_domain_helper(const T *var_ptr, { intermediate_field["association"] = "vertex"; - // TODO handle min and max index case (check_using_whole_coordset case) - - // TODO what to do about colmajor case? + // we don't need to worry about the min_index and max_index case + // nor do we need to worry about strides + // nor do we need to worry about colmajor versus row major + // I have it on good authority that these cases are meaningless for point var data + // each of the above attributes are dependent on ndims > 0, which seems to never be + // the case for point variable data. } // if we have volume dependence we can track it From 91331efc2a91e8da3929d76523904ee56c654a9e Mon Sep 17 00:00:00 2001 From: Justin Privitera Date: Tue, 23 Apr 2024 10:55:10 -0700 Subject: [PATCH 55/56] I did this --- src/libs/relay/conduit_relay_io_silo.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libs/relay/conduit_relay_io_silo.cpp b/src/libs/relay/conduit_relay_io_silo.cpp index e5c132f60..d84ce01af 100644 --- a/src/libs/relay/conduit_relay_io_silo.cpp +++ b/src/libs/relay/conduit_relay_io_silo.cpp @@ -2044,7 +2044,6 @@ read_matset_domain(DBfile* matset_domain_file_to_use, } // TODO still need to find colmajor data to test this - // TODO are there other places where I'm reading where things could be rowmajor or colmajor intermediate_matset["material_ids"].set(material_ids.data(), material_ids.size()); intermediate_matset["volume_fractions"].set(volume_fractions.data(), volume_fractions.size()); From 14291031bdf39446e9cdb6db0e84ba823dea7776 Mon Sep 17 00:00:00 2001 From: Justin Privitera Date: Tue, 23 Apr 2024 10:57:14 -0700 Subject: [PATCH 56/56] missing comment --- src/libs/relay/conduit_relay_io_silo.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libs/relay/conduit_relay_io_silo.cpp b/src/libs/relay/conduit_relay_io_silo.cpp index d84ce01af..a40e52730 100644 --- a/src/libs/relay/conduit_relay_io_silo.cpp +++ b/src/libs/relay/conduit_relay_io_silo.cpp @@ -2839,6 +2839,8 @@ read_root_silo_index(const std::string &root_file_path, root_node[mesh_name_to_read]["mesh_types"].set(mesh_type); root_node[mesh_name_to_read]["mesh_paths"].append().set(mesh_name_to_read); + // TODO should we check here if vars are associated with this mesh? + // we have logic to get the right ones later, but it could be quick to check now for (int i = 0; i < num_vars; i ++) { const std::string var_name = var_names[i];