diff --git a/src/cmake/Setup3rdParty.cmake b/src/cmake/Setup3rdParty.cmake index 034981397..f8bc762b4 100644 --- a/src/cmake/Setup3rdParty.cmake +++ b/src/cmake/Setup3rdParty.cmake @@ -123,6 +123,19 @@ if(HDF5_DIR) endif() endif() +################################ +# Setup CGNS if available +################################ +# Search for CGNS. +if(CGNS_DIR) + include(cmake/thirdparty/SetupCGNS.cmake) + include_directories(${CGNS_INCLUDE_DIRS}) + # if we don't find CGNS, throw a fatal error + if(NOT CGNS_FOUND) + message(FATAL_ERROR "CGNS_DIR is set, but CGNS wasn't found.") + endif() +endif() + ################################ # Setup Silo if available ################################ diff --git a/src/cmake/thirdparty/SetupCGNS.cmake b/src/cmake/thirdparty/SetupCGNS.cmake new file mode 100644 index 000000000..cfd3cefd3 --- /dev/null +++ b/src/cmake/thirdparty/SetupCGNS.cmake @@ -0,0 +1,56 @@ + +# first Check for CGNS_DIR +if(NOT CGNS_DIR) + MESSAGE(FATAL_ERROR "CGNS support needs explicit CGNS_DIR") +endif() + +set(CGNS_ROOT ${CGNS_DIR}) + + +find_package(CGNS REQUIRED) + +if(TARGET CGNS::cgns_shared) + get_target_property(_inc CGNS::cgns_shared INTERFACE_INCLUDE_DIRECTORIES) + get_target_property(_lib CGNS::cgns_shared IMPORTED_LOCATION_RELEASE) + get_target_property(_link CGNS::cgns_shared INTERFACE_LINK_LIBRARIES) + message(STATUS "CGNS imported target INTERFACE_INCLUDE_DIRECTORIES: ${_inc}") + message(STATUS " INTERFACE_LINK_LIBRARIES: ${_link}") + if(_lib) + get_filename_component(_libdir "${_lib}" DIRECTORY) + message(STATUS "CGNS imported library location: ${_lib}") + message(STATUS "Guessed CGNS lib dir: ${_libdir}") + endif() + blt_register_library(NAME cgns + DEFINES "-DOMPI_SKIP_MPICXX" + INCLUDES ${_inc} + LIBRARIES ${_link} ${_lib} + ) +endif() + +# Abort CMake here for debugging +# message(FATAL_ERROR "Aborting configuration: reached intentional stop in SetupCGNS.cmake") + + + # if(TARGET CGNS::cgns_shared) + + + + # message(STATUS "Found imported target CGNS::cgns_shared") + # get_target_property(_inc CGNS::cgns_shared INTERFACE_INCLUDE_DIRECTORIES) + # message(STATUS " INTERFACE_INCLUDE_DIRECTORIES: ${_inc}") + # get_target_property(_link CGNS::cgns_shared INTERFACE_LINK_LIBRARIES) + # message(STATUS " INTERFACE_LINK_LIBRARIES: ${_link}") + + # message(STATUS "CGNS_ROOT/include: ${CGNS_ROOT}/include") + # message(STATUS "CGNS_ROOT/lib: ${CGNS_ROOT}/lib") + + # blt_register_library(NAME cgns + # INCLUDES ${_inc} + # LIBRARIES ${_link} + # ) + + # endif() + +# message(STATUS "CGNS_INCLUDE_DIRS: ${_inc}") +# message(STATUS "CGNS_LIBRARIES: ${CGNS_LIBRARIES}") + diff --git a/src/docs/sphinx/building.rst b/src/docs/sphinx/building.rst index bc0ebf791..96cfdf092 100644 --- a/src/docs/sphinx/building.rst +++ b/src/docs/sphinx/building.rst @@ -143,6 +143,9 @@ CMake Options for Third-party Library Paths * - ``ZLIB_DIR`` - Path to a Zlib install (optional). (Needed for HDF5 support) + * - ``CGNS_DIR`` + - Path to a CGNS install (optional). Controls if CGNS I/O support is built into *conduit_relay*. + * - ``SILO_DIR`` - Path to a Silo install (optional). Controls if Silo I/O support is built into *conduit_relay*. Requires HDF5. diff --git a/src/libs/relay/CMakeLists.txt b/src/libs/relay/CMakeLists.txt index fc63bf2dd..e5957644c 100644 --- a/src/libs/relay/CMakeLists.txt +++ b/src/libs/relay/CMakeLists.txt @@ -16,6 +16,17 @@ if(HDF5_FOUND) SET(CONDUIT_RELAY_IO_HDF5_ENABLED TRUE) endif() +if(CGNS_FOUND) + SET(CONDUIT_RELAY_IO_CGNS_ENABLED TRUE) + MESSAGE("CGNS is found. CGNS relay I/O support enabled.") +else() + MESSAGE("CGNS not found. CGNS relay I/O support disabled.") +endif() + +if(CGNS_FOUND AND MPI_FOUND) + SET(CONDUIT_RELAY_IO_MPI_CGNS_ENABLED TRUE) +endif() + if(H5ZZFP_FOUND) SET(CONDUIT_RELAY_IO_H5ZZFP_ENABLED TRUE) endif() @@ -119,6 +130,14 @@ if(HDF5_FOUND) list(APPEND conduit_relay_sources conduit_relay_io_hdf5.cpp) endif() +if(CGNS_FOUND) + list(APPEND conduit_relay_headers + conduit_relay_io_cgns.hpp + conduit_relay_io_cgns_api.hpp + ) + list(APPEND conduit_relay_sources conduit_relay_io_cgns.cpp) +endif() + if(SILO_FOUND) list(APPEND conduit_relay_headers conduit_relay_silo.hpp @@ -193,6 +212,10 @@ if(HDF5_FOUND) endif() endif() +if(CGNS_FOUND) + list(APPEND conduit_relay_deps cgns) +endif() + if(H5ZZFP_FOUND) list(APPEND conduit_relay_deps h5zzfp) endif() @@ -389,6 +412,12 @@ if(SILO_FOUND) list(APPEND conduit_relay_mpi_io_deps silo) endif() +if(CGNS_FOUND) + list(APPEND conduit_relay_mpi_io_headers conduit_relay_mpi_io_cgns.hpp) + list(APPEND conduit_relay_mpi_io_sources conduit_relay_io_cgns.cpp) + list(APPEND conduit_relay_mpi_io_deps cgns) +endif() + if(HDF5_FOUND) list(APPEND conduit_relay_mpi_io_headers conduit_relay_mpi_io_hdf5.hpp) list(APPEND conduit_relay_mpi_io_sources conduit_relay_io_hdf5.cpp) diff --git a/src/libs/relay/conduit_relay_config.h.in b/src/libs/relay/conduit_relay_config.h.in index 060b875da..ad01279f5 100644 --- a/src/libs/relay/conduit_relay_config.h.in +++ b/src/libs/relay/conduit_relay_config.h.in @@ -22,6 +22,8 @@ #cmakedefine CONDUIT_RELAY_IO_HDF5_ENABLED +#cmakedefine CONDUIT_RELAY_IO_CGNS_ENABLED + #cmakedefine CONDUIT_RELAY_IO_H5ZZFP_ENABLED #cmakedefine CONDUIT_RELAY_IO_SILO_ENABLED diff --git a/src/libs/relay/conduit_relay_io.cpp b/src/libs/relay/conduit_relay_io.cpp index 0d5e8910d..29004ed02 100644 --- a/src/libs/relay/conduit_relay_io.cpp +++ b/src/libs/relay/conduit_relay_io.cpp @@ -27,6 +27,10 @@ #include "conduit_relay_io_silo.hpp" #endif +#ifdef CONDUIT_RELAY_IO_CGNS_ENABLED +#include "conduit_relay_io_cgns.hpp" +#endif + #ifdef CONDUIT_RELAY_IO_ADIOS_ENABLED #include "conduit_relay_io_adios.hpp" #endif @@ -98,6 +102,12 @@ about(Node &n) io_protos["sidre_hdf5"] = "enabled"; #endif +#ifdef CONDUIT_RELAY_IO_CGNS_ENABLED + io_protos["cgns"] = "enabled"; +#else + io_protos["cgns"] = "disabled"; +#endif + #ifdef CONDUIT_RELAY_IO_H5ZZFP_ENABLED io_protos["h5z-zfp"] = "enabled"; #else @@ -318,6 +328,9 @@ save(const Node &node, const std::string &protocol_, const Node &options) { + std::cout << "save: " << path << " protocol: " << protocol_ << std::endl; + std::cout << "options: " << std::endl; + options.print(); // we expect options to unused if all 3rd party i/o options are disabled // avoid warning using CONDUIT_UNUSED macro. CONDUIT_UNUSED(options); @@ -392,6 +405,16 @@ save(const Node &node, #else CONDUIT_ERROR("conduit_relay lacks Silo support: " << "Failed to save conduit node to path " << path); +#endif + } + else if( protocol == "cgns") + { +#ifdef CONDUIT_RELAY_IO_CGNS_ENABLED + // Node + cgns_write(node,path); +#else + CONDUIT_ERROR("conduit_relay lacks CGNS support: " << + "Failed to save conduit node to path " << path); #endif } else if( protocol == "adios") @@ -548,6 +571,11 @@ save_merged(const Node &node, "Failed to save conduit node to path " << path); #endif } + else if( protocol == "cgns") + { + CONDUIT_ERROR("conduit_relay_io::save_merged lacks CGNS support: " << + "Failed to save conduit node to path " << path); + } else { CONDUIT_ERROR("unknown conduit_relay protocol: " << protocol); diff --git a/src/libs/relay/conduit_relay_io_blueprint.cpp b/src/libs/relay/conduit_relay_io_blueprint.cpp index 2a3cbec26..c4e256057 100644 --- a/src/libs/relay/conduit_relay_io_blueprint.cpp +++ b/src/libs/relay/conduit_relay_io_blueprint.cpp @@ -21,6 +21,9 @@ #ifdef CONDUIT_RELAY_IO_SILO_ENABLED #include "conduit_relay_mpi_io_silo.hpp" #endif + #ifdef CONDUIT_RELAY_IO_CGNS_ENABLED + #include "conduit_relay_mpi_io_cgns.hpp" + #endif #else #include "conduit_relay_io_blueprint.hpp" #endif @@ -29,6 +32,10 @@ #include "conduit_relay_io_silo.hpp" #endif +#ifdef CONDUIT_RELAY_IO_CGNS_ENABLED + #include "conduit_relay_io_cgns.hpp" +#endif + #ifdef CONDUIT_RELAY_IO_MPI_ENABLED // Define an argument macro that adds the communicator argument. #define CONDUIT_RELAY_COMMUNICATOR_ARG(ARG) ,ARG @@ -603,7 +610,9 @@ identify_protocol(const std::string &path) std::string("."), file_name_ext, file_name_base); - +std::cout << "identify_protocol: file_path = " << file_path << std::endl; +std::cout << "identify_protocol: file_name_base = " << file_name_base << std::endl; +std::cout << "identify_protocol: file_name_ext = " << file_name_ext << std::endl; // default std::string io_type = "bin"; @@ -911,6 +920,25 @@ void write_mesh(const Node &mesh, << "conduit build lacks silo support\n"); #endif } + + if(file_protocol == "cgns") + { +#ifdef CONDUIT_RELAY_IO_CGNS_ENABLED + #ifdef CONDUIT_RELAY_IO_MPI_ENABLED + return conduit::relay::mpi::io::cgns::write_mesh(mesh, + path, + opts, + mpi_comm); + #else + return conduit::relay::io::cgns::write_mesh(mesh, + path, + opts); + #endif +#else + CONDUIT_ERROR("write_mesh invalid protocol option: `cgns`" + << "conduit build lacks CGNS support\n"); +#endif + } // The assumption here is that everything is multi domain diff --git a/src/libs/relay/conduit_relay_io_cgns.cpp b/src/libs/relay/conduit_relay_io_cgns.cpp new file mode 100644 index 000000000..e1392a448 --- /dev/null +++ b/src/libs/relay/conduit_relay_io_cgns.cpp @@ -0,0 +1,652 @@ +#include "conduit_relay_io_cgns.hpp" + +//----------------------------------------------------------------------------- +// external lib includes +//----------------------------------------------------------------------------- +#ifdef CONDUIT_RELAY_IO_CGNS_ENABLED +#include +#endif + +namespace +{ + +std::string meshType(const conduit::Node& node) +{ + return node["/topologies/mesh/elements/shape"].as_string(); +} + +int physicalDimension(const conduit::Node& node) +{ + return node["/coordsets/coords/values"].number_of_children(); +} + +int cellDimension(const conduit::Node& node) +{ + const auto type = meshType(node); + if (type == "tet") + { + return 3; + } + else if (type == "hex") + { + return 3; + } + else if (type == "tri") + { + return 2; + } + else if (type == "quad") + { + return 2; + } + else if (type == "mixed") + { + return 3; // todo this could be 2d + } + else + { + CONDUIT_ERROR("cellDimension: unknown type " << type); + } + return 0; +} + +std::int64_t nVerts(const conduit::Node& node) +{ + return node["/coordsets/coords/values/x"].dtype().is_float64() + ? node["/coordsets/coords/values/x"].as_float64_array().number_of_elements() + : node["/coordsets/coords/values/x"].as_float32_array().number_of_elements(); +} + +std::int64_t nConn(const conduit::Node& node) +{ + return node["/topologies/mesh/elements/connectivity"].dtype().is_int64() + ? node["/topologies/mesh/elements/connectivity"].as_int64_array().number_of_elements() + : node["/topologies/mesh/elements/connectivity"].as_int32_array().number_of_elements(); +} + +std::int64_t nCells(const conduit::Node& node) +{ + const std::int64_t nconn = nConn(node); + const auto type = meshType(node); + if (type == "tet") + { + return nconn / 4; + } + else if (type == "hex") + { + return nconn / 8; + } + else if (type == "tri") + { + return nconn / 3; + } + else if (type == "quad") + { + return nconn / 4; + } + else if (type == "mixed") + { + std::cout << "need to work on ncells for mixed" << std::endl; + } + else + { + CONDUIT_ERROR("ncells: unknown type " << type); + } + return 0; +} + +} // namespace + +//----------------------------------------------------------------------------- +// -- begin conduit:: -- +//----------------------------------------------------------------------------- +namespace conduit +{ + +//----------------------------------------------------------------------------- +// -- begin conduit::relay -- +//----------------------------------------------------------------------------- +namespace relay +{ + +//----------------------------------------------------------------------------- +// -- begin conduit::relay::io -- +//----------------------------------------------------------------------------- +namespace io +{ + + +void cgns_write(const conduit::Node& mesh, const std::string& path) +{ + std::cout << "path: " << path << std::endl; + std::cout << "mesh: " << std::endl; + mesh.print(); + + // std::cout << mesh.has_path("/[0]/coordsets") << " has coordsets" << std::endl; + // for (const auto& child : mesh.child_names()){ + // std::cout << "child: " << child << std::endl; + // } + + // std::cout << mesh.has_path(mesh.children()[0]) << "mesh.has_path(mesh.children()[0]) " << std::endl; + + + const Node& mesh_node = mesh.child(0); + + + int domain_id = 0; + if (mesh_node.has_path("state/domain_id")) + { + domain_id = mesh_node["state/domain_id"].as_int32(); + } + std::ostringstream domain_id_ss; + domain_id_ss << std::setw(6) << std::setfill('0') << domain_id; + const std::string full_path = path + "." + domain_id_ss.str() + ".cgns"; + + const Node& mesh_topo = mesh_node["topologies/mesh"]; + std::cout << "mesh_topo: " << std::endl; + + mesh_topo.print(); + if (mesh_topo["type"].as_string() != "unstructured") + { + CONDUIT_ERROR("cgns::save_mesh only supports saving 'unstructured' type"); + } + if (mesh_topo["elements/shape"].as_string() != "mixed") + { + CONDUIT_ERROR("cgns::save_mesh only supports saving 'mixed' shape"); + } + std::cout << "elements/shapes.print()" << std::endl; + // mesh_topo["elements/shapes"].print(); + const auto& mesh_shapes = mesh_topo["elements/shapes"]; + if (mesh_shapes.dtype().is_empty()) + { + CONDUIT_ERROR("cgns::save_mesh requires mesh_topo['elements/shapes'] to be non-empty"); + } + std::map shape_map; + const auto& mesh_shape_map = mesh_topo["elements/shape_map"]; + // std::cout << "iterating through shape map children" << std::endl; + for (const auto& child : mesh_shape_map.children()) + { + shape_map[child.as_int32()] = child.name(); + } + std::cout << "shape_map: " << std::endl; + for (const auto& pair : shape_map) + { + std::cout << "shape_map[" << pair.first << "] = " << pair.second << std::endl; + } + + const std::map shape_counts = [&]() + { + std::map counts; + const auto shapes = mesh_shapes.as_int32_array(); + for (int64_t i = 0; i < shapes.number_of_elements(); ++i) + { + counts[shape_map[shapes[i]]]++; + } + return counts; + }(); + + std::cout << "shape_counts: " << std::endl; + for (const auto& pair : shape_counts) + { + std::cout << "shape_counts[" << pair.first << "] = " << pair.second << std::endl; + } + + struct CGNSElementSection + { + std::string section_name; + CG_ElementType_t element_type; + std::vector connectivity; + cgsize_t n_cells; + }; + + const std::map element_sections = + [](const conduit::Node& elem_node) + { + std::map shape_map; + const auto& mesh_shape_map = elem_node["shape_map"]; + // std::cout << "iterating through shape map children" << std::endl; + for (const auto& child : mesh_shape_map.children()) + { + shape_map[child.as_int32()] = child.name(); + } + + std::map sections; + const auto offsets = elem_node["offsets"].as_int32_array(); + const auto connectivity = elem_node["connectivity"].as_int32_array(); + const auto shapes = elem_node["shapes"].as_int32_array(); + const auto sizes = elem_node["sizes"].as_int32_array(); + + for (int64_t i = 0; i < shapes.number_of_elements(); ++i) + { + const int shape_id = shapes[i]; + const std::string shape_name = shape_map[shape_id]; + // CGNSElementSection section; + const int nconn = sizes[i]; + auto& conn = sections[shape_name].connectivity; + for (int64_t j = offsets[i]; j < offsets[i] + nconn; ++j) + { + conn.push_back(connectivity[j] + 1); // CGNS is 1-based indexing + } + // sections.at(shape_name). + // section.section_name = shape_name; + } + + for (auto& section : sections) + { + const auto& cell_type = section.first; + if (cell_type == "hex") + { + section.second.element_type = CG_HEXA_8; + section.second.n_cells = section.second.connectivity.size() / 8; + section.second.section_name = "Elem_HEXA_8"; + } + else if (cell_type == "tet") + { + section.second.element_type = CG_TETRA_4; + section.second.n_cells = section.second.connectivity.size() / 4; + section.second.section_name = "Elem_TETRA_4"; + } + else if (cell_type == "tri") + { + section.second.element_type = CG_TRI_3; + section.second.n_cells = section.second.connectivity.size() / 3; + section.second.section_name = "Elem_TRI_3"; + } + else if (cell_type == "quad") + { + section.second.element_type = CG_QUAD_4; + section.second.n_cells = section.second.connectivity.size() / 4; + section.second.section_name = "Elem_QUAD_4"; + } + else if (cell_type == "wedge") + { + section.second.element_type = CG_PENTA_6; + section.second.n_cells = section.second.connectivity.size() / 6; + section.second.section_name = "Elem_PENTA_6"; + } + else if (cell_type == "pyramid") + { + section.second.element_type = CG_PYRA_5; + section.second.n_cells = section.second.connectivity.size() / 5; + section.second.section_name = "Elem_PYRA_5"; + } + else + { + CONDUIT_ERROR("cgns::save_mesh: unknown cell type: " << cell_type); + } + // std::cout << "section: " << section.section_name << std::endl; + } + + + + return sections; + }(mesh_topo["elements"]); + + int file_index; + std::cout << "opening file: " << full_path << std::endl; + if (cg_open(full_path.c_str(), CG_MODE_WRITE, &file_index)) + { + cg_error_exit(); + } + // const int cell_dim = cellDimension(mesh_node); + + const int cell_dim = [&]() + { + // if shape_counts contains the key "tet", "hex", "pyramid", or "wedge", return 3; + if (shape_counts.count("tet") || shape_counts.count("hex") || shape_counts.count("pyramid") || + shape_counts.count("wedge")) + { + return 3; + } + // if shape_counts contains the key "tri", "quad", return 2; + if (shape_counts.count("tri") || shape_counts.count("quad")) + { + return 2; + } + CONDUIT_ERROR("cgns::save_mesh: unsupported cell dimension for mesh"); + return 0; + }(); + + const int phys_dim = physicalDimension(mesh_node); + + int base_index; + std::cout << "writing base" << std::endl; + if (cg_base_write(file_index, "Base", cell_dim, phys_dim, &base_index)) + { + cg_error_exit(); + } + std::cout << "base written" << std::endl; + + mesh_topo["elements/connectivity"].print(); + + const int64_t n_total_cells = [&]() + { + int64_t ncells = 0; + for (const auto& kv : shape_counts) + { + ncells += kv.second; + } + return ncells; + }(); + + cgsize_t isize[3][1]; + isize[0][0] = nVerts(mesh_node); + // isize[1][0] = nCells(mesh_node); + isize[1][0] = n_total_cells; + isize[2][0] = 0; + int zone_index; + std::cout << "writing zone" << std::endl; + if (cg_zone_write(file_index, base_index, "Zone_Domain", isize[0], CG_Unstructured, &zone_index)) + { + cg_error_exit(); + } + std::cout << "zone written" << std::endl; + + auto write_coord = [&](const conduit::Node& coords, const std::string& coord_name) + { + // todo need to be 1 based + int coord_index; + if (coords.dtype().is_float64()) + { + if (cg_coord_write(file_index, + base_index, + zone_index, + CG_RealDouble, + coord_name.c_str(), + coords.as_float64_array().data_ptr(), + &coord_index)) + { + cg_error_exit(); + } + } + else if (coords.dtype().is_float32()) + { + if (cg_coord_write(file_index, + base_index, + zone_index, + CG_RealSingle, + coord_name.c_str(), + coords.as_float32_array().data_ptr(), + &coord_index)) + { + cg_error_exit(); + } + } + else + { + CONDUIT_ERROR("CGNS coordinate only supports float32 and float64."); + } + }; + + + std::cout << "writing coords" << std::endl; + write_coord(mesh_node["/coordsets/coords/values/x"], "CoordinateX"); + if (phys_dim > 1) + { + write_coord(mesh_node["/coordsets/coords/values/y"], "CoordinateY"); + } + if (phys_dim > 2) + { + write_coord(mesh_node["/coordsets/coords/values/z"], "CoordinateZ"); + } + std::cout << "coords written" << std::endl; + + + std::cout << "writing element sections" << std::endl; + + cgsize_t element_start_offset = 1; + for (const auto& kv : element_sections) + { + std::cout << kv.first << ": " << kv.second.section_name << std::endl; + std::cout << " n_cells: " << kv.second.n_cells << std::endl; + + int section_index; + if (cg_section_write(file_index, + base_index, + zone_index, + kv.second.section_name.c_str(), + kv.second.element_type, + element_start_offset, + element_start_offset + kv.second.n_cells - 1, + 0, + kv.second.connectivity.data(), + §ion_index)) + { + cg_error_exit(); + } + element_start_offset += kv.second.n_cells; + } + + + // mesh_node["/fields"] + + + if (mesh_node.has_path("/fields")) + { + const bool has_vertex_fields = [&]() + { + for (const auto& field : mesh_node["/fields"].children()) + { + if (field["association"].as_string() == "vertex") + { + return true; + } + } + return false; + }(); + + const bool has_cell_fields = [&]() + { + for (const auto& field : mesh_node["/fields"].children()) + { + if (field["association"].as_string() == "element") + { + return true; + } + } + return false; + }(); + + int centroid_solution; + int vertex_solution; + if (has_cell_fields) + { + if (cg_sol_write(file_index, base_index, zone_index, "CellCenterSolution", CG_CellCenter, ¢roid_solution)) + { + cg_error_exit(); + } + } + if (has_vertex_fields) + { + if (cg_sol_write(file_index, base_index, zone_index, "VertexSolution", CG_Vertex, &vertex_solution)) + { + cg_error_exit(); + } + } + + for (const auto& field : mesh_node["/fields"].children()) + { + const std::string field_name = field.name(); + std::cout << "writing field: " << field_name << std::endl; + field.print(); + + const CG_DataType_t datatype = + field["values"].dtype().is_float64() ? CG_RealDouble : CG_RealSingle; + const int isol = + field["association"].as_string() == "vertex" ? vertex_solution : centroid_solution; + + int ifield; + cg_field_write(file_index, + base_index, + zone_index, + isol, + datatype, + field_name.c_str(), + field["values"].data_ptr(), + &ifield); + } + } + + if (cg_close(file_index)) + { + cg_error_exit(); + } +} + +//----------------------------------------------------------------------------- +// -- begin conduit::relay::io::cgns -- +//----------------------------------------------------------------------------- +namespace cgns +{ + + +void write_mesh(const conduit::Node& mesh, const std::string& path, const conduit::Node&) +{ + save_mesh(mesh, path); +} + +void save_mesh(const conduit::Node& mesh, const std::string& path) +{ + + CONDUIT_INFO("conduit::relay::io::cgns__save_mesh(const Node &node, const " + "std::string &path)\n"); + + try { + if (mesh["/coordsets/coords/type"].as_string() != "explicit") + { + CONDUIT_ERROR("cgns::save_mesh requires explicit coordsets type"); + } +} +catch (std::exception& e){ + std::cout << "caught exception on mesh[\"/coordsets/coords/type\"].as_string(): " << e.what() << std::endl; + +} + + mesh.print(); + + // for multi-file, maybe ".root.cgns" + const std::string filename = path + ".cgns"; + + int file_index; + if (cg_open(filename.c_str(), CG_MODE_WRITE, &file_index)) + { + cg_error_exit(); + } + const int cell_dim = cellDimension(mesh); + const int phys_dim = physicalDimension(mesh); + + int base_index; + if (cg_base_write(file_index, "Base", cell_dim, phys_dim, &base_index)) + { + cg_error_exit(); + } + + + cgsize_t isize[3][1]; + isize[0][0] = nVerts(mesh); + isize[1][0] = nCells(mesh); + isize[2][0] = 0; + int zone_index; + if (cg_zone_write(file_index, base_index, "Zone", isize[0], CG_Unstructured, &zone_index)) + { + cg_error_exit(); + } + + + auto write_coord = [&](const std::string& coord_path, const std::string& coord_name) + { + int coord_index; + if (mesh[coord_path].dtype().is_float64()) + { + if (cg_coord_write(file_index, + base_index, + zone_index, + CG_RealDouble, + coord_name.c_str(), + mesh[coord_path].as_float64_array().data_ptr(), + &coord_index)) + { + cg_error_exit(); + } + } + else if (mesh[coord_path].dtype().is_float32()) + { + if (cg_coord_write(file_index, + base_index, + zone_index, + CG_RealSingle, + coord_name.c_str(), + mesh[coord_path].as_float32_array().data_ptr(), + &coord_index)) + { + cg_error_exit(); + } + } + else + { + CONDUIT_ERROR("CGNSHandle coordinate only supports float32 and float64."); + } + }; + + write_coord("/coordsets/coords/values/x", "CoordinateX"); + if (phys_dim > 1) + { + write_coord("/coordsets/coords/values/y", "CoordinateY"); + } + if (phys_dim > 2) + { + write_coord("/coordsets/coords/values/z", "CoordinateZ"); + } + + if (cg_close(file_index)) + { + cg_error_exit(); + } +} + +void load_mesh(const std::string& root_file_path, conduit::Node& mesh) {} + +} // namespace cgns +//----------------------------------------------------------------------------- +// -- end conduit::relay::io::cgns -- +//----------------------------------------------------------------------------- + +} // namespace io +//----------------------------------------------------------------------------- +// -- end conduit::relay::io -- +//----------------------------------------------------------------------------- + +} // namespace relay +// namespace relay +//----------------------------------------------------------------------------- +// -- end conduit::relay -- +//----------------------------------------------------------------------------- + +} // namespace conduit +// namespace conduit +//----------------------------------------------------------------------------- +// -- end conduit:: -- +//----------------------------------------------------------------------------- + + +#ifdef CONDUIT_RELAY_IO_MPI_ENABLED +namespace conduit { +namespace relay { +namespace mpi { +namespace io { +namespace cgns { + +void write_mesh(const conduit::Node& mesh, const std::string& path, const conduit::Node& opts, MPI_Comm comm) +{ + std::cout << "reached conduit::relay::mpi::io::cgns::write_mesh" << std::endl; + //abort(); + // conduit::relay::io::cgns::save_mesh(mesh.child(0), path); + conduit::relay::io::cgns_write(mesh, path); +} + +} +} +} +} +} +#endif \ No newline at end of file diff --git a/src/libs/relay/conduit_relay_io_cgns.hpp b/src/libs/relay/conduit_relay_io_cgns.hpp new file mode 100644 index 000000000..130865eca --- /dev/null +++ b/src/libs/relay/conduit_relay_io_cgns.hpp @@ -0,0 +1,96 @@ +// Copyright (c) Lawrence Livermore National Security, LLC and other Conduit +// Project developers. See top-level LICENSE AND COPYRIGHT files for dates and +// other details. No copyright assignment is required to contribute to Conduit. + +//----------------------------------------------------------------------------- +/// +/// file: conduit_relay_io_cgns.hpp +/// +//----------------------------------------------------------------------------- + +#ifndef CONDUIT_RELAY_IO_CGNS_HPP +#define CONDUIT_RELAY_IO_CGNS_HPP + +//----------------------------------------------------------------------------- +// conduit lib include +//----------------------------------------------------------------------------- +#include "conduit.hpp" +#include "conduit_relay_config.h" +#include "conduit_relay_exports.h" + +//----------------------------------------------------------------------------- +// -- begin conduit:: -- +//----------------------------------------------------------------------------- +namespace conduit { + +//----------------------------------------------------------------------------- +// -- begin conduit::relay -- +//----------------------------------------------------------------------------- +namespace relay { + +//----------------------------------------------------------------------------- +// -- begin conduit::relay::io -- +//----------------------------------------------------------------------------- +namespace io { + +#include "conduit_relay_io_cgns_api.hpp" + +//----------------------------------------------------------------------------- +// -- begin conduit::relay::io::cgns -- +//----------------------------------------------------------------------------- +namespace cgns { + +//----------------------------------------------------------------------------- +// Save a blueprint mesh to cgns +//----------------------------------------------------------------------------- +/// These methods assume `mesh` is a valid blueprint mesh. +/// +/// Note: These methods use "save" semantics, they will overwrite existing +/// files. +/// +/// +//----------------------------------------------------------------------------- +void CONDUIT_RELAY_API save_mesh(const conduit::Node &mesh, + const std::string &path); + +void CONDUIT_RELAY_API write_mesh(const conduit::Node &mesh, + const std::string &path, + const conduit::Node &opts); + +//----------------------------------------------------------------------------- +// The load semantics, the mesh node is reset before reading. +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +void CONDUIT_RELAY_API load_mesh(const std::string &root_file_path, + conduit::Node &mesh); + +} +//----------------------------------------------------------------------------- +// -- end conduit::relay::io::cgns -- +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +/// Save node data to a given path. +/// +/// Save Semantics: Existing file will be overwritten +//----------------------------------------------------------------------------- +void CONDUIT_RELAY_API cgns_save(const Node &node, + const std::string &path); +} +//----------------------------------------------------------------------------- +// -- end conduit::relay::io -- +//----------------------------------------------------------------------------- + +} + // namespace relay +//----------------------------------------------------------------------------- +// -- end conduit::relay -- +//----------------------------------------------------------------------------- + +} // namespace conduit +//----------------------------------------------------------------------------- +// -- end conduit:: -- +//----------------------------------------------------------------------------- + +#endif diff --git a/src/libs/relay/conduit_relay_io_cgns_api.hpp b/src/libs/relay/conduit_relay_io_cgns_api.hpp new file mode 100644 index 000000000..62ecfa8ed --- /dev/null +++ b/src/libs/relay/conduit_relay_io_cgns_api.hpp @@ -0,0 +1,20 @@ +// Copyright (c) Lawrence Livermore National Security, LLC and other Conduit +// Project developers. See top-level LICENSE AND COPYRIGHT files for dates and +// other details. No copyright assignment is required to contribute to Conduit. +#ifndef CONDUIT_RELAY_CGNS_API_HPP +#define CONDUIT_RELAY_CGNS_API_HPP + +//----------------------------------------------------------------------------- +/// +/// file: conduit_relay_cgns_api.hpp +/// +//----------------------------------------------------------------------------- + + +/// NOTE: This file is included from other headers that provide namespaces. +/// Do not directly include this file! + +void CONDUIT_RELAY_API cgns_write(const Node &node, + const std::string &path); + +#endif // CONDUIT_RELAY_CGNS_API_HPP \ No newline at end of file diff --git a/src/libs/relay/conduit_relay_io_handle.cpp b/src/libs/relay/conduit_relay_io_handle.cpp index 53d42d49b..b031a32b5 100644 --- a/src/libs/relay/conduit_relay_io_handle.cpp +++ b/src/libs/relay/conduit_relay_io_handle.cpp @@ -23,6 +23,10 @@ #include "conduit_relay_io_hdf5.hpp" #endif +#ifdef CONDUIT_RELAY_IO_CGNS_ENABLED + // #include +#endif + //----------------------------------------------------------------------------- // standard lib includes @@ -160,6 +164,424 @@ class HDF5Handle: public IOHandle::HandleInterface //----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +// CGNSHandle -- IO Handle implementation for CGNS +//----------------------------------------------------------------------------- +#ifdef CONDUIT_RELAY_IO_CGNS_ENABLED +//----------------------------------------------------------------------------- +// class CGNSHandle: public IOHandle::HandleInterface +// { +// public: +// CGNSHandle(const std::string &path, +// const std::string &protocol, +// const Node &options) +// : HandleInterface(path, protocol, options) +// {} +// virtual ~CGNSHandle(){} + +// // void open(); + +// bool is_open() const override { +// CONDUIT_INFO("CGNSHandle::is_open not implemented\n"); +// return false; +// } + +// // main interface methods +// void read(Node &node) override { +// CONDUIT_ERROR("CGNSHandle::read(Node &node) not implemented\n"); +// } +// void read(Node &node, const Node &opts) override { +// CONDUIT_ERROR( +// "CGNSHandle::read(Node &node, const Node &opts) not implemented\n"); +// } +// void read(const std::string &path, Node &node) override { +// CONDUIT_ERROR("CGNSHandle::read(const std::string &path, Node &node) not " +// "implemented\n"); +// } +// void read(const std::string &path, Node &node, const Node &opts) override { +// CONDUIT_ERROR("CGNSHandle::read(const std::string &path, Node &node, " +// "const Node &opts) not implemented\n"); +// } + +// void write(const Node &node) override { +// CONDUIT_ERROR("CGNSHandle::write(const Node &node) not implemented\n"); +// } +// void write(const Node &node, const Node &opts) override; + +// void write(const Node &node, +// const std::string &path) override { +// CONDUIT_ERROR("CGNSHandle::write(const Node &node, const std::string " +// "&path) not implemented\n"); +// } +// void write(const Node &node, +// const std::string &path, +// const Node &opts) override; + +// void remove(const std::string &path) override { +// CONDUIT_ERROR("CGNSHandle::remove(const std::string &path) not " +// "implemented\n"); +// } + +// void list_child_names(std::vector &res) override { +// CONDUIT_ERROR("CGNSHandle::list_child_names not implemented\n"); +// } + +// void list_child_names(const std::string &path, +// std::vector &res) override { +// CONDUIT_ERROR("CGNSHandle::list_child_names not implemented\n"); +// } + +// bool has_path(const std::string &path) override { +// CONDUIT_ERROR("CGNSHandle::has_path not implemented\n"); +// } + +// void close() override { +// CONDUIT_INFO("CGNSHandle::close\n"); +// if (is_open_){ +// if (cg_close(file_index_)){ +// cg_error_exit(); +// } +// } +// } + +// private: +// int file_index_; +// int base_index_; +// int zone_index_; +// int vertex_sol_index_; +// int element_sol_index_; +// std::string filename_; + +// bool is_open_ = false; + +// Node data_ref_node_; + +// std::int64_t nVerts(const Node &node) const; +// std::int64_t nConn(const Node &node) const; +// std::int64_t nCells(const Node &node) const; + + +// int physicalDimension(const Node& node) const +// { +// return node["/coordsets/coords/values"].number_of_children(); +// } + + +// int cellDimension(const Node& node) const +// { +// const auto type = meshType(node); +// if (type == "tet"){ +// return 3; +// } else if (type == "hex"){ +// return 3; +// } else if (type == "tri"){ +// return 2; +// } else if (type == "quad"){ +// return 2; +// } else { +// CONDUIT_ERROR("CGNSHandle::cellDimension: unknown type " << type); +// } +// return 0; +// } + + +// std::string meshType(const Node& node) const { +// return node["/topologies/mesh/elements/shape"].as_string(); +// } + +// bool hasVertexAssociatedFields(const Node& node) const { +// auto iter = node["fields"].children(); +// while(iter.has_next()){ +// const Node& field = iter.next(); +// if (field["association"].as_string() == "vertex"){ +// return true; +// } +// } +// return false; +// } + +// bool hasElementAssociatedFields(const Node& node) const { +// auto iter = node["fields"].children(); +// while(iter.has_next()){ +// const Node& field = iter.next(); +// if (field["association"].as_string() == "element"){ +// return true; +// } +// } +// return false; +// } +// }; + +// std::int64_t CGNSHandle::nVerts(const Node &node) const { +// return node["/coordsets/coords/values/x"].dtype().is_float64() +// ? node["/coordsets/coords/values/x"].as_float64_array() +// .number_of_elements() +// : node["/coordsets/coords/values/x"].as_float32_array() +// .number_of_elements(); +// } + +// std::int64_t CGNSHandle::nConn(const Node &node) const { +// return node["/topologies/mesh/elements/connectivity"] +// .dtype() +// .is_int64() +// ? node["/topologies/mesh/elements/connectivity"] +// .as_int64_array() +// .number_of_elements() +// : node["/topologies/mesh/elements/connectivity"] +// .as_int32_array() +// .number_of_elements(); +// } + +// std::int64_t CGNSHandle::nCells(const Node &node) const { +// const std::int64_t nconn = nConn(node); +// const auto type = meshType(node); +// if (type == "tet"){ +// return nconn/4; +// } else if (type == "hex"){ +// return nconn/8; +// } else if (type == "tri"){ +// return nconn/3; +// } else if (type == "quad"){ +// return nconn/4; +// } else { +// CONDUIT_ERROR("CGNSHandle::ncells: unknown type " << type); +// } +// return 0; +// } + + +// void CGNSHandle::write(const Node &node, const Node &opts) { +// CONDUIT_INFO("CGNSHandle::write(const Node &node, const Node &opts) not " +// "implemented\n"); +// // todo apparently need to rename file here +// // std::cout << "node to print:\n"; +// // node.print(); +// // std::cout << "opts to print:\n"; +// // opts.print(); +// // std::cout << "data_ref_node_:\n"; +// // data_ref_node_.print(); +// } + +// void CGNSHandle::write(const Node &node, const std::string &path, +// const Node &opts) { + +// CONDUIT_INFO("CGNSHandle:::write(const Node &node, const std::string &path, const Node &opts)\n"); +// if (node["/coordsets/coords/type"].as_string() != "explicit"){ +// CONDUIT_ERROR("CGNSHandle only supports reading 'explicit' coordinate sets at this time."); +// } + +// data_ref_node_["state"].set_external_node(node["state"]); + + +// std::cout << "node to print:\n"; +// node.print(); +// std::cout << "path: " << path << "\n"; +// std::cout << "opts:\n"; +// opts.print(); + +// filename_ = "temp.cgns"; +// if (cg_open(filename_.c_str(), CG_MODE_WRITE, &file_index_)){ +// cg_error_exit(); +// } +// const int cell_dim = cellDimension(node); +// const int phys_dim = physicalDimension(node); + +// if (cg_base_write(file_index_, "Base", cell_dim, phys_dim, &base_index_)){ +// cg_error_exit(); +// } + + +// cgsize_t isize[3][1]; +// isize[0][0] = nVerts(node); +// isize[1][0] = nCells(node); +// isize[2][0] = 0; +// if (cg_zone_write(file_index_, base_index_, "Zone", isize[0], CG_Unstructured, +// &zone_index_)) { +// cg_error_exit(); +// } + +// auto write_coord = [&](const std::string &coord_path, +// const std::string &coord_name) { +// int coord_index; +// if (node[coord_path].dtype().is_float64()) { +// if (cg_coord_write(file_index_, base_index_, zone_index_, CG_RealDouble, +// coord_name.c_str(), +// node[coord_path].as_float64_array().data_ptr(), +// &coord_index)) { +// cg_error_exit(); +// } +// } else if (node[coord_path].dtype().is_float32()) { +// if (cg_coord_write(file_index_, base_index_, zone_index_, CG_RealSingle, +// coord_name.c_str(), +// node[coord_path].as_float32_array().data_ptr(), +// &coord_index)) { +// cg_error_exit(); +// } +// } else { +// CONDUIT_ERROR("CGNSHandle coordinate only supports float32 and float64."); +// } +// }; + +// write_coord("/coordsets/coords/values/x", "CoordinateX"); +// if (phys_dim > 1){ +// write_coord("/coordsets/coords/values/y", "CoordinateY"); +// } +// if (phys_dim > 2){ +// write_coord("/coordsets/coords/values/z", "CoordinateZ"); +// } + + + +// std::cout << node["topologies/mesh/elements/connectivity"].dtype().name() << "\n"; + +// static_assert(sizeof(cgsize_t) == sizeof(std::int64_t)); + +// const std::vector conn = [&]() { +// std::vector conn_1based(nConn(node)); + +// if (node["topologies/mesh/elements/connectivity"].dtype().is_int64()) { +// auto *conn_ptr = reinterpret_cast(node["/topologies/mesh/elements/connectivity"] +// .as_int64_array() +// .data_ptr()); +// for (size_t i = 0; i < conn.size(); ++i) { +// conn_1based[i] = conn_ptr[i] + 1; // CGNS expects 1-based connectivity +// } +// } else if (node["topologies/mesh/elements/connectivity"] +// .dtype() +// .is_int32()) { +// auto *conn_ptr = reinterpret_cast(node["/topologies/mesh/elements/connectivity"] +// .as_int32_array() +// .data_ptr()); +// for (size_t i = 0; i < conn.size(); ++i) { +// conn_1based[i] = static_cast( +// conn_ptr[i] + 1); // CGNS expects 1-based connectivity +// } +// } +// return conn_1based; +// }(); + +// const std::string& mesh_type = node["/topologies/mesh/elements/shape"].as_string(); + +// int section_index; +// if (mesh_type == "tet") { +// if (cg_section_write(file_index_, base_index_, zone_index_, "Elem_TETRA_4", +// CG_TETRA_4, 1, nCells(node), NULL, conn.data(), +// §ion_index)) { +// cg_error_exit(); +// } +// } +// else if (mesh_type == "hex"){ +// if (cg_section_write(file_index_, base_index_, zone_index_, "Elem_HEXA_8", +// CG_HEXA_8, 1, nCells(node), NULL, conn.data(), +// §ion_index)) { +// cg_error_exit(); +// } +// } +// else { +// CONDUIT_ERROR("CGNSHandle does not support mesh type: " << mesh_type); +// } + +// if (hasVertexAssociatedFields(node)){ +// cg_sol_write(file_index_, base_index_, zone_index_, "VertexSolution", CG_Vertex, &vertex_sol_index_); +// } +// if (hasElementAssociatedFields(node)){ +// cg_sol_write(file_index_, base_index_, zone_index_, "CellCenterSolution", CG_CellCenter, &element_sol_index_); +// } + +// auto field_write = [&](const Node &field, const std::string& name, CG_GridLocation_t location) { + +// const int flow_index = (location == CG_Vertex) ? vertex_sol_index_ : element_sol_index_; +// int field_index; + +// if (field["values"].dtype().is_float32()) { +// if (cg_field_write(file_index_, base_index_, zone_index_, flow_index, +// CG_RealSingle, name.c_str(), +// field["values"].as_float32_array().data_ptr(), +// &field_index)) { +// cg_error_exit(); +// } +// } else if (field["values"].dtype().is_float64()) { +// if (cg_field_write(file_index_, base_index_, zone_index_, flow_index, +// CG_RealDouble, name.c_str(), +// field["values"].as_float64_array().data_ptr(), +// &field_index)) { +// cg_error_exit(); +// } +// } else { +// CONDUIT_ERROR( +// "CGNSHandle only supports float32 and float64 field values. " +// << field["values"].dtype().name() << " was given for field " +// << field.name()); +// } +// }; + +// { +// auto iter = node["fields"].children(); +// while (iter.has_next()) { +// const Node &field = iter.next(); + +// const CG_GridLocation_t location = [&]() { +// if (field["association"].as_string() == "vertex") { +// return CG_Vertex; +// } else if (field["association"].as_string() == "element") { +// return CG_CellCenter; +// } +// }(); + +// std::cout << "num children: " << field.name() << " " +// << field["values"].number_of_children() << "\n"; + +// // write scalar fields as-is +// if (field["values"].number_of_children() == 0) { +// field_write(field, field.name(), location); +// } +// else // iterate over vector valued fields +// { +// auto field_iter = field["values"].children(); +// CONDUIT_INFO("CGNSHandle vector-valued output not implmented for field " + field.name()); + + +// while (field_iter.has_next()) { +// const Node &scalar_field = field_iter.next(); +// const std::string name = +// field.name() + "_" + scalar_field.name(); + +// const int flow_index = +// (location == CG_Vertex) ? vertex_sol_index_ : element_sol_index_; +// int field_index; + +// scalar_field.print(); +// if (scalar_field.dtype().is_float64()) { +// if (cg_field_write(file_index_, base_index_, zone_index_, +// flow_index, CG_RealDouble, name.c_str(), +// scalar_field.as_float64_ptr(), +// &field_index)) { +// cg_error_exit(); +// } +// } else if (scalar_field.dtype().is_float32()) { +// if (cg_field_write(file_index_, base_index_, zone_index_, +// flow_index, CG_RealDouble, name.c_str(), +// scalar_field.as_float32_ptr(), +// &field_index)) { +// cg_error_exit(); +// } +// } +// else { +// CONDUIT_ERROR( +// "CGNSHandle only supports float32 and float64 field values. " +// << scalar_field.dtype().name() << " was given for field " +// << scalar_field.name()); +// } +// } +// } +// } +// } +// } + +//----------------------------------------------------------------------------- +#endif +//----------------------------------------------------------------------------- + //----------------------------------------------------------------------------- // HandleInterface Implementation //----------------------------------------------------------------------------- @@ -342,6 +764,15 @@ IOHandle::HandleInterface::create(const std::string &path, "Cannot create Relay I/O Handle for HDF5" << path); #endif } + else if( protocol == "cgns" ) + { + // #ifdef CONDUIT_RELAY_IO_CGNS_ENABLED + // res = new CGNSHandle(path, protocol, options); + // #else + CONDUIT_ERROR("conduit_relay lacks CGNS support: " << + "Cannot create Relay I/O Handle for CGNS" << path); + // #endif + } else { CONDUIT_ERROR("Relay I/O Handle does not support the protocol: " diff --git a/src/libs/relay/conduit_relay_io_identify_protocol.cpp b/src/libs/relay/conduit_relay_io_identify_protocol.cpp index 92cd9842d..4857fe503 100644 --- a/src/libs/relay/conduit_relay_io_identify_protocol.cpp +++ b/src/libs/relay/conduit_relay_io_identify_protocol.cpp @@ -87,6 +87,10 @@ identify_protocol(const std::string &path, { io_type = "hdf5"; } + else if (file_name_ext == "cgns") + { + io_type = "cgns"; + } else if(file_name_ext == "silo") { io_type = "conduit_silo"; diff --git a/src/libs/relay/conduit_relay_mpi_io.cpp b/src/libs/relay/conduit_relay_mpi_io.cpp index 2d4b85746..c9bb3cd8c 100644 --- a/src/libs/relay/conduit_relay_mpi_io.cpp +++ b/src/libs/relay/conduit_relay_mpi_io.cpp @@ -276,6 +276,11 @@ save(const Node &node, "Failed to save conduit node to path " << path); #endif } + else if( protocol == "cgns") + { + CONDUIT_ERROR("conduit_relay_mpi_io lacks CGNS support: " << + "Failed to save conduit node to path " << path); + } else { CONDUIT_ERROR("unknown conduit_relay protocol: " << protocol); @@ -378,6 +383,11 @@ save_merged(const Node &node, "Failed to save conduit node to path " << path); #endif } + else if( protocol == "cgns") + { + CONDUIT_ERROR("conduit_relay_mpi_io lacks CGNS support in save_merged: " << + "Failed to save conduit node to path " << path); + } else { CONDUIT_ERROR("unknown conduit_relay protocol: " << protocol); diff --git a/src/libs/relay/conduit_relay_mpi_io_cgns.hpp b/src/libs/relay/conduit_relay_mpi_io_cgns.hpp new file mode 100644 index 000000000..eb88e12a6 --- /dev/null +++ b/src/libs/relay/conduit_relay_mpi_io_cgns.hpp @@ -0,0 +1,183 @@ +// Copyright (c) Lawrence Livermore National Security, LLC and other Conduit +// Project developers. See top-level LICENSE AND COPYRIGHT files for dates and +// other details. No copyright assignment is required to contribute to Conduit. +//----------------------------------------------------------------------------- +/// +/// file: conduit_relay_mpi_io_cgns.hpp +/// +//----------------------------------------------------------------------------- + +#ifndef CONDUIT_RELAY_MPI_IO_CGNS_HPP +#define CONDUIT_RELAY_MPI_IO_CGNS_HPP + +//----------------------------------------------------------------------------- +// external lib includes +//----------------------------------------------------------------------------- +// #include + +//----------------------------------------------------------------------------- +// conduit lib include +//----------------------------------------------------------------------------- +#include "conduit.hpp" +#include "conduit_relay_exports.h" +#include "conduit_relay_config.h" + +#include + +//----------------------------------------------------------------------------- +// -- begin conduit:: -- +//----------------------------------------------------------------------------- +namespace conduit +{ + +//----------------------------------------------------------------------------- +// -- begin conduit::relay -- +//----------------------------------------------------------------------------- +namespace relay +{ + +//----------------------------------------------------------------------------- +// -- begin conduit::relay::mpi -- +//----------------------------------------------------------------------------- +namespace mpi +{ + +//----------------------------------------------------------------------------- +// -- begin conduit::relay::mpi::io -- +//----------------------------------------------------------------------------- +namespace io +{ + +// Functions are provided by this include file. +#include "conduit_relay_io_cgns_api.hpp" + +//----------------------------------------------------------------------------- +// -- begin <>::cgns -- +//----------------------------------------------------------------------------- +namespace cgns +{ + +//----------------------------------------------------------------------------- +// Write a blueprint mesh to cgns +//----------------------------------------------------------------------------- +/// These methods assume `mesh` is a valid blueprint mesh. +/// +/// Note: These methods use "write" semantics, they will append to existing +/// files. +/// +/// +//----------------------------------------------------------------------------- +void CONDUIT_RELAY_API write_mesh(const conduit::Node &mesh, + const std::string &path, + MPI_Comm comm); + +//----------------------------------------------------------------------------- +/// The following options can be passed via the opts Node: +//----------------------------------------------------------------------------- +/// opts: +/// TODO +/// +//----------------------------------------------------------------------------- +void CONDUIT_RELAY_API write_mesh(const conduit::Node &mesh, + const std::string &path, + const conduit::Node &opts, + MPI_Comm comm); + +//----------------------------------------------------------------------------- +// Save a blueprint mesh to cgns +//----------------------------------------------------------------------------- +/// These methods assume `mesh` is a valid blueprint mesh. +/// +/// Note: These methods use "save" semantics, they will overwrite existing +/// files. +/// +/// +//----------------------------------------------------------------------------- +void CONDUIT_RELAY_API save_mesh(const conduit::Node &mesh, + const std::string &path, + MPI_Comm comm); + +//----------------------------------------------------------------------------- +/// The following options can be passed via the opts Node: +//----------------------------------------------------------------------------- +/// opts: +/// TODO +/// +//----------------------------------------------------------------------------- +void CONDUIT_RELAY_API save_mesh(const conduit::Node &mesh, + const std::string &path, + const conduit::Node &opts, + MPI_Comm comm); + +//----------------------------------------------------------------------------- +// The load semantics, the mesh node is reset before reading. +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +void CONDUIT_RELAY_API load_mesh(const std::string &root_file_path, + conduit::Node &mesh, + MPI_Comm comm); + + +//----------------------------------------------------------------------------- +/// +/// opts: +/// TODO +/// +//----------------------------------------------------------------------------- +void CONDUIT_RELAY_API load_mesh(const std::string &root_file_path, + const conduit::Node &opts, + conduit::Node &mesh, + MPI_Comm comm); + +//----------------------------------------------------------------------------- +// Load a blueprint mesh from root + file set +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +void CONDUIT_RELAY_API read_mesh(const std::string &root_file_path, + conduit::Node &mesh, + MPI_Comm comm); + + +//----------------------------------------------------------------------------- +/// +/// opts: +/// TODO +/// +//----------------------------------------------------------------------------- +void CONDUIT_RELAY_API read_mesh(const std::string &root_file_path, + const conduit::Node &opts, + conduit::Node &mesh, + MPI_Comm comm); + + +} +//----------------------------------------------------------------------------- +// -- end <>::cgns -- +//----------------------------------------------------------------------------- + +} +//----------------------------------------------------------------------------- +// -- end conduit::relay::mpi::io -- +//----------------------------------------------------------------------------- + +} +//----------------------------------------------------------------------------- +// -- end conduit::relay::mpi -- +//----------------------------------------------------------------------------- + +} +//----------------------------------------------------------------------------- +// -- end conduit::relay -- +//----------------------------------------------------------------------------- + + +} +//----------------------------------------------------------------------------- +// -- end conduit:: -- +//----------------------------------------------------------------------------- + + +#endif + diff --git a/src/tests/blueprint/t_blueprint_mesh_relay.cpp b/src/tests/blueprint/t_blueprint_mesh_relay.cpp index 9528afa5b..0fc677ee0 100644 --- a/src/tests/blueprint/t_blueprint_mesh_relay.cpp +++ b/src/tests/blueprint/t_blueprint_mesh_relay.cpp @@ -177,6 +177,66 @@ TEST(conduit_blueprint_mesh_relay, save_read_mesh) EXPECT_FALSE(data.child(2).diff(n_read.child(2),info)); } +//----------------------------------------------------------------------------- +TEST(conduit_blueprint_mesh_relay, save_read_mesh_cgns) +{ + Node io_protos; + relay::io::about(io_protos["io"]); + bool cgns_enabled = io_protos["io/protocols/cgns"].as_string() == "enabled"; + if(!cgns_enabled) + { + CONDUIT_INFO("CGNS disabled, skipping save_read_mesh_truncate test"); + return; + } + std::cout << "cgns_enabled: " << cgns_enabled << "\n"; + + + { + Node data; + blueprint::mesh::examples::braid("tets", + 4, + 4, + 4, + data.append()); + + + relay::io::blueprint::write_mesh(data, "all_tet", "cgns"); + // todo needs read_mesh test + // Node read_data; + // relay::io::blueprint::read_mesh("temp.cgns", read_data); + // Node diff_info; + // // diff == false, no diff == diff clean + // EXPECT_FALSE(data.diff(read_data.child(0),diff_info)); + } + + { + Node data; + blueprint::mesh::examples::braid("hexs", + 4, + 4, + 4, + data.append()); + + + relay::io::blueprint::save_mesh(data, "all_hex", "cgns"); + // todo needs read_mesh test + } + // { + // Node data; + // blueprint::mesh::examples::braid("mixed", + // 4, + // 4, + // 4, + // data.append()); + + + // relay::io::blueprint::save_mesh(data, "mixed_element", "cgns"); + // // todo needs read_mesh test + // } + + ASSERT_TRUE(false); +} + //----------------------------------------------------------------------------- TEST(conduit_blueprint_mesh_relay, save_read_mesh_truncate) { diff --git a/src/tests/relay/CMakeLists.txt b/src/tests/relay/CMakeLists.txt index 2bb21a9a3..bea0b2fcf 100644 --- a/src/tests/relay/CMakeLists.txt +++ b/src/tests/relay/CMakeLists.txt @@ -35,6 +35,7 @@ set(RELAY_HDF5_TESTS t_relay_io_hdf5 t_relay_io_hdf5_diag_verbose t_relay_io_hdf5_ident_report) +set(RELAY_CGNS_TESTS t_relay_io_cgns) set(RELAY_MPI_IO_TESTS t_relay_io_mpi_smoke) set(RELAY_ADIOS_TESTS t_relay_io_adios) @@ -102,6 +103,26 @@ else() message(STATUS "Silo disabled: Skipping conduit_relay silo tests") endif() +if(CGNS_FOUND) + message(STATUS "CGNS enabled: Adding conduit_relay CGNS unit tests") + foreach(TEST ${RELAY_CGNS_TESTS}) + add_cpp_test(TEST ${TEST} + DEPENDS_ON conduit conduit_relay + FOLDER tests/relay) + endforeach() + if(MPI_FOUND) + message(STATUS "CGNS + MPI enabled: Adding conduit_relay_mpi_cgns unit tests") + foreach(TEST ${RELAY_MPI_CGNS_TESTS}) + add_cpp_mpi_test(TEST ${TEST} + NUM_MPI_TASKS 2 + DEPENDS_ON conduit conduit_relay_mpi_io + FOLDER tests/relay) + endforeach() + endif() +else() + message(STATUS "CGNS disabled: Skipping conduit_relay cgns tests") +endif() + if(ADIOS_FOUND) if(NOT MPI_FOUND) message(STATUS "ADIOS enabled: Adding conduit_relay ADIOS unit tests") diff --git a/src/tests/relay/t_relay_io_cgns.cpp b/src/tests/relay/t_relay_io_cgns.cpp new file mode 100644 index 000000000..9dc4cd290 --- /dev/null +++ b/src/tests/relay/t_relay_io_cgns.cpp @@ -0,0 +1,80 @@ + +//----------------------------------------------------------------------------- +/// +/// file: t_relay_io_cgns.cpp +/// +//----------------------------------------------------------------------------- + +#include "silo_test_utils.hpp" + +#include "conduit_relay.hpp" +#include "conduit_relay_io_cgns.hpp" + +#include +#include "gtest/gtest.h" + +using namespace conduit; +using namespace conduit::utils; +using namespace conduit::relay; + +//----------------------------------------------------------------------------- +TEST(conduit_relay_io_cgns, 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"), + // // TODO + // // std::make_pair("polyhedra", "3") + // }; + const std::vector> mesh_types = { + std::make_pair("tets", "3"), + std::make_pair("hexs", "3"), + }; + for (int i = 0; i < mesh_types.size(); ++i) + { + const std::string dim = mesh_types[i].second; + const index_t nx = 3; + const index_t ny = 4; + const 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 = "cgns_basic_" + mesh_type + "_" + dim + "D"; + const std::string filename = basename; + + remove_path_if_exists(filename); + io::cgns::save_mesh(save_mesh, basename); + io::cgns::load_mesh(filename, load_mesh); + // EXPECT_TRUE(blueprint::mesh::verify(load_mesh, info)); + + std::cout << "saved mesh:\n"; + save_mesh.print(); + std::cout << "\nloaded mesh:\n"; + load_mesh.print(); + std::cout << "\n"; + + // // 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)); + } +} \ No newline at end of file