13#include <unordered_set>
21#define CHECK_NETCDF_CODE(return_code)\
23 if ((return_code) != NC_NOERR)\
25 MFEM_ABORT("NetCDF error: " << nc_strerror((return_code)));\
29#if defined(MFEM_USE_DOUBLE)
30#define MFEM_NETCDF_REAL_T NC_DOUBLE
31#elif defined(MFEM_USE_SINGLE)
32#define MFEM_NETCDF_REAL_T NC_FLOAT
40namespace ExodusIISideMaps
64namespace ExodusIINodeOrderings
70 1, 2, 3, 4, 5, 8, 6, 7, 9, 10
75 1, 2, 3, 4, 5, 6, 7, 8, 9,
76 10, 11, 12, 17, 18, 19, 20, 13, 14,
77 15, 16, 27, 21, 26, 25, 23, 22, 24
82 1, 2, 3, 4, 5, 6, 7, 8, 9, 13, 14, 15, 10, 11, 12, 16, 17, 18
87 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14
92namespace ExodusIILabels
134 ExodusIIWriter(
Mesh & mesh) : mesh{mesh} {}
136 ExodusIIWriter() =
delete;
144 void PrintExodusII(
const std::string &fpath,
int flags = NC_CLOBBER);
150 static void PrintExodusII(Mesh & mesh,
const std::string &fpath,
151 int flags = NC_CLOBBER);
155 void OpenExodusII(
const std::string &fpath,
int flags);
158 void CloseExodusII();
165 void GenerateExodusIIElementBlocks();
169 void GenerateExodusIIBoundaryInfo();
173 std::unordered_set<int> GenerateUniqueNodeIDs();
176 void ExtractVertexCoordinates(std::vector<real_t> &coordx,
177 std::vector<real_t> &coordy,
178 std::vector<real_t> &coordz);
182 void WriteNodeConnectivityForBlock(
const int block_id);
185 void WriteBoundaries();
188 void WriteBlockIDs();
194 void WriteNumOfElements();
197 void WriteFloatingPointWordSize();
200 void WriteAPIVersion();
203 void WriteDatabaseVersion();
206 void WriteMaxLineLength();
209 void WriteMaxNameLength();
212 void WriteNumElementBlocks();
215 void WriteElementBlocks();
219 void WriteElementBlockParameters(
int block_id);
222 void WriteNodalCoordinates();
227 void WriteFileSize();
230 void WriteNodeSets();
233 void WriteMeshDimension();
237 void WriteTimesteps();
244 void WriteDummyVariable();
247 void DefineDimension(
const char *name,
size_t len,
int *dim_id);
250 void DefineVar(
const char *name, nc_type xtype,
int ndims,
const int *dimidsp,
255 void PutVar(
int varid,
const void * data);
258 void DefineAndPutVar(
const char *name, nc_type xtype,
int ndims,
259 const int *dimidsp,
const void *data);
263 void PutAtt(
int varid,
const char *name, nc_type xtype,
size_t len,
268 char * GenerateLabel(
const char * format, ...);
272 void WriteExodusIIFileInformation();
275 void WriteExodusIIMeshInformation();
279 void CheckNodalFESpaceIsSecondOrderH1()
const;
285 bool file_open{
false};
291 std::vector<int> block_ids;
292 std::map<int, Element::Type> element_type_for_block_id;
293 std::map<int, std::vector<int>> element_ids_for_block_id;
295 std::vector<int> boundary_ids;
296 std::map<int, std::vector<int>> exodusII_element_ids_for_boundary_id;
297 std::map<int, std::vector<int>> exodusII_side_ids_for_boundary_id;
302 ExodusIIWriter::PrintExodusII(*
this, fpath);
305void ExodusIIWriter::DefineDimension(
const char *name,
size_t len,
int *dim_id)
308 CHECK_NETCDF_CODE(nc_def_dim(exid, name, len, dim_id));
311void ExodusIIWriter::DefineVar(
const char *name, nc_type xtype,
int ndims,
312 const int *dimidsp,
int *varidp)
315 CHECK_NETCDF_CODE(nc_def_var(exid, name, xtype, ndims, dimidsp,
319void ExodusIIWriter::PutAtt(
int varid,
const char *name, nc_type xtype,
320 size_t len,
const void * data)
323 CHECK_NETCDF_CODE(nc_put_att(exid, varid, name, xtype, len, data));
326void ExodusIIWriter::PutVar(
int varid,
const void * data)
329 CHECK_NETCDF_CODE(nc_put_var(exid, varid, data));
332void ExodusIIWriter::DefineAndPutVar(
const char *name, nc_type xtype,
int ndims,
333 const int *dimidsp,
const void *data)
336 DefineVar(name, xtype, ndims, dimidsp, &varid);
340void ExodusIIWriter::WriteExodusIIFileInformation()
344 WriteDatabaseVersion();
347 WriteFloatingPointWordSize();
351 WriteMaxNameLength();
352 WriteMaxLineLength();
354 WriteDummyVariable();
357void ExodusIIWriter::WriteExodusIIMeshInformation()
359 WriteMeshDimension();
360 WriteNumOfElements();
364 WriteNodalCoordinates();
366 WriteElementBlocks();
371void ExodusIIWriter::PrintExodusII(
const std::string &fpath,
int flags)
373 OpenExodusII(fpath, flags);
375 WriteExodusIIFileInformation();
376 WriteExodusIIMeshInformation();
380 mfem::out <<
"Mesh successfully written to Exodus II file" << std::endl;
383void ExodusIIWriter::PrintExodusII(Mesh &mesh,
const std::string &fpath,
386 ExodusIIWriter writer(mesh);
388 writer.PrintExodusII(fpath, flags);
391void ExodusIIWriter::OpenExodusII(
const std::string &fpath,
int flags)
395 CHECK_NETCDF_CODE(nc_create(fpath.c_str(), flags, &exid));
400void ExodusIIWriter::CloseExodusII()
402 if (!file_open) {
return; }
404 CHECK_NETCDF_CODE(nc_close(exid));
410ExodusIIWriter::~ExodusIIWriter()
415void ExodusIIWriter::WriteTitle()
422void ExodusIIWriter::WriteNumOfElements()
429void ExodusIIWriter::WriteFloatingPointWordSize()
431 const int word_size =
sizeof(
real_t);
437void ExodusIIWriter::WriteAPIVersion()
444void ExodusIIWriter::WriteDatabaseVersion()
447 MFEM_NETCDF_REAL_T, 1,
451void ExodusIIWriter::WriteMaxNameLength()
457void ExodusIIWriter::WriteMaxLineLength()
463void ExodusIIWriter::WriteBlockIDs()
474void ExodusIIWriter::WriteElementBlocks()
476 GenerateExodusIIElementBlocks();
478 WriteNumElementBlocks();
481 for (
int block_id : block_ids)
483 WriteElementBlockParameters(block_id);
487char * ExodusIIWriter::GenerateLabel(
const char * format, ...)
490 va_start(arglist, format);
492 const int buffer_size = 100;
494 static char buffer[buffer_size];
495 int nwritten = vsnprintf(buffer, buffer_size, format, arglist);
497 bool ok = (nwritten > 0 && nwritten < buffer_size);
500 MFEM_ABORT(
"Unable to write characters to buffer.");
507void ExodusIIWriter::WriteElementBlockParameters(
int block_id)
509 char * label{
nullptr};
511 const std::vector<int> & block_element_ids = element_ids_for_block_id.at(
513 const Element * front_element = mesh.GetElement(block_element_ids.front());
516 label = GenerateLabel(
"num_el_in_blk%d", block_id);
518 int num_el_in_blk_id;
519 DefineDimension(label, block_element_ids.size(),
523 label = GenerateLabel(
"num_nod_per_el%d", block_id);
525 int num_node_per_el_id;
529 CheckNodalFESpaceIsSecondOrderH1();
532 const FiniteElementSpace * fespace = mesh.GetNodalFESpace();
534 auto & block_elements = element_ids_for_block_id.at(block_id);
536 int first_element_id = block_elements.front();
539 fespace->GetElementDofs(first_element_id, dofs);
541 DefineDimension(label, dofs.Size(),
542 &num_node_per_el_id);
546 DefineDimension(label, front_element->GetNVertices(),
547 &num_node_per_el_id);
551 label = GenerateLabel(
"num_edg_per_el%d", block_id);
553 int num_edg_per_el_id;
554 DefineDimension(label, front_element->GetNEdges(),
558 label = GenerateLabel(
"num_fac_per_el%d", block_id);
560 int num_fac_per_el_id;
561 DefineDimension(label, front_element->GetNFaces(),
565 WriteNodeConnectivityForBlock(block_id);
568 std::string element_type;
570 const FiniteElementSpace * fespace = mesh.GetNodalFESpace();
573 MFEM_ASSERT((!fespace || (fespace &&
574 !fespace->IsVariableOrder())),
575 "Spaces with varying element orders are not supported.");
577 bool higher_order = (fespace && fespace->GetMaxElementOrder() > 1);
579 switch (front_element->GetType())
582 element_type = higher_order ?
"HEX27" :
"Hex8";
585 element_type = higher_order ?
"TETRA10" :
"TETRA4";
588 element_type = higher_order ?
"WEDGE18" :
"WEDGE6";
591 element_type = higher_order ?
"PYRAMID14" :
"PYRAMID5";
594 MFEM_ABORT(
"Unsupported MFEM element type: " << front_element->GetType());
597 label = GenerateLabel(
"connect%d", block_id);
600 CHECK_NETCDF_CODE(nc_inq_varid(exid, label, &connect_id));
603 element_type.length(),
604 element_type.c_str());
607void ExodusIIWriter::WriteNodalCoordinates()
610 std::unordered_set<int> unique_node_ids = GenerateUniqueNodeIDs();
611 const size_t num_nodes = unique_node_ids.size();
615 DefineDimension(
"num_nodes", num_nodes, &num_nodes_id);
620 std::vector<real_t> coordx(num_nodes);
621 std::vector<real_t> coordy(num_nodes);
622 std::vector<real_t> coordz(mesh.Dimension() == 3 ? num_nodes : 0);
624 ExtractVertexCoordinates(coordx, coordy, coordz);
634 if (mesh.Dimension() == 3)
642void ExodusIIWriter::WriteBoundaries()
645 GenerateExodusIIBoundaryInfo();
648 int num_side_sets_ids;
654 int boundary_ids_dim;
661 boundary_ids.data());
664 for (
int boundary_id : boundary_ids)
666 size_t num_elements_for_boundary = exodusII_element_ids_for_boundary_id.at(
669 char * label = GenerateLabel(
"num_side_ss%d", boundary_id);
672 DefineDimension(label, num_elements_for_boundary,
677 for (
int boundary_id : boundary_ids)
679 const std::vector<int> & side_ids = exodusII_side_ids_for_boundary_id.at(
682 char * label = GenerateLabel(
"side_ss%d_dim", boundary_id);
685 DefineDimension(label, side_ids.size(), &side_id_dim);
687 label = GenerateLabel(
"side_ss%d", boundary_id);
688 DefineAndPutVar(label, NC_INT, 1, &side_id_dim, side_ids.data());
692 for (
int boundary_id : boundary_ids)
694 const std::vector<int> & element_ids = exodusII_element_ids_for_boundary_id.at(
697 char * label = GenerateLabel(
"elem_ss%d_dim", boundary_id);
700 DefineDimension(label, element_ids.size(), &elem_ids_dim);
702 label = GenerateLabel(
"elem_ss%d", boundary_id);
703 DefineAndPutVar(label, NC_INT, 1, &elem_ids_dim,
708void ExodusIIWriter::WriteNodeConnectivityForBlock(
const int block_id)
710 std::vector<int> block_node_connectivity;
712 int * node_ordering_map =
nullptr;
715 Element::Type block_type = element_type_for_block_id.at(block_id);
720 node_ordering_map = (
int *)
724 node_ordering_map = (
int *)
728 node_ordering_map = (
int *)
732 node_ordering_map = (
int *)
736 MFEM_ABORT(
"Higher-order elements of type '" << block_type <<
737 "' are not supported.");
740 const FiniteElementSpace * fespace = mesh.GetNodalFESpace();
742 Array<int> element_dofs;
743 for (
int element_id : element_ids_for_block_id.at(block_id))
747 fespace->GetElementDofs(element_id, element_dofs);
749 for (
int j = 0; j < element_dofs.Size(); j++)
751 int dof_index = node_ordering_map[j] - 1;
752 int dof = element_dofs[dof_index];
754 block_node_connectivity.push_back(dof + 1);
759 mesh.GetElementVertices(element_id, element_dofs);
761 for (
int vertex_id : element_dofs)
763 block_node_connectivity.push_back(vertex_id + 1);
768 char * label = GenerateLabel(
"connect%d_dim", block_id);
770 int node_connectivity_dim;
771 DefineDimension(label, block_node_connectivity.size(),
772 &node_connectivity_dim);
775 label = GenerateLabel(
"connect%d", block_id);
776 DefineAndPutVar(label, NC_INT, 1, &node_connectivity_dim,
777 block_node_connectivity.data());
781void ExodusIIWriter::ExtractVertexCoordinates(std::vector<real_t> & coordx,
782 std::vector<real_t> & coordy,
783 std::vector<real_t> & coordz)
787 std::unordered_set<int> unordered_node_ids = GenerateUniqueNodeIDs();
789 std::vector<int> sorted_node_ids(unordered_node_ids.size());
790 sorted_node_ids.assign(unordered_node_ids.begin(), unordered_node_ids.end());
791 std::sort(sorted_node_ids.begin(), sorted_node_ids.end());
794 for (
size_t i = 0; i < sorted_node_ids.size(); i++)
796 int node_id = sorted_node_ids[i];
798 mesh.GetNode(node_id, coordinates);
800 coordx[node_id] = coordinates[0];
801 coordy[node_id] = coordinates[1];
803 if (mesh.Dimension() == 3)
805 coordz[node_id] = coordinates[2];
811 for (
int ivertex = 0; ivertex < mesh.GetNV(); ivertex++)
813 real_t *coordinates = mesh.GetVertex(ivertex);
815 coordx[ivertex] = coordinates[0];
816 coordy[ivertex] = coordinates[1];
818 if (mesh.Dimension() == 3)
820 coordz[ivertex] = coordinates[2];
826void ExodusIIWriter::WriteFileSize()
830 const int file_size = 1;
836void ExodusIIWriter::WriteMeshDimension()
843void ExodusIIWriter::WriteNodeSets()
846 int num_node_sets_ids;
851void ExodusIIWriter::WriteTimesteps()
858void ExodusIIWriter::WriteDummyVariable()
860 int dummy_var_dim_id, dummy_value = 1;
862 DefineDimension(
"dummy_var_dim", 1, &dummy_var_dim_id);
864 DefineAndPutVar(
"dummy_var", NC_INT, 1, &dummy_var_dim_id,
868void ExodusIIWriter::GenerateExodusIIElementBlocks()
871 element_ids_for_block_id.clear();
872 element_type_for_block_id.clear();
874 std::unordered_set<int> observed_block_ids;
877 for (
int ielement = 0; ielement < mesh.GetNE(); ielement++)
881 int block_id = mesh.GetAttribute(ielement);
883 if (observed_block_ids.count(block_id) == 0)
885 block_ids.push_back(block_id);
887 element_type_for_block_id[block_id] = element_type;
888 element_ids_for_block_id[block_id] = { ielement };
890 observed_block_ids.insert(block_id);
894 auto & block_element_ids = element_ids_for_block_id.at(block_id);
895 block_element_ids.push_back(ielement);
899 if (element_type != element_type_for_block_id.at(block_id))
901 MFEM_ABORT(
"Multiple element types are defined for block: " << block_id);
907void ExodusIIWriter::WriteNumElementBlocks()
916std::unordered_set<int> ExodusIIWriter::GenerateUniqueNodeIDs()
918 std::unordered_set<int> unique_node_ids;
920 const FiniteElementSpace * fespace = mesh.GetNodalFESpace();
923 for (
int ielement = 0; ielement < mesh.GetNE(); ielement++)
927 fespace->GetElementDofs(ielement, element_dofs);
931 mesh.GetElementVertices(ielement, element_dofs);
934 for (
int dof : element_dofs)
936 unique_node_ids.insert(dof);
940 return unique_node_ids;
943void ExodusIIWriter::GenerateExodusIIBoundaryInfo()
946 boundary_ids.clear();
947 exodusII_element_ids_for_boundary_id.clear();
948 exodusII_side_ids_for_boundary_id.clear();
955 struct GlobalFaceIndexInfo
958 int local_face_index;
960 GlobalFaceIndexInfo() : element_index{0}, local_face_index{0} {}
962 GlobalFaceIndexInfo(
int element_index,
int local_face_index)
964 this->element_index = element_index;
965 this->local_face_index = local_face_index;
969 std::unordered_map<int, GlobalFaceIndexInfo>
970 mfem_face_index_info_for_global_face_index;
971 std::unordered_set<int> blacklisted_global_face_indices;
973 Array<int> global_face_indices, orient;
974 for (
int ielement = 0; ielement < mesh.GetNE(); ielement++)
976 mesh.GetElementFaces(ielement, global_face_indices, orient);
978 for (
int iface = 0; iface < global_face_indices.Size(); iface++)
980 int face_index = global_face_indices[iface];
982 if (blacklisted_global_face_indices.count(face_index))
987 if (mfem_face_index_info_for_global_face_index.count(face_index))
990 blacklisted_global_face_indices.insert(face_index);
991 mfem_face_index_info_for_global_face_index.erase(face_index);
995 mfem_face_index_info_for_global_face_index[face_index] = GlobalFaceIndexInfo(
1000 std::unordered_set<int> unique_boundary_attributes;
1002 for (
int ibdr_element = 0; ibdr_element < mesh.GetNBE(); ibdr_element++)
1004 int boundary_id = mesh.GetBdrAttribute(ibdr_element);
1005 int bdr_element_face_index = mesh.GetBdrElementFaceIndex(ibdr_element);
1008 if (mesh.FaceIsInterior(bdr_element_face_index))
1010 MFEM_WARNING(
"Skipping internal boundary " << ibdr_element);
1015 auto & element_face_info = mfem_face_index_info_for_global_face_index.at(
1016 bdr_element_face_index);
1018 int ielement = element_face_info.element_index;
1019 int iface = element_face_info.local_face_index;
1022 int exodusII_element_id = ielement + 1;
1025 int exodusII_face_id;
1028 switch (element_type)
1043 MFEM_ABORT(
"Cannot handle element of type " << element_type);
1046 unique_boundary_attributes.insert(boundary_id);
1048 exodusII_element_ids_for_boundary_id[boundary_id].push_back(
1049 exodusII_element_id);
1050 exodusII_side_ids_for_boundary_id[boundary_id].push_back(exodusII_face_id);
1053 boundary_ids.assign(unique_boundary_attributes.begin(),
1054 unique_boundary_attributes.end());
1055 std::sort(boundary_ids.begin(), boundary_ids.end());
1058void ExodusIIWriter::CheckNodalFESpaceIsSecondOrderH1()
const
1060 const FiniteElementSpace * fespace = mesh.GetNodalFESpace();
1063 MFEM_ABORT(
"The mesh has no nodal fespace.");
1067 const int fespace_order = fespace->GetMaxElementOrder();
1068 if (fespace_order != 2)
1070 MFEM_ABORT(
"Nodal fespace is of order " << fespace_order <<
1071 ". Expected 2nd order.");
1075 const FiniteElementCollection * fec = fespace->FEColl();
1078 MFEM_ABORT(
"No FECollection associated with nodal fespace.");
1082 if (strncmp(fec->Name(),
"H1", 2) != 0)
1084 MFEM_ABORT(
"Nodal fespace's FECollection is '" << fec->Name() <<
Type
Constants for the classes derived from Element.
void PrintExodusII(const std::string &fpath)
Export a mesh to an Exodus II file.
const char * EXODUS_DATABASE_VERSION_LABEL
const char * EXODUS_NUM_DIM_LABEL
const char * EXODUS_COORDY_LABEL
const char * EXODUS_MESH_TITLE
const int EXODUS_MAX_NAME_LENGTH
const char * EXODUS_NUM_ELEM_LABEL
const char * EXODUS_COORDZ_LABEL
const char * EXODUS_TIME_STEP_LABEL
const char * EXODUS_FILE_SIZE_LABEL
const char * EXODUS_TITLE_LABEL
const char * EXODUS_NUM_BOUNDARIES_LABEL
const char * EXODUS_NUM_NODE_SETS_LABEL
const char * EXODUS_NUM_BLOCKS_LABEL
const char * EXODUS_NUM_ELEMENT_BLOCKS_LABEL
const float EXODUS_DATABASE_VERSION
const char * EXODUS_MAX_LINE_LENGTH_LABEL
const char * EXODUS_ELEMENT_TYPE_LABEL
const char * EXODUS_COORDX_LABEL
const char * EXODUS_ELEMENT_BLOCK_IDS_LABEL
const char * EXODUS_MAX_NAME_LENGTH_LABEL
const char * EXODUS_API_VERSION_LABEL
const char * EXODUS_NUM_SIDE_SETS_LABEL
const float EXODUS_API_VERSION
const int EXODUS_MAX_LINE_LENGTH
const char * EXODUS_FLOATING_POINT_WORD_SIZE_LABEL
const char * EXODUS_SIDE_SET_IDS_LABEL
const int mfem_to_exodusII_node_ordering_pyramid14[]
const int mfem_to_exodusII_node_ordering_wedge18[]
const int mfem_to_exodusII_node_ordering_tet10[]
const int mfem_to_exodusII_node_ordering_hex27[]
const int mfem_to_exodusII_side_map_wedge6[]
const int mfem_to_exodusII_side_map_hex8[]
const int mfem_to_exodusII_side_map_pyramid5[]
const int mfem_to_exodusII_side_map_tet4[]
Convert from the MFEM face numbering to the ExodusII face numbering.
OutStream out(std::cout)
Global stream used by the library for standard output. Initially it uses the same std::streambuf as s...