Mesh Oriented datABase  (version 5.6.0)
An array-based unstructured mesh library
DirectAccessWithHoles.cpp
Go to the documentation of this file.
1 /**
2  * @file DirectAccessWithHoles.cpp
3  * @brief Example demonstrating direct access to MOAB data with holes in entity handle space
4  *
5  * This example shows how to:
6  * - Create a structured mesh with holes in the entity handle space
7  * - Use direct access iterators to avoid API overhead
8  * - Handle non-contiguous entity handles efficiently
9  * - Access vertex coordinates, element connectivity, and tag data directly
10  * - Work with adjacency lists for vertex-to-element relationships
11  * - Use maps to track data chunks for non-contiguous handles
12  *
13  * The program creates a 1D row of quad elements with specified holes,
14  * then demonstrates direct access to MOAB's internal data structures
15  * even when entity handles are not contiguous.
16  *
17  * @author MOAB Development Team
18  * @date 2024
19  *
20 
21  * \brief Use direct access to MOAB data to avoid calling through API \n
22  *
23  * This example creates a 1d row of quad elements, with a user-specified number of "holes" (missing
24  * quads) in the row: \verbatim
25  * ---------------------- ---------------------- --------
26  * | | | | | | | | | |
27  * | | | |(hole)| | | |(hole)| | ...
28  * | | | | | | | | | |
29  * ---------------------- ---------------------- --------
30  * \endverbatim
31  * This makes (nholes+1) contiguous runs of quad handles in the handle space
32  * This example shows how to use the xxx_iterate functions in MOAB (xxx = coords, connect, tag,
33  * adjacencies) to get direct pointer access to MOAB internal storage, which can be used without
34  * calling through the MOAB API.
35  *
36  * -# Initialize MOAB \n
37  * -# Create a quad mesh with holes, as depicted above
38  * -# Create 2 dense tags (tag1, tag2) for avg position to assign to quads, and # verts per quad
39  * (tag3)
40  * -# Get connectivity, coordinate, tag1 iterators
41  * -# Iterate through quads, computing midpoint based on vertex positions, set on quad-based
42  * tag1
43  * -# Set up map from starting quad handle for a chunk to struct of (tag1_ptr, tag2_ptr,
44  * tag3_ptr), pointers to the dense tag storage for those tags for the chunk
45  * -# Iterate through vertices, summing positions into tag2 on connected quads and incrementing
46  * vertex count
47  * -# Iterate through quads, normalizing tag2 by vertex count and comparing values of tag1 and
48  * tag2
49  *
50  * <b>To compile</b>: \n
51  * make DirectAccessWithHoles \n
52  * <b>To run</b>: ./DirectAccess [-nquads <# quads>] [-holes <# holes>]\n
53  *
54  * @param argc Number of command line arguments
55  * @param argv Command line arguments array
56  * @return 0 on success, 1 on failure
57  */
58 
59 #include "moab/Core.hpp"
60 #include "moab/ProgOptions.hpp"
61 #include "moab/ReadUtilIface.hpp"
62 #include <map>
63 #include <iostream>
64 #include <cassert>
65 
66 using namespace moab;
67 using namespace std;
68 
69 ErrorCode create_mesh_with_holes( Interface* mbImpl, int nquads, int nholes );
70 
71 struct tag_struct
72 {
73  double* avg_ptr;
74  int* nv_ptr;
75 };
76 
77 int main( int argc, char** argv )
78 {
79  // Get MOAB instance
80  Interface* mbImpl = new( std::nothrow ) Core;
81  if( NULL == mbImpl ) return 1;
82 
83  int nquads = 1000, nholes = 1;
84 
85  // Parse options
86  ProgOptions opts;
87  opts.addOpt< int >( string( "nquads,n" ), string( "Number of quads in the mesh (default = 1000" ), &nquads );
88  opts.addOpt< int >( string( "holes,H" ), string( "Number of holes in the element handle space (default = 1" ),
89  &nholes );
90  opts.parseCommandLine( argc, argv );
91  if( nholes >= nquads )
92  {
93  cerr << "Number of holes needs to be < number of elements." << endl;
94  return 1;
95  }
96 
97  // Create simple structured mesh with hole, but using unstructured representation
98  MB_CHK_SET_ERR( create_mesh_with_holes( mbImpl, nquads, nholes ), "Trouble creating mesh" );
99 
100  // Get all vertices and non-vertex entities
101  Range verts, elems;
102  MB_CHK_SET_ERR( mbImpl->get_entities_by_handle( 0, elems ), "Trouble getting all entities" );
103  verts = elems.subset_by_dimension( 0 );
104  elems -= verts;
105 
106  // Create tag1 (element-based avg), tag2 (vertex-based avg), tag3 (# connected verts)
107  Tag tag1, tag2, tag3;
108  MB_CHK_SET_ERR( mbImpl->tag_get_handle( "tag1", 3, MB_TYPE_DOUBLE, tag1, MB_TAG_DENSE | MB_TAG_CREAT ),
109  "Trouble creating tag1" );
110  double def_val[3] = { 0.0, 0.0, 0.0 }; // Need a default value for tag2 because we sum into it
111  MB_CHK_SET_ERR( mbImpl->tag_get_handle( "tag2", 3, MB_TYPE_DOUBLE, tag2, MB_TAG_DENSE | MB_TAG_CREAT, def_val ),
112  "Trouble creating tag2" );
113  int def_val_int = 0; // Need a default value for tag3 because we increment it
115  &def_val_int ),
116  "Trouble creating tag3" );
117 
118  // Get connectivity, coordinate, tag, and adjacency iterators
119  EntityHandle* conn_ptr;
120  double *x_ptr, *y_ptr, *z_ptr, *tag1_ptr, *tag2_ptr;
121  int* tag3_ptr;
122 
123  // First vertex is at start of range (ranges are sorted), and is offset for vertex index
124  // calculation
125  EntityHandle first_vert = *verts.begin();
126 
127  // When iterating over elements, each chunk can have a different # vertices; also, count tells
128  // you how many elements are in the current chunk
129  int vpere, count;
130 
131  // Get coordinates iterator, just need this once because we know verts handle space doesn't have
132  // holes
133  MB_CHK_SET_ERR( mbImpl->coords_iterate( verts.begin(), verts.end(), x_ptr, y_ptr, z_ptr, count ),
134  "Error in coords_iterate" );
135  assert( count == (int)verts.size() ); // Should end up with just one contiguous chunk of vertices
136 
137  // Iterate through elements, computing midpoint based on vertex positions, set on element-based
138  // tag1 Control loop by iterator over elem range
139  Range::iterator e_it = elems.begin();
140 
141  while( e_it != elems.end() )
142  {
143  // Get conn_ptr, tag1_ptr for next contiguous chunk of element handles, and coords pointers
144  // for all verts
145  MB_CHK_SET_ERR( mbImpl->connect_iterate( e_it, elems.end(), conn_ptr, vpere, count ),
146  "Error in connect_iterate" );
147  MB_CHK_SET_ERR( mbImpl->tag_iterate( tag1, e_it, elems.end(), count, (void*&)tag1_ptr ),
148  "Error in tag1_iterate" );
149 
150  // Iterate over elements in this chunk
151  for( int i = 0; i < count; i++ )
152  {
153  tag1_ptr[0] = tag1_ptr[1] = tag1_ptr[2] = 0.0; // Initialize position for this element
154  for( int j = 0; j < vpere; j++ )
155  { // Loop over vertices in this element
156  int v_index = conn_ptr[j] - first_vert; // vert index is just the offset from first vertex
157  tag1_ptr[0] += x_ptr[v_index];
158  tag1_ptr[1] += y_ptr[v_index]; // Sum vertex positions into tag1...
159  tag1_ptr[2] += z_ptr[v_index];
160  }
161  tag1_ptr[0] /= vpere;
162  tag1_ptr[1] /= vpere; // Then normalize
163  tag1_ptr[2] /= vpere;
164 
165  // Done with this element; advance connect_ptr and tag1_ptr to next element
166  conn_ptr += vpere;
167  tag1_ptr += 3;
168  } // Loop over elements in chunk
169 
170  // Done with chunk; advance range iterator by count; will skip over gaps in range
171  e_it += count;
172  } // While loop over all elements
173 
174  // Iterate through vertices, summing positions into tag2 on connected elements and incrementing
175  // vertex count Iterate over chunks the same as elements, even though we know we have only one
176  // chunk here, just to show how it's done
177 
178  // Create a std::map from EntityHandle (first entity handle in chunk) to
179  // tag_struct (ptrs to start of avg/#verts tags for that chunk); then for a given entity handle,
180  // we can quickly find the chunk it's in using map::lower_bound; could have set up this map in
181  // earlier loop over elements, but do it here for clarity
182 
183  map< EntityHandle, tag_struct > elem_map;
184  e_it = elems.begin();
185  while( e_it != elems.end() )
186  {
187  tag_struct ts = { NULL, NULL };
188  MB_CHK_SET_ERR( mbImpl->tag_iterate( tag2, e_it, elems.end(), count, (void*&)ts.avg_ptr ),
189  "Error in tag2_iterate" );
190  MB_CHK_SET_ERR( mbImpl->tag_iterate( tag3, e_it, elems.end(), count, (void*&)ts.nv_ptr ),
191  "Error in tag3_iterate" );
192  elem_map[*e_it] = ts;
193  e_it += count;
194  }
195 
196  // Call a vertex-quad adjacencies function to generate vertex-element adjacencies in MOAB
197  Range::iterator v_it = verts.begin();
198  Range dum_range;
199  MB_CHK_SET_ERR( mbImpl->get_adjacencies( &( *v_it ), 1, 2, false, dum_range ), "Error in get_adjacencies" );
200  const vector< EntityHandle >** adjs_ptr;
201  while( v_it != verts.end() )
202  {
203  // Get coords ptrs, adjs_ptr; can't set tag2_ptr by direct access, because of hole in
204  // element handle space
205  MB_CHK_SET_ERR( mbImpl->coords_iterate( v_it, verts.end(), x_ptr, y_ptr, z_ptr, count ),
206  "Error in coords_iterate" );
207  MB_CHK_SET_ERR( mbImpl->adjacencies_iterate( v_it, verts.end(), adjs_ptr, count ),
208  "Error in adjacencies_iterate" );
209 
210  for( int v = 0; v < count; v++ )
211  {
212  const vector< EntityHandle >* avec = *( adjs_ptr + v );
213  for( vector< EntityHandle >::const_iterator ait = avec->begin(); ait != avec->end(); ++ait )
214  {
215  // Get chunk that this element resides in; upper_bound points to the first element
216  // strictly > key, so get that and decrement (would work the same as lower_bound
217  // with an if-test and decrement)
218  map< EntityHandle, tag_struct >::iterator mit = elem_map.upper_bound( *ait );
219  --mit;
220  // Index of *ait in that chunk
221  int a_ind = *ait - ( *mit ).first;
222  tag_struct ts = ( *mit ).second;
223  ts.avg_ptr[3 * a_ind + 0] += x_ptr[v]; // Tag on each element is 3 doubles, x/y/z
224  ts.avg_ptr[3 * a_ind + 1] += y_ptr[v];
225  ts.avg_ptr[3 * a_ind + 2] += z_ptr[v];
226  ts.nv_ptr[a_ind]++; // Increment the vertex count
227  }
228  }
229 
230  v_it += count;
231  }
232 
233  // Normalize tag2 by vertex count; loop over elements using same approach as before
234  // At the same time, compare values of tag1 and tag2
235  e_it = elems.begin();
236  while( e_it != elems.end() )
237  {
238  // Get conn_ptr, tag1_ptr for next contiguous chunk of element handles, and coords pointers
239  // for all verts
240  MB_CHK_SET_ERR( mbImpl->tag_iterate( tag1, e_it, elems.end(), count, (void*&)tag1_ptr ),
241  "Error in tag1_iterate" );
242  MB_CHK_SET_ERR( mbImpl->tag_iterate( tag2, e_it, elems.end(), count, (void*&)tag2_ptr ),
243  "Error in tag2_iterate" );
244  MB_CHK_SET_ERR( mbImpl->tag_iterate( tag3, e_it, elems.end(), count, (void*&)tag3_ptr ),
245  "Error in tag3_iterate" );
246 
247  // Iterate over elements in this chunk
248  for( int i = 0; i < count; i++ )
249  {
250  for( int j = 0; j < 3; j++ )
251  tag2_ptr[3 * i + j] /= (double)tag3_ptr[i]; // Normalize by # verts
252  if( tag1_ptr[3 * i] != tag2_ptr[3 * i] || tag1_ptr[3 * i + 1] != tag2_ptr[3 * i + 1] ||
253  tag1_ptr[3 * i + 2] != tag2_ptr[3 * i + 2] )
254  cout << "Tag1, tag2 disagree for element " << *e_it + i << endl;
255  }
256 
257  e_it += count;
258  }
259 
260  // Ok, we're done, shut down MOAB
261  delete mbImpl;
262 
263  return 0;
264 }
265 
266 ErrorCode create_mesh_with_holes( Interface* mbImpl, int nquads, int nholes )
267 {
268  // First make the mesh, a 1d array of quads with left hand side x = elem_num; vertices are
269  // numbered in layers
270  ReadUtilIface* read_iface;
271  MB_CHK_SET_ERR( mbImpl->query_interface( read_iface ), "Error in query_interface" );
272  vector< double* > coords;
273  EntityHandle start_vert, start_elem, *connect;
274  // Create verts, num is 4(nquads+1) because they're in a 1d row; will initialize coords in loop
275  // over elems later
276  MB_CHK_SET_ERR( read_iface->get_node_coords( 3, 2 * ( nquads + 1 ), 0, start_vert, coords ),
277  "Error in get_node_arrays" );
278  // Create elems
279  MB_CHK_SET_ERR( read_iface->get_element_connect( nquads, 4, MBQUAD, 0, start_elem, connect ),
280  "Error in get_element_connect" );
281  for( int i = 0; i < nquads; i++ )
282  {
283  coords[0][2 * i] = coords[0][2 * i + 1] = (double)i; // x values are all i
284  coords[1][2 * i] = 0.0;
285  coords[1][2 * i + 1] = 1.0; // y coords
286  coords[2][2 * i] = coords[2][2 * i + 1] = (double)0.0; // z values, all zero (2d mesh)
287  EntityHandle quad_v = start_vert + 2 * i;
288  for( int j = 0; j < 4; j++ )
289  connect[4 * i + j] = quad_v + j; // Connectivity of each quad is a sequence starting from quad_v
290  }
291  // Last two vertices
292  // Cppcheck warning (false positive): variable coords is assigned a value that is never used
293  coords[0][2 * nquads] = coords[0][2 * nquads + 1] = (double)nquads;
294  coords[1][2 * nquads] = 0.0;
295  coords[1][2 * nquads + 1] = 1.0; // y coords
296  coords[2][2 * nquads] = coords[2][2 * nquads + 1] = (double)0.0; // z values, all zero (2d mesh)
297 
298  // Now delete nholes elements, spaced approximately equally through mesh, so contiguous size is
299  // about nquads/(nholes + 1) reinterpret start_elem as the next element to be deleted
300  int de = nquads / ( nholes + 1 );
301  for( int i = 0; i < nholes; i++ )
302  {
303  start_elem += de;
304  MB_CHK_SET_ERR( mbImpl->delete_entities( &start_elem, 1 ), "Error in delete_entities" );
305  }
306 
307  return MB_SUCCESS;
308 }