Example program that shows the use case for performing tag data exchange between parallel processors in order to sync data on shared entities.
This example :
NOTE: –debug option can be added to write out extra files in h5m format to visualize some output (written from root task only)
#ifndef MOAB_HAVE_MPI
#error "Please build MOAB with MPI..."
#endif
#include <iostream>
#include <string>
using namespace std;
#define dbgprint( MSG ) \
do \
{ \
if( context.proc_id == 0 ) std::cout << MSG << std::endl; \
} while( false )
#define runchk( CODE, MSG ) \
do \
{ \
moab::ErrorCode err = CODE; \
MB_CHK_SET_ERR( err, MSG ); \
} while( false )
#define runchk_cont( CODE, MSG ) \
do \
{ \
moab::ErrorCode err = CODE; \
MB_CHK_ERR_CONT( err ); \
if( err ) std::cout << "Error:: " << MSG << std::endl; \
} while( false )
{
public:
int dimension{ 2 };
std::string input_filename;
int ghost_layers{ 3 };
std::string scalar_tagname;
std::string vector_tagname;
int vector_length{ 3 };
int num_max_exchange{ 10 };
bool debug_output{ false };
int proc_id{ 1 };
int num_procs{ 1 };
double last_counter{ 0.0 };
void ParseCLOptions( int argc, char* argv[] );
inline void timer_push( const std::string& operation );
void timer_pop( const int nruns = 1 );
inline double last_elapsed() const;
inline double evaluate_function( double lon, double lat, int type = 1, double multiplier = 1.0 ) const
{
switch( type )
{
case 1:
return ( 2.0 + std::pow( sin( 2.0 * lat ), 16.0 ) * cos( 16.0 * lon ) ) * multiplier;
default:
return ( 2.0 + cos( lon ) * cos( lon ) * cos( 2.0 * lat ) ) * multiplier;
}
}
private:
std::vector< double > compute_centroids(
const moab::Range& entities )
const;
double mTimerOps{ 0.0 };
std::string mOpName;
};
int main(
int argc,
char** argv )
{
MPI_Init( &argc, &argv );
{
dbgprint(
"********** Exchange halos example **********\n" );
context.ParseCLOptions( argc, argv );
double elapsed_times[4];
context.timer_push(
"Read input file" );
{
runchk(
context.load_file(
false ),
"MOAB::load_file failed for filename: " <<
context.input_filename );
}
elapsed_times[0] =
context.last_elapsed();
dbgprint(
"\n- Starting execution -\n" );
context.timer_push(
"Setup ghost layers" );
{
for(
int ighost = 0; ighost <
context.ghost_layers; ++ighost )
{
int ghost_dimension =
context.dimension;
int bridge_dimension =
context.dimension - 1;
ghost_dimension, bridge_dimension, ( ighost + 1 ), 0, true ,
"Exchange ghost cells failed" );
if( ighost <
context.ghost_layers - 1 )
runchk(
context.parallel_communicator->correct_thin_ghost_layers(),
"Thin layer correction failed" );
}
}
elapsed_times[1] =
context.last_elapsed();
Range dimEnts;
{
"Getting 2D entities failed" );
"Filtering pstatus failed" );
auto numEntities = dimEnts.size();
int numTotalEntities = 0;
MPI_Reduce( &numEntities, &numTotalEntities, 1, MPI_INT, MPI_SUM, 0,
context.parallel_communicator->proc_config().proc_comm() );
dbgprint(
"Total number of " <<
context.dimension <<
"D elements in the mesh = " << numTotalEntities );
}
runchk(
context.create_sv_tags( tagScalar, tagVector, dimEnts ),
"Unable to create scalar and vector tags" );
{
dbgprint(
"> Writing to file *before* ghost exchange " );
runchk(
context.moab_interface.write_file(
"exchangeHalos_output_rank0_pre.h5m",
"H5M",
"" ),
"Writing to disk failed" );
}
dbgprint(
"> Exchanging tags between processors " );
context.timer_push(
"Exchange scalar tag data" );
for(
auto irun = 0; irun <
context.num_max_exchange; ++irun )
{
runchk(
context.parallel_communicator->exchange_tags( tagScalar, dimEnts ),
"Exchanging scalar tag between processors failed" );
}
elapsed_times[2] =
context.last_elapsed();
context.timer_push(
"Exchange vector tag data" );
for(
auto irun = 0; irun <
context.num_max_exchange; ++irun )
{
runchk(
context.parallel_communicator->exchange_tags( tagVector, dimEnts ),
"Exchanging vector tag between processors failed" );
}
elapsed_times[3] =
context.last_elapsed();
{
dbgprint(
"> Writing to file *after* ghost exchange " );
runchk(
context.moab_interface.write_file(
"exchangeHalos_output_rank0_post.h5m",
"H5M",
"" ),
"Writing to disk failed" );
}
{
dbgprint(
"> Writing out the final mesh and data in MOAB h5m format. File = " <<
context.output_filename );
string write_options = (
context.num_procs > 1 ?
"PARALLEL=WRITE_PART;DEBUG_IO=0;" :
"" );
runchk(
context.moab_interface.write_file(
context.output_filename.c_str(),
"H5M", write_options.c_str() ),
"File write failed" );
}
dbgprint(
"\n> Consolidated: [" <<
context.num_procs <<
", " <<
context.ghost_layers <<
", " << elapsed_times[0]
<< ", " << elapsed_times[1] << ", " << elapsed_times[2] << ", "
<< elapsed_times[3] << "]," );
dbgprint(
"\n********** ExchangeHalos Example DONE! **********" );
}
MPI_Finalize();
return 0;
}
: input_filename( std::string(
MESH_DIR ) + std::string(
"/io/mpasx1.642.t.2.nc" ) ),
output_filename(
"exchangeHalos_output.h5m" ), scalar_tagname(
"scalar_variable" ),
vector_tagname( "vector_variable" )
{
proc_id = parallel_communicator->rank();
num_procs = parallel_communicator->size();
}
{
}
{
opts.
addOpt< std::string >(
"input",
"Input mesh filename to load in parallel. Default=data/default_mesh_holes.h5m",
opts.
addOpt<
void >(
"debug",
"Should we write output file? Default=false", &
debug_output );
opts.
addOpt< std::string >(
"output",
"Output mesh filename for verification (use --debug). Default=exchangeHalos_output.h5m",
opts.
addOpt<
int >(
"vtaglength",
"Size of vector components per each entity. Default=3", &
vector_length );
opts.
addOpt<
int >(
"nghosts",
"Number of ghost layers (halos) to exchange. Default=3", &
ghost_layers );
opts.
addOpt<
int >(
"nexchanges",
"Number of ghost-halo exchange iterations to perform. Default=10",
}
{
}
{
double avgElapsed = 0;
double maxElapsed = 0;
{
if( nruns > 1 )
std::cout <<
"[LOG] Time taken to " <<
mOpName.c_str() <<
", averaged over " << nruns
<< " runs : max = " << maxElapsed / nruns << ", avg = " << avgElapsed / nruns << "\n";
else
std::cout <<
"[LOG] Time taken to " <<
mOpName.c_str() <<
" : max = " << maxElapsed
<< ", avg = " << avgElapsed << "\n";
}
}
{
}
{
double defSTagValue = -1.0;
bool createdTScalar = false;
"Retrieving scalar tag handle failed" );
assert( createdTScalar );
{
std::vector< double > tagValues( entities.
size(), -1.0 );
std::generate( tagValues.begin(), tagValues.end(), [=, &entCoords]() {
static int index = 0;
const int offset = index++ * 2;
return evaluate_function( entCoords[offset], entCoords[offset + 1] );
} );
"Setting scalar tag data failed" );
}
bool createdTVector = false;
&createdTVector ),
"Retrieving vector tag handle failed" );
assert( createdTVector );
{
std::vector< double > tagValues( entities.
size() * veclength, -1.0 );
std::generate( tagValues.begin(), tagValues.end(), [=, &entCoords]() {
static int index = 0;
const int offset = ( index++ / veclength ) * 2;
return this->evaluate_function( entCoords[offset], entCoords[offset + 1], 2, ( index % veclength + 1.0 ) );
} );
"Setting vector tag data failed" );
}
}
{
std::string read_options = "DEBUG_IO=0;";
if(
num_procs > 1 && idx != std::string::npos )
{
if( !extension.compare( "nc" ) )
read_options += "PARALLEL=READ_PART;PARTITION_METHOD=RCBZOLTAN;"
"PARALLEL_RESOLVE_SHARED_ENTS;NO_EDGES;NO_MIXED_ELEMENTS;VARIABLE=;";
else if( !extension.compare( "h5m" ) )
read_options +=
"PARALLEL=READ_PART;PARTITION=PARALLEL_PARTITION;"
"PARALLEL_RESOLVE_SHARED_ENTS;" +
( load_ghosts ?
"PARALLEL_THIN_GHOST_LAYER;PARALLEL_GHOSTS=2.1." + std::to_string(
ghost_layers ) +
";"
: "" );
else
{
std::cout <<
"Error unsupported file type (only h5m and nc) for this example: " <<
input_filename
<< std::endl;
}
}
}
{
double node[3];
std::vector< double > eCentroids( entities.
size() * 2 );
size_t offset = 0;
for( auto entity : entities )
{
double magnitude = std::sqrt( node[0] * node[0] + node[1] * node[1] + node[2] * node[2] );
node[0] /= magnitude;
node[1] /= magnitude;
node[2] /= magnitude;
eCentroids[offset] = atan2( node[1], node[0] );
if( eCentroids[offset] < 0.0 ) eCentroids[offset] += 2.0 * M_PI;
eCentroids[offset + 1] = asin( node[2] );
offset += 2;
}
return eCentroids;
}