Mesh Oriented datABase  (version 5.6.0)
An array-based unstructured mesh library
ReadWriteTest.cpp
Go to the documentation of this file.
1 /** @example ReadWriteTest.cpp
2  * @brief Example demonstrating parallel mesh reading and writing with performance timing.
3  *
4  * This example shows how to:
5  * - Read a mesh file in parallel with specific options
6  * - Write a mesh file in parallel with specific options
7  * - Measure and report read/write performance times
8  * - Handle parallel mesh operations with shared entities
9  * - Use different partition methods for parallel processing
10  *
11  * The example is designed for stress testing of MOAB's parallel
12  * reader/writer capabilities and provides timing information
13  * for performance analysis.
14  *
15  * @note This example requires MOAB to be compiled with MPI support.
16  *
17  * @par Usage:
18  * @code
19  * mpiexec -np <num_procs> ReadWriteTest [input] [output] -O <read_opts> -o <write_opts>
20  * @endcode
21  *
22  * @par Example:
23  * @code
24  * mpiexec -np 4 ReadWriteTest input.nc output.h5m \
25  * -O "PARALLEL=READ_PART;PARTITION_METHOD=SQIJ;PARALLEL_RESOLVE_SHARED_ENTS;VARIABLE=T,U" \
26  * -o "PARALLEL=WRITE_PART;VARIABLE=T,U"
27  * @endcode
28  */
29 
30 #include "moab/Core.hpp"
31 #include <iostream>
32 #include <chrono>
33 #include <string>
34 #include <memory>
35 #include <cstdlib>
36 
37 // Only include MPI-specific headers if MOAB was built with MPI support
38 #ifdef MOAB_HAVE_MPI
39 #include "moab/ParallelComm.hpp"
40 #include "MBParallelConventions.h"
41 #include <mpi.h>
42 #endif
43 
44 // Using declarations for cleaner code
45 using moab::Core;
46 using moab::EntityHandle;
47 using moab::ErrorCode;
48 using moab::ParallelComm;
49 
50 namespace
51 {
52 // Default values for command line arguments
53 struct Config
54 {
55  std::string input_file;
56  std::string output_file;
57  std::string read_opts;
58  std::string write_opts;
59  bool use_defaults = false;
60 };
61 
62 // Print usage information
63 void print_usage( const char* program_name )
64 {
65  std::cout << "Usage: mpiexec -np <num_procs> " << program_name << " [input_file] [output_file] [options]\n"
66  << "Options:\n"
67  << " -O <read_opts> Options for reading the input file\n"
68  << " -o <write_opts> Options for writing the output file\n"
69  << "\nExample:\n"
70  << " mpiexec -np 4 " << program_name << " input.nc output.h5m \\\n"
71  << " -O \"PARALLEL=READ_PART;PARTITION_METHOD=SQIJ\" \\n"
72  << " -o \"PARALLEL=WRITE_PART\"\n";
73 }
74 
75 // Parse command line arguments
76 Config parse_arguments( int argc, char** argv )
77 {
78  Config config;
79 
80  // Set default values
81 #ifdef MOAB_HAVE_NETCDF
82  config.input_file = std::string( MESH_DIR ) + "/io/fv3x46x72.t.3.nc";
83  config.output_file = "ReadWriteTestOut.h5m";
84  config.read_opts = "PARALLEL=READ_PART;PARTITION_METHOD=SQIJ;PARALLEL_RESOLVE_SHARED_ENTS;VARIABLE=T,U";
85  config.write_opts = "PARALLEL=WRITE_PART";
86  config.use_defaults = true;
87 #else
88  if( argc < 3 )
89  {
90  print_usage( argv[0] );
91  std::exit( 1 );
92  }
93 #endif
94 
95  // Parse command line arguments if provided
96  if( argc >= 3 )
97  {
98  config.input_file = argv[1];
99  config.output_file = argv[2];
100  config.use_defaults = false;
101  }
102 
103  // Parse options
104  for( int i = 3; i < argc; ++i )
105  {
106  std::string arg = argv[i];
107  if( arg == "-O" && i + 1 < argc )
108  {
109  config.read_opts = argv[++i];
110  }
111  else if( arg == "-o" && i + 1 < argc )
112  {
113  config.write_opts = argv[++i];
114  }
115  else if( arg == "-h" || arg == "--help" )
116  {
117  print_usage( argv[0] );
118  std::exit( 0 );
119  }
120  }
121 
122  return config;
123 }
124 
125 // Print configuration information
126 void print_config( const Config& config, int rank, int nprocs )
127 {
128  if( rank == 0 )
129  {
130  std::cout << "\n=== MOAB Parallel Read/Write Test ===\n"
131  << "Input file: " << config.input_file << "\n"
132  << "Output file: " << config.output_file << "\n"
133  << "Read options: " << ( config.read_opts.empty() ? "(default)" : config.read_opts ) << "\n"
134  << "Write options: " << ( config.write_opts.empty() ? "(default)" : config.write_opts ) << "\n"
135  << "Running on " << nprocs << " MPI processes\n"
136  << std::endl;
137  }
138 }
139 } // namespace
140 
141 int main( int argc, char** argv )
142 {
143  // Initialize MPI
144  int mpi_initialized = 0;
145  MPI_Initialized( &mpi_initialized );
146  if( !mpi_initialized )
147  {
148  MPI_Init( &argc, &argv );
149  }
150 
151  int rank = 0;
152  int nprocs = 1;
153  MPI_Comm_rank( MPI_COMM_WORLD, &rank );
154  MPI_Comm_size( MPI_COMM_WORLD, &nprocs );
155 
156  try
157  {
158  // Parse command line arguments
159  Config config = parse_arguments( argc, argv );
160  if( rank == 0 )
161  {
162  print_config( config, rank, nprocs );
163  }
164 
165  // Create MOAB instance with smart pointer for automatic cleanup
166  auto moab = std::make_unique< Core >();
167  if( !moab )
168  {
169  std::cerr << "Error: Failed to create MOAB instance" << std::endl;
170  MPI_Abort( MPI_COMM_WORLD, 1 );
171  }
172 
173  // Create parallel communication interface
174  auto pcomm = std::make_unique< ParallelComm >( moab.get(), MPI_COMM_WORLD );
175 
176  // Create a mesh set to store the loaded mesh
177  EntityHandle mesh_set;
178  MB_CHK_ERR( moab->create_meshset( moab::MESHSET_SET, mesh_set ) );
179 
180  // Time the read operation
181  auto read_start = std::chrono::high_resolution_clock::now();
182 
183  if( rank == 0 )
184  {
185  std::cout << "Reading mesh file..." << std::endl;
186  }
187 
188  // Read the mesh file
189  MB_CHK_SET_ERR( moab->load_file( config.input_file.c_str(), &mesh_set, config.read_opts.c_str() ),
190  "Failed to read input file: " << config.input_file );
191 
192  auto read_end = std::chrono::high_resolution_clock::now();
193  std::chrono::duration< double > read_elapsed = read_end - read_start;
194 
195  if( rank == 0 )
196  {
197  std::cout << "Read completed in " << read_elapsed.count() << " seconds" << std::endl;
198  std::cout << "Writing mesh file..." << std::endl;
199  }
200 
201  // Time the write operation
202  auto write_start = std::chrono::high_resolution_clock::now();
203 
204  // Write the mesh file
205  MB_CHK_SET_ERR( moab->write_file( config.output_file.c_str(), nullptr, config.write_opts.c_str(), &mesh_set,
206  1 ),
207  "Failed to write output file: " << config.output_file );
208 
209  auto write_end = std::chrono::high_resolution_clock::now();
210  std::chrono::duration< double > write_elapsed = write_end - write_start;
211 
212  if( rank == 0 )
213  {
214  std::cout << "Write completed in " << write_elapsed.count() << " seconds" << std::endl;
215  std::cout << "\n=== Test completed successfully ===" << std::endl;
216  }
217 
218  // Clean up
219  pcomm.reset();
220  moab.reset();
221 
222  // Only finalize MPI if we initialized it
223  if( !mpi_initialized )
224  {
225  MPI_Finalize();
226  }
227 
228  return 0;
229  }
230  catch( const std::exception& e )
231  {
232  std::cerr << "Error on rank " << rank << ": " << e.what() << std::endl;
233  MPI_Abort( MPI_COMM_WORLD, 1 );
234  return 1;
235  }
236  catch( ... )
237  {
238  std::cerr << "Unknown error occurred on rank " << rank << std::endl;
239  MPI_Abort( MPI_COMM_WORLD, 1 );
240  return 1;
241  }
242 }