Mesh Oriented datABase  (version 5.6.0)
An array-based unstructured mesh library
StructuredMeshSimple.cpp
Go to the documentation of this file.
1 /**
2  * @file StructuredMeshSimple.cpp
3  * @brief Example demonstrating creation and query of structured meshes in MOAB
4  *
5  * This example shows how to:
6  * - Create structured meshes in 1D, 2D, or 3D
7  * - Work with structured mesh interfaces (ScdInterface)
8  * - Handle both serial and parallel structured mesh creation
9  * - Query mesh entities and their connectivity
10  * - Access element coordinates and connectivity
11  * - Use parametric indexing for structured meshes
12  *
13  * In serial mode, a single N*N*N block of elements is created.
14  * In parallel mode, each processor gets an N*N*N block arranged
15  * in a 1D column, sharing vertices and faces at interfaces.
16  *
17  * @author MOAB Development Team
18  * @date 2024
19  *
20 
21  * \brief Show creation and query of structured mesh, serial or parallel, through MOAB's structured
22  * mesh interface. This is an example showing creation and query of a 3D structured mesh. In
23  * serial, a single N*N*N block of elements is created; in parallel, each proc gets an N*N*N block,
24  * with blocks arranged in a 1d column, sharing vertices and faces at their interfaces (proc 0 has
25  * no left neighbor and proc P-1 no right neighbor). Each square block of hex elements is then
26  * referenced by its ijk parameterization. 1D and 2D examples could be made simply by changing the
27  * dimension parameter passed into the MOAB functions. \n
28  *
29  * <b>This example </b>:
30  * -# Instantiate MOAB and get the structured mesh interface
31  * -# Decide what the local parameters of the mesh will be, based on parallel/serial and rank.
32  * -# Create a N^d structured mesh, which includes (N+1)^d vertices and N^d elements.
33  * -# Get the vertices and elements from moab and check their numbers against (N+1)^d and N^d,
34  * resp.
35  * -# Loop over elements in d nested loops over i, j, k; for each (i,j,k):
36  * -# Get the element corresponding to (i,j,k)
37  * -# Get the connectivity of the element
38  * -# Get the coordinates of the vertices comprising that element
39  * -# Release the structured mesh interface and destroy the MOAB instance
40  *
41  * <b> To run: </b> ./StructuredMeshSimple [d [N] ] \n
42  * (default values so can run w/ no user interaction)
43  *
44  * @param argc Number of command line arguments
45  * @param argv Command line arguments array
46  * @return 0 on success, 1 on failure
47  */
48 
49 #include "moab/Core.hpp"
50 #include "moab/ScdInterface.hpp"
51 #include "moab/ProgOptions.hpp"
52 #include "moab/CN.hpp"
53 
54 #ifdef MOAB_HAVE_MPI
55 #include "moab_mpi.h"
56 #include <mpi.h>
57 #endif
58 
59 #include <array>
60 #include <cmath>
61 #include <iostream>
62 #include <memory>
63 #include <stdexcept>
64 #include <vector>
65 
66 // Using declarations for cleaner code
67 using moab::Core;
68 using moab::EntityHandle;
69 using moab::ErrorCode;
70 using moab::Range;
71 using moab::ScdBox;
72 using moab::ScdInterface;
73 
74 // Constants
75 namespace
76 {
77 constexpr int DEFAULT_DIMENSION = 3;
78 constexpr int DEFAULT_ELEMENTS_PER_SIDE = 10;
79 } // namespace
80 
81 int main( int argc, char** argv )
82 {
83  // Initialize MPI if available
84 #ifdef MOAB_HAVE_MPI
85  if( MPI_Init( &argc, &argv ) != MPI_SUCCESS )
86  {
87  std::cerr << "MPI_Init failed" << std::endl;
88  return 1;
89  }
90 
91  // Ensure MPI is properly finalized when we exit
92  struct MPIFinalizer
93  {
94  void operator()( int* ) const
95  {
96  MPI_Finalize();
97  }
98  };
99  std::unique_ptr< int, MPIFinalizer > mpi_guard( nullptr );
100 #endif
101 
102  // Parse command line options
103  int dimension = DEFAULT_DIMENSION;
104  int elements_per_side = DEFAULT_ELEMENTS_PER_SIDE;
105 
106  try
107  {
108  ProgOptions opts;
109  opts.addOpt< int >( "dim,d", "Dimension of mesh (default=3)", &dimension );
110  opts.addOpt< int >( ",n", "Number of elements on a side (default=10)", &elements_per_side );
111  opts.parseCommandLine( argc, argv );
112 
113  // Validate input
114  if( dimension < 1 || dimension > 3 )
115  {
116  throw std::invalid_argument( "Dimension must be 1, 2, or 3" );
117  }
118  if( elements_per_side < 1 )
119  {
120  throw std::invalid_argument( "Number of elements must be positive" );
121  }
122  }
123  catch( const std::exception& e )
124  {
125  std::cerr << "Error parsing command line: " << e.what() << std::endl;
126  return 1;
127  }
128 
129  // Initialize MOAB
130  std::unique_ptr< Core > moab_instance = std::make_unique< Core >();
131  if( !moab_instance )
132  {
133  std::cerr << "Failed to create MOAB instance" << std::endl;
134  return 1;
135  }
136 
137  // Get the structured mesh interface
138  ScdInterface* scd_interface = nullptr;
139  MB_CHK_SET_ERR( moab_instance->query_interface( scd_interface ), "Failed to get ScdInterface" );
140  if( !scd_interface )
141  {
142  std::cerr << "Failed to get ScdInterface: null pointer returned" << std::endl;
143  return 1;
144  }
145 
146  // 1. Determine local parameters based on parallel/serial and rank
147  int rank = 0;
148  int ilow = 0;
149  int ihigh = elements_per_side;
150 
151 #ifdef MOAB_HAVE_MPI
152  int nprocs = 1;
153  MPI_Comm_rank( MPI_COMM_WORLD, &rank );
154  MPI_Comm_size( MPI_COMM_WORLD, &nprocs );
155 
156  // Calculate local range for this process
157  const int elements_per_proc = elements_per_side / nprocs;
158  const int remainder = elements_per_side % nprocs;
159 
160  // Distribute elements among processes
161  ilow = rank * elements_per_proc + std::min( rank, remainder );
162  ihigh = ilow + elements_per_proc + ( rank < remainder ? 1 : 0 );
163 #endif
164 
165  std::string write_options = "";
166  if( nprocs > 1 ) write_options = "PARALLEL=WRITE_PART";
167 
168  // 2. Create a N^d structured mesh, which includes (N+1)^d vertices and N^d elements.
169  ScdBox* box = nullptr;
170 
171  // Calculate box bounds based on dimension
172  const moab::HomCoord low( ilow, ( dimension > 1 ) ? 0 : -1, ( dimension > 2 ) ? 0 : -1 );
173 
174  const moab::HomCoord high( ihigh, ( dimension > 1 ) ? elements_per_side : -1,
175  ( dimension > 2 ) ? elements_per_side : -1 );
176 
177  // Create the structured mesh box
178  MB_CHK_SET_ERR( scd_interface->construct_box( low, high,
179  nullptr, // No coordinates array
180  0, // No coordinates
181  box, // Output parameter
182  nullptr, // Periodicity
183  nullptr, // Parallel data
184  true, // No coordinates
185  0 // Resolve dimensionality
186  ),
187  "Failed to construct structured box" );
188 
189  if( !box )
190  {
191  std::cerr << "Failed to construct structured box: null box returned" << std::endl;
192  return 1;
193  }
194 
195  // 3. Get the vertices and elements from moab and check their numbers against (N+1)^d and N^d
196  Range vertices, elements;
197 
198  // Get all vertices (dimension = 0) and elements (dimension = mesh dimension)
199  MB_CHK_SET_ERR( moab_instance->get_entities_by_dimension( 0, 0, vertices ), "Failed to get vertices" );
200  MB_CHK_SET_ERR( moab_instance->get_entities_by_dimension( 0, dimension, elements ), "Failed to get elements" );
201 
202 #ifdef MOAB_HAVE_MPI
203  std::size_t local_entities_size[2] = { vertices.size(), elements.size() }, global_entities_size[2] = { 0, 0 };
204  MPI_Allreduce( local_entities_size, global_entities_size, 2, MPI_UNSIGNED_LONG_LONG, MPI_SUM, MPI_COMM_WORLD );
205 #endif
206 
207  // Calculate expected number of vertices and elements
208  const std::size_t expected_vertices = static_cast< std::size_t >( std::pow( elements_per_side + 1, dimension ) );
209  const std::size_t expected_elements = static_cast< std::size_t >( std::pow( elements_per_side, dimension ) );
210 
211  // Only print from rank 0 in parallel
212  auto print_message = [rank]( const auto& message ) {
213  if( rank == 0 )
214  {
215  std::cout << message << std::endl;
216  }
217  };
218 
219  // Verify the number of created elements and vertices
220  if( expected_elements == global_entities_size[1] )
221  {
222  const auto element_type = moab_instance->type_from_handle( *elements.begin() );
223  std::ostringstream msg;
224  msg << "Created " << global_entities_size[1] << " " << moab::CN::EntityTypeName( element_type )
225  << " elements and " << global_entities_size[0] << " vertices.";
226  print_message( msg.str() );
227  }
228  else
229  {
230  std::ostringstream err_msg;
231  err_msg << "Error: Expected " << expected_elements << " elements and " << expected_vertices
232  << " vertices, but got " << global_entities_size[1] << " elements and " << global_entities_size[0]
233  << " vertices.";
234  print_message( err_msg.str() );
235  return 1;
236  }
237 
238  // 4. Loop over elements in nested loops over i, j, k based on dimension
239  // const int i_max = elements_per_side - 1; // 0-based indexing
240  const int j_max = ( dimension > 1 ) ? elements_per_side - 1 : 0;
241  const int k_max = ( dimension > 2 ) ? elements_per_side - 1 : 0;
242 
243  // Pre-allocate vectors to avoid reallocation
244 
245  std::vector< double > coordinates;
246  for( int k = 0; k <= k_max; ++k )
247  {
248  for( int j = 0; j <= j_max; ++j )
249  {
250  for( int i = ilow; i < ihigh; ++i )
251  {
252  // 4a. Get the element corresponding to (i,j,k)
253  const EntityHandle element = box->get_element( i, j, k );
254  if( 0 == element )
255  {
256  std::cerr << "Failed to get element at (" << i << ", " << j << ", " << k << ")" << std::endl;
257  // return 1;
258  continue;
259  }
260 
261  // 4b. Get the connectivity of the element
262  std::vector< EntityHandle > connectivity;
263  MB_CHK_SET_ERR( moab_instance->get_connectivity( &element, 1, connectivity ),
264  "Failed to get connectivity for element at (" << i << ", " << j << ", " << k
265  << ") with handle " << element );
266 
267  // 4c. Get the coordinates of the vertices comprising that element
268  coordinates.resize( 3 * connectivity.size() );
269  MB_CHK_SET_ERR( moab_instance->get_coords( connectivity.data(), connectivity.size(),
270  coordinates.data() ),
271  "Failed to get coordinates for element at (" << i << ", " << j << ", " << k
272  << ") with handle " << element );
273  }
274  }
275  }
276 
277  // 5. Write the mesh file to disk
278  MB_CHK_SET_ERR( moab_instance->write_file( "structured_mesh.h5m", "h5m", write_options.c_str() ),
279  "Failed to write mesh file" );
280 
281  // 6. Clean up
282  MB_CHK_SET_ERR( moab_instance->release_interface( scd_interface ), "Failed to release interface" );
283 
284  // MPI_Finalize is handled by the mpi_guard destructor if MPI is enabled
285  return 0;
286 }