146 std::unique_ptr< moab::CpuTimer >
timer;
164 :
mbcore( icore ), pcomm( p_pcomm ),
proc_id( p_pcomm ? p_pcomm->rank() : 0 ),
167 if( !icore )
throw std::invalid_argument(
"MOAB Core instance cannot be null" );
168 if( !p_pcomm )
throw std::invalid_argument(
"ParallelComm cannot be null in MPI mode" );
186 timer = std::make_unique< moab::CpuTimer >();
221 double avgElapsed = locElapsed;
222 double maxElapsed = locElapsed;
225 MPI_Reduce( &locElapsed, &maxElapsed, 1, MPI_DOUBLE, MPI_MAX, 0, pcomm->
comm() );
226 MPI_Reduce( &locElapsed, &avgElapsed, 1, MPI_DOUBLE, MPI_SUM, 0, pcomm->
comm() );
232 std::cout <<
"[LOG] Time taken to " <<
opName <<
": max = " << maxElapsed <<
", avg = " << avgElapsed
249 std::string expectedFName =
"output.exo";
250 std::string expectedMethod =
"fv";
251 std::string expectedFVMethod =
"none";
252 std::string expectedDofTagName =
"GLOBAL_ID";
253 int expectedOrder = 1;
255 int nlayer_input = -1;
256 bool version_info =
false;
261 std::cout <<
"Command line options provided to mbtempest:\n ";
262 for(
int i = 0; i < argc; ++i )
264 std::cout << argv[i] <<
" ";
266 std::cout <<
"\n" << std::endl;
270 ProgOptions opts(
"mbtempest - A mesh generation and remapping tool" );
273 opts.
addOpt<
int >(
"type,t",
274 "Type of mesh (default=CS; Choose from [CS=0, RLL=1, ICO=2, OVERLAP_FILES=3, "
275 "OVERLAP_MEMORY=4, OVERLAP_MOAB=5])",
278 opts.
addOpt<
int >(
"res,r",
"Resolution of the mesh (default=5)", &
blockSize );
280 opts.
addOpt<
void >(
"dual,d",
"Output the dual of the mesh (relevant only for ICO mesh type)", &
computeDual );
282 opts.
addOpt< std::string >(
"file,f",
"Output computed mesh or remapping weights to specified filename",
286 opts.
addOpt< std::string >(
287 "load,l",
"Input mesh filenames for source and target meshes. (relevant only when computing weights)",
294 opts.
addOpt<
void >(
"advfront,a",
295 "Use the advancing front intersection instead of the Kd-tree based algorithm to compute "
296 "mesh intersections.",
299 opts.
addOpt< std::string >(
"intx,i",
"Output TempestRemap intersection mesh filename", &
intxFilename );
303 "Compute and output the weights using the overlap mesh (generally relevant only for OVERLAP mesh)",
308 "verbose,v",
"Print verbose diagnostic messages during intersection and map computation (default=false)",
311 opts.
addOpt< std::string >(
"method,m",
"Discretization method for the source and target solution fields",
314 opts.
addOpt<
int >(
"order,o",
"Discretization orders for the source and target solution fields",
317 opts.
addOpt< std::string >(
"global_id,g",
318 "Tag name that contains the global DoF IDs for source and target solution fields",
319 &expectedDofTagName );
322 opts.
addOpt< std::string >(
"fvmethod",
323 "Sub-type method for FV-FV projections (invdist, delaunay, bilin, intbilin, "
324 "intbilingb, none. Default: none)",
327 opts.
addOpt< std::string >(
329 "Formula used to compute spherical areas: vos (Van Oosterom-Strackee, default), "
330 "lhuiller, girard, or gquad (Gauss quadrature).",
334 "noconserve",
"Do not apply conservation to the resultant weights (relevant only when computing weights)",
338 "volumetric",
"Apply a volumetric projection to compute the weights (relevant only when computing weights)",
343 opts.
addOpt<
void >(
"skip_output",
"For performance studies, skip all I/O operations.", &
skip_io );
345 opts.
addOpt<
void >(
"gnomonic",
"Use Gnomonic plane projections to compute coverage mesh.",
348 opts.
addOpt<
void >(
"enforce_convexity",
"Check convexity of input meshes to compute mesh intersections",
351 opts.
addOpt<
void >(
"nobubble",
"Do not use bubble on interior of spectral element nodes",
356 "Use sparse solver for constraints when we have high-valence (typical with high-res RLL mesh)",
361 "At least one of the meshes is a regionally refined grid (relevant to accelerate intersection computation)",
364 opts.
addOpt<
void >(
"checkmap",
"Check the generated map for conservation and consistency", &
fCheck );
366 opts.
addOpt<
void >(
"verify",
367 "Verify the accuracy of the maps by projecting analytical functions from source to target "
368 "grid by applying the maps",
371 opts.
addOpt< std::string >(
"var",
372 "Tag name of the variable to use in the verification study (error metrics for user "
373 "defined variables may not be available)",
376 opts.
addOpt<
int >(
"monotonicity",
"Ensure monotonicity in the weight generation. Options=[0,1,2,3]",
379 opts.
addOpt<
int >(
"ghost",
380 "Number of ghost layers in coverage mesh (overrides automatic selection: 0 for FV order 1, "
381 "p+1 for FV order p>1)",
384 opts.
addOpt<
double >(
"boxeps",
"The tolerance for boxes (default=1e-7)", &
boxeps );
386 opts.
addOpt<
int >(
"limiter",
"Apply nonlinear filter after linear map application", &useCAAS );
390 opts.
addOpt<
void >(
"manual",
"Show documentation about usage with examples" );
392 opts.
addOpt<
void >(
"version",
"Show version information", &version_info );
404 <<
"\"; expected one of: vos, lhuiller, girard, gquad" << std::endl;
411 if( this->proc_id == 0 )
420 if( this->proc_id == 0 )
422 std::cout <<
"mbtempest is part of the MOAB library version " << std::string(
MOAB_PACKAGE_VERSION )
475 if( !expectedFName.empty() )
477 this->inFilenames = { expectedFName };
483 this->disc_orders = { expectedOrder, expectedOrder };
484 this->disc_methods = { expectedMethod, expectedMethod };
485 this->doftag_names = { expectedDofTagName, expectedDofTagName };
491 this->mapOptions.nPin = this->disc_orders[0];
492 this->mapOptions.nPout = this->disc_orders[1];
493 this->mapOptions.fSourceConcave =
false;
494 this->mapOptions.fTargetConcave =
false;
495 this->mapOptions.strMethod =
"";
522 const size_t last_dot = filename.find_last_of(
"." );
523 if( last_dot == std::string::npos )
528 const std::string extension = filename.substr( last_dot + 1 );
531 if( extension ==
"h5m" )
533 return "PARALLEL=READ_PART;PARTITION=PARALLEL_PARTITION;PARALLEL_RESOLVE_SHARED_ENTS;";
537 if( extension ==
"nc" )
540 #ifdef MOAB_HAVE_ZOLTAN
541 std::string netcdf_options =
"PARALLEL=READ_PART;PARTITION_METHOD=RCBZOLTAN;";
543 std::string netcdf_options =
"PARALLEL=READ_PART;PARTITION_METHOD=TRIVIAL;";
546 #ifdef MOAB_HAVE_NETCDF
549 NcFile
ncFile( filename.c_str(), NcFile::ReadOnly );
553 return netcdf_options;
557 int format_flags = 0;
558 for(
int i = 0; i <
ncFile.num_dims(); i++ )
560 const std::string dim_name =
ncFile.get_dim( i )->name();
562 if( dim_name ==
"grid_size" || dim_name ==
"grid_corners" || dim_name ==
"grid_rank" )
566 else if( dim_name ==
"nodeCount" || dim_name ==
"elementCount" || dim_name ==
"maxNodePElement" )
570 else if( dim_name ==
"nCells" || dim_name ==
"nEdges" || dim_name ==
"nVertices" ||
571 dim_name ==
"vertexDegree" )
578 if( format_flags & 2 )
580 netcdf_options +=
"PARALLEL_RESOLVE_SHARED_ENTS;VARIABLE=;";
582 else if( format_flags & 1 )
584 netcdf_options +=
"";
586 else if( format_flags & 4 )
588 netcdf_options +=
"PARALLEL_RESOLVE_SHARED_ENTS;NO_EDGES;NO_MIXED_ELEMENTS;VARIABLE=;";
595 int line_size = netcdf_options.size();
596 MPI_Bcast( &line_size, 1, MPI_INT, 0, MPI_COMM_WORLD );
599 netcdf_options.resize( line_size );
601 MPI_Bcast(
const_cast< char*
>( netcdf_options.data() ), line_size, MPI_CHAR, 0, MPI_COMM_WORLD );
604 return netcdf_options;
608 return "PARALLEL=BCAST_DELETE;PARTITION=TRIVIAL;PARALLEL_RESOLVE_SHARED_ENTS;";
618 if( this->proc_id != 0 )
return;
620 std::cout <<
"MOAB-Tempest: A mesh generation and remapping tool\n"
621 <<
"==================================================\n\n"
622 <<
"Usage: " << progName <<
" [OPTIONS]\n\n"
623 <<
"Mesh Generation Options:\n"
624 <<
" -t, --type TYPE Type of mesh to generate (required for mesh generation):\n"
625 <<
" 0 = Cubed-Sphere (CS)\n"
626 <<
" 1 = Regular Latitude-Longitude (RLL)\n"
627 <<
" 2 = Icosahedral (ICO)\n"
628 <<
" 3 = TempestRemap overlap (thin interface))\n"
629 <<
" 4 = MOAB with TempestRemap overlap in memory\n"
630 <<
" 5 = Parallel handling of Overlap meshes with MOAB (recommended)\n\n"
631 <<
" -r, --res N Resolution (number of elements on edge, default: 10)\n"
632 <<
" -f, --file FILE Output filename (default: output.h5m)\n\n"
633 <<
"Discretization Options:\n"
634 <<
" -m, --method METHOD Discretization method (default: fv):\n"
635 <<
" fv = Finite Volume\n"
636 <<
" cgll = Continuous Galerkin with Legendre-Gauss-Lobatto\n"
637 <<
" dgll = Discontinuous Galerkin with Legendre-Gauss-Lobatto\n\n"
638 <<
" -o, --order N Discretization order (default: 1, range: 1-4)\n\n"
639 <<
"Remapping Options:\n"
640 <<
" --mono N Monotonicity constraints (default: 0):\n"
641 <<
" 0 = No monotonicity\n"
642 <<
" 1 = Basic monotonicity\n"
643 <<
" 2 = Full monotonicity with bounds\n"
644 <<
" 3 = Strict monotonicity\n\n"
645 <<
" --limiter TYPE Nonlinear limiting (optional):\n"
646 <<
" none = No limiting (default)\n"
647 <<
" global = Global CAAS limiting\n"
648 <<
" local = Localized CAAS limiting\n"
649 <<
" qlt = Quasi-Local Tree-based limiting\n\n"
650 <<
"Input/Output Options:\n"
651 <<
" -l, --load FILE Load input mesh file (use twice for source and target)\n"
652 <<
" -i, --global_id TAG Global ID tag name (default: GLOBAL_ID)\n"
653 <<
" --diagnostics Print diagnostic information\n\n"
654 <<
"Miscellaneous Options:\n"
655 <<
" --manual Show this help message and exit\n"
656 <<
" --version Show version information\n\n"
658 <<
" # Generate a cubed-sphere mesh with resolution 25\n"
659 <<
" " << progName <<
" --type 0 --res 25 -f cs_mesh.h5m\n\n"
660 <<
" # Generate a latitude-longitude mesh with resolution 180\n"
661 <<
" " << progName <<
" --type 1 --res 180 -f rll_mesh.h5m\n\n"
662 <<
" # Create a map between two meshes with order 4\n"
663 <<
" " << progName <<
" --type 5 --load source_mesh.h5m --load target_mesh.h5m \\\n"
664 <<
" --method cgll --order 4 --global_id GLOBAL_DOFS \\\n"
665 <<
" --method fv --order 1 --limiter 1 --file map.nc\n";
677 return "Cubed-Sphere";
679 return "Latitude-Longitude";
681 return "Icosahedral";
683 return "Overlap (files)";
685 return "Overlap (memory)";
687 return "Overlap (MOAB)";
704 std::vector< std::string > inputFiles;
706 if( !inputFiles.empty() )
708 this->inFilenames = inputFiles;
709 if( this->inFilenames.size() != 2 )
711 throw std::runtime_error(
"Exactly two input filenames must be provided with -l/--load" );
716 std::vector< int > orders;
718 if( !orders.empty() )
720 this->disc_orders = orders;
721 if( this->disc_orders.size() == 1 )
723 this->disc_orders.push_back( this->disc_orders[0] );
725 else if( this->disc_orders.size() != 2 )
727 throw std::runtime_error(
"Must specify 1 or 2 values for order (source [target])" );
730 for(
const auto& order : this->disc_orders )
732 if( order < 1 || order > 4 )
734 throw std::runtime_error(
"Discretization order must be between 1 and 4" );
740 std::vector< std::string > methods;
742 if( !methods.empty() )
744 this->disc_methods = methods;
745 if( this->disc_methods.size() == 1 )
748 this->disc_methods.push_back( this->disc_methods[0] );
750 else if( this->disc_methods.size() != 2 )
752 throw std::runtime_error(
"Must specify 1 or 2 values for method (source [target])" );
756 for(
const auto& method : this->disc_methods )
758 if( method !=
"fv" && method !=
"cgll" && method !=
"dgll" && method !=
"pcloud" )
760 throw std::runtime_error(
"Invalid method '" + method +
"'. Must be one of: fv, cgll, dgll" );
766 std::vector< std::string > tags;
770 this->doftag_names = tags;
771 if( this->doftag_names.size() == 1 )
774 this->doftag_names.push_back( this->doftag_names[0] );
776 else if( this->doftag_names.size() != 2 )
778 throw std::runtime_error(
"Must specify 1 or 2 values for DOF tag names (source [target])" );
784 if( opts.
getOpt(
"file,f", &outFile ) )
796 if( this->proc_id != 0 )
return;
798 constexpr
int width = 60;
800 std::cout << std::string( width,
'=' ) <<
"\n";
801 std::cout <<
" MOAB-TempestRemap Runtime Configuration " <<
"\n";
802 std::cout << std::string( width,
'=' );
807 if( !this->inFilenames.empty() )
809 std::cout <<
"\n\nInput Files:";
810 std::cout <<
"\n Source mesh: " << this->inFilenames[0];
811 std::cout <<
"\n Target mesh: " << this->inFilenames[1];
814 std::cout <<
"\n\nOutput Files:";
816 std::cout <<
"\n Intersection mesh: "
822 std::cout <<
"\n\nMesh Configuration:";
825 std::cout <<
"\n Resolution: " << this->
blockSize;
827 std::cout <<
"\n Compute dual: " << ( this->
computeDual ?
"Yes" :
"No" );
832 std::cout <<
"\n Intersection algorithm: " << ( this->
kdtreeSearch ?
"KdTree search" :
"Advancing front" );
836 std::cout <<
"\n\nDiscretization:";
837 std::cout <<
"\n Source: " << this->disc_methods[0] <<
" (order " << this->disc_orders[0]
839 std::cout <<
"\n Target: " << this->disc_methods[1] <<
" (order " << this->disc_orders[1]
843 std::cout <<
"\n\nRemapping Options:";
844 std::cout <<
"\n Method: "
845 << ( this->mapOptions.strMethod.empty() ?
"Default" : this->mapOptions.strMethod );
847 std::cout <<
"\n Volumetric: " << ( this->
fVolumetric ?
"Yes" :
"No" );
848 std::cout <<
"\n Check consistency: " << ( this->
fCheck ?
"Yes" :
"No" );
849 std::cout <<
"\n Skip intersection: " << ( this->
skip_intersection ?
"Yes" :
"No" );
853 std::cout <<
"\n\nParallel Configuration:";
854 std::cout <<
"\n MPI Processes: " << this->
n_procs;
857 std::cout <<
"\n\n" << std::string( width,
'=' ) <<
"\n\n";
867 this->mapOptions.nPin = ( this->disc_orders.empty() ) ? 1 : this->disc_orders[0];
868 this->mapOptions.nPout = ( this->disc_orders.size() > 1 ) ? this->disc_orders[1] : this->mapOptions.nPin;
871 this->mapOptions.fSourceConcave =
false;
872 this->mapOptions.fTargetConcave =
false;
873 this->mapOptions.strMethod.clear();
878 this->mapOptions.strMethod = this->
fvMethod +
";";
879 this->mapOptions.fNoConservation =
true;
887 this->mapOptions.fMonotone =
false;
890 this->mapOptions.strMethod +=
"mono3;";
891 this->mapOptions.fMonotone =
true;
894 this->mapOptions.strMethod +=
"mono2;";
895 this->mapOptions.fMonotone =
true;
899 this->mapOptions.fMonotone =
true;
904 this->mapOptions.fNoCorrectAreas =
false;
905 this->mapOptions.fNoCheck = !this->
fCheck;
910 this->mapOptions.strMethod +=
"volumetric;";
930 const bool needsDualMesh = ( this->
fvMethod ==
"delaunay" || this->
fvMethod ==
"bilin" ||
945 this->
nlayers = ( this->mapOptions.nPin > 1 ) ? this->mapOptions.nPin + 1 : 0;
949 if( nlayer_input >= 0 )
955 this->mapOptions.strOutputMapFile = this->
outFilename;
956 this->mapOptions.strOutputFormat =
"Netcdf4";
962 static inline constexpr
double sample_constant(
double dLon,
double dLat ) noexcept;
970 int main(
int argc,
char* argv[] )
974 #ifdef MOAB_HAVE_NETCDF
975 NcError
error( NcError::verbose_nonfatal );
977 std::stringstream sstr;
978 std::string historyStr;
982 MPI_Init( &argc, &argv );
983 MPI_Comm_rank( MPI_COMM_WORLD, &
proc_id );
984 MPI_Comm_size( MPI_COMM_WORLD, &nprocs );
989 if(
nullptr == mbCore )
995 for(
int ia = 0; ia < argc; ++ia )
996 historyStr += std::string( argv[ia] ) +
" ";
1003 const char* writeOptions = ( nprocs > 1 ?
"PARALLEL=WRITE_PART" :
"" );
1006 const char* writeOptions =
"";
1010 const double radius_src = 1.0 ;
1011 const double radius_dest = 1.0 ;
1015 #ifdef MOAB_HAVE_MPI
1032 Mesh* tempest_mesh =
new Mesh();
1039 assert( runCtx->
meshes.size() == 3 );
1041 #ifdef MOAB_HAVE_MPI
1052 "Failed to write TempestRemap intersection mesh in MOAB format" );
1056 size_t velist[6], gvelist[6];
1060 "Failed to get vertices" );
1062 "Failed to get elements" );
1063 velist[0] = rintxverts.
size();
1064 velist[1] = rintxelems.
size();
1068 "Failed to get vertices" );
1070 "Failed to get elements" );
1071 velist[2] = bintxverts.
size();
1072 velist[3] = bintxelems.
size();
1080 runCtx->
timer_push(
"setup the intersector" );
1087 #ifdef MOAB_HAVE_MPI
1088 mbintx->set_parallel_comm( pcomm );
1091 "Failed to find max edges" );
1093 #ifdef MOAB_HAVE_MPI
1096 "Failed to build processor euler boxes" );
1101 runCtx->
timer_push(
"communicate the mesh" );
1108 "Failed to construct covering set" );
1115 "Failed to get vertices" );
1117 "Failed to get elements" );
1118 velist[4] = cintxverts.
size();
1119 velist[5] = cintxelems.
size();
1122 MPI_Reduce( velist, gvelist, 6, MPI_UINT64_T, MPI_SUM, 0, MPI_COMM_WORLD );
1126 for(
int i = 0; i < 6; i++ )
1127 gvelist[i] = velist[i];
1134 outputFormatter.
printf( 0,
"The covering set contains %lu vertices and %lu elements \n", gvelist[2],
1142 runCtx->
timer_push(
"compute intersections with MOAB" );
1145 "Can't compute the intersection of meshes on the sphere" );
1156 "Failed to get vertices" );
1158 intxelems.
size(), intxverts.
size() );
1160 double initial_sarea =
1163 double initial_tarea =
1166 double intx_area = areaAdaptor.
area_on_sphere( mbCore, intxset, radius_src );
1168 outputFormatter.
printf( 0,
"mesh areas: source = %12.10f, target = %12.10f, intersection = %12.10f \n",
1169 initial_sarea, initial_tarea, intx_area );
1171 fabs( intx_area - initial_sarea ) / initial_sarea,
1172 fabs( intx_area - initial_tarea ) / initial_tarea );
1179 "Failed to write the intersection" );
1184 runCtx->
timer_push(
"compute weights with the Tempest meshes" );
1186 OfflineMap weightMap;
1187 if( GenerateOfflineMapWithMeshes( *runCtx->
meshes[0], *runCtx->
meshes[1], *runCtx->
meshes[2],
1191 throw std::runtime_error(
"Could not generate offline map with TempestRemap" );
1194 std::map< std::string, std::string > mapAttributes;
1195 #ifdef MOAB_HAVE_NETCDF
1196 if( !runCtx->
skip_io ) weightMap.Write(
"outWeights.nc", mapAttributes );
1198 (void)mapAttributes;
1200 "Writing a TempestRemap OfflineMap (OVERLAP_MEMORY path) requires NetCDF; use the "
1201 "MOAB (OVERLAP_MOAB) workflow, which writes weights via PnetCDF/HDF5 instead" );
1208 #ifdef MOAB_HAVE_MPI
1213 size_t velist[4] = { 0, 0, 0, 0 }, gvelist[4] = { 0, 0, 0, 0 };
1217 "Failed to get vertices" );
1219 "Failed to get elements" );
1221 "Failed to fix degenerate quads" );
1225 "Failed to enforce convexity" );
1228 "Failed to enforce positive orientation" );
1229 velist[0] = srcverts.
size();
1230 velist[1] = srcelems.
size();
1234 "Failed to get vertices" );
1236 "Failed to get elements" );
1238 "Failed to fix degenerate quads" );
1242 "Failed to enforce convexity" );
1245 "Failed to enforce positive orientation" );
1246 velist[2] = tgtverts.
size();
1247 velist[3] = tgtelems.
size();
1260 runCtx->
timer_push(
"construct covering set for intersection" );
1268 std::cout <<
" [WARNING] --gnomonic is ignored when ghost layers are used (nlayers = "
1269 << runCtx->
nlayers <<
"); pass '--ghost 0' to force the gnomonic coverage path.\n";
1274 "Failed to construct covering set" );
1277 #ifdef MOAB_HAVE_MPI
1278 MPI_Reduce( velist, gvelist, 4, MPI_UINT64_T, MPI_SUM, 0, MPI_COMM_WORLD );
1280 for(
int i = 0; i < 4; i++ )
1281 gvelist[i] = velist[i];
1298 runCtx->
timer_push(
"setup and compute mesh intersections" );
1300 "Failed to compute mesh intersections" );
1308 #ifdef MOAB_HAVE_MPI
1311 "Failed to get ghost overlap entities" );
1314 double dTotalOverlapArea = 0.0;
1318 double local_areas[3] = { 0, 0, 0 },
1319 global_areas[3] = { 0, 0, 0 };
1330 std::vector< int > masks( cells.
size(), 1 );
1334 for(
auto it = cells.
begin(); it != cells.
end(); ++it, ++idx )
1335 if( !masks[idx] ) maskedCells.
insert( *it );
1345 local_areas[0] = area_unmasked( runCtx->
meshsets[0], radius_src );
1346 local_areas[1] = area_unmasked( runCtx->
meshsets[1], radius_dest );
1351 "Failed to get overlap elements" );
1352 ownedOverlapElems =
moab::subtract( ownedOverlapElems, ghostOverlapElems );
1355 "Can't create owned overlap meshset" );
1357 "Can't add owned overlap elements" );
1358 local_areas[2] = areaAdaptor.
area_on_sphere( mbCore, ownedOverlapSet, radius_src );
1362 #ifdef MOAB_HAVE_MPI
1363 MPI_Allreduce( &local_areas[0], &global_areas[0], 3, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD );
1365 global_areas[0] = local_areas[0];
1366 global_areas[1] = local_areas[1];
1367 global_areas[2] = local_areas[2];
1372 "initial area: source mesh = %12.14f, target mesh = "
1373 "%12.14f, overlap mesh = %12.14f\n",
1374 global_areas[0], global_areas[1], global_areas[2] );
1376 fabs( global_areas[0] - global_areas[2] ) / global_areas[0],
1377 fabs( global_areas[1] - global_areas[2] ) / global_areas[1] );
1385 "regional mesh: overlap covers %6.2f%% of source and %6.2f%% of "
1387 100.0 * global_areas[2] / global_areas[0],
1388 100.0 * global_areas[2] / global_areas[1] );
1391 dTotalOverlapArea = global_areas[2];
1403 #ifdef MOAB_HAVE_MPI
1413 std::stringstream filename;
1414 filename <<
"aug_overlap" << runCtx->pcomm->rank() <<
".h5m";
1416 "Failed to write the overlap set" );
1423 #ifdef MOAB_HAVE_MPI
1425 if( nprocs > 1 && !runCtx->
skip_io )
1427 std::stringstream filename;
1428 filename <<
"writable_intx_" << runCtx->pcomm->rank() <<
".h5m";
1430 "Failed to write the writable overlap set" );
1435 size_t lastindex = runCtx->
intxFilename.find_last_of(
"." );
1437 sstr << runCtx->
intxFilename.substr( 0, lastindex ) <<
".h5m";
1439 std::cout <<
"Writing out the MOAB intersection mesh file to " << sstr.str() << std::endl;
1445 "Failed to write the writable overlap set" );
1452 if( !runCtx->
proc_id ) std::cout << std::endl;
1454 runCtx->
timer_push(
"setup computation of weights" );
1459 runCtx->
timer_push(
"compute weights with TempestRemap" );
1467 "Failed to generate remapping weights" );
1476 const double dNormalTolerance = 1.0E-8;
1477 const double dStrictTolerance = 1.0E-12;
1479 dNormalTolerance, dStrictTolerance, dTotalOverlapArea );
1484 std::map< std::string, std::string > attrMap;
1486 attrMap[
"Title"] =
"MOAB-TempestRemap (mbtempest) Offline Regridding Weight Generator";
1487 attrMap[
"normalization"] =
"ovarea";
1488 attrMap[
"remap_options"] = runCtx->
mapOptions.strMethod;
1495 attrMap[
"concave_a"] = runCtx->
mapOptions.fSourceConcave ?
"true" :
"false";
1498 attrMap[
"concave_b"] = runCtx->
mapOptions.fTargetConcave ?
"true" :
"false";
1499 attrMap[
"bubble"] = runCtx->
mapOptions.fNoBubble ?
"false" :
"true";
1500 attrMap[
"history"] = historyStr;
1506 "Failed writing the parallel map to disk" );
1513 bool userVariable =
false;
1534 runCtx->
timer_push(
"describe a solution on source grid" );
1538 "AnalyticalSolnSrcExact",
1540 "Failed to define analytical solution on source grid" );
1549 runCtx->
timer_push(
"describe a solution on target grid" );
1552 testFunction, &tgtProjectedFunction,
"ProjectedSolnTgt" ),
1553 "Failed to define analytical solution on target grid" );
1559 "Failed to get analytical solution on source grid" );
1561 tgtProjectedFunction,
1563 "Failed to get projected solution on target grid" );
1570 "Failed to write the source mesh with solution tag" );
1573 runCtx->
timer_push(
"compute solution projection on target grid" );
1576 "Failed to apply weights" );
1583 "Failed to write the target mesh with projected solution tag" );
1593 "Failed to get entities by dimension" );
1598 "Failed to get entities by dimension" );
1600 std::vector< int > globIds( tgtEntities.
size() );
1601 std::vector< double > vals( tgtEntities.
size() );
1604 "Failed to get projected solution tag" );
1608 "Failed to get projected solution" );
1610 fs.open( runCtx->
baselineFile.c_str(), std::fstream::out );
1611 fs << std::setprecision( 15 );
1612 for(
size_t i = 0; i < tgtEntities.
size(); i++ )
1613 fs << globIds[i] <<
" " << vals[i] <<
"\n";
1621 "Failed to write the source mesh with solution tag" );
1628 runCtx->
timer_push(
"compute error metrics against analytical solution on target grid" );
1629 std::map< std::string, double > errMetrics;
1631 tgtProjectedFunction, errMetrics,
true ),
1632 "Failed to compute error metrics" );
1646 #ifdef MOAB_HAVE_MPI
1651 catch(
const std::exception& e )
1653 std::cerr <<
"[mbtempest] Fatal error: " << e.what() << std::endl;
1654 #ifdef MOAB_HAVE_MPI
1655 MPI_Abort( MPI_COMM_WORLD, 1 );
1661 std::cerr <<
"[mbtempest] Fatal: unknown exception caught" << std::endl;
1662 #ifdef MOAB_HAVE_MPI
1663 MPI_Abort( MPI_COMM_WORLD, 1 );
1675 #define TR_CHK_SET_ERR( err, msg ) \
1678 std::cout << "MOAB-TempestRemap Failure. ErrorCode (" << ( err ) << ") "; \
1679 MB_CHK_SET_ERR( moab::MB_FAILURE, msg ); \
1682 #ifdef MOAB_HAVE_NETCDF
1688 using namespace moab;
1700 "Failed to load MOAB Source mesh" );
1704 "Failed to load MOAB Target mesh" );
1709 "Failed to generate TempestRemap OverlapMesh" );
1711 remapper.
SetMesh( Remapper::OverlapMesh, tempest_mesh );
1720 using namespace moab;
1730 constexpr
double radius_src = 1.0;
1731 constexpr
double radius_dest = 1.0;
1735 std::vector< int > metadata;
1748 additional_read_opts_tgt.c_str() ),
1749 "Failed to load MOAB Target mesh" );
1751 #ifdef MOAB_HAVE_MPI
1755 Range beforeGhost, afterGhost;
1758 ctx.pcomm->set_debug_verbosity( 5 );
1760 "Failed to exchange ghost cells for MOAB Target mesh" );
1761 ctx.pcomm->set_debug_verbosity( 0 );
1764 std::cout << ctx.
proc_id <<
": N(before) = " << beforeGhost.
size() <<
", N(after) = " << afterGhost.
size()
1767 std::vector< Tag > taglist;
1771 "Failed to get global dofs tag for MOAB Target mesh" );
1772 taglist.push_back( gdofTag );
1773 MB_CHK_SET_ERR( ctx.pcomm->exchange_tags( taglist, taglist, afterGhost ),
1774 "Failed to exchange global dofs for MOAB Target mesh" );
1793 if( !metadata.empty() )
1795 remapper.
SetMeshType( Remapper::TargetMesh, metadata );
1799 "Failed to preprocess MOAB Target mesh" );
1804 std::vector< int > metadata;
1806 #ifdef MOAB_HAVE_MPI
1810 additional_read_opts_src =
1811 additional_read_opts_src +
"PARALLEL_COMM=" + std::to_string( ctx.pcomm->get_id() ) +
";";
1815 additional_read_opts_src.c_str() ),
1816 "Failed to load MOAB Source mesh" );
1818 if( !metadata.empty() )
1820 remapper.
SetMeshType( Remapper::SourceMesh, metadata );
1824 "Failed to preprocess MOAB Source mesh" );
1831 "Failed to convert MOAB Source mesh to TempestRemap mesh" );
1835 "Failed to convert MOAB Target mesh to TempestRemap mesh" );
1842 #ifdef MOAB_HAVE_NETCDF
1847 ctx.
timer_push(
"create Tempest OverlapMesh" );
1849 "NetCDF4",
"exact",
true ),
1850 "Failed to create Tempest OverlapMesh" );
1854 ctx.
meshes.push_back( tempest_mesh );
1871 const size_t dot = outFile.find_last_of(
"." );
1874 const std::string ext = outFile.substr(
dot + 1 );
1881 ctx.
timer_push(
"convert TempestRemap mesh to MOAB format" );
1883 "Failed to convert TempestRemap mesh to MOAB format" );
1891 "Failed to fix degenerate quads in converted mesh" );
1892 ctx.
timer_push(
"write MOAB mesh to h5m file" );
1894 "Failed to write MOAB mesh to h5m file" );
1906 const size_t dot = trFilename.find_last_of(
"." );
1907 if(
dot != std::string::npos && trFilename.substr(
dot + 1 ) ==
"h5m" )
1908 trFilename = trFilename.substr( 0,
dot ) +
".g";
1910 ctx.
timer_push(
"generate ICO mesh with TempestRemap" );
1912 "Failed to generate ICO mesh with TempestRemap" );
1916 ctx.
meshes.push_back( tempest_mesh );
1919 "Failed to convert and write MOAB mesh" );
1927 const size_t dot = trFilename.find_last_of(
"." );
1928 if(
dot != std::string::npos && trFilename.substr(
dot + 1 ) ==
"h5m" )
1929 trFilename = trFilename.substr( 0,
dot ) +
".g";
1931 ctx.
timer_push(
"generate RLL mesh with TempestRemap" );
1936 false,
false,
false,
1944 "Failed to generate RLL mesh with TempestRemap" );
1948 ctx.
meshes.push_back( tempest_mesh );
1951 "Failed to convert and write MOAB mesh" );
1960 const size_t dot = trFilename.find_last_of(
"." );
1961 if(
dot != std::string::npos && trFilename.substr(
dot + 1 ) ==
"h5m" )
1962 trFilename = trFilename.substr( 0,
dot ) +
".g";
1964 ctx.
timer_push(
"generate CS mesh with TempestRemap" );
1966 "Failed to generate CS mesh with TempestRemap" );
1970 ctx.
meshes.push_back( tempest_mesh );
1974 "Failed to convert and write MOAB mesh" );
1991 using namespace moab;
2000 case RemapperType::OVERLAP_FILES:
2001 #ifdef MOAB_HAVE_NETCDF
2003 return handleOverlapFiles( ctx, tempest_mesh );
2006 "OVERLAP_FILES mode requires NetCDF (TempestRemap file-based overlap "
2007 "generation); build with NetCDF or use OVERLAP_MOAB mode instead" );
2010 case RemapperType::OVERLAP_MEMORY:
2011 #ifdef MOAB_HAVE_NETCDF
2014 return handleOverlapMemory( ctx, remapper, tempest_mesh );
2017 "OVERLAP_MEMORY mode requires NetCDF (TempestRemap-native mesh loading); "
2018 "build with NetCDF or use OVERLAP_MOAB mode instead" );
2021 case RemapperType::OVERLAP_MOAB:
2026 case RemapperType::ICO:
2030 case RemapperType::RLL:
2039 catch(
const std::exception& e )
2041 std::cerr <<
"Error in CreateTempestMesh: " << e.what() <<
"\n";
2087 const double cosLat = std::cos( dLat );
2088 return 2.0 + cosLat * cosLat * std::cos( 2.0 * dLon );
2100 const double sin2Lat = std::sin( 2.0 * dLat );
2102 sin2Lat * sin2Lat * sin2Lat * sin2Lat * sin2Lat * sin2Lat * sin2Lat * sin2Lat * std::cos( 16.0 * dLon );
2118 const double dSinT = std::sin( dLat );
2119 const double dCosT = std::cos( dLat );
2121 const double dTrm = dCosT * std::cos( dLon -
VORTEX_LON0 );
2122 const double dX = dSinC * dTrm - dCosC * dSinT;
2123 const double dY = dCosT * std::sin( dLon -
VORTEX_LON0 );
2124 const double dZ = dSinC * dSinT + dCosC * dTrm;
2127 double dNewLon = std::atan2( dY, dX );
2130 dNewLon += 2.0 * M_PI;
2132 const double dNewLat = std::asin( dZ );
2135 const double dRho =
VORTEX_R0 * std::cos( dNewLat );
2136 const double dVt = 3.0 * std::sqrt( 3.0 ) / 2.0 / std::cosh( dRho ) / std::cosh( dRho ) * std::tanh( dRho );
2139 const double dOmega = ( dRho == 0.0 ) ? 0.0 : ( dVt / dRho );
2142 return ( 1.0 - std::tanh( dRho /
VORTEX_D * std::sin( dNewLon - dOmega *
VORTEX_T ) ) );