143 std::unique_ptr< moab::CpuTimer >
timer;
161 :
mbcore( icore ), pcomm( p_pcomm ),
proc_id( p_pcomm ? p_pcomm->rank() : 0 ),
164 if( !icore )
throw std::invalid_argument(
"MOAB Core instance cannot be null" );
165 if( !p_pcomm )
throw std::invalid_argument(
"ParallelComm cannot be null in MPI mode" );
183 timer = std::make_unique< moab::CpuTimer >();
218 double avgElapsed = locElapsed;
219 double maxElapsed = locElapsed;
222 MPI_Reduce( &locElapsed, &maxElapsed, 1, MPI_DOUBLE, MPI_MAX, 0, pcomm->
comm() );
223 MPI_Reduce( &locElapsed, &avgElapsed, 1, MPI_DOUBLE, MPI_SUM, 0, pcomm->
comm() );
229 std::cout <<
"[LOG] Time taken to " <<
opName <<
": max = " << maxElapsed <<
", avg = " << avgElapsed
246 std::string expectedFName =
"output.exo";
247 std::string expectedMethod =
"fv";
248 std::string expectedFVMethod =
"none";
249 std::string expectedDofTagName =
"GLOBAL_ID";
250 int expectedOrder = 1;
252 int nlayer_input = -1;
253 bool version_info =
false;
258 std::cout <<
"Command line options provided to mbtempest:\n ";
259 for(
int i = 0; i < argc; ++i )
261 std::cout << argv[i] <<
" ";
263 std::cout <<
"\n" << std::endl;
267 ProgOptions opts(
"mbtempest - A mesh generation and remapping tool" );
270 opts.
addOpt<
int >(
"type,t",
271 "Type of mesh (default=CS; Choose from [CS=0, RLL=1, ICO=2, OVERLAP_FILES=3, "
272 "OVERLAP_MEMORY=4, OVERLAP_MOAB=5])",
275 opts.
addOpt<
int >(
"res,r",
"Resolution of the mesh (default=5)", &
blockSize );
277 opts.
addOpt<
void >(
"dual,d",
"Output the dual of the mesh (relevant only for ICO mesh type)", &
computeDual );
279 opts.
addOpt< std::string >(
"file,f",
"Output computed mesh or remapping weights to specified filename",
283 opts.
addOpt< std::string >(
284 "load,l",
"Input mesh filenames for source and target meshes. (relevant only when computing weights)",
287 opts.
addOpt<
void >(
"advfront,a",
288 "Use the advancing front intersection instead of the Kd-tree based algorithm to compute "
289 "mesh intersections.",
292 opts.
addOpt< std::string >(
"intx,i",
"Output TempestRemap intersection mesh filename", &
intxFilename );
296 "Compute and output the weights using the overlap mesh (generally relevant only for OVERLAP mesh)",
301 "verbose,v",
"Print verbose diagnostic messages during intersection and map computation (default=false)",
304 opts.
addOpt< std::string >(
"method,m",
"Discretization method for the source and target solution fields",
307 opts.
addOpt<
int >(
"order,o",
"Discretization orders for the source and target solution fields",
310 opts.
addOpt< std::string >(
"global_id,g",
311 "Tag name that contains the global DoF IDs for source and target solution fields",
312 &expectedDofTagName );
315 opts.
addOpt< std::string >(
"fvmethod",
316 "Sub-type method for FV-FV projections (invdist, delaunay, bilin, intbilin, "
317 "intbilingb, none. Default: none)",
321 "noconserve",
"Do not apply conservation to the resultant weights (relevant only when computing weights)",
325 "volumetric",
"Apply a volumetric projection to compute the weights (relevant only when computing weights)",
330 opts.
addOpt<
void >(
"skip_output",
"For performance studies, skip all I/O operations.", &
skip_io );
332 opts.
addOpt<
void >(
"gnomonic",
"Use Gnomonic plane projections to compute coverage mesh.",
335 opts.
addOpt<
void >(
"enforce_convexity",
"Check convexity of input meshes to compute mesh intersections",
338 opts.
addOpt<
void >(
"nobubble",
"Do not use bubble on interior of spectral element nodes",
343 "Use sparse solver for constraints when we have high-valence (typical with high-res RLL mesh)",
348 "At least one of the meshes is a regionally refined grid (relevant to accelerate intersection computation)",
351 opts.
addOpt<
void >(
"checkmap",
"Check the generated map for conservation and consistency", &
fCheck );
353 opts.
addOpt<
void >(
"verify",
354 "Verify the accuracy of the maps by projecting analytical functions from source to target "
355 "grid by applying the maps",
358 opts.
addOpt< std::string >(
"var",
359 "Tag name of the variable to use in the verification study (error metrics for user "
360 "defined variables may not be available)",
363 opts.
addOpt<
int >(
"monotonicity",
"Ensure monotonicity in the weight generation. Options=[0,1,2,3]",
366 opts.
addOpt<
int >(
"ghost",
367 "Number of ghost layers in coverage mesh (overrides automatic selection: 0 for FV order 1, "
368 "p+1 for FV order p>1)",
371 opts.
addOpt<
double >(
"boxeps",
"The tolerance for boxes (default=1e-7)", &
boxeps );
373 opts.
addOpt<
int >(
"limiter",
"Apply nonlinear filter after linear map application", &useCAAS );
377 opts.
addOpt<
void >(
"manual",
"Show documentation about usage with examples" );
379 opts.
addOpt<
void >(
"version",
"Show version information", &version_info );
387 if( this->proc_id == 0 )
396 if( this->proc_id == 0 )
398 std::cout <<
"mbtempest is part of the MOAB library version " << std::string(
MOAB_PACKAGE_VERSION )
451 if( !expectedFName.empty() )
453 this->inFilenames = { expectedFName };
459 this->disc_orders = { expectedOrder, expectedOrder };
460 this->disc_methods = { expectedMethod, expectedMethod };
461 this->doftag_names = { expectedDofTagName, expectedDofTagName };
467 this->mapOptions.nPin = this->disc_orders[0];
468 this->mapOptions.nPout = this->disc_orders[1];
469 this->mapOptions.fSourceConcave =
false;
470 this->mapOptions.fTargetConcave =
false;
471 this->mapOptions.strMethod =
"";
498 const size_t last_dot = filename.find_last_of(
"." );
499 if( last_dot == std::string::npos )
504 const std::string extension = filename.substr( last_dot + 1 );
507 if( extension ==
"h5m" )
509 return "PARALLEL=READ_PART;PARTITION=PARALLEL_PARTITION;PARALLEL_RESOLVE_SHARED_ENTS;";
513 if( extension ==
"nc" )
516 #ifdef MOAB_HAVE_ZOLTAN
517 std::string netcdf_options =
"PARALLEL=READ_PART;PARTITION_METHOD=RCBZOLTAN;";
519 std::string netcdf_options =
"PARALLEL=READ_PART;PARTITION_METHOD=TRIVIAL;";
524 NcFile
ncFile( filename.c_str(), NcFile::ReadOnly );
528 return netcdf_options;
532 int format_flags = 0;
533 for(
int i = 0; i <
ncFile.num_dims(); i++ )
535 const std::string dim_name =
ncFile.get_dim( i )->name();
537 if( dim_name ==
"grid_size" || dim_name ==
"grid_corners" || dim_name ==
"grid_rank" )
541 else if( dim_name ==
"nodeCount" || dim_name ==
"elementCount" || dim_name ==
"maxNodePElement" )
545 else if( dim_name ==
"nCells" || dim_name ==
"nEdges" || dim_name ==
"nVertices" ||
546 dim_name ==
"vertexDegree" )
553 if( format_flags & 2 )
555 netcdf_options +=
"PARALLEL_RESOLVE_SHARED_ENTS;VARIABLE=;";
557 else if( format_flags & 1 )
559 netcdf_options +=
"";
561 else if( format_flags & 4 )
563 netcdf_options +=
"PARALLEL_RESOLVE_SHARED_ENTS;NO_EDGES;NO_MIXED_ELEMENTS;VARIABLE=;";
569 int line_size = netcdf_options.size();
570 MPI_Bcast( &line_size, 1, MPI_INT, 0, MPI_COMM_WORLD );
573 netcdf_options.resize( line_size );
575 MPI_Bcast(
const_cast< char*
>( netcdf_options.data() ), line_size, MPI_CHAR, 0, MPI_COMM_WORLD );
578 return netcdf_options;
582 return "PARALLEL=BCAST_DELETE;PARTITION=TRIVIAL;PARALLEL_RESOLVE_SHARED_ENTS;";
592 if( this->proc_id != 0 )
return;
594 std::cout <<
"MOAB-Tempest: A mesh generation and remapping tool\n"
595 <<
"==================================================\n\n"
596 <<
"Usage: " << progName <<
" [OPTIONS]\n\n"
597 <<
"Mesh Generation Options:\n"
598 <<
" -t, --type TYPE Type of mesh to generate (required for mesh generation):\n"
599 <<
" 0 = Cubed-Sphere (CS)\n"
600 <<
" 1 = Regular Latitude-Longitude (RLL)\n"
601 <<
" 2 = Icosahedral (ICO)\n"
602 <<
" 3 = TempestRemap overlap (thin interface))\n"
603 <<
" 4 = MOAB with TempestRemap overlap in memory\n"
604 <<
" 5 = Parallel handling of Overlap meshes with MOAB (recommended)\n\n"
605 <<
" -r, --res N Resolution (number of elements on edge, default: 10)\n"
606 <<
" -f, --file FILE Output filename (default: output.h5m)\n\n"
607 <<
"Discretization Options:\n"
608 <<
" -m, --method METHOD Discretization method (default: fv):\n"
609 <<
" fv = Finite Volume\n"
610 <<
" cgll = Continuous Galerkin with Legendre-Gauss-Lobatto\n"
611 <<
" dgll = Discontinuous Galerkin with Legendre-Gauss-Lobatto\n\n"
612 <<
" -o, --order N Discretization order (default: 1, range: 1-4)\n\n"
613 <<
"Remapping Options:\n"
614 <<
" --mono N Monotonicity constraints (default: 0):\n"
615 <<
" 0 = No monotonicity\n"
616 <<
" 1 = Basic monotonicity\n"
617 <<
" 2 = Full monotonicity with bounds\n"
618 <<
" 3 = Strict monotonicity\n\n"
619 <<
" --limiter TYPE Nonlinear limiting (optional):\n"
620 <<
" none = No limiting (default)\n"
621 <<
" global = Global CAAS limiting\n"
622 <<
" local = Localized CAAS limiting\n"
623 <<
" qlt = Quasi-Local Tree-based limiting\n\n"
624 <<
"Input/Output Options:\n"
625 <<
" -l, --load FILE Load input mesh file (use twice for source and target)\n"
626 <<
" -i, --global_id TAG Global ID tag name (default: GLOBAL_ID)\n"
627 <<
" --diagnostics Print diagnostic information\n\n"
628 <<
"Miscellaneous Options:\n"
629 <<
" --manual Show this help message and exit\n"
630 <<
" --version Show version information\n\n"
632 <<
" # Generate a cubed-sphere mesh with resolution 25\n"
633 <<
" " << progName <<
" --type 0 --res 25 -f cs_mesh.h5m\n\n"
634 <<
" # Generate a latitude-longitude mesh with resolution 180\n"
635 <<
" " << progName <<
" --type 1 --res 180 -f rll_mesh.h5m\n\n"
636 <<
" # Create a map between two meshes with order 4\n"
637 <<
" " << progName <<
" --type 5 --load source_mesh.h5m --load target_mesh.h5m \\\n"
638 <<
" --method cgll --order 4 --global_id GLOBAL_DOFS \\\n"
639 <<
" --method fv --order 1 --limiter 1 --file map.nc\n";
651 return "Cubed-Sphere";
653 return "Latitude-Longitude";
655 return "Icosahedral";
657 return "Overlap (files)";
659 return "Overlap (memory)";
661 return "Overlap (MOAB)";
678 std::vector< std::string > inputFiles;
680 if( !inputFiles.empty() )
682 this->inFilenames = inputFiles;
683 if( this->inFilenames.size() != 2 )
685 throw std::runtime_error(
"Exactly two input filenames must be provided with -l/--load" );
690 std::vector< int > orders;
692 if( !orders.empty() )
694 this->disc_orders = orders;
695 if( this->disc_orders.size() == 1 )
697 this->disc_orders.push_back( this->disc_orders[0] );
699 else if( this->disc_orders.size() != 2 )
701 throw std::runtime_error(
"Must specify 1 or 2 values for order (source [target])" );
704 for(
const auto& order : this->disc_orders )
706 if( order < 1 || order > 4 )
708 throw std::runtime_error(
"Discretization order must be between 1 and 4" );
714 std::vector< std::string > methods;
716 if( !methods.empty() )
718 this->disc_methods = methods;
719 if( this->disc_methods.size() == 1 )
722 this->disc_methods.push_back( this->disc_methods[0] );
724 else if( this->disc_methods.size() != 2 )
726 throw std::runtime_error(
"Must specify 1 or 2 values for method (source [target])" );
730 for(
const auto& method : this->disc_methods )
732 if( method !=
"fv" && method !=
"cgll" && method !=
"dgll" && method !=
"pcloud" )
734 throw std::runtime_error(
"Invalid method '" + method +
"'. Must be one of: fv, cgll, dgll" );
740 std::vector< std::string > tags;
744 this->doftag_names = tags;
745 if( this->doftag_names.size() == 1 )
748 this->doftag_names.push_back( this->doftag_names[0] );
750 else if( this->doftag_names.size() != 2 )
752 throw std::runtime_error(
"Must specify 1 or 2 values for DOF tag names (source [target])" );
758 if( opts.
getOpt(
"file,f", &outFile ) )
770 if( this->proc_id != 0 )
return;
772 constexpr
int width = 60;
774 std::cout << std::string( width,
'=' ) <<
"\n";
775 std::cout <<
" MOAB-TempestRemap Runtime Configuration " <<
"\n";
776 std::cout << std::string( width,
'=' );
781 if( !this->inFilenames.empty() )
783 std::cout <<
"\n\nInput Files:";
784 std::cout <<
"\n Source mesh: " << this->inFilenames[0];
785 std::cout <<
"\n Target mesh: " << this->inFilenames[1];
788 std::cout <<
"\n\nOutput Files:";
790 std::cout <<
"\n Intersection mesh: "
796 std::cout <<
"\n\nMesh Configuration:";
799 std::cout <<
"\n Resolution: " << this->
blockSize;
801 std::cout <<
"\n Compute dual: " << ( this->
computeDual ?
"Yes" :
"No" );
806 std::cout <<
"\n Intersection algorithm: " << ( this->
kdtreeSearch ?
"KdTree search" :
"Advancing front" );
809 std::cout <<
"\n\nDiscretization:";
810 std::cout <<
"\n Source: " << this->disc_methods[0] <<
" (order " << this->disc_orders[0]
812 std::cout <<
"\n Target: " << this->disc_methods[1] <<
" (order " << this->disc_orders[1]
816 std::cout <<
"\n\nRemapping Options:";
817 std::cout <<
"\n Method: "
818 << ( this->mapOptions.strMethod.empty() ?
"Default" : this->mapOptions.strMethod );
820 std::cout <<
"\n Volumetric: " << ( this->
fVolumetric ?
"Yes" :
"No" );
821 std::cout <<
"\n Check consistency: " << ( this->
fCheck ?
"Yes" :
"No" );
822 std::cout <<
"\n Skip intersection: " << ( this->
skip_intersection ?
"Yes" :
"No" );
826 std::cout <<
"\n\nParallel Configuration:";
827 std::cout <<
"\n MPI Processes: " << this->
n_procs;
830 std::cout <<
"\n\n" << std::string( width,
'=' ) <<
"\n\n";
840 this->mapOptions.nPin = ( this->disc_orders.empty() ) ? 1 : this->disc_orders[0];
841 this->mapOptions.nPout = ( this->disc_orders.size() > 1 ) ? this->disc_orders[1] : this->mapOptions.nPin;
844 this->mapOptions.fSourceConcave =
false;
845 this->mapOptions.fTargetConcave =
false;
846 this->mapOptions.strMethod.clear();
851 this->mapOptions.strMethod = this->
fvMethod +
";";
852 this->mapOptions.fNoConservation =
true;
860 this->mapOptions.fMonotone =
false;
863 this->mapOptions.strMethod +=
"mono3;";
864 this->mapOptions.fMonotone =
true;
867 this->mapOptions.strMethod +=
"mono2;";
868 this->mapOptions.fMonotone =
true;
872 this->mapOptions.fMonotone =
true;
877 this->mapOptions.fNoCorrectAreas =
false;
878 this->mapOptions.fNoCheck = !this->
fCheck;
883 this->mapOptions.strMethod +=
"volumetric;";
897 this->
nlayers = ( this->mapOptions.nPin > 1 ) ? this->mapOptions.nPin + 1 : 0;
901 if( nlayer_input >= 0 )
907 this->mapOptions.strOutputMapFile = this->
outFilename;
908 this->mapOptions.strOutputFormat =
"Netcdf4";
914 static inline constexpr
double sample_constant(
double dLon,
double dLat ) noexcept;
922 int main(
int argc,
char* argv[] )
926 NcError
error( NcError::verbose_nonfatal );
927 std::stringstream sstr;
928 std::string historyStr;
932 MPI_Init( &argc, &argv );
933 MPI_Comm_rank( MPI_COMM_WORLD, &
proc_id );
934 MPI_Comm_size( MPI_COMM_WORLD, &nprocs );
939 if(
nullptr == mbCore )
945 for(
int ia = 0; ia < argc; ++ia )
946 historyStr += std::string( argv[ia] ) +
" ";
953 const char* writeOptions = ( nprocs > 1 ?
"PARALLEL=WRITE_PART" :
"" );
956 const char* writeOptions =
"";
960 const double radius_src = 1.0 ;
961 const double radius_dest = 1.0 ;
977 Mesh* tempest_mesh =
new Mesh();
984 assert( runCtx->
meshes.size() == 3 );
997 "Failed to write TempestRemap intersection mesh in MOAB format" );
1001 size_t velist[6], gvelist[6];
1005 "Failed to get vertices" );
1007 "Failed to get elements" );
1008 velist[0] = rintxverts.
size();
1009 velist[1] = rintxelems.
size();
1013 "Failed to get vertices" );
1015 "Failed to get elements" );
1016 velist[2] = bintxverts.
size();
1017 velist[3] = bintxelems.
size();
1025 runCtx->
timer_push(
"setup the intersector" );
1032 #ifdef MOAB_HAVE_MPI
1033 mbintx->set_parallel_comm( pcomm );
1036 "Failed to find max edges" );
1038 #ifdef MOAB_HAVE_MPI
1041 "Failed to build processor euler boxes" );
1046 runCtx->
timer_push(
"communicate the mesh" );
1053 "Failed to construct covering set" );
1060 "Failed to get vertices" );
1062 "Failed to get elements" );
1063 velist[4] = cintxverts.
size();
1064 velist[5] = cintxelems.
size();
1067 MPI_Reduce( velist, gvelist, 6, MPI_UINT64_T, MPI_SUM, 0, MPI_COMM_WORLD );
1071 for(
int i = 0; i < 6; i++ )
1072 gvelist[i] = velist[i];
1079 outputFormatter.
printf( 0,
"The covering set contains %lu vertices and %lu elements \n", gvelist[2],
1087 runCtx->
timer_push(
"compute intersections with MOAB" );
1090 "Can't compute the intersection of meshes on the sphere" );
1101 "Failed to get vertices" );
1103 intxelems.
size(), intxverts.
size() );
1105 double initial_sarea =
1108 double initial_tarea =
1111 double intx_area = areaAdaptor.
area_on_sphere( mbCore, intxset, radius_src );
1113 outputFormatter.
printf( 0,
"mesh areas: source = %12.10f, target = %12.10f, intersection = %12.10f \n",
1114 initial_sarea, initial_tarea, intx_area );
1116 fabs( intx_area - initial_sarea ) / initial_sarea,
1117 fabs( intx_area - initial_tarea ) / initial_tarea );
1124 "Failed to write the intersection" );
1129 runCtx->
timer_push(
"compute weights with the Tempest meshes" );
1131 OfflineMap weightMap;
1132 if( GenerateOfflineMapWithMeshes( *runCtx->
meshes[0], *runCtx->
meshes[1], *runCtx->
meshes[2],
1136 throw std::runtime_error(
"Could not generate offline map with TempestRemap" );
1139 std::map< std::string, std::string > mapAttributes;
1140 if( !runCtx->
skip_io ) weightMap.Write(
"outWeights.nc", mapAttributes );
1146 #ifdef MOAB_HAVE_MPI
1151 size_t velist[4] = { 0, 0, 0, 0 }, gvelist[4] = { 0, 0, 0, 0 };
1155 "Failed to get vertices" );
1157 "Failed to get elements" );
1159 "Failed to fix degenerate quads" );
1163 "Failed to enforce convexity" );
1166 "Failed to enforce positive orientation" );
1167 velist[0] = srcverts.
size();
1168 velist[1] = srcelems.
size();
1172 "Failed to get vertices" );
1174 "Failed to get elements" );
1176 "Failed to fix degenerate quads" );
1180 "Failed to enforce convexity" );
1183 "Failed to enforce positive orientation" );
1184 velist[2] = tgtverts.
size();
1185 velist[3] = tgtelems.
size();
1198 runCtx->
timer_push(
"construct covering set for intersection" );
1203 "Failed to construct covering set" );
1206 #ifdef MOAB_HAVE_MPI
1207 MPI_Reduce( velist, gvelist, 4, MPI_UINT64_T, MPI_SUM, 0, MPI_COMM_WORLD );
1209 for(
int i = 0; i < 4; i++ )
1210 gvelist[i] = velist[i];
1227 runCtx->
timer_push(
"setup and compute mesh intersections" );
1229 "Failed to compute mesh intersections" );
1237 #ifdef MOAB_HAVE_MPI
1240 "Failed to get ghost overlap entities" );
1243 double dTotalOverlapArea = 0.0;
1247 double local_areas[3] = { 0, 0, 0 },
1248 global_areas[3] = { 0, 0, 0 };
1259 std::vector< int > masks( cells.
size(), 1 );
1263 for(
auto it = cells.
begin(); it != cells.
end(); ++it, ++idx )
1264 if( !masks[idx] ) maskedCells.
insert( *it );
1274 local_areas[0] = area_unmasked( runCtx->
meshsets[0], radius_src );
1275 local_areas[1] = area_unmasked( runCtx->
meshsets[1], radius_dest );
1280 "Failed to get overlap elements" );
1281 ownedOverlapElems =
moab::subtract( ownedOverlapElems, ghostOverlapElems );
1284 "Can't create owned overlap meshset" );
1286 "Can't add owned overlap elements" );
1287 local_areas[2] = areaAdaptor.
area_on_sphere( mbCore, ownedOverlapSet, radius_src );
1291 #ifdef MOAB_HAVE_MPI
1292 MPI_Allreduce( &local_areas[0], &global_areas[0], 3, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD );
1294 global_areas[0] = local_areas[0];
1295 global_areas[1] = local_areas[1];
1296 global_areas[2] = local_areas[2];
1301 "initial area: source mesh = %12.14f, target mesh = "
1302 "%12.14f, overlap mesh = %12.14f\n",
1303 global_areas[0], global_areas[1], global_areas[2] );
1305 fabs( global_areas[0] - global_areas[2] ) / global_areas[0],
1306 fabs( global_areas[1] - global_areas[2] ) / global_areas[1] );
1308 dTotalOverlapArea = global_areas[2];
1320 #ifdef MOAB_HAVE_MPI
1330 std::stringstream filename;
1331 filename <<
"aug_overlap" << runCtx->pcomm->rank() <<
".h5m";
1333 "Failed to write the overlap set" );
1340 #ifdef MOAB_HAVE_MPI
1342 if( nprocs > 1 && !runCtx->
skip_io )
1344 std::stringstream filename;
1345 filename <<
"writable_intx_" << runCtx->pcomm->rank() <<
".h5m";
1347 "Failed to write the writable overlap set" );
1352 size_t lastindex = runCtx->
intxFilename.find_last_of(
"." );
1354 sstr << runCtx->
intxFilename.substr( 0, lastindex ) <<
".h5m";
1356 std::cout <<
"Writing out the MOAB intersection mesh file to " << sstr.str() << std::endl;
1362 "Failed to write the writable overlap set" );
1369 if( !runCtx->
proc_id ) std::cout << std::endl;
1371 runCtx->
timer_push(
"setup computation of weights" );
1376 runCtx->
timer_push(
"compute weights with TempestRemap" );
1384 "Failed to generate remapping weights" );
1393 const double dNormalTolerance = 1.0E-8;
1394 const double dStrictTolerance = 1.0E-12;
1396 dNormalTolerance, dStrictTolerance, dTotalOverlapArea );
1401 std::map< std::string, std::string > attrMap;
1403 attrMap[
"Title"] =
"MOAB-TempestRemap (mbtempest) Offline Regridding Weight Generator";
1404 attrMap[
"normalization"] =
"ovarea";
1405 attrMap[
"remap_options"] = runCtx->
mapOptions.strMethod;
1412 attrMap[
"concave_a"] = runCtx->
mapOptions.fSourceConcave ?
"true" :
"false";
1415 attrMap[
"concave_b"] = runCtx->
mapOptions.fTargetConcave ?
"true" :
"false";
1416 attrMap[
"bubble"] = runCtx->
mapOptions.fNoBubble ?
"false" :
"true";
1417 attrMap[
"history"] = historyStr;
1423 "Failed writing the parallel map to disk" );
1430 bool userVariable =
false;
1451 runCtx->
timer_push(
"describe a solution on source grid" );
1455 "AnalyticalSolnSrcExact",
1457 "Failed to define analytical solution on source grid" );
1466 runCtx->
timer_push(
"describe a solution on target grid" );
1469 testFunction, &tgtProjectedFunction,
"ProjectedSolnTgt" ),
1470 "Failed to define analytical solution on target grid" );
1476 "Failed to get analytical solution on source grid" );
1478 tgtProjectedFunction,
1480 "Failed to get projected solution on target grid" );
1487 "Failed to write the source mesh with solution tag" );
1490 runCtx->
timer_push(
"compute solution projection on target grid" );
1493 "Failed to apply weights" );
1500 "Failed to write the target mesh with projected solution tag" );
1510 "Failed to get entities by dimension" );
1515 "Failed to get entities by dimension" );
1517 std::vector< int > globIds( tgtEntities.
size() );
1518 std::vector< double > vals( tgtEntities.
size() );
1521 "Failed to get projected solution tag" );
1525 "Failed to get projected solution" );
1527 fs.open( runCtx->
baselineFile.c_str(), std::fstream::out );
1528 fs << std::setprecision( 15 );
1529 for(
size_t i = 0; i < tgtEntities.
size(); i++ )
1530 fs << globIds[i] <<
" " << vals[i] <<
"\n";
1538 "Failed to write the source mesh with solution tag" );
1545 runCtx->
timer_push(
"compute error metrics against analytical solution on target grid" );
1546 std::map< std::string, double > errMetrics;
1548 tgtProjectedFunction, errMetrics,
true ),
1549 "Failed to compute error metrics" );
1563 #ifdef MOAB_HAVE_MPI
1568 catch(
const std::exception& e )
1570 std::cerr <<
"[mbtempest] Fatal error: " << e.what() << std::endl;
1571 #ifdef MOAB_HAVE_MPI
1572 MPI_Abort( MPI_COMM_WORLD, 1 );
1578 std::cerr <<
"[mbtempest] Fatal: unknown exception caught" << std::endl;
1579 #ifdef MOAB_HAVE_MPI
1580 MPI_Abort( MPI_COMM_WORLD, 1 );
1592 #define TR_CHK_SET_ERR( err, msg ) \
1595 std::cout << "MOAB-TempestRemap Failure. ErrorCode (" << ( err ) << ") "; \
1596 MB_CHK_SET_ERR( moab::MB_FAILURE, msg ); \
1601 using namespace moab;
1613 "Failed to load MOAB Source mesh" );
1617 "Failed to load MOAB Target mesh" );
1622 "Failed to generate TempestRemap OverlapMesh" );
1624 remapper.
SetMesh( Remapper::OverlapMesh, tempest_mesh );
1632 using namespace moab;
1642 constexpr
double radius_src = 1.0;
1643 constexpr
double radius_dest = 1.0;
1647 std::vector< int > metadata;
1660 additional_read_opts_tgt.c_str() ),
1661 "Failed to load MOAB Target mesh" );
1663 #ifdef MOAB_HAVE_MPI
1667 Range beforeGhost, afterGhost;
1670 ctx.pcomm->set_debug_verbosity( 5 );
1672 "Failed to exchange ghost cells for MOAB Target mesh" );
1673 ctx.pcomm->set_debug_verbosity( 0 );
1676 std::cout << ctx.
proc_id <<
": N(before) = " << beforeGhost.
size() <<
", N(after) = " << afterGhost.
size()
1679 std::vector< Tag > taglist;
1683 "Failed to get global dofs tag for MOAB Target mesh" );
1684 taglist.push_back( gdofTag );
1685 MB_CHK_SET_ERR( ctx.pcomm->exchange_tags( taglist, taglist, afterGhost ),
1686 "Failed to exchange global dofs for MOAB Target mesh" );
1705 if( !metadata.empty() )
1707 remapper.
SetMeshType( Remapper::TargetMesh, metadata );
1711 "Failed to preprocess MOAB Target mesh" );
1716 std::vector< int > metadata;
1718 #ifdef MOAB_HAVE_MPI
1722 additional_read_opts_src =
1723 additional_read_opts_src +
"PARALLEL_COMM=" + std::to_string( ctx.pcomm->get_id() ) +
";";
1727 additional_read_opts_src.c_str() ),
1728 "Failed to load MOAB Source mesh" );
1730 if( !metadata.empty() )
1732 remapper.
SetMeshType( Remapper::SourceMesh, metadata );
1736 "Failed to preprocess MOAB Source mesh" );
1743 "Failed to convert MOAB Source mesh to TempestRemap mesh" );
1747 "Failed to convert MOAB Target mesh to TempestRemap mesh" );
1756 ctx.
timer_push(
"create Tempest OverlapMesh" );
1758 "NetCDF4",
"exact",
true ),
1759 "Failed to create Tempest OverlapMesh" );
1763 ctx.
meshes.push_back( tempest_mesh );
1779 const size_t dot = outFile.find_last_of(
"." );
1782 const std::string ext = outFile.substr(
dot + 1 );
1789 ctx.
timer_push(
"convert TempestRemap mesh to MOAB format" );
1791 "Failed to convert TempestRemap mesh to MOAB format" );
1799 "Failed to fix degenerate quads in converted mesh" );
1800 ctx.
timer_push(
"write MOAB mesh to h5m file" );
1802 "Failed to write MOAB mesh to h5m file" );
1814 const size_t dot = trFilename.find_last_of(
"." );
1815 if(
dot != std::string::npos && trFilename.substr(
dot + 1 ) ==
"h5m" )
1816 trFilename = trFilename.substr( 0,
dot ) +
".g";
1818 ctx.
timer_push(
"generate ICO mesh with TempestRemap" );
1820 "Failed to generate ICO mesh with TempestRemap" );
1824 ctx.
meshes.push_back( tempest_mesh );
1827 "Failed to convert and write MOAB mesh" );
1835 const size_t dot = trFilename.find_last_of(
"." );
1836 if(
dot != std::string::npos && trFilename.substr(
dot + 1 ) ==
"h5m" )
1837 trFilename = trFilename.substr( 0,
dot ) +
".g";
1839 ctx.
timer_push(
"generate RLL mesh with TempestRemap" );
1844 false,
false,
false,
1852 "Failed to generate RLL mesh with TempestRemap" );
1856 ctx.
meshes.push_back( tempest_mesh );
1859 "Failed to convert and write MOAB mesh" );
1868 const size_t dot = trFilename.find_last_of(
"." );
1869 if(
dot != std::string::npos && trFilename.substr(
dot + 1 ) ==
"h5m" )
1870 trFilename = trFilename.substr( 0,
dot ) +
".g";
1872 ctx.
timer_push(
"generate CS mesh with TempestRemap" );
1874 "Failed to generate CS mesh with TempestRemap" );
1878 ctx.
meshes.push_back( tempest_mesh );
1882 "Failed to convert and write MOAB mesh" );
1899 using namespace moab;
1908 case RemapperType::OVERLAP_FILES:
1912 case RemapperType::OVERLAP_MEMORY:
1917 case RemapperType::OVERLAP_MOAB:
1922 case RemapperType::ICO:
1926 case RemapperType::RLL:
1935 catch(
const std::exception& e )
1937 std::cerr <<
"Error in CreateTempestMesh: " << e.what() <<
"\n";
1983 const double cosLat = std::cos( dLat );
1984 return 2.0 + cosLat * cosLat * std::cos( 2.0 * dLon );
1996 const double sin2Lat = std::sin( 2.0 * dLat );
1998 sin2Lat * sin2Lat * sin2Lat * sin2Lat * sin2Lat * sin2Lat * sin2Lat * sin2Lat * std::cos( 16.0 * dLon );
2014 const double dSinT = std::sin( dLat );
2015 const double dCosT = std::cos( dLat );
2017 const double dTrm = dCosT * std::cos( dLon -
VORTEX_LON0 );
2018 const double dX = dSinC * dTrm - dCosC * dSinT;
2019 const double dY = dCosT * std::sin( dLon -
VORTEX_LON0 );
2020 const double dZ = dSinC * dSinT + dCosC * dTrm;
2023 double dNewLon = std::atan2( dY, dX );
2026 dNewLon += 2.0 * M_PI;
2028 const double dNewLat = std::asin( dZ );
2031 const double dRho =
VORTEX_R0 * std::cos( dNewLat );
2032 const double dVt = 3.0 * std::sqrt( 3.0 ) / 2.0 / std::cosh( dRho ) / std::cosh( dRho ) * std::tanh( dRho );
2035 const double dOmega = ( dRho == 0.0 ) ? 0.0 : ( dVt / dRho );
2038 return ( 1.0 - std::tanh( dRho /
VORTEX_D * std::sin( dNewLon - dOmega *
VORTEX_T ) ) );