Mesh Oriented datABase  (version 5.6.0)
An array-based unstructured mesh library
DirectAccessNoHoles.cpp
Go to the documentation of this file.
1 /**
2  * @file DirectAccessNoHoles.cpp
3  * @brief Example demonstrating direct access to MOAB data for performance optimization
4  *
5  * @details This example shows how to:
6  * - Create a structured mesh with contiguous entity handles
7  * - Use direct access iterators to avoid API overhead
8  * - Access vertex coordinates, element connectivity, and tag data directly
9  * - Work with adjacency lists for vertex-to-element relationships
10  * - Perform efficient mesh operations using pointer arithmetic
11  *
12  * The program creates a 1D row of quad elements with contiguous handles,
13  * then demonstrates direct access to MOAB's internal data structures for
14  * maximum performance when the mesh topology is static.
15  *
16  * @author MOAB Team
17  * @date 2024
18  *
19  * @param[in] argc Number of command line arguments
20  * @param[in] argv Command line arguments array
21  * @param[in] -nquads Number of quads in the mesh (default: 1000)
22  *
23  * @return 0 on success, 1 on failure
24  *
25  * @par Usage:
26  * @code
27  * ./DirectAccessNoHoles [-nquads <# quads>]
28  * @endcode
29  *
30  * @par Example:
31  * @code
32  * ./DirectAccessNoHoles -nquads 500
33  * @endcode
34  *
35  * @see Core, Interface, ReadUtilIface, ProgOptions
36  *
37 
38  * \brief Use direct access to MOAB data to avoid calling through API \n
39  *
40  * This example creates a 1d row of quad elements, such that all quad and vertex handles
41  * are contiguous in the handle space and in the database. Then it shows how to get access
42  * to pointers to MOAB-native data for vertex coordinates, quad connectivity, tag storage,
43  * and vertex to quad adjacency lists. This allows applications to access this data directly
44  * without going through MOAB's API. In cases where the mesh is not changing (or only mesh
45  * vertices are moving), this can save significant execution time in applications.
46  * \verbatim
47  * ----------------------
48  * | | | |
49  * | | | | ...
50  * | | | |
51  * ----------------------
52  * \endverbatim
53  * -# Initialize MOAB \n
54  * -# Create a quad mesh, as depicted above
55  * -# Create 2 dense tags (tag1, tag2) for avg position to assign to quads, and # verts per quad
56  * (tag3)
57  * -# Get connectivity, coordinate, tag1 iterators
58  * -# Iterate through quads, computing midpoint based on vertex positions, set on quad-based
59  * tag1
60  * -# Iterate through vertices, summing positions into tag2 on connected quads and incrementing
61  * vertex count
62  * -# Iterate through quads, normalizing tag2 by vertex count and comparing values of tag1 and
63  * tag2
64  *
65  * <b>To compile</b>: \n
66  * make DirectAccessNoHoles \n
67  * <b>To run</b>: ./DirectAccessNoHoles [-nquads <# quads>]\n
68  *
69  */
70 
71 #include "moab/Core.hpp"
72 #include "moab/ProgOptions.hpp"
73 #include "moab/ReadUtilIface.hpp"
74 #include <map>
75 #include <iostream>
76 #include <cassert>
77 
78 using namespace moab;
79 using namespace std;
80 
81 ErrorCode create_mesh_no_holes( Interface* mbImpl, int nquads );
82 
83 int main( int argc, char** argv )
84 {
85  // Get MOAB instance
86  Interface* mbImpl = new( std::nothrow ) Core;
87  if( NULL == mbImpl ) return 1;
88 
89  int nquads = 1000;
90 
91  // Parse options
92  ProgOptions opts;
93  opts.addOpt< int >( string( "nquads,n" ), string( "Number of quads in the mesh (default = 1000" ), &nquads );
94  opts.parseCommandLine( argc, argv );
95 
96  // Create simple structured mesh with hole, but using unstructured representation
97  MB_CHK_SET_ERR( create_mesh_no_holes( mbImpl, nquads ), "Trouble creating mesh" );
98 
99  // Get all vertices and non-vertex entities
100  Range verts, quads;
101  MB_CHK_SET_ERR( mbImpl->get_entities_by_handle( 0, quads ), "Trouble getting all entities" );
102  verts = quads.subset_by_dimension( 0 );
103  quads -= verts;
104 
105  // Create tag1 (element-based avg), tag2 (vertex-based avg), tag3 (# connected verts)
106  Tag tag1, tag2, tag3;
107  MB_CHK_SET_ERR( mbImpl->tag_get_handle( "tag1", 3, MB_TYPE_DOUBLE, tag1, MB_TAG_DENSE | MB_TAG_CREAT ),
108  "Trouble creating tag1" );
109  double def_val[3] = { 0.0, 0.0, 0.0 }; // need a default value for tag2 because we sum into it
110  MB_CHK_SET_ERR( mbImpl->tag_get_handle( "tag2", 3, MB_TYPE_DOUBLE, tag2, MB_TAG_DENSE | MB_TAG_CREAT, def_val ),
111  "Trouble creating tag2" );
112  int def_val_int = 0; // need a default value for tag3 because we increment it
114  &def_val_int ),
115  "Trouble creating tag3" );
116 
117  // Get pointers to connectivity, coordinate, tag, and adjacency arrays; each of these returns a
118  // count, which should be compared to the # entities you expect to verify there's only one chunk
119  // (no holes)
120  int count, vpere;
121  EntityHandle* conn_ptr;
122  MB_CHK_SET_ERR( mbImpl->connect_iterate( quads.begin(), quads.end(), conn_ptr, vpere, count ),
123  "Error in connect_iterate" );
124  assert( count == (int)quads.size() ); // Should end up with just one contiguous chunk of quads
125 
126  double *x_ptr, *y_ptr, *z_ptr;
127  MB_CHK_SET_ERR( mbImpl->coords_iterate( verts.begin(), verts.end(), x_ptr, y_ptr, z_ptr, count ),
128  "Error in coords_iterate" );
129  assert( count == (int)verts.size() ); // Should end up with just one contiguous chunk of vertices
130 
131  double *tag1_ptr, *tag2_ptr;
132  int* tag3_ptr;
133  MB_CHK_SET_ERR( mbImpl->tag_iterate( tag1, quads.begin(), quads.end(), count, (void*&)tag1_ptr ),
134  "Error in tag1_iterate" );
135  assert( count == (int)quads.size() ); // Should end up with just one contiguous chunk of quads
136  MB_CHK_SET_ERR( mbImpl->tag_iterate( tag2, quads.begin(), quads.end(), count, (void*&)tag2_ptr ),
137  "Error in tag2_iterate" );
138  assert( count == (int)quads.size() ); // Should end up with just one contiguous chunk of quads
139  MB_CHK_SET_ERR( mbImpl->tag_iterate( tag3, quads.begin(), quads.end(), count, (void*&)tag3_ptr ),
140  "Error in tag3_iterate" );
141  assert( count == (int)quads.size() ); // Should end up with just one contiguous chunk of quads
142 
143  const vector< EntityHandle >** adjs_ptr;
144  MB_CHK_SET_ERR( mbImpl->adjacencies_iterate( verts.begin(), verts.end(), adjs_ptr, count ),
145  "Error in adjacencies_iterate" );
146  assert( count == (int)verts.size() ); // Should end up with just one contiguous chunk of vertices
147  // Start_ handles used to compute indices into vertex/quad arrays; can use direct subtraction
148  // because we know there aren't any holes in the handle spaces for verts or quads
149  EntityHandle start_vert = *verts.begin(), start_quad = *quads.begin();
150 
151  // Iterate over elements, computing tag1 from coords positions
152  for( int i = 0; i < nquads; i++ )
153  {
154  tag1_ptr[3 * i + 0] = tag1_ptr[3 * i + 1] = tag1_ptr[3 * i + 2] = 0.0; // Initialize position for this element
155  for( int j = 0; j < vpere; j++ )
156  { // Loop over vertices in this element
157  int v_index = conn_ptr[vpere * i + j] - start_vert; // vert index is just the offset from start vertex
158  tag1_ptr[3 * i + 0] += x_ptr[v_index];
159  tag1_ptr[3 * i + 1] += y_ptr[v_index]; // Sum vertex positions into tag1...
160  tag1_ptr[3 * i + 2] += z_ptr[v_index];
161  }
162  tag1_ptr[3 * i + 0] /= vpere;
163  tag1_ptr[3 * i + 1] /= vpere; // Then normalize
164  tag1_ptr[3 * i + 2] /= vpere;
165  } // Loop over elements in chunk
166 
167  // Iterate through vertices, summing positions into tag2 on connected elements and incrementing
168  // vertex count
169  for( int v = 0; v < count; v++ )
170  {
171  const vector< EntityHandle >* avec = *( adjs_ptr + v );
172  for( vector< EntityHandle >::const_iterator ait = avec->begin(); ait != avec->end(); ++ait )
173  {
174  // *ait is the quad handle, its index is computed by subtracting the start quad handle
175  int a_ind = *ait - start_quad;
176  tag2_ptr[3 * a_ind + 0] += x_ptr[v]; // Tag on each element is 3 doubles, x/y/z
177  tag2_ptr[3 * a_ind + 1] += y_ptr[v];
178  tag2_ptr[3 * a_ind + 2] += z_ptr[v];
179  tag3_ptr[a_ind]++; // Increment the vertex count
180  }
181  }
182 
183  // Normalize tag2 by vertex count (tag3); loop over elements using same approach as before
184  // At the same time, compare values of tag1 and tag2
185  int n_dis = 0;
186  for( Range::iterator q_it = quads.begin(); q_it != quads.end(); ++q_it )
187  {
188  int i = *q_it - start_quad;
189  for( int j = 0; j < 3; j++ )
190  tag2_ptr[3 * i + j] /= (double)tag3_ptr[i]; // Normalize by # verts
191  if( tag1_ptr[3 * i] != tag2_ptr[3 * i] || tag1_ptr[3 * i + 1] != tag2_ptr[3 * i + 1] ||
192  tag1_ptr[3 * i + 2] != tag2_ptr[3 * i + 2] )
193  {
194  cout << "Tag1, tag2 disagree for element " << *q_it + i << endl;
195  n_dis++;
196  }
197  }
198  if( !n_dis ) cout << "All tags agree, success!" << endl;
199 
200  // Ok, we're done, shut down MOAB
201  delete mbImpl;
202 
203  return 0;
204 }
205 
207 {
208  // First make the mesh, a 1d array of quads with left hand side x = elem_num; vertices are
209  // numbered in layers
210  ReadUtilIface* read_iface;
211  MB_CHK_SET_ERR( mbImpl->query_interface( read_iface ), "Error in query_interface" );
212  vector< double* > coords;
213  EntityHandle start_vert, start_elem, *connect;
214  // Create verts, num is 2(nquads+1) because they're in a 1d row; will initialize coords in loop
215  // over quads later
216  MB_CHK_SET_ERR( read_iface->get_node_coords( 3, 2 * ( nquads + 1 ), 0, start_vert, coords ),
217  "Error in get_node_arrays" );
218  // Create quads
219  MB_CHK_SET_ERR( read_iface->get_element_connect( nquads, 4, MBQUAD, 0, start_elem, connect ),
220  "Error in get_element_connect" );
221  for( int i = 0; i < nquads; i++ )
222  {
223  coords[0][2 * i] = coords[0][2 * i + 1] = (double)i; // x values are all i
224  coords[1][2 * i] = 0.0;
225  coords[1][2 * i + 1] = 1.0; // y coords
226  coords[2][2 * i] = coords[2][2 * i + 1] = (double)0.0; // z values, all zero (2d mesh)
227  EntityHandle quad_v = start_vert + 2 * i;
228  connect[4 * i + 0] = quad_v;
229  connect[4 * i + 1] = quad_v + 2;
230  connect[4 * i + 2] = quad_v + 3;
231  connect[4 * i + 3] = quad_v + 1;
232  }
233 
234  // Last two vertices
235  // Cppcheck warning (false positive): variable coords is assigned a value that is never used
236  coords[0][2 * nquads] = coords[0][2 * nquads + 1] = (double)nquads;
237  coords[1][2 * nquads] = 0.0;
238  coords[1][2 * nquads + 1] = 1.0; // y coords
239  coords[2][2 * nquads] = coords[2][2 * nquads + 1] = (double)0.0; // z values, all zero (2d mesh)
240 
241  // Call a vertex-quad adjacencies function to generate vertex-element adjacencies in MOAB
242  Range dum_range;
243  MB_CHK_SET_ERR( mbImpl->get_adjacencies( &start_vert, 1, 2, false, dum_range ), "Error in get_adjacencies" );
244  assert( !dum_range.empty() );
245 
246  return MB_SUCCESS;
247 }