Mesh Oriented datABase  (version 5.6.0)
An array-based unstructured mesh library
mbtempest.cpp File Reference

MOAB-Tempest: A powerful mesh generation and remapping tool for climate and weather applications. More...

#include <iostream>
#include <iomanip>
#include <cstdlib>
#include <vector>
#include <string>
#include <memory>
#include <sstream>
#include <cassert>
#include "moab/Core.hpp"
#include "moab/IntxMesh/IntxUtils.hpp"
#include "moab/Remapping/TempestRemapper.hpp"
#include "moab/Remapping/TempestOnlineMap.hpp"
#include "moab/ProgOptions.hpp"
#include "moab/CpuTimer.hpp"
#include "DebugOutput.hpp"
+ Include dependency graph for mbtempest.cpp:

Go to the source code of this file.

Classes

class  ToolContext
 Context class for MOAB-TempestRemap tool configuration and state management. More...
 

Namespaces

 anonymous_namespace{mbtempest.cpp}
 Sample functions for testing remapping operations.
 

Macros

#define TR_CHK_SET_ERR(err, msg)
 

Functions

static moab::ErrorCode CreateTempestMesh (ToolContext &ctx, moab::TempestRemapper &remapper, Mesh *tempest_mesh)
 Creates a TempestRemap mesh based on the provided context and mesh type. More...
 
static constexpr double sample_constant (double, double) noexcept
 Constant sample function. More...
 
static double sample_slow_harmonic (double dLon, double dLat) noexcept
 Sample function with slow harmonic variation. More...
 
static double sample_fast_harmonic (double dLon, double dLat) noexcept
 Sample function with fast harmonic variation. More...
 
static double sample_stationary_vortex (double dLon, double dLat) noexcept
 Sample function representing a stationary vortex. More...
 
int main (int argc, char *argv[])
 
moab::ErrorCode anonymous_namespace{mbtempest.cpp}::handleOverlapMOAB (ToolContext &ctx, moab::TempestRemapper &remapper)
 
moab::ErrorCode anonymous_namespace{mbtempest.cpp}::convertAndWriteMOABMesh (ToolContext &ctx, moab::TempestRemapper &remapper, Mesh *tempest_mesh)
 Convert a generated TempestRemap mesh to MOAB format and write as h5m file. More...
 
moab::ErrorCode anonymous_namespace{mbtempest.cpp}::handleICOMesh (ToolContext &ctx, moab::TempestRemapper &remapper, Mesh *tempest_mesh)
 
moab::ErrorCode anonymous_namespace{mbtempest.cpp}::handleRLLMesh (ToolContext &ctx, moab::TempestRemapper &remapper, Mesh *tempest_mesh)
 
moab::ErrorCode anonymous_namespace{mbtempest.cpp}::handleCSMesh (ToolContext &ctx, moab::TempestRemapper &remapper, Mesh *tempest_mesh)
 

Variables

constexpr double anonymous_namespace{mbtempest.cpp}::VORTEX_LON0 = 0.0
 
constexpr double anonymous_namespace{mbtempest.cpp}::VORTEX_LAT0 = 0.6
 
constexpr double anonymous_namespace{mbtempest.cpp}::VORTEX_R0 = 3.0
 
constexpr double anonymous_namespace{mbtempest.cpp}::VORTEX_D = 5.0
 
constexpr double anonymous_namespace{mbtempest.cpp}::VORTEX_T = 6.0
 

Detailed Description

MOAB-Tempest: A powerful mesh generation and remapping tool for climate and weather applications.

Overview

MOAB-Tempest is a command-line tool that provides mesh generation and conservative remapping capabilities for climate and weather modeling. It combines the power of MOAB (Mesh-Oriented datABase) with the TempestRemap library to enable high-performance, parallel mesh generation and remapping operations.

Key Features

  • Generation of various spherical mesh types (Cubed-Sphere, RLL, Icosahedral)
  • Support for high-order discretization methods (FV, CGLL, DGLL)
  • Conservative remapping between different mesh types
  • Parallel processing support via MPI
  • Flexible I/O with support for multiple file formats
  • Built-in analytical functions for testing and validation

Supported Algorithms

  • Mesh Generation:
    • Cubed-Sphere (CS) meshes
    • Regular Latitude-Longitude (RLL) meshes
    • Icosahedral (ICO) meshes
    • Overlap meshes for remapping
  • Remapping Methods:
    • Finite Volume (FV)
    • Continuous Galerkin (CGLL)
    • Discontinuous Galerkin (DGLL)
    • Monotonic and high-order variants

Basic Usage Examples

# Generate a Cubed-Sphere mesh with resolution 25
./mbtempest --type 0 --res 25 --file cubed_sphere_mesh.h5m
# Generate a RLL mesh with resolution 90x180 (lon x lat)
./mbtempest --type 1 --res 90 --file rll_mesh.h5m
# Generate an Icosahedral mesh with resolution 25 (dual mesh)
./mbtempest --type 2 --res 25 --dual --file icosahedral_dual_mesh.h5m
# Compute overlap between two meshes
./mbtempest --type 5 --load mesh1.h5m --load mesh2.h5m intx intersection_mesh.h5m
# Generate a remapping weights file between two meshes: FV to FV (default)
./mbtempest --type 5 --load source_mesh.h5m --load target_mesh.h5m --file weights.nc
# Generate remapping weights file between two meshes: making it explicit (SE to FV)
./mbtempest --type 5 --load source_mesh.h5m --load target_mesh.h5m \
--order 4 --method cgll --global_id GLOBAL_DOFS \
--order 1 --method fv --global_id GLOBAL_ID \
--file weights_se_to_fv.nc

Command Line Options

Run './mbtempest –help' for a complete list of available options.

Notes

  • For parallel execution, use MPI launcher (e.g., mpirun, mpiexec)
  • Output formats: .h5m (MOAB), .nc (NetCDF), .exo (ExodusII)
  • Requires MOAB and TempestRemap libraries
Author
MOAB Development Team
Date
Created: 2023

Definition in file mbtempest.cpp.

Macro Definition Documentation

◆ TR_CHK_SET_ERR

#define TR_CHK_SET_ERR (   err,
  msg 
)
Value:
if( err ) \
{ \
std::cout << "MOAB-TempestRemap Failure. ErrorCode (" << ( err ) << ") "; \
MB_CHK_SET_ERR( moab::MB_FAILURE, msg ); \
}

Definition at line 1675 of file mbtempest.cpp.

Function Documentation

◆ CreateTempestMesh()

static moab::ErrorCode CreateTempestMesh ( ToolContext &  ctx,
moab::TempestRemapper &  remapper,
Mesh *  tempest_mesh 
)
static

Creates a TempestRemap mesh based on the provided context and mesh type.

Parameters
ctxTool context containing configuration and state
remapperTempestRemap instance for mesh operations
tempest_meshOutput parameter for the created mesh
Returns
moab::ErrorCode Status of the operation

Definition at line 1989 of file mbtempest.cpp.

1990 {
1991  using namespace moab;
1992  using RemapperType = moab::TempestRemapper;
1993 
1994  auto& outputFormatter = ctx.outputFormatter;
1995 
1996  try
1997  {
1998  switch( ctx.meshType )
1999  {
2000  case RemapperType::OVERLAP_FILES:
2001 #ifdef MOAB_HAVE_NETCDF
2002  if( !ctx.proc_id ) outputFormatter.printf( 0, "Creating TempestRemap overlap mesh ...\n" );
2003  return handleOverlapFiles( ctx, tempest_mesh );
2004 #else
2005  MB_CHK_SET_ERR( moab::MB_FAILURE,
2006  "OVERLAP_FILES mode requires NetCDF (TempestRemap file-based overlap "
2007  "generation); build with NetCDF or use OVERLAP_MOAB mode instead" );
2008 #endif
2009 
2010  case RemapperType::OVERLAP_MEMORY:
2011 #ifdef MOAB_HAVE_NETCDF
2012  if( !ctx.proc_id )
2013  outputFormatter.printf( 0, "Convert MOAB overlap files to TempestRemap format in-memory ...\n" );
2014  return handleOverlapMemory( ctx, remapper, tempest_mesh );
2015 #else
2016  MB_CHK_SET_ERR( moab::MB_FAILURE,
2017  "OVERLAP_MEMORY mode requires NetCDF (TempestRemap-native mesh loading); "
2018  "build with NetCDF or use OVERLAP_MOAB mode instead" );
2019 #endif
2020 
2021  case RemapperType::OVERLAP_MOAB:
2022  if( !ctx.proc_id )
2023  outputFormatter.printf( 0, "Convert MOAB meshes to TempestRemap format in-memory ...\n" );
2024  return handleOverlapMOAB( ctx, remapper );
2025 
2026  case RemapperType::ICO:
2027  if( !ctx.proc_id ) outputFormatter.printf( 0, "Creating TempestRemap ICO mesh ...\n" );
2028  return handleICOMesh( ctx, remapper, tempest_mesh );
2029 
2030  case RemapperType::RLL:
2031  if( !ctx.proc_id ) outputFormatter.printf( 0, "Creating TempestRemap RLL mesh ...\n" );
2032  return handleRLLMesh( ctx, remapper, tempest_mesh );
2033 
2034  default: // Default to CS mesh
2035  if( !ctx.proc_id ) outputFormatter.printf( 0, "Creating TempestRemap CS mesh ...\n" );
2036  return handleCSMesh( ctx, remapper, tempest_mesh );
2037  }
2038  }
2039  catch( const std::exception& e )
2040  {
2041  std::cerr << "Error in CreateTempestMesh: " << e.what() << "\n";
2042  return MB_FAILURE;
2043  }
2044 }

References anonymous_namespace{mbtempest.cpp}::handleCSMesh(), anonymous_namespace{mbtempest.cpp}::handleICOMesh(), anonymous_namespace{mbtempest.cpp}::handleOverlapMOAB(), anonymous_namespace{mbtempest.cpp}::handleRLLMesh(), MB_CHK_SET_ERR, ToolContext::meshType, ToolContext::outputFormatter, moab::DebugOutput::printf(), and ToolContext::proc_id.

Referenced by main().

◆ main()

int main ( int  argc,
char *  argv[] 
)

Definition at line 970 of file mbtempest.cpp.

971 {
972  try
973  {
974 #ifdef MOAB_HAVE_NETCDF
975  NcError error( NcError::verbose_nonfatal );
976 #endif
977  std::stringstream sstr;
978  std::string historyStr;
979 
980  int proc_id = 0, nprocs = 1;
981 #ifdef MOAB_HAVE_MPI
982  MPI_Init( &argc, &argv );
983  MPI_Comm_rank( MPI_COMM_WORLD, &proc_id );
984  MPI_Comm_size( MPI_COMM_WORLD, &nprocs );
985 #endif
986 
987  moab::Core* mbCore = new( std::nothrow ) moab::Core;
988 
989  if( nullptr == mbCore )
990  {
991  return 1;
992  }
993 
994  // Build the history string
995  for( int ia = 0; ia < argc; ++ia )
996  historyStr += std::string( argv[ia] ) + " ";
997 
998  ToolContext* runCtx;
999 #ifdef MOAB_HAVE_MPI
1000  moab::ParallelComm* pcomm = new moab::ParallelComm( mbCore, MPI_COMM_WORLD, 0 );
1001 
1002  runCtx = new ToolContext( mbCore, pcomm );
1003  const char* writeOptions = ( nprocs > 1 ? "PARALLEL=WRITE_PART" : "" );
1004 #else
1005  runCtx = new ToolContext( mbCore );
1006  const char* writeOptions = "";
1007 #endif
1008  runCtx->ParseCLOptions( argc, argv );
1009 
1010  const double radius_src = 1.0 /*2.0*acos(-1.0)*/;
1011  const double radius_dest = 1.0 /*2.0*acos(-1.0)*/;
1012 
1013  moab::DebugOutput& outputFormatter = runCtx->outputFormatter;
1014 
1015 #ifdef MOAB_HAVE_MPI
1016  moab::TempestRemapper remapper( mbCore, pcomm );
1017 #else
1018  moab::TempestRemapper remapper( mbCore );
1019 #endif
1020  remapper.meshValidate = true;
1021  remapper.constructEdgeMap = true;
1022  remapper.initialize();
1023  // The command-line choice supersedes the remapper default, so that intersection,
1024  // coverage construction and orientation fixups all use one formula.
1025  remapper.SetAreaMethod( runCtx->areaMethod );
1026  // --rrmgrids: the two domains need not coincide, so cells may be partially covered.
1027  remapper.SetRegionalMesh( runCtx->rrmGrids );
1028 
1029  // Area formula selected by --area_method (default: Van Oosterom-Strackee)
1030  moab::IntxAreaUtils areaAdaptor( runCtx->areaMethod );
1031 
1032  Mesh* tempest_mesh = new Mesh();
1033  MB_CHK_SET_ERR( CreateTempestMesh( *runCtx, remapper, tempest_mesh ), "Failed to create tempest mesh" );
1034 
1036  {
1037  // Compute intersections with MOAB
1038  // For the overlap method, choose between: "fuzzy", "exact" or "mixed"
1039  assert( runCtx->meshes.size() == 3 );
1040 
1041 #ifdef MOAB_HAVE_MPI
1042  MB_CHK_SET_ERR( pcomm->check_all_shared_handles(), "Failed to check all shared handles" );
1043 #endif
1044 
1045  // Load the meshes and validate
1046  MB_CHK_SET_ERR( remapper.ConvertTempestMesh( moab::Remapper::SourceMesh ), "Failed to convert source mesh" );
1047  MB_CHK_SET_ERR( remapper.ConvertTempestMesh( moab::Remapper::TargetMesh ), "Failed to convert target mesh" );
1048  MB_CHK_SET_ERR( remapper.ConvertTempestMesh( moab::Remapper::OverlapMesh ), "Failed to convert overlap mesh" );
1049  if( !runCtx->skip_io )
1050  {
1051  MB_CHK_SET_ERR( mbCore->write_mesh( "tempest_intersection.h5m", &runCtx->meshsets[2], 1 ),
1052  "Failed to write TempestRemap intersection mesh in MOAB format" );
1053  }
1054 
1055  // print verbosely about the problem setting
1056  size_t velist[6], gvelist[6];
1057  {
1058  moab::Range rintxverts, rintxelems;
1059  MB_CHK_SET_ERR( mbCore->get_entities_by_dimension( runCtx->meshsets[0], 0, rintxverts ),
1060  "Failed to get vertices" );
1061  MB_CHK_SET_ERR( mbCore->get_entities_by_dimension( runCtx->meshsets[0], 2, rintxelems ),
1062  "Failed to get elements" );
1063  velist[0] = rintxverts.size();
1064  velist[1] = rintxelems.size();
1065 
1066  moab::Range bintxverts, bintxelems;
1067  MB_CHK_SET_ERR( mbCore->get_entities_by_dimension( runCtx->meshsets[1], 0, bintxverts ),
1068  "Failed to get vertices" );
1069  MB_CHK_SET_ERR( mbCore->get_entities_by_dimension( runCtx->meshsets[1], 2, bintxelems ),
1070  "Failed to get elements" );
1071  velist[2] = bintxverts.size();
1072  velist[3] = bintxelems.size();
1073  }
1074 
1075  moab::EntityHandle intxset; // == remapper.GetMeshSet(moab::Remapper::OverlapMesh);
1076 
1077  // Compute intersections with MOAB
1078  {
1079  // Create the intersection on the sphere object
1080  runCtx->timer_push( "setup the intersector" );
1081 
1082  moab::Intx2MeshOnSphere* mbintx = new moab::Intx2MeshOnSphere( mbCore, runCtx->areaMethod );
1083  mbintx->set_error_tolerance( runCtx->epsrel );
1084  mbintx->set_box_error( runCtx->boxeps );
1085  mbintx->set_radius_source_mesh( radius_src );
1086  mbintx->set_radius_destination_mesh( radius_dest );
1087 #ifdef MOAB_HAVE_MPI
1088  mbintx->set_parallel_comm( pcomm );
1089 #endif
1090  MB_CHK_SET_ERR( mbintx->FindMaxEdges( runCtx->meshsets[0], runCtx->meshsets[1] ),
1091  "Failed to find max edges" );
1092 
1093 #ifdef MOAB_HAVE_MPI
1094  moab::Range local_verts;
1095  MB_CHK_SET_ERR( mbintx->build_processor_euler_boxes( runCtx->meshsets[1], local_verts ),
1096  "Failed to build processor euler boxes" );
1097 
1098  runCtx->timer_pop();
1099 
1100  moab::EntityHandle covering_set;
1101  runCtx->timer_push( "communicate the mesh" );
1102  // we compute just intersection here, no need for extra ghost layers anyway
1103  // ghost layers are needed in coverage for bilinear map, which does not actually need intersection
1104  // this will be fixed in the future, bilinear map needs just coverage, not intersection
1105  // so I am not passing the ghost layer here, even though there is an option in runCtx for a ghost layer
1106  // NOTE: This is a communication-heavy kernel if mesh is distributed very differently
1107  MB_CHK_SET_ERR( mbintx->construct_covering_set( runCtx->meshsets[0], covering_set ),
1108  "Failed to construct covering set" );
1109  runCtx->timer_pop();
1110 
1111  // print verbosely about the problem setting
1112  {
1113  moab::Range cintxverts, cintxelems;
1114  MB_CHK_SET_ERR( mbCore->get_entities_by_dimension( covering_set, 0, cintxverts ),
1115  "Failed to get vertices" );
1116  MB_CHK_SET_ERR( mbCore->get_entities_by_dimension( covering_set, 2, cintxelems ),
1117  "Failed to get elements" );
1118  velist[4] = cintxverts.size();
1119  velist[5] = cintxelems.size();
1120  }
1121 
1122  MPI_Reduce( velist, gvelist, 6, MPI_UINT64_T, MPI_SUM, 0, MPI_COMM_WORLD );
1123 
1124 #else
1125  moab::EntityHandle covering_set = runCtx->meshsets[0];
1126  for( int i = 0; i < 6; i++ )
1127  gvelist[i] = velist[i];
1128 #endif
1129 
1130  if( !proc_id )
1131  {
1132  outputFormatter.printf( 0, "The source set contains %lu vertices and %lu elements \n", gvelist[0],
1133  gvelist[0] );
1134  outputFormatter.printf( 0, "The covering set contains %lu vertices and %lu elements \n", gvelist[2],
1135  gvelist[2] );
1136  outputFormatter.printf( 0, "The target set contains %lu vertices and %lu elements \n", gvelist[1],
1137  gvelist[1] );
1138  }
1139 
1140  // Now let's invoke the MOAB intersection algorithm in parallel with a
1141  // source and target mesh set representing two different decompositions
1142  runCtx->timer_push( "compute intersections with MOAB" );
1143  MB_CHK_SET_ERR( mbCore->create_meshset( moab::MESHSET_SET, intxset ), "Can't create new set" );
1144  MB_CHK_SET_ERR( mbintx->intersect_meshes( covering_set, runCtx->meshsets[1], intxset ),
1145  "Can't compute the intersection of meshes on the sphere" );
1146  runCtx->timer_pop();
1147 
1148  // free the memory
1149  delete mbintx;
1150  }
1151 
1152  {
1153  moab::Range intxelems, intxverts;
1154  MB_CHK_SET_ERR( mbCore->get_entities_by_dimension( intxset, 2, intxelems ), "Failed to get elements" );
1155  MB_CHK_SET_ERR( mbCore->get_entities_by_dimension( intxset, 0, intxverts, true ),
1156  "Failed to get vertices" );
1157  outputFormatter.printf( 0, "The intersection set contains %lu elements and %lu vertices \n",
1158  intxelems.size(), intxverts.size() );
1159 
1160  double initial_sarea =
1161  areaAdaptor.area_on_sphere( mbCore, runCtx->meshsets[0],
1162  radius_src ); // use the target to compute the initial area
1163  double initial_tarea =
1164  areaAdaptor.area_on_sphere( mbCore, runCtx->meshsets[1],
1165  radius_dest ); // use the target to compute the initial area
1166  double intx_area = areaAdaptor.area_on_sphere( mbCore, intxset, radius_src );
1167 
1168  outputFormatter.printf( 0, "mesh areas: source = %12.10f, target = %12.10f, intersection = %12.10f \n",
1169  initial_sarea, initial_tarea, intx_area );
1170  outputFormatter.printf( 0, "relative error w.r.t source = %12.10e, target = %12.10e \n",
1171  fabs( intx_area - initial_sarea ) / initial_sarea,
1172  fabs( intx_area - initial_tarea ) / initial_tarea );
1173  }
1174 
1175  // Write out our computed intersection file
1176  if( !runCtx->skip_io )
1177  {
1178  MB_CHK_SET_ERR( mbCore->write_mesh( "moab_intersection.h5m", &intxset, 1 ),
1179  "Failed to write the intersection" );
1180  }
1181 
1182  if( runCtx->computeWeights )
1183  {
1184  runCtx->timer_push( "compute weights with the Tempest meshes" );
1185  // Call to generate an offline map with the tempest meshes
1186  OfflineMap weightMap;
1187  if( GenerateOfflineMapWithMeshes( *runCtx->meshes[0], *runCtx->meshes[1], *runCtx->meshes[2],
1188  runCtx->disc_methods[0], // std::string strInputType
1189  runCtx->disc_methods[1], // std::string strOutputType,
1190  runCtx->mapOptions, weightMap ) != 0 )
1191  throw std::runtime_error( "Could not generate offline map with TempestRemap" );
1192  runCtx->timer_pop();
1193 
1194  std::map< std::string, std::string > mapAttributes;
1195 #ifdef MOAB_HAVE_NETCDF
1196  if( !runCtx->skip_io ) weightMap.Write( "outWeights.nc", mapAttributes );
1197 #else
1198  (void)mapAttributes;
1199  MB_CHK_SET_ERR( moab::MB_FAILURE,
1200  "Writing a TempestRemap OfflineMap (OVERLAP_MEMORY path) requires NetCDF; use the "
1201  "MOAB (OVERLAP_MOAB) workflow, which writes weights via PnetCDF/HDF5 instead" );
1202 #endif
1203  }
1204  }
1205  else if( runCtx->meshType == moab::TempestRemapper::OVERLAP_MOAB )
1206  {
1207  // Usage: mpiexec -n 2 tools/mbtempest -t 5 -l mycs_2.h5m -l myico_2.h5m -f myoverlap_2.h5m
1208 #ifdef MOAB_HAVE_MPI
1209  MB_CHK_SET_ERR( pcomm->check_all_shared_handles(), "Checking shared handles failed." );
1210 #endif
1211 
1212  // print verbosely about the problem setting
1213  size_t velist[4] = { 0, 0, 0, 0 }, gvelist[4] = { 0, 0, 0, 0 };
1214  {
1215  moab::Range srcverts, srcelems;
1216  MB_CHK_SET_ERR( mbCore->get_entities_by_dimension( runCtx->meshsets[0], 0, srcverts ),
1217  "Failed to get vertices" );
1218  MB_CHK_SET_ERR( mbCore->get_entities_by_dimension( runCtx->meshsets[0], 2, srcelems ),
1219  "Failed to get elements" );
1221  "Failed to fix degenerate quads" );
1222  if( runCtx->enforceConvexity )
1223  {
1224  MB_CHK_SET_ERR( moab::IntxUtils::enforce_convexity( mbCore, runCtx->meshsets[0], proc_id ),
1225  "Failed to enforce convexity" );
1226  }
1227  MB_CHK_SET_ERR( areaAdaptor.positive_orientation( mbCore, runCtx->meshsets[0], radius_src ),
1228  "Failed to enforce positive orientation" );
1229  velist[0] = srcverts.size();
1230  velist[1] = srcelems.size();
1231 
1232  moab::Range tgtverts, tgtelems;
1233  MB_CHK_SET_ERR( mbCore->get_entities_by_dimension( runCtx->meshsets[1], 0, tgtverts ),
1234  "Failed to get vertices" );
1235  MB_CHK_SET_ERR( mbCore->get_entities_by_dimension( runCtx->meshsets[1], 2, tgtelems ),
1236  "Failed to get elements" );
1238  "Failed to fix degenerate quads" );
1239  if( runCtx->enforceConvexity )
1240  {
1241  MB_CHK_SET_ERR( moab::IntxUtils::enforce_convexity( mbCore, runCtx->meshsets[1], proc_id ),
1242  "Failed to enforce convexity" );
1243  }
1244  MB_CHK_SET_ERR( areaAdaptor.positive_orientation( mbCore, runCtx->meshsets[1], radius_dest ),
1245  "Failed to enforce positive orientation" );
1246  velist[2] = tgtverts.size();
1247  velist[3] = tgtelems.size();
1248  }
1249  //MB_CHK_SET_ERR( mbCore->write_file( "source_mesh.h5m", nullptr, writeOptions, &runCtx->meshsets[0], 1 ), "Could not write source mesh" );
1250  //MB_CHK_SET_ERR( mbCore->write_file( "target_mesh.h5m", nullptr, writeOptions, &runCtx->meshsets[1], 1 ), "Could not write target mesh" );
1251 
1252  // if( runCtx->nlayers && nprocs > 1 )
1253  // {
1254  // remapper.ResetMeshSet( moab::Remapper::SourceMesh, runCtx->meshsets[3] );
1255  // runCtx->meshes[0] = remapper.GetMesh( moab::Remapper::SourceMesh ); // ?
1256  // }
1257 
1258  // First compute the covering set such that the target elements are fully covered by the
1259  // local source grid
1260  runCtx->timer_push( "construct covering set for intersection" );
1261  // Ghosting and the gnomonic-projection coverage path are mutually
1262  // exclusive. Ghost layers are now enabled by default for the
1263  // bilinear-family methods, so warn rather than silently dropping an
1264  // explicit --gnomonic request.
1265  if( runCtx->nlayers > 0 && runCtx->useGnomonicProjection )
1266  {
1267  if( !proc_id )
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";
1270  runCtx->useGnomonicProjection = false;
1271  }
1272  MB_CHK_SET_ERR( remapper.ConstructCoveringSet( runCtx->epsrel, 1.0, 1.0, runCtx->boxeps, runCtx->rrmGrids,
1273  runCtx->useGnomonicProjection, runCtx->nlayers ),
1274  "Failed to construct covering set" );
1275  runCtx->timer_pop();
1276 
1277 #ifdef MOAB_HAVE_MPI
1278  MPI_Reduce( velist, gvelist, 4, MPI_UINT64_T, MPI_SUM, 0, MPI_COMM_WORLD );
1279 #else
1280  for( int i = 0; i < 4; i++ )
1281  gvelist[i] = velist[i];
1282 #endif
1283  if( !proc_id && runCtx->print_diagnostics )
1284  {
1285  outputFormatter.printf( 0, "The source set contains %lu vertices and %lu elements \n", gvelist[0],
1286  gvelist[1] );
1287  outputFormatter.printf( 0, "The target set contains %lu vertices and %lu elements \n", gvelist[2],
1288  gvelist[3] );
1289  }
1290 
1291  if( runCtx->skip_intersection )
1292  {
1293  if( !proc_id ) outputFormatter.printf( 0, "Skipping mesh intersection computation.\n" );
1294  }
1295  else
1296  {
1297  // Compute intersections with MOAB with either the Kd-tree or the advancing front algorithm
1298  runCtx->timer_push( "setup and compute mesh intersections" );
1299  MB_CHK_SET_ERR( remapper.ComputeOverlapMesh( runCtx->kdtreeSearch, false ),
1300  "Failed to compute mesh intersections" );
1301  runCtx->timer_pop();
1302  }
1303 
1304  // print some diagnostic checks to see if the overlap grid resolved the input meshes
1305  // correctly
1306  // Compute ghost overlap elements once; reused for both area diagnostics and intx file write
1307  moab::Range ghostOverlapElems;
1308 #ifdef MOAB_HAVE_MPI
1309  if( nprocs > 1 && !runCtx->skip_intersection )
1310  MB_CHK_SET_ERR( remapper.GetOverlapAugmentedEntities( ghostOverlapElems ),
1311  "Failed to get ghost overlap entities" );
1312 #endif
1313 
1314  double dTotalOverlapArea = 0.0;
1315  if( runCtx->print_diagnostics && !runCtx->skip_intersection )
1316  {
1317  // Areas for source, target, overlap meshes
1318  double local_areas[3] = { 0, 0, 0 },
1319  global_areas[3] = { 0, 0, 0 };
1320 
1321  // Helper: compute area of a meshset excluding cells with GRID_IMASK==0.
1322  // Both source and target SCRIP grids may have a land/sea mask; the intersection
1323  // only covers unmasked cells, so comparing full-mesh areas gives a misleading error.
1324  auto area_unmasked = [&]( moab::EntityHandle meshset, double radius ) -> double {
1325  moab::Tag imaskTag = 0;
1326  mbCore->tag_get_handle( "GRID_IMASK", imaskTag );
1327  if( !imaskTag ) return areaAdaptor.area_on_sphere( mbCore, meshset, radius );
1328  moab::Range cells;
1329  mbCore->get_entities_by_dimension( meshset, 2, cells );
1330  std::vector< int > masks( cells.size(), 1 );
1331  mbCore->tag_get_data( imaskTag, cells, masks.data() );
1332  moab::Range maskedCells;
1333  size_t idx = 0;
1334  for( auto it = cells.begin(); it != cells.end(); ++it, ++idx )
1335  if( !masks[idx] ) maskedCells.insert( *it );
1336  moab::Range unmasked = moab::subtract( cells, maskedCells );
1337  moab::EntityHandle tmpSet;
1338  mbCore->create_meshset( moab::MESHSET_SET, tmpSet );
1339  mbCore->add_entities( tmpSet, unmasked );
1340  double area = areaAdaptor.area_on_sphere( mbCore, tmpSet, radius );
1341  mbCore->delete_entities( &tmpSet, 1 );
1342  return area;
1343  };
1344 
1345  local_areas[0] = area_unmasked( runCtx->meshsets[0], radius_src );
1346  local_areas[1] = area_unmasked( runCtx->meshsets[1], radius_dest );
1347  // Exclude ghost overlap elements from area sum to avoid double-counting after MPI_Allreduce
1348  {
1349  moab::Range ownedOverlapElems;
1350  MB_CHK_SET_ERR( mbCore->get_entities_by_dimension( runCtx->meshsets[2], 2, ownedOverlapElems ),
1351  "Failed to get overlap elements" );
1352  ownedOverlapElems = moab::subtract( ownedOverlapElems, ghostOverlapElems );
1353  moab::EntityHandle ownedOverlapSet;
1354  MB_CHK_SET_ERR( mbCore->create_meshset( moab::MESHSET_SET, ownedOverlapSet ),
1355  "Can't create owned overlap meshset" );
1356  MB_CHK_SET_ERR( mbCore->add_entities( ownedOverlapSet, ownedOverlapElems ),
1357  "Can't add owned overlap elements" );
1358  local_areas[2] = areaAdaptor.area_on_sphere( mbCore, ownedOverlapSet, radius_src );
1359  MB_CHK_SET_ERR( mbCore->delete_entities( &ownedOverlapSet, 1 ), "Can't delete temp meshset" );
1360  }
1361 
1362 #ifdef MOAB_HAVE_MPI
1363  MPI_Allreduce( &local_areas[0], &global_areas[0], 3, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD );
1364 #else
1365  global_areas[0] = local_areas[0];
1366  global_areas[1] = local_areas[1];
1367  global_areas[2] = local_areas[2];
1368 #endif
1369  if( !proc_id )
1370  {
1371  outputFormatter.printf( 0,
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] );
1375  outputFormatter.printf( 0, "relative error w.r.t source = %12.14e, and target = %12.14e\n",
1376  fabs( global_areas[0] - global_areas[2] ) / global_areas[0],
1377  fabs( global_areas[1] - global_areas[2] ) / global_areas[1] );
1378  if( runCtx->rrmGrids )
1379  {
1380  // For coincident domains the shortfall below is roundoff and the
1381  // relative errors above already say so. For a regional mesh it is
1382  // the physically meaningful quantity: how much of each mesh the
1383  // other one actually covers.
1384  outputFormatter.printf( 0,
1385  "regional mesh: overlap covers %6.2f%% of source and %6.2f%% of "
1386  "target area\n",
1387  100.0 * global_areas[2] / global_areas[0],
1388  100.0 * global_areas[2] / global_areas[1] );
1389  }
1390  }
1391  dTotalOverlapArea = global_areas[2];
1392  }
1393 
1394  if( runCtx->intxFilename.size() && !runCtx->skip_intersection )
1395  {
1396  moab::EntityHandle writableOverlapSet;
1397  MB_CHK_SET_ERR( mbCore->create_meshset( moab::MESHSET_SET, writableOverlapSet ), "Can't create new set" );
1398  moab::EntityHandle meshOverlapSet = remapper.GetMeshSet( moab::Remapper::OverlapMesh );
1399  moab::Range ovEnts;
1400  MB_CHK_SET_ERR( mbCore->get_entities_by_dimension( meshOverlapSet, 2, ovEnts ), "Can't create new set" );
1401  MB_CHK_SET_ERR( mbCore->get_entities_by_dimension( meshOverlapSet, 0, ovEnts ), "Can't create new set" );
1402 
1403 #ifdef MOAB_HAVE_MPI
1404  // Exclude ghost overlap elements from the write: each ghost element is owned by another
1405  // rank and will be written from there. Including ghosts here causes duplicate entity
1406  // handles in the parallel HDF5 output and deadlocks the collective write.
1407  if( nprocs > 1 )
1408  {
1409  ovEnts = moab::subtract( ovEnts, ghostOverlapElems );
1410 #ifdef MOAB_DBG
1411  if( !runCtx->skip_io )
1412  {
1413  std::stringstream filename;
1414  filename << "aug_overlap" << runCtx->pcomm->rank() << ".h5m";
1415  MB_CHK_SET_ERR( mbCore->write_file( filename.str().c_str(), 0, 0, &meshOverlapSet, 1 ),
1416  "Failed to write the overlap set" );
1417  }
1418 #endif
1419  }
1420 #endif
1421  MB_CHK_SET_ERR( mbCore->add_entities( writableOverlapSet, ovEnts ), "adding local intx cells failed" );
1422 
1423 #ifdef MOAB_HAVE_MPI
1424 #ifdef MOAB_DBG
1425  if( nprocs > 1 && !runCtx->skip_io )
1426  {
1427  std::stringstream filename;
1428  filename << "writable_intx_" << runCtx->pcomm->rank() << ".h5m";
1429  MB_CHK_SET_ERR( mbCore->write_file( filename.str().c_str(), 0, 0, &writableOverlapSet, 1 ),
1430  "Failed to write the writable overlap set" );
1431  }
1432 #endif
1433 #endif
1434 
1435  size_t lastindex = runCtx->intxFilename.find_last_of( "." );
1436  sstr.str( "" );
1437  sstr << runCtx->intxFilename.substr( 0, lastindex ) << ".h5m";
1438  if( !runCtx->proc_id )
1439  std::cout << "Writing out the MOAB intersection mesh file to " << sstr.str() << std::endl;
1440 
1441  // Write out our computed intersection file
1442  if( !runCtx->skip_io )
1443  {
1444  MB_CHK_SET_ERR( mbCore->write_file( sstr.str().c_str(), nullptr, writeOptions, &writableOverlapSet, 1 ),
1445  "Failed to write the writable overlap set" );
1446  }
1447  }
1448 
1449  if( runCtx->computeWeights )
1450  {
1451  runCtx->meshes[2] = remapper.GetMesh( moab::Remapper::OverlapMesh );
1452  if( !runCtx->proc_id ) std::cout << std::endl;
1453 
1454  runCtx->timer_push( "setup computation of weights" );
1455  // Call to generate the remapping weights with the tempest meshes
1456  moab::TempestOnlineMap* weightMap = new moab::TempestOnlineMap( &remapper );
1457  runCtx->timer_pop();
1458 
1459  runCtx->timer_push( "compute weights with TempestRemap" );
1461  runCtx->disc_methods[0], // std::string strInputType
1462  runCtx->disc_methods[1], // std::string strOutputType,
1463  runCtx->mapOptions, // const GenerateOfflineMapAlgorithmOptions& options
1464  runCtx->doftag_names[0], // const std::string& source_tag_name
1465  runCtx->doftag_names[1] // const std::string& target_tag_name
1466  ),
1467  "Failed to generate remapping weights" );
1468  runCtx->timer_pop();
1469 
1470  weightMap->PrintMapStatistics();
1471 
1472  // Invoke the CheckMap routine on the TempestRemap serial interface directly, if running
1473  // on a single process
1474  if( runCtx->fCheck )
1475  {
1476  const double dNormalTolerance = 1.0E-8;
1477  const double dStrictTolerance = 1.0E-12;
1478  weightMap->CheckMap( runCtx->fCheck, runCtx->fCheck, runCtx->fCheck && ( runCtx->ensureMonotonicity ),
1479  dNormalTolerance, dStrictTolerance, dTotalOverlapArea );
1480  }
1481 
1482  if( runCtx->outFilename.size() && !runCtx->skip_io )
1483  {
1484  std::map< std::string, std::string > attrMap;
1485  attrMap["MOABversion"] = std::string( MOAB_PACKAGE_VERSION );
1486  attrMap["Title"] = "MOAB-TempestRemap (mbtempest) Offline Regridding Weight Generator";
1487  attrMap["normalization"] = "ovarea";
1488  attrMap["remap_options"] = runCtx->mapOptions.strMethod;
1489  attrMap["domain_a"] = runCtx->inFilenames[0];
1490  attrMap["domain_b"] = runCtx->inFilenames[1];
1491  if( runCtx->intxFilename.size() ) attrMap["domain_aUb"] = runCtx->intxFilename;
1492  attrMap["map_aPb"] = runCtx->outFilename;
1493  attrMap["methodorder_a"] = runCtx->disc_methods[0] + ":" + std::to_string( runCtx->disc_orders[0] ) +
1494  ":" + std::string( runCtx->doftag_names[0] );
1495  attrMap["concave_a"] = runCtx->mapOptions.fSourceConcave ? "true" : "false";
1496  attrMap["methodorder_b"] = runCtx->disc_methods[1] + ":" + std::to_string( runCtx->disc_orders[1] ) +
1497  ":" + std::string( runCtx->doftag_names[1] );
1498  attrMap["concave_b"] = runCtx->mapOptions.fTargetConcave ? "true" : "false";
1499  attrMap["bubble"] = runCtx->mapOptions.fNoBubble ? "false" : "true";
1500  attrMap["history"] = historyStr;
1501 
1502  // Write the map file to disk in parallel using either HDF5 or SCRIP interface
1503  // in extra case; maybe need a better solution, just create it with the right meshset
1504  // from the beginning;
1505  MB_CHK_SET_ERR( weightMap->WriteParallelMap( runCtx->outFilename.c_str(), attrMap ),
1506  "Failed writing the parallel map to disk" );
1507  }
1508 
1509  if( runCtx->verifyWeights )
1510  {
1511  // Let us pick a sampling test function for solution evaluation
1512  // SH, SV, FH, C, USERVAR
1513  bool userVariable = false;
1515  if( !runCtx->variableToVerify.compare( "SH" ) )
1516  testFunction = &sample_slow_harmonic;
1517  else if( !runCtx->variableToVerify.compare( "FH" ) )
1518  testFunction = &sample_fast_harmonic;
1519  else if( !runCtx->variableToVerify.compare( "SV" ) )
1520  testFunction = &sample_stationary_vortex;
1521  else if( !runCtx->variableToVerify.compare( "C" ) )
1522  testFunction = &sample_constant;
1523  else
1524  {
1525  userVariable = runCtx->variableToVerify.size() ? true : false;
1526  testFunction = runCtx->variableToVerify.size() ? nullptr : sample_stationary_vortex;
1527  }
1528 
1529  moab::Tag srcAnalyticalFunction;
1530  moab::Tag tgtAnalyticalFunction;
1531  moab::Tag tgtProjectedFunction;
1532  if( testFunction )
1533  {
1534  runCtx->timer_push( "describe a solution on source grid" );
1535  // MB_CHK_SET_ERR( mbCore->tag_get_handle( runCtx->variableToVerify.c_str(), srcAnalyticalFunction ),
1536  // "Failed to get analytical solution on source grid" );
1537  MB_CHK_SET_ERR( weightMap->DefineAnalyticalSolution( srcAnalyticalFunction,
1538  "AnalyticalSolnSrcExact",
1539  moab::Remapper::SourceMesh, testFunction ),
1540  "Failed to define analytical solution on source grid" );
1541  runCtx->timer_pop();
1542 
1543  // runCtx->timer_push( "exchange solution on source grid" );
1544  // moab::Range& srccovEnts = remapper.GetMeshEntities( moab::Remapper::CoveringMesh );
1545  // MB_CHK_SET_ERR( pcomm->exchange_tags( srcAnalyticalFunction, srccovEnts ),
1546  // "Failed to exchange analytical solution on source grid" );
1547  // runCtx->timer_pop();
1548 
1549  runCtx->timer_push( "describe a solution on target grid" );
1551  tgtAnalyticalFunction, "AnalyticalSolnTgtExact", moab::Remapper::TargetMesh,
1552  testFunction, &tgtProjectedFunction, "ProjectedSolnTgt" ),
1553  "Failed to define analytical solution on target grid" );
1554  runCtx->timer_pop();
1555  }
1556  else
1557  {
1558  MB_CHK_SET_ERR( mbCore->tag_get_handle( runCtx->variableToVerify.c_str(), srcAnalyticalFunction ),
1559  "Failed to get analytical solution on source grid" );
1560  MB_CHK_SET_ERR( mbCore->tag_get_handle( "ProjectedSolnTgt", 1, moab::MB_TYPE_DOUBLE,
1561  tgtProjectedFunction,
1563  "Failed to get projected solution on target grid" );
1564  }
1565 
1566  // if( !runCtx->skip_io )
1567  {
1568  MB_CHK_SET_ERR( mbCore->write_file( "srcWithSolnTag.h5m", nullptr, writeOptions,
1569  &runCtx->meshsets[0], 1 ),
1570  "Failed to write the source mesh with solution tag" );
1571  }
1572 
1573  runCtx->timer_push( "compute solution projection on target grid" );
1574  MB_CHK_SET_ERR( weightMap->ApplyWeights( srcAnalyticalFunction, tgtProjectedFunction, false,
1575  runCtx->cassType ),
1576  "Failed to apply weights" );
1577  runCtx->timer_pop();
1578 
1579  // if( !runCtx->skip_io )
1580  {
1581  MB_CHK_SET_ERR( mbCore->write_file( "tgtWithSolnTag2.h5m", nullptr, writeOptions,
1582  &runCtx->meshsets[1], 1 ),
1583  "Failed to write the target mesh with projected solution tag" );
1584  }
1585 
1586  if( nprocs == 1 && runCtx->baselineFile.size() )
1587  {
1588  // save the field from tgtWithSolnTag2 in a text file, and global ids for cells
1589  moab::Range tgtEntities;
1590  if( runCtx->disc_methods[1] == "pcloud" )
1591  {
1592  MB_CHK_SET_ERR( mbCore->get_entities_by_dimension( runCtx->meshsets[1], 0, tgtEntities ),
1593  "Failed to get entities by dimension" );
1594  }
1595  else
1596  {
1597  MB_CHK_SET_ERR( mbCore->get_entities_by_dimension( runCtx->meshsets[1], 2, tgtEntities ),
1598  "Failed to get entities by dimension" );
1599  }
1600  std::vector< int > globIds( tgtEntities.size() );
1601  std::vector< double > vals( tgtEntities.size() );
1602  moab::Tag projTag;
1603  MB_CHK_SET_ERR( mbCore->tag_get_handle( "ProjectedSolnTgt", projTag ),
1604  "Failed to get projected solution tag" );
1605  moab::Tag gid = mbCore->globalId_tag();
1606  MB_CHK_SET_ERR( mbCore->tag_get_data( gid, tgtEntities, &globIds[0] ), "Failed to get global ids" );
1607  MB_CHK_SET_ERR( mbCore->tag_get_data( projTag, tgtEntities, &vals[0] ),
1608  "Failed to get projected solution" );
1609  std::fstream fs;
1610  fs.open( runCtx->baselineFile.c_str(), std::fstream::out );
1611  fs << std::setprecision( 15 ); // maximum precision for doubles
1612  for( size_t i = 0; i < tgtEntities.size(); i++ )
1613  fs << globIds[i] << " " << vals[i] << "\n";
1614  fs.close();
1615  // for good measure, save the source file too, with the tag AnalyticalSolnSrcExact
1616  // it will be used later to test, along with a target file
1617  if( !runCtx->skip_io )
1618  {
1619  MB_CHK_SET_ERR( mbCore->write_file( "srcWithSolnTag.h5m", nullptr, writeOptions,
1620  &runCtx->meshsets[0], 1 ),
1621  "Failed to write the source mesh with solution tag" );
1622  }
1623  }
1624 
1625  // compute error metrics if it is a known analytical functional
1626  if( !userVariable )
1627  {
1628  runCtx->timer_push( "compute error metrics against analytical solution on target grid" );
1629  std::map< std::string, double > errMetrics;
1630  MB_CHK_SET_ERR( weightMap->ComputeMetrics( moab::Remapper::TargetMesh, tgtAnalyticalFunction,
1631  tgtProjectedFunction, errMetrics, true ),
1632  "Failed to compute error metrics" );
1633  runCtx->timer_pop();
1634  }
1635  }
1636 
1637  delete weightMap;
1638  }
1639  }
1640 
1641  // Clean up
1642  remapper.clear();
1643  delete runCtx;
1644  delete mbCore;
1645 
1646 #ifdef MOAB_HAVE_MPI
1647  MPI_Finalize();
1648 #endif
1649  return 0;
1650  }
1651  catch( const std::exception& e )
1652  {
1653  std::cerr << "[mbtempest] Fatal error: " << e.what() << std::endl;
1654 #ifdef MOAB_HAVE_MPI
1655  MPI_Abort( MPI_COMM_WORLD, 1 );
1656 #endif
1657  return 1;
1658  }
1659  catch( ... )
1660  {
1661  std::cerr << "[mbtempest] Fatal: unknown exception caught" << std::endl;
1662 #ifdef MOAB_HAVE_MPI
1663  MPI_Abort( MPI_COMM_WORLD, 1 );
1664 #endif
1665  return 1;
1666  }
1667 }

References moab::Core::add_entities(), moab::TempestOnlineMap::ApplyWeights(), moab::IntxAreaUtils::area_on_sphere(), ToolContext::areaMethod, ToolContext::baselineFile, moab::Range::begin(), ToolContext::boxeps, ToolContext::cassType, moab::ParallelComm::check_all_shared_handles(), moab::TempestRemapper::clear(), moab::TempestOnlineMap::ComputeMetrics(), moab::TempestRemapper::ComputeOverlapMesh(), ToolContext::computeWeights, moab::TempestRemapper::ConstructCoveringSet(), moab::TempestRemapper::constructEdgeMap, moab::TempestRemapper::ConvertTempestMesh(), moab::Core::create_meshset(), CreateTempestMesh(), moab::TempestOnlineMap::DefineAnalyticalSolution(), moab::Core::delete_entities(), ToolContext::disc_methods, ToolContext::disc_orders, ToolContext::doftag_names, moab::Range::end(), moab::IntxUtils::enforce_convexity(), ToolContext::enforceConvexity, ToolContext::ensureMonotonicity, ToolContext::epsrel, error(), ToolContext::fCheck, moab::Intx2Mesh::FindMaxEdges(), moab::IntxUtils::fix_degenerate_quads(), moab::TempestOnlineMap::GenerateRemappingWeights(), moab::Core::get_entities_by_dimension(), moab::TempestRemapper::GetMesh(), moab::TempestRemapper::GetMeshSet(), moab::TempestRemapper::GetOverlapAugmentedEntities(), moab::Core::globalId_tag(), ToolContext::inFilenames, moab::TempestRemapper::initialize(), moab::Range::insert(), moab::Intx2Mesh::intersect_meshes(), ToolContext::intxFilename, ToolContext::kdtreeSearch, ToolContext::mapOptions, MB_CHK_SET_ERR, MB_TAG_CREAT, MB_TAG_DENSE, MB_TYPE_DOUBLE, ToolContext::meshes, MESHSET_SET, ToolContext::meshsets, ToolContext::meshType, moab::TempestRemapper::meshValidate, MOAB_PACKAGE_VERSION, ToolContext::nlayers, ToolContext::outFilename, ToolContext::outputFormatter, moab::TempestRemapper::OVERLAP_MEMORY, moab::TempestRemapper::OVERLAP_MOAB, moab::Remapper::OverlapMesh, ToolContext::ParseCLOptions(), moab::IntxAreaUtils::positive_orientation(), ToolContext::print_diagnostics, moab::DebugOutput::printf(), moab::TempestOnlineMap::PrintMapStatistics(), ToolContext::proc_id, radius, ToolContext::rrmGrids, sample_constant(), sample_fast_harmonic(), sample_slow_harmonic(), sample_stationary_vortex(), moab::Intx2Mesh::set_box_error(), moab::Intx2Mesh::set_error_tolerance(), moab::Intx2MeshOnSphere::set_radius_destination_mesh(), moab::Intx2MeshOnSphere::set_radius_source_mesh(), moab::TempestRemapper::SetAreaMethod(), moab::TempestRemapper::SetRegionalMesh(), moab::Range::size(), ToolContext::skip_intersection, ToolContext::skip_io, moab::Remapper::SourceMesh, moab::subtract(), moab::Core::tag_get_data(), moab::Core::tag_get_handle(), moab::Remapper::TargetMesh, ToolContext::timer_pop(), ToolContext::timer_push(), ToolContext::ToolContext(), ToolContext::useGnomonicProjection, ToolContext::variableToVerify, ToolContext::verifyWeights, moab::Core::write_file(), moab::Core::write_mesh(), and moab::TempestOnlineMap::WriteParallelMap().

◆ sample_constant()

static constexpr double sample_constant ( double  ,
double   
)
inlinestaticconstexprnoexcept

Constant sample function.

Returns
double Always returns 1.0

Definition at line 2073 of file mbtempest.cpp.

2074 {
2075  return 1.0;
2076 }

Referenced by main().

◆ sample_fast_harmonic()

static double sample_fast_harmonic ( double  dLon,
double  dLat 
)
inlinestaticnoexcept

Sample function with fast harmonic variation.

Parameters
dLonLongitude in radians
dLatLatitude in radians
Returns
double Function value at (dLon, dLat)

Definition at line 2098 of file mbtempest.cpp.

2099 {
2100  const double sin2Lat = std::sin( 2.0 * dLat );
2101  return 2.0 +
2102  sin2Lat * sin2Lat * sin2Lat * sin2Lat * sin2Lat * sin2Lat * sin2Lat * sin2Lat * std::cos( 16.0 * dLon );
2103 }

Referenced by main().

◆ sample_slow_harmonic()

static double sample_slow_harmonic ( double  dLon,
double  dLat 
)
inlinestaticnoexcept

Sample function with slow harmonic variation.

Parameters
dLonLongitude in radians
dLatLatitude in radians
Returns
double Function value at (dLon, dLat)

Definition at line 2085 of file mbtempest.cpp.

2086 {
2087  const double cosLat = std::cos( dLat );
2088  return 2.0 + cosLat * cosLat * std::cos( 2.0 * dLon );
2089 }

Referenced by main().

◆ sample_stationary_vortex()

static double sample_stationary_vortex ( double  dLon,
double  dLat 
)
inlinestaticnoexcept

Sample function representing a stationary vortex.

Parameters
dLonLongitude in radians
dLatLatitude in radians
Returns
double Function value at (dLon, dLat)

Definition at line 2112 of file mbtempest.cpp.

2113 {
2114  // Find the rotated longitude and latitude of a point on a sphere
2115  // with pole at (dLonC, dLatC)
2116  const double dSinC = std::sin( VORTEX_LAT0 );
2117  const double dCosC = std::cos( VORTEX_LAT0 );
2118  const double dSinT = std::sin( dLat );
2119  const double dCosT = std::cos( dLat );
2120 
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;
2125 
2126  // Calculate new longitude and latitude in rotated coordinate system
2127  double dNewLon = std::atan2( dY, dX );
2128  if( dNewLon < 0.0 )
2129  {
2130  dNewLon += 2.0 * M_PI;
2131  }
2132  const double dNewLat = std::asin( dZ );
2133 
2134  // Calculate vortex profile
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 );
2137 
2138  // Calculate angular velocity (avoid division by zero)
2139  const double dOmega = ( dRho == 0.0 ) ? 0.0 : ( dVt / dRho );
2140 
2141  // Return the final vortex profile
2142  return ( 1.0 - std::tanh( dRho / VORTEX_D * std::sin( dNewLon - dOmega * VORTEX_T ) ) );
2143 }

References anonymous_namespace{mbtempest.cpp}::VORTEX_D, anonymous_namespace{mbtempest.cpp}::VORTEX_LAT0, anonymous_namespace{mbtempest.cpp}::VORTEX_LON0, anonymous_namespace{mbtempest.cpp}::VORTEX_R0, and anonymous_namespace{mbtempest.cpp}::VORTEX_T.

Referenced by main().