Mesh Oriented datABase  (version 5.6.0)
An array-based unstructured mesh library
moab::TempestOnlineMap Class Reference

An offline map between two Meshes. More...

#include <TempestOnlineMap.hpp>

+ Inheritance diagram for moab::TempestOnlineMap:
+ Collaboration diagram for moab::TempestOnlineMap:

Public Types

enum  DiscretizationType { DiscretizationType_FV = 0 , DiscretizationType_CGLL = 1 , DiscretizationType_DGLL = 2 , DiscretizationType_PCLOUD = 3 }
 
enum  CAASType {
  CAAS_NONE = 0 , CAAS_GLOBAL = 1 , CAAS_LOCAL = 2 , CAAS_LOCAL_ADJACENT = 3 ,
  CAAS_QLT = 4
}
 
typedef double(* sample_function) (double, double)
 

Public Member Functions

 TempestOnlineMap (moab::TempestRemapper *remapper)
 Generate the metadata associated with the offline map. More...
 
virtual ~TempestOnlineMap ()
 Define a virtual destructor. More...
 
moab::ErrorCode GenerateRemappingWeights (std::string strInputType, std::string strOutputType, const GenerateOfflineMapAlgorithmOptions &mapOptions, const std::string &srcDofTagName="GLOBAL_ID", const std::string &tgtDofTagName="GLOBAL_ID")
 Generate the offline map, given the source and target mesh and discretization details. This method generates the mapping between the two meshes based on the overlap and stores the result in the SparseMatrix. More...
 
moab::ErrorCode ReadParallelMap (const char *strSource, const std::vector< int > &tgt_dof_ids, int arearead, std::vector< double > &areaA, int &nA, std::vector< double > &areaB, int &nB)
 Generate the metadata associated with the offline map. More...
 
moab::ErrorCode WriteParallelMap (const std::string &strTarget, const std::map< std::string, std::string > &attrMap)
 Write the TempestOnlineMap to a parallel NetCDF file. More...
 
virtual int IsConsistent (double dTolerance)
 Determine if the map is first-order accurate. More...
 
virtual int IsConservative (double dTolerance)
 Determine if the map is conservative. More...
 
virtual int IsMonotone (double dTolerance)
 Determine if the map is monotone. More...
 
const DataArray1D< double > & GetGlobalSourceAreas () const
 If we computed the reduction, get the vector representing the source areas for all entities in the mesh More...
 
const DataArray1D< double > & GetGlobalTargetAreas () const
 If we computed the reduction, get the vector representing the target areas for all entities in the mesh More...
 
void PrintMapStatistics ()
 Print information and metadata about the remapping weights. More...
 
moab::ErrorCode SetDOFmapTags (const std::string srcDofTagName, const std::string tgtDofTagName)
 Store the tag names associated with global DoF ids for source and target meshes to be used for mapping.

Parameters
srcDofTagNameThe tag name associated with global DoF ids for the source mesh
tgtDofTagNameThe tag name associated with global DoF ids for the target mesh
More...
 
moab::ErrorCode SetDOFmapAssociation (DiscretizationType srcType, int srcOrder, bool isSrcContinuous, DataArray3D< int > *srcdataGLLNodes, DataArray3D< int > *srcdataGLLNodesSrc, DiscretizationType destType, int destOrder, bool isTgtContinuous, DataArray3D< int > *tgtdataGLLNodes)
 
std::pair< double, double > ApplyBoundsLimiting (std::vector< double > &dataInDouble, std::vector< double > &dataOutDouble, CAASType caasType=CAAS_GLOBAL, int caasIteration=0, double mismatch=0.0)
 
void ComputeAdjacencyRelations (std::vector< std::unordered_set< int > > &vecAdjFaces, int nrings, const Range &entities, bool useMOABAdjacencies=true, Mesh *trMesh=nullptr)
 
int GetSourceGlobalNDofs ()
 Get the number of total Degrees-Of-Freedom defined on the source mesh. More...
 
int GetDestinationGlobalNDofs ()
 Get the number of total Degrees-Of-Freedom defined on the destination mesh. More...
 
int GetSourceLocalNDofs ()
 Get the number of local Degrees-Of-Freedom defined on the source mesh. More...
 
int GetDestinationLocalNDofs ()
 Get the number of local Degrees-Of-Freedom defined on the destination mesh. More...
 
int GetSourceNDofsPerElement ()
 Get the number of Degrees-Of-Freedom per element on the source mesh. More...
 
int GetDestinationNDofsPerElement ()
 Get the number of Degrees-Of-Freedom per element on the destination mesh. More...
 
void SetSourceNDofsPerElement (int ns)
 Set the number of Degrees-Of-Freedom per element on the source mesh. More...
 
void SetDestinationNDofsPerElement (int nt)
 Get the number of Degrees-Of-Freedom per element on the destination mesh. More...
 
int GetRowGlobalDoF (int localID) const
 Get the global Degrees-Of-Freedom ID on the destination mesh. More...
 
int GetIndexOfRowGlobalDoF (int globalRowDoF) const
 Get the index of globaRowDoF. More...
 
int GetColGlobalDoF (int localID) const
 Get the global Degrees-Of-Freedom ID on the source mesh. More...
 
int GetIndexOfColGlobalDoF (int globalColDoF) const
 Get the index of globaColDoF. More...
 
moab::ErrorCode ApplyWeights (moab::Tag srcSolutionTag, moab::Tag tgtSolutionTag, bool transpose=false, CAASType caasType=CAAS_NONE, double default_projection=0.0)
 Apply the weight matrix onto the source vector (tag) provided as input, and return the column vector (solution projection) in a tag, after the map application Compute: tgtVals = A(S->T) * More...
 
moab::ErrorCode ApplyWeightsWithDualMap (moab::Tag srcSolutionTag, moab::Tag tgtSolutionTag, TempestOnlineMap *loWeightMap, CAASType caasType=CAAS_LOCAL)
 Apply the high-order weight matrix onto the source vector (tag), and then enforce bounds computed from the stencil of a separate low-order weight map using the Clip-And-Assert-Sum (CAAS) algorithm. This implements the dual-map nonlinear remapping pattern used in E3SM coupling. loWeightMap provides the low-order (monotone) map whose per-row stencil defines the min/max bounds. The CAAS filter clips the high-order result to those bounds and redistributes mass proportionally to maintain conservation. More...
 
moab::ErrorCode DefineAnalyticalSolution (moab::Tag &exactSolnTag, const std::string &solnName, Remapper::IntersectionContext ctx, sample_function testFunction, moab::Tag *clonedSolnTag=NULL, std::string cloneSolnName="")
 Define an analytical solution over the given (source or target) mesh, as specificed in the context. This routine will define a tag that is compatible with the specified discretization method type and order and sample the solution exactly using the analytical function provided by the user. More...
 
moab::ErrorCode ComputeMetrics (Remapper::IntersectionContext ctx, moab::Tag &exactTag, moab::Tag &approxTag, std::map< std::string, double > &metrics, bool verbose=true)
 Compute the error between a sampled (exact) solution and a projected solution in various error norms. More...
 
moab::ErrorCode fill_col_ids (std::vector< int > &ids_of_interest)
 
moab::ErrorCode set_col_dc_dofs (std::vector< int > &values_entities)
 
moab::ErrorCode set_row_dc_dofs (std::vector< int > &values_entities)
 
int GlobalSourceDofCount () const
 Global number of source DoFs declared by the map file (n_a), or -1 if unknown. More...
 
int CountAbsentColumns (int &first_absent_gid) const
 Count weight-matrix columns that the migrated coverage did not supply (i.e. no covering cell maps to them). For offline file maps this equals the set of source DoFs the map references but that are absent from the source mesh. Returns the count and, in first_absent_gid, one example 1-based source GID (or -1 if none). More...
 
int DropAbsentColumns ()
 Zero the weights of columns the migrated coverage did not supply, then compact the matrix. Use ONLY for columns proven globally absent (masked source grid): the projection already treats them as zero-source, so this is a BfB-safe drop that lets the dual-map CAAS bounds check skip them instead of aborting. Returns the number of columns dropped. More...
 
void SetMeshInput (Mesh *imesh)
 
const std::vector< int > & GetRowDofMap () const
 Read-only access to the matrix-row -> matrix-col DOF index maps. Used by callers (e.g. iMOAB diagnostic helpers) that need to translate matrix indices back into source/target tag-vector indices. More...
 
const std::vector< int > & GetColDofMap () const
 

Private Member Functions

moab::ErrorCode LinearRemapNN_MOAB (bool use_GID_matching=false, bool strict_check=false)
 Compute the remapping weights as a permutation matrix that relates DoFs on the source mesh to DoFs on the target mesh. More...
 
void LinearRemapFVtoFV_Tempest_MOAB (int nOrder)
 Compute the remapping weights for a FV field defined on the source to a FV field defined on the target mesh. More...
 
void LinearRemapSE0_Tempest_MOAB (const DataArray3D< int > &dataGLLNodes, const DataArray3D< double > &dataGLLJacobian)
 Generate the OfflineMap for linear conserative element-average spectral element to element average remapping. More...
 
void LinearRemapSE4_Tempest_MOAB (const DataArray3D< int > &dataGLLNodes, const DataArray3D< double > &dataGLLJacobian, int nMonotoneType, bool fContinuousIn, bool fNoConservation, bool fSparseConstraints)
 Generate the OfflineMap for cubic conserative element-average spectral element to element average remapping. More...
 
void LinearRemapFVtoGLL_MOAB (const DataArray3D< int > &dataGLLNodes, const DataArray3D< double > &dataGLLJacobian, const DataArray1D< double > &dataGLLNodalArea, int nOrder, int nMonotoneType, bool fContinuous, bool fNoConservation)
 Generate the OfflineMap for remapping from finite volumes to finite elements. More...
 
void LinearRemapGLLtoGLL2_MOAB (const DataArray3D< int > &dataGLLNodesIn, const DataArray3D< double > &dataGLLJacobianIn, const DataArray3D< int > &dataGLLNodesOut, const DataArray3D< double > &dataGLLJacobianOut, const DataArray1D< double > &dataNodalAreaOut, int nPin, int nPout, int nMonotoneType, bool fContinuousIn, bool fContinuousOut, bool fNoConservation)
 Generate the OfflineMap for remapping from finite elements to finite elements. More...
 
void LinearRemapGLLtoGLL2_Pointwise_MOAB (const DataArray3D< int > &dataGLLNodesIn, const DataArray3D< double > &dataGLLJacobianIn, const DataArray3D< int > &dataGLLNodesOut, const DataArray3D< double > &dataGLLJacobianOut, const DataArray1D< double > &dataNodalAreaOut, int nPin, int nPout, int nMonotoneType, bool fContinuousIn, bool fContinuousOut)
 Generate the OfflineMap for remapping from finite elements to finite elements (pointwise interpolation). More...
 
moab::ErrorCode WriteSCRIPMapFile (const std::string &strOutputFile, const std::map< std::string, std::string > &attrMap)
 Copy the local matrix from Tempest SparseMatrix representation (ELL) to the parallel CSR Eigen Matrix for scalable application of matvec needed for projections. More...
 
moab::ErrorCode WriteHDF5MapFile (const std::string &filename)
 Parallel I/O with NetCDF to write out the SCRIP file from multiple processors. More...
 
template<typename SparseMatrixType >
void serializeSparseMatrix (const SparseMatrixType &mat, const std::string &filename)
 
void setup_sizes_dimensions ()
 
void CAASLimiter (std::vector< double > &dataCorrectedField, std::vector< double > &dataLowerBound, std::vector< double > &dataUpperBound, double &dMass)
 
double QLTLimiter (int caasIteration, std::vector< double > &dataCorrectedField, std::vector< double > &dataLowerBound, std::vector< double > &dataUpperBound, std::vector< double > &dMassDefect)
 
moab::ErrorCode ApplyWeights (std::vector< double > &srcVals, std::vector< double > &tgtVals, bool transpose=false)
 Apply the weight matrix onto the source vector provided as input, and return the column vector (solution projection) after the map application Compute: tgtVals = A(S->T) * More...
 

Private Attributes

moab::TempestRemapper * m_remapper
 The fundamental remapping operator object. More...
 
moab::Interface * m_interface
 The reference to the moab::Core object that contains source/target and overlap sets. More...
 
moab::Tag m_dofTagSrc
 The original tag data and local to global DoF mapping to associate matrix values to solution More...
 
moab::Tag m_dofTagDest
 
std::vector< unsigned > row_gdofmap
 
std::vector< unsigned > col_gdofmap
 
std::vector< unsigned > srccol_gdofmap
 
std::vector< int > row_dtoc_dofmap
 
std::vector< int > col_dtoc_dofmap
 
std::vector< int > srccol_dtoc_dofmap
 
std::map< int, int > rowMap
 
std::map< int, int > colMap
 
int m_input_order
 
int m_output_order
 
DataArray3D< int > dataGLLNodesSrc
 
DataArray3D< int > dataGLLNodesSrcCov
 
DataArray3D< int > dataGLLNodesDest
 
DiscretizationType m_srcDiscType
 
DiscretizationType m_destDiscType
 
int m_nTotDofs_Src
 
int m_nTotDofs_SrcCov
 
int m_nTotDofs_Dest
 
int m_nTotDofs_SrcGlobal
 
int m_nDofsPEl_Src
 
int m_nDofsPEl_Dest
 
DiscretizationType m_eInputType
 
DiscretizationType m_eOutputType
 
bool m_bConserved
 
int m_iMonotonicity
 
Mesh * m_meshInput
 
Mesh * m_meshInputCov
 
Mesh * m_meshOutput
 
Mesh * m_meshOverlap
 
bool is_parallel
 
bool is_root
 
int rank
 
int size
 

Detailed Description

An offline map between two Meshes.

Definition at line 68 of file TempestOnlineMap.hpp.

Member Typedef Documentation

◆ sample_function

typedef double( * moab::TempestOnlineMap::sample_function) (double, double)

Definition at line 437 of file TempestOnlineMap.hpp.

Member Enumeration Documentation

◆ CAASType

Enumerator
CAAS_NONE 
CAAS_GLOBAL 
CAAS_LOCAL 
CAAS_LOCAL_ADJACENT 
CAAS_QLT 

Definition at line 93 of file TempestOnlineMap.hpp.

94  {
95  CAAS_NONE = 0,
96  CAAS_GLOBAL = 1,
97  CAAS_LOCAL = 2,
99  CAAS_QLT = 4
100  };

◆ DiscretizationType

Enumerator
DiscretizationType_FV 
DiscretizationType_CGLL 
DiscretizationType_DGLL 
DiscretizationType_PCLOUD 

Definition at line 84 of file TempestOnlineMap.hpp.

Constructor & Destructor Documentation

◆ TempestOnlineMap()

moab::TempestOnlineMap::TempestOnlineMap ( moab::TempestRemapper *  remapper)

Generate the metadata associated with the offline map.

Definition at line 70 of file TempestOnlineMap.cpp.

70  : OfflineMap(), m_remapper( remapper )
71 {
72  // Get the references for the MOAB core objects
74 #ifdef MOAB_HAVE_MPI
75  m_pcomm = m_remapper->get_parallel_communicator();
76 #endif
77 
78  // now let us re-update the reference to the input source mesh
80  // now let us re-update the reference to the covering mesh
82  // now let us re-update the reference to the output target mesh
84  // now let us re-update the reference to the output target mesh
86 
87  is_parallel = remapper->is_parallel;
88  is_root = remapper->is_root;
89  rank = remapper->rank;
90  size = remapper->size;
91 
92  // set default order
94 
95  // unknown until a map file is read (ReadParallelMap sets it to n_a)
97 
98  // Initialize dimension information from file
99  this->setup_sizes_dimensions();
100 }

References moab::Remapper::get_interface(), moab::TempestRemapper::GetCoveringMesh(), moab::TempestRemapper::GetMesh(), is_parallel, moab::TempestRemapper::is_parallel, is_root, moab::TempestRemapper::is_root, m_input_order, m_interface, m_meshInput, m_meshInputCov, m_meshOutput, m_meshOverlap, m_nTotDofs_SrcGlobal, m_output_order, m_remapper, moab::Remapper::OverlapMesh, rank, moab::TempestRemapper::rank, setup_sizes_dimensions(), size, moab::TempestRemapper::size, moab::Remapper::SourceMesh, and moab::Remapper::TargetMesh.

◆ ~TempestOnlineMap()

moab::TempestOnlineMap::~TempestOnlineMap ( )
virtual

Define a virtual destructor.

Definition at line 127 of file TempestOnlineMap.cpp.

128 {
129  m_interface = nullptr;
130 #ifdef MOAB_HAVE_MPI
131  m_pcomm = nullptr;
132 #endif
133  m_meshInput = nullptr;
134  m_meshOutput = nullptr;
135  m_meshOverlap = nullptr;
136 }

Member Function Documentation

◆ ApplyBoundsLimiting()

std::pair< double, double > moab::TempestOnlineMap::ApplyBoundsLimiting ( std::vector< double > &  dataInDouble,
std::vector< double > &  dataOutDouble,
CAASType  caasType = CAAS_GLOBAL,
int  caasIteration = 0,
double  mismatch = 0.0 
)

ApplyBoundsLimiting - Apply bounds limiting to the data field

Parameters
dataInDouble- input data field
dataOutDouble- output data field
caasType- type of limiter
caasIteration- iteration number of limiter
Returns
- pair of mass defect pre and post limiter application

Definition at line 632 of file TempestLinearRemap.cpp.

637 {
638  // Currently only implemented for FV to FV remapping
639  // We should generalize this to other types of remapping
640  assert( !dataGLLNodesSrcCov.IsAttached() && !dataGLLNodesDest.IsAttached() );
641 
642  std::pair< double, double > massDefect( 0.0, 0.0 );
643 
644  // Check if the source and target data are of the same size
645  const size_t nTargetCount = dataOutDouble.size();
646  const DataArray1D< double >& m_dOverlapAreas = this->m_remapper->m_overlap->vecFaceArea;
647 
648  // Apply the offline map to the data
649  double dMassDiff = 0.0;
650  std::vector< double > x( nTargetCount );
651  std::vector< double > dataLowerBound( nTargetCount );
652  std::vector< double > dataUpperBound( nTargetCount );
653  std::vector< double > massVector( nTargetCount );
654  std::vector< std::unordered_set< int > > vecSourceOvTarget( nTargetCount );
655 
656 #undef USE_ComputeAdjacencyRelations
657  constexpr bool useMOABAdjacencies = true;
658 #ifdef USE_ComputeAdjacencyRelations
659  // Compute the adjacent faces to the source face
660  // However, calling MOAB to do this does not work correctly as we need ixS to be the index
661  // Cannot just iterate over all entities in the source covering mesh
662  if( caasType == CAAS_QLT || caasType == CAAS_LOCAL_ADJACENT )
663  {
664  if( useMOABAdjacencies )
665  {
666  moab::ErrorCode rval =
667  ComputeAdjacencyRelations( vecSourceOvTarget, caasIteration, m_remapper->m_covering_source_entities,
668  useMOABAdjacencies );MB_CHK_SET_ERR_CONT( rval, "Failed to get adjacent faces" );
669  }
670  else
671  {
672  moab::ErrorCode rval =
673  ComputeAdjacencyRelations( vecSourceOvTarget, caasIteration, m_remapper->m_covering_source_entities,
674  useMOABAdjacencies, m_meshInputCov );MB_CHK_SET_ERR_CONT( rval, "Failed to get adjacent faces" );
675  }
676  }
677 #else
679 #endif
680 
681  // Initialize the bounds on the given source and target data
682  double dSourceMin = dataInDouble[0];
683  double dSourceMax = dataInDouble[0];
684  double dTargetMin = dataOutDouble[0];
685  double dTargetMax = dataOutDouble[0];
686  for( size_t i = 0; i < m_meshOverlap->faces.size(); i++ )
687  {
688  const int ixS = m_meshOverlap->vecSourceFaceIx[i];
689  const int ixT = m_meshOverlap->vecTargetFaceIx[i];
690 
691  if( ixT < 0 ) continue; // skip ghost target faces
692 
693  assert( m_dOverlapAreas[i] > 0.0 );
694  assert( ixS >= 0 );
695  assert( ixT >= 0 );
696 
697 #ifndef USE_ComputeAdjacencyRelations
698  // Compute the adjacent faces to the target face
699  vecSourceOvTarget[ixT].insert( ixS ); // map target face to source face
700  if( ( caasType == CAAS_QLT || caasType == CAAS_LOCAL_ADJACENT ) )
701  {
702  if( useMOABAdjacencies )
703  {
704  moab::Range ents;
706  moab::Range adjEnts;
707  moab::ErrorCode rval = mtu.get_bridge_adjacencies( ents, 0, 2, adjEnts, caasIteration );MB_CHK_SET_ERR_CONT( rval, "Failed to get adjacent faces" );
708  for( moab::Range::iterator it = adjEnts.begin(); it != adjEnts.end(); ++it )
709  {
710  int adjIndex = m_remapper->m_covering_source_entities.index( *it );
711  if( adjIndex >= 0 ) vecSourceOvTarget[ixT].insert( adjIndex );
712  }
713  }
714  else
715  {
716  // Compute the adjacent faces to the target face
717  AdjacentFaceVector vecAdjFaces;
718  GetAdjacentFaceVectorByEdge( *m_meshInputCov, ixS,
719  ( caasIteration ) * ( m_input_order + 1 ) * ( m_input_order + 1 ),
720  vecAdjFaces );
721 
722  //Compute min/max over neighboring faces
723  for( size_t iadj = 0; iadj < vecAdjFaces.size(); iadj++ )
724  vecSourceOvTarget[ixT].insert( vecAdjFaces[iadj].first ); // map target face to source face
725  }
726  }
727 #endif
728 
729  // Update the min and max values of the source data
730  dSourceMax = fmax( dSourceMax, dataInDouble[ixS] );
731  dSourceMin = fmin( dSourceMin, dataInDouble[ixS] );
732 
733  // Update the min and max values of the target data
734  dTargetMin = fmin( dTargetMin, dataOutDouble[ixT] );
735  dTargetMax = fmax( dTargetMax, dataOutDouble[ixT] );
736 
737  const double locMassDiff = ( dataInDouble[ixS] * m_dOverlapAreas[i] ) - // source mass
738  ( dataOutDouble[ixT] * m_dOverlapAreas[i] ); // target mass
739 
740  // Update the mass difference between source and target faces
741  // linked to the overlap mesh element
742  dMassDiff += locMassDiff; // target mass
743  massVector[ixT] += locMassDiff;
744  }
745 
746 #ifdef MOAB_HAVE_MPI
747  std::vector< double > localMinMaxDefects( 5, 0.0 ), globalMinMaxDefects( 5, 0.0 );
748  localMinMaxDefects[0] = dSourceMin;
749  localMinMaxDefects[1] = dTargetMin;
750  localMinMaxDefects[2] = dSourceMax;
751  localMinMaxDefects[3] = dTargetMax;
752  localMinMaxDefects[4] = dMassDiff;
753 
754  if( caasType == CAAS_GLOBAL )
755  {
756  MPI_Allreduce( localMinMaxDefects.data(), globalMinMaxDefects.data(), 2, MPI_DOUBLE, MPI_MIN, m_pcomm->comm() );
757  MPI_Allreduce( localMinMaxDefects.data() + 2, globalMinMaxDefects.data() + 2, 2, MPI_DOUBLE, MPI_MAX,
758  m_pcomm->comm() );
759  dSourceMin = globalMinMaxDefects[0];
760  dSourceMax = globalMinMaxDefects[2];
761  dTargetMin = globalMinMaxDefects[1];
762  dTargetMax = globalMinMaxDefects[3];
763  }
764  if( caasIteration == 1 )
765  MPI_Allreduce( localMinMaxDefects.data() + 4, globalMinMaxDefects.data() + 4, 1, MPI_DOUBLE, MPI_SUM,
766  m_pcomm->comm() );
767  else
768  globalMinMaxDefects[4] = mismatch;
769 
770  dMassDiff = localMinMaxDefects[4];
771  // massDefect.first = localMinMaxDefects[4];
772  massDefect.first = globalMinMaxDefects[4];
773 #else
774 
775  // massDefect.first = fabs( dMassDiff / ( dSourceMax - dSourceMin ) );
776  massDefect.first = dMassDiff;
777 #endif
778 
779  // Early exit if the values are monotone already.
780  // if( ( dTargetMax <= dSourceMax && dTargetMin <= dSourceMin ) || fabs( massDefect.first ) < 1e-16 )
781  if( fabs( massDefect.first ) > 1e-20 )
782  {
783  if( caasType == CAAS_GLOBAL )
784  {
785  for( size_t i = 0; i < nTargetCount; i++ )
786  {
787  dataLowerBound[i] = dSourceMin - dataOutDouble[i];
788  dataUpperBound[i] = dSourceMax - dataOutDouble[i];
789  }
790  } // if( caasType == CAAS_GLOBAL )
791  else // caasType == CAAS_LOCAL
792  {
793  // Compute the local min and max values of the target data
794  std::vector< double > vecLocalUpperBound( nTargetCount );
795  std::vector< double > vecLocalLowerBound( nTargetCount );
796  // Loop over the target faces and compute the min and max values
797  // of the source data linked to the target faces
798  for( size_t i = 0; i < nTargetCount; i++ )
799  {
800  assert( vecSourceOvTarget[i].size() );
801 
802  double dMinI = 1E10; // dataInDouble[vecSourceOvTarget[i][0]];
803  double dMaxI = -1E10; // dataInDouble[vecSourceOvTarget[i][0]];
804 
805  // Compute max over intersecting source faces
806  for( const auto& srcElem : vecSourceOvTarget[i] )
807  {
808  dMinI = fmin( dMinI, dataInDouble[srcElem] ); // min over intersecting source faces
809  dMaxI = fmax( dMaxI, dataInDouble[srcElem] ); // max over intersecting source faces
810  }
811 
812  // Update the min and max values of the target data
813  vecLocalLowerBound[i] = dMinI;
814  vecLocalUpperBound[i] = dMaxI;
815  }
816 
817  for( size_t i = 0; i < nTargetCount; i++ )
818  {
819  dataLowerBound[i] = vecLocalLowerBound[i] - dataOutDouble[i];
820  dataUpperBound[i] = vecLocalUpperBound[i] - dataOutDouble[i];
821  }
822  } // caasType == CAAS_LOCAL
823 
824  // Invoke CAAS or QLT application on the map
825  if( fabs( dMassDiff ) > 1e-20 )
826  {
827  if( caasType == CAAS_QLT )
828  dMassDiff = QLTLimiter( caasIteration, dataOutDouble, dataLowerBound, dataUpperBound, massVector );
829  else
830  CAASLimiter( dataOutDouble, dataLowerBound, dataUpperBound, dMassDiff );
831  }
832 
833  // Announce output mass
834  double dMassDiffPost = 0.0;
835  for( size_t i = 0; i < m_meshOverlap->faces.size(); i++ )
836  {
837  const int ixS = m_meshOverlap->vecSourceFaceIx[i];
838  const int ixT = m_meshOverlap->vecTargetFaceIx[i];
839 
840  if( ixT < 0 ) continue; // skip ghost target faces
841 
842  // Update the mass difference between source and target faces
843  // linked to the overlap mesh element
844  dMassDiffPost += ( dataInDouble[ixS] * m_dOverlapAreas[i] ) - // source mass
845  ( dataOutDouble[ixT] * m_dOverlapAreas[i] ); // target mass
846  }
847  // massDefect.second = fabs( dMassDiffPost / ( dSourceMax - dSourceMin ) );
848  massDefect.second = dMassDiffPost;
849  }
850 
851  // Ideally should perform an AllReduce here to get the global mass difference across all processors
852  // But if we satisfy the constraint on every task, essentially, the global mass difference should be zero!
853  return massDefect;
854 }

References moab::Range::begin(), moab::Range::end(), ErrorCode, moab::GeomUtil::first(), moab::MeshTopoUtil::get_bridge_adjacencies(), moab::Range::insert(), and MB_CHK_SET_ERR_CONT.

◆ ApplyWeights() [1/2]

moab::ErrorCode moab::TempestOnlineMap::ApplyWeights ( moab::Tag  srcSolutionTag,
moab::Tag  tgtSolutionTag,
bool  transpose = false,
CAASType  caasType = CAAS_NONE,
double  default_projection = 0.0 
)

Apply the weight matrix onto the source vector (tag) provided as input, and return the column vector (solution projection) in a tag, after the map application Compute: tgtVals = A(S->T) *

Note
Source values, or if (transpose) tgtVals = [A(T->S)]^T *
Source values

Definition at line 1602 of file TempestOnlineMap.cpp.

1607 {
1608  std::vector< double > solSTagVals;
1609  std::vector< double > solTTagVals;
1610 
1611  moab::Range sents, tents;
1613  {
1615  {
1617  solSTagVals.resize( covSrcEnts.size(), default_projection );
1618  sents = covSrcEnts;
1619  }
1620  else
1621  {
1623  solSTagVals.resize( covSrcEnts.size() * this->GetSourceNDofsPerElement() * this->GetSourceNDofsPerElement(),
1624  default_projection );
1625  sents = covSrcEnts;
1626  }
1628  {
1630  solTTagVals.resize( tgtEnts.size(), default_projection );
1631  tents = tgtEnts;
1632  }
1633  else
1634  {
1636  solTTagVals.resize( tgtEnts.size() * this->GetDestinationNDofsPerElement() *
1637  this->GetDestinationNDofsPerElement(),
1638  default_projection );
1639  tents = tgtEnts;
1640  }
1641  }
1642  else
1643  {
1646  solSTagVals.resize( covSrcEnts.size() * this->GetSourceNDofsPerElement() * this->GetSourceNDofsPerElement(),
1647  default_projection );
1648  solTTagVals.resize( tgtEnts.size() * this->GetDestinationNDofsPerElement() *
1649  this->GetDestinationNDofsPerElement(),
1650  default_projection );
1651 
1652  sents = covSrcEnts;
1653  tents = tgtEnts;
1654  }
1655 
1656  // The tag data is np*np*n_el_src
1657  MB_CHK_SET_ERR( m_interface->tag_get_data( srcSolutionTag, sents, &solSTagVals[0] ),
1658  "Getting local tag data failed" );
1659 
1660  // Compute the application of weights on the suorce solution data and store it in the
1661  // destination solution vector data Optionally, can also perform the transpose application of
1662  // the weight matrix. Set the 3rd argument to true if this is needed
1663  MB_CHK_SET_ERR( this->ApplyWeights( solSTagVals, solTTagVals, transpose ),
1664  "Applying remap operator onto source vector data failed" );
1665 
1666  // The tag data is np*np*n_el_dest
1667  MB_CHK_SET_ERR( m_interface->tag_set_data( tgtSolutionTag, tents, &solTTagVals[0] ),
1668  "Setting target tag data failed" );
1669 
1670  if( caasType != CAAS_NONE )
1671  {
1672  std::string tgtSolutionTagName;
1673  MB_CHK_SET_ERR( m_interface->tag_get_name( tgtSolutionTag, tgtSolutionTagName ), "Getting tag name failed" );
1674 
1675  // Perform CAAS iterations iteratively until convergence
1676  constexpr int nmax_caas_iterations = 10;
1677  double mismatch = 1.0;
1678  int caasIteration = 0;
1679  double initialMismatch = 0.0;
1680  while( ( fabs( mismatch / initialMismatch ) > 1e-15 && fabs( mismatch ) > 1e-15 ) &&
1681  caasIteration++ < nmax_caas_iterations ) // iterate until convergence or a maximum of 5 iterations
1682  {
1683  double dMassDiffPostGlobal;
1684  std::pair< double, double > mDefect =
1685  this->ApplyBoundsLimiting( solSTagVals, solTTagVals, caasType, caasIteration, mismatch );
1686 #ifdef MOAB_HAVE_MPI
1687  double dMassDiffPost = mDefect.second;
1688  MPI_Allreduce( &dMassDiffPost, &dMassDiffPostGlobal, 1, MPI_DOUBLE, MPI_SUM, m_pcomm->comm() );
1689 #else
1690  dMassDiffPostGlobal = mDefect.second;
1691 #endif
1692  if( caasIteration == 1 ) initialMismatch = mDefect.first;
1693  if( m_remapper->verbose && is_root )
1694  {
1695  printf( "Field {%s} -> CAAS iteration: %d, mass defect: %3.4e, post-CAAS: %3.4e\n",
1696  tgtSolutionTagName.c_str(), caasIteration, mDefect.first, dMassDiffPostGlobal );
1697  }
1698  mismatch = dMassDiffPostGlobal;
1699 
1700  // The tag data is np*np*n_el_dest
1701  MB_CHK_SET_ERR( m_interface->tag_set_data( tgtSolutionTag, tents, &solTTagVals[0] ),
1702  "Setting local tag data failed" );
1703  }
1704  }
1705 
1706  return moab::MB_SUCCESS;
1707 }

References moab::Remapper::CoveringMesh, MB_CHK_SET_ERR, MB_SUCCESS, moab::Range::size(), and moab::Remapper::TargetMesh.

Referenced by ApplyWeightsWithDualMap(), and main().

◆ ApplyWeights() [2/2]

moab::ErrorCode moab::TempestOnlineMap::ApplyWeights ( std::vector< double > &  srcVals,
std::vector< double > &  tgtVals,
bool  transpose = false 
)
private

Apply the weight matrix onto the source vector provided as input, and return the column vector (solution projection) after the map application Compute: tgtVals = A(S->T) *

Note
Source values, or if (transpose) tgtVals = [A(T->S)]^T *
Source values

Definition at line 205 of file ApplyWeights.cpp.

208 {
209  // Reset the source and target data first
210  m_rowVector.setZero();
211  m_colVector.setZero();
212 
213 #ifdef VERBOSE
214  std::stringstream sstr;
215  static int callId = 0;
216  callId++;
217  sstr << "projection_id_" << callId << "_s_" << size << "_rk_" << rank << ".txt";
218  std::ofstream output_file( sstr.str() );
219 #endif
220  // Perform the actual projection of weights: application of weight matrix onto the source
221  // solution vector
222 
223  if( transpose )
224  {
225  // Permute the source data first
226  for( unsigned i = 0; i < srcVals.size(); ++i )
227  {
228  if( row_dtoc_dofmap[i] >= 0 )
229  m_rowVector( row_dtoc_dofmap[i] ) = srcVals[i]; // permute and set the row (source) vector properly
230  }
231 
232  // Now apply the adjoint operator: m_colVector = m_weightMatrix.adjoint() * m_rowVector;
233  deterministicSparseMatTransposeVecMulClean( m_weightMatrix, m_rowVector, m_colVector );
234  // deterministicSparseMatTransposeVecMul( m_weightMatrix, m_rowVector, m_colVector );
235  // deterministicSparseMatTransposeVecMulNative( m_weightMatrix, m_rowVector, m_colVector );
236 
237  // Permute the resulting target data back
238  for( unsigned i = 0; i < tgtVals.size(); ++i )
239  {
240  if( col_dtoc_dofmap[i] >= 0 )
241  tgtVals[i] = m_colVector( col_dtoc_dofmap[i] ); // permute and set the row (source) vector properly
242  }
243  }
244  else
245  {
246 #ifdef VERBOSE
247  output_file << "ColVector: " << m_colVector.size() << ", SrcVals: " << srcVals.size()
248  << ", Sizes: " << m_nTotDofs_SrcCov << ", " << col_dtoc_dofmap.size() << "\n";
249 #endif
250  for( unsigned i = 0; i < srcVals.size(); ++i )
251  {
252  if( col_dtoc_dofmap[i] >= 0 )
253  m_colVector( col_dtoc_dofmap[i] ) = srcVals[i]; // permute and set the row (source) vector properly
254 #ifdef VERBOSE
255  output_file << i << " " << col_gdofmap[col_dtoc_dofmap[i]] + 1 << " " << srcVals[i] << "\n";
256 #endif
257  }
258 
259  // Now apply the operator: m_rowVector = m_weightMatrix * m_colVector;
260  deterministicSparseMatVecMulClean( m_weightMatrix, m_colVector, m_rowVector );
261  // deterministicSparseMatVecMul( m_weightMatrix, m_colVector, m_rowVector );
262  // deterministicSparseMatVecMulNative( m_weightMatrix, m_colVector, m_rowVector );
263  // deterministicSparseMatVecMulKahan( m_weightMatrix, m_colVector, m_rowVector );
264 
265  // Permute the resulting target data back
266 #ifdef VERBOSE
267  output_file << "RowVector: " << m_rowVector.size() << ", TgtVals:" << tgtVals.size()
268  << ", Sizes: " << m_nTotDofs_Dest << ", " << row_gdofmap.size() << "\n";
269 #endif
270  for( unsigned i = 0; i < tgtVals.size(); ++i )
271  {
272  if( row_dtoc_dofmap[i] >= 0 )
273  {
274  tgtVals[i] = m_rowVector( row_dtoc_dofmap[i] ); // permute and set the row (source) vector properly
275 #ifdef VERBOSE
276  output_file << i << " " << row_gdofmap[row_dtoc_dofmap[i]] + 1 << " " << tgtVals[i] << "\n";
277 #endif
278  }
279  }
280  }
281 
282  // if( caasType != CAAS_NONE )
283  // {
284  // constexpr int nmax_caas_iterations = 5;
285  // double mismatch = 1.0;
286  // int caasIteration = 0;
287  // while( mismatch > 1e-15 &&
288  // caasIteration++ < nmax_caas_iterations ) // iterate until convergence or a maximum of 5 iterations
289  // {
290  // std::pair< double, double > mDefect = this->ApplyCAASLimiting( srcVals, tgtVals, caasType );
291  // if( m_remapper->verbose )
292  // printf( "Rank %d: -- Iteration: %d, Net original mass defect: %3.4e, mass defect post-CAAS: %3.4e\n",
293  // m_remapper->rank, caasIteration, mDefect.first, mDefect.second );
294  // mismatch = mDefect.second;
295  // }
296  // }
297 
298 #ifdef VERBOSE
299  output_file.flush(); // required here
300  output_file.close();
301 #endif
302 
303  // All done with matvec application
304  return moab::MB_SUCCESS;
305 }

References col_dtoc_dofmap, col_gdofmap, deterministicSparseMatTransposeVecMulClean(), deterministicSparseMatVecMulClean(), m_nTotDofs_Dest, m_nTotDofs_SrcCov, MB_SUCCESS, rank, row_dtoc_dofmap, row_gdofmap, and size.

◆ ApplyWeightsWithDualMap()

moab::ErrorCode moab::TempestOnlineMap::ApplyWeightsWithDualMap ( moab::Tag  srcSolutionTag,
moab::Tag  tgtSolutionTag,
TempestOnlineMap *  loWeightMap,
CAASType  caasType = CAAS_LOCAL 
)

Apply the high-order weight matrix onto the source vector (tag), and then enforce bounds computed from the stencil of a separate low-order weight map using the Clip-And-Assert-Sum (CAAS) algorithm. This implements the dual-map nonlinear remapping pattern used in E3SM coupling. loWeightMap provides the low-order (monotone) map whose per-row stencil defines the min/max bounds. The CAAS filter clips the high-order result to those bounds and redistributes mass proportionally to maintain conservation.

Definition at line 1709 of file TempestOnlineMap.cpp.

1713 {
1714  // Setup entity ranges (same pattern as ApplyWeights(Tag, Tag))
1715  std::vector< double > solSTagVals, solTTagVals;
1716  moab::Range sents, tents;
1717 
1719  {
1721  {
1723  solSTagVals.resize( covSrcEnts.size(), 0.0 );
1724  sents = covSrcEnts;
1725  }
1726  else
1727  {
1729  solSTagVals.resize( covSrcEnts.size() * this->GetSourceNDofsPerElement() *
1730  this->GetSourceNDofsPerElement(),
1731  0.0 );
1732  sents = covSrcEnts;
1733  }
1735  {
1737  solTTagVals.resize( tgtEnts.size(), 0.0 );
1738  tents = tgtEnts;
1739  }
1740  else
1741  {
1743  solTTagVals.resize( tgtEnts.size() * this->GetDestinationNDofsPerElement() *
1744  this->GetDestinationNDofsPerElement(),
1745  0.0 );
1746  tents = tgtEnts;
1747  }
1748  }
1749  else
1750  {
1753  solSTagVals.resize( covSrcEnts.size() * this->GetSourceNDofsPerElement() * this->GetSourceNDofsPerElement(),
1754  0.0 );
1755  solTTagVals.resize(
1756  tgtEnts.size() * this->GetDestinationNDofsPerElement() * this->GetDestinationNDofsPerElement(), 0.0 );
1757  sents = covSrcEnts;
1758  tents = tgtEnts;
1759  }
1760 
1761  // Read source tag data from coverage mesh
1762  MB_CHK_SET_ERR( m_interface->tag_get_data( srcSolutionTag, sents, &solSTagVals[0] ),
1763  "Getting source tag data failed" );
1764 
1765  // Apply high-order projection only (no CAAS — bounds come from the low-order map)
1766  MB_CHK_SET_ERR( this->ApplyWeights( solSTagVals, solTTagVals, false ),
1767  "High-order projection failed" );
1768 
1769  // Write initial projection to target tag
1770  MB_CHK_SET_ERR( m_interface->tag_set_data( tgtSolutionTag, tents, &solTTagVals[0] ),
1771  "Setting target tag data failed" );
1772 
1773  if( caasType == CAAS_NONE || loWeightMap == nullptr ) return moab::MB_SUCCESS;
1774 
1775  // =====================================================================
1776  // Dual-map CAAS (Clip-And-Assured-Sum) — bit-for-bit port of MCT's
1777  // seq_nlmap_avNormArr (driver-mct/main/seq_nlmap_mod.F90).
1778  //
1779  // PURPOSE
1780  // Conservative, bounds-preserving remap of a source field x onto a
1781  // target mesh using TWO weight matrices: a high-order
1782  // non-monotone map (A, = `this`) and a low-order monotone &
1783  // conservative map (Am, = `loWeightMap`). The high-order map gives
1784  // accuracy; the low-order map gives the conservation reference and
1785  // the bounds-preservation safety net. This routine wires them
1786  // together using the Clip-And-Assured-Sum scheme of
1787  // Bradley, Bosler & Guba, "Conservation with bounded variation
1788  // and limiters in semi-Lagrangian transport schemes",
1789  // SIAM J. Sci. Comput. 41(5), 2019, doi:10.1137/18M1165414.
1790  //
1791  // NOTATION (matching the reference)
1792  // x source field values on the coverage mesh (solSTagVals)
1793  // A high-order map (this->m_weightMatrix)
1794  // Am low-order map (loWeightMap)
1795  // y_hi = A * x high-order projection (in solTTagVals)
1796  // y_lo = Am * x low-order projection (mass reference)
1797  // [lo, hi] per-row source-value bounds taken over A's stencil
1798  // gmins/gmaxs unscaled global min/max of the per-row [lo, hi] —
1799  // used as a final safety clip
1800  // norm8wt fractional-coverage weight (one scalar per source cell)
1801  // propagated by the E3SM driver as a side-channel tag;
1802  // when present, all bounds & redistribution arithmetic is
1803  // rescaled to match MCT's lnorm=.true. branch exactly
1804  //
1805  // ALGORITHM (one pass, FP-order-preserved vs MCT)
1806  // 1) y_hi = A * x (done above, in solTTagVals)
1807  // 2) y_lo = Am * x [Step 2]
1808  // 2b) Pull source norm8wt side-channel tag if present [Step 2b]
1809  // 3) Per-row bounds [lo, hi] over A's stencil columns [Step 3]
1810  // Divide source value by srcNorm8wt before tracking
1811  // min/max so bounds are over RECOVERED x, not (frac*x).
1812  // 4) y_lo == 0 mask: where the low-order projection is zero,
1813  // force y_hi = lo = hi = 0 to drop the cell. [Step 4]
1814  // 4b) Snapshot UNSCALED global extrema gmins/gmaxs from the
1815  // masked, but not-yet-norm-scaled, per-row [lo, hi]. [Step 4b]
1816  // 4c) mappedNorm8wt = Am * srcNorm8wt (or Am * 1 if absent). [Step 4c]
1817  // 4d) Scale per-row bounds: lo *= mappedNorm8wt, hi *= ... [Step 4d]
1818  // 5) Per-cell CAAS quantities (clipping defect, room to lower/raise) [Step 5]
1819  // 6) Reproducible global reductions of the per-cell quantities [Step 7]
1820  // 7) dM_total = dM_clip + (M_low - M_hi_unclipped) [Step 8]
1821  // 8) Redistribute the deficit across cells with room. [Step 9]
1822  // 9) Final hard clip to gmins/gmaxs (skipping yLow==0 cells).
1823  //
1824  // REPRODUCIBILITY MODEL
1825  // "BfB with MCT" means: for the same inputs, this routine produces
1826  // the same target values MCT produces, BIT FOR BIT, regardless of
1827  // MPI rank count or mesh decomposition. This requires three things
1828  // that the code below enforces explicitly:
1829  //
1830  // (i) Same area values. MCT uses 'aream' = area_b from the netcdf
1831  // map file. We read the same MOAB 'aream' tag (loaded by
1832  // iMOAB_LoadMapFile). Recomputing spherical-polygon areas via
1833  // lHuiller from mesh geometry is FP-different and is only used
1834  // as a final fallback for online-computed maps with no aream.
1835  //
1836  // (ii) Same FP operation order in the per-cell arithmetic. The
1837  // redistribute step computes `(hi - yc)/cap_g * dM_total`, NOT
1838  // the algebraically-equivalent `(hi - yc) * (dM_total/cap_g)`.
1839  // See Step 9 below for why. The dM_total computation also uses
1840  // MCT's exact two-step form (subtract, then add), preserving
1841  // the catastrophic-cancellation residual MCT carries.
1842  //
1843  // (iii) Order-independent global reductions. We use Worley's
1844  // IntegerReprosum (the MOAB port of shr_reprosum_int), which
1845  // is MCT's default reprosum path. It is decomposition- and
1846  // order-independent by construction (integer-vector MPI sum).
1847  // A Kahan + sort-by-gid summation lambda is also defined
1848  // below as a reference alternative but is not the active
1849  // reducer — using two different algorithms would defeat BfB.
1850  //
1851  // GUARDRAILS
1852  // Bounds extraction (Step 3) hard-aborts the run if any owned
1853  // high-order row references a coverage column not present on this
1854  // rank. Silently dropping such columns would produce
1855  // decomposition-dependent bounds and break BfB. The error message
1856  // tells the caller exactly which row/column/weight failed and
1857  // recommends widening the ghost-layer count (nghlay_cov in the
1858  // E3SM coupler driver). See lines below the bounds loop.
1859  //
1860  // EARLY RETURN
1861  // If caasType == CAAS_NONE or loWeightMap is null, we keep the raw
1862  // high-order projection that was already written to tgtSolutionTag
1863  // above. The dual-map machinery only runs when the caller explicitly
1864  // activates it with a non-null low-order map and a non-CAAS_NONE
1865  // filter type.
1866 
1867  const size_t nTargetDofs = solTTagVals.size();
1868  const size_t nSourceDofs = solSTagVals.size();
1869 
1870  // Map from target tag index to matrix row index. Both A and Am must share
1871  // the same row layout (same target mesh, same partitioning); this is true
1872  // because both maps are loaded onto the same intersection application.
1873  if( row_dtoc_dofmap.size() < nTargetDofs )
1874  {
1875  MB_CHK_SET_ERR( moab::MB_FAILURE, "row_dtoc_dofmap smaller than target tag size" );
1876  }
1877 
1878  // ----- Step 2: low-order projection y_lo = Am * x ---------------------
1879  std::vector< double > yLow( nTargetDofs, 0.0 );
1880  MB_CHK_SET_ERR( loWeightMap->ApplyWeights( solSTagVals, yLow, false ),
1881  "Low-order projection failed" );
1882 
1883  // ----- Step 2b: pull source norm8wt side-channel (if present) ---------
1884  //
1885  // BACKGROUND
1886  // When the E3SM driver coupler asks for a normalized projection
1887  // (lnorm=.true.), it does NOT send raw source values x to the
1888  // remapper. Instead, in seq_map_avNormArr it pre-multiplies each
1889  // source data field by a fractional-coverage weight `frac`, and
1890  // sends the products (frac * x) over to the intersection app
1891  // together with a parallel single-component tag named "norm8wt"
1892  // that carries (frac) on each source coverage cell.
1893  //
1894  // The driver later UN-DOES this pre-norm on the target side by
1895  // dividing each mapped data field by the mapped norm8wt — so the
1896  // final value on the target is (Am*(frac*x)) / (Am*frac). That
1897  // per-cell weighted average is the conservative answer when source
1898  // cells are only partially covered (e.g. land/ocean coastlines).
1899  //
1900  // WHY THE CAAS KERNEL NEEDS TO SEE norm8wt
1901  // To match MCT's seq_nlmap_avNormArr bit-for-bit, two things have
1902  // to happen INSIDE the CAAS kernel — neither can be done by the
1903  // driver as a post-pass:
1904  //
1905  // (a) Per-row bounds [lo, hi] must be the min/max of RECOVERED x
1906  // over the high-order stencil, not the min/max of (frac*x).
1907  // MCT does
1908  // tmp = solSTagVals[srcIdx]
1909  // tmp = tmp / xPrimeAV(natt+1, col) ! divide by frac
1910  // in sMat_avMult_and_calc_bounds before extending bounds, and
1911  // skips columns where frac == 0 (the field can't say anything
1912  // meaningful at a cell with no source coverage). Without this
1913  // divide, bounds would be 0-suppressed in coastal regions and
1914  // the CAAS clip would lose accuracy.
1915  //
1916  // (b) The mapped-norm8wt scale factor used in Step 4d must be the
1917  // LOW-ORDER projection of the ACTUAL source `frac`, not the
1918  // low-order projection of constant-1. MCT computes this in
1919  // the same mct_sMat_avMult call that produces avp_o data — the
1920  // natt+1 column gets sum_l w_lo[j,l] * frac(l), and that's
1921  // what the bounds get scaled by.
1922  //
1923  // FALLBACK
1924  // If no "norm8wt" tag exists on the intersection-side mesh (callers
1925  // that never pre-normed), we set hasNorm8wt=false. In that branch
1926  // srcNorm8wt is treated as constant-1 for both (a) and (b), which is
1927  // mathematically correct: with no pre-norm, frac would have been
1928  // 1.0 everywhere and the divide / scale are no-ops.
1929  //
1930  // SHAPE CONSTRAINT
1931  // norm8wt is single-component (one double per source coverage cell).
1932  // For the FV-FV configuration on the active CAAS path,
1933  // sents.size() == nSourceDofs and the tag values map directly to
1934  // solSTagVals indices. If a future caller wires an SE source layout
1935  // where nSourceDofs > sents.size() (multi-DOF per cell), the
1936  // per-cell norm8wt cannot be unambiguously expanded to per-DOF
1937  // values here — we deliberately fall back to the constant-1 path
1938  // rather than guess an expansion that would silently break BfB.
1939  std::vector< double > srcNorm8wt;
1940  bool hasNorm8wt = false;
1941  {
1942  moab::Tag normTag = nullptr;
1943  moab::ErrorCode rvalN = m_interface->tag_get_handle( "norm8wt", normTag );
1944  if( MB_SUCCESS == rvalN && normTag != nullptr )
1945  {
1946  // Single-component tag (one double per source coverage entity).
1947  // For FV-FV (the only configuration on the active CAAS path)
1948  // sents.size() == nSourceDofs. If the source layout is multi-DOF
1949  // (e.g. SE) the per-cell norm8wt cannot be unambiguously expanded
1950  // to per-DOF values here; bail to the constant-1 fallback rather
1951  // than guess.
1952  srcNorm8wt.resize( sents.size(), 0.0 );
1953  moab::ErrorCode rvalD = m_interface->tag_get_data( normTag, sents, &srcNorm8wt[0] );
1954  if( MB_SUCCESS == rvalD && srcNorm8wt.size() == nSourceDofs )
1955  {
1956  hasNorm8wt = true;
1957  }
1958  else
1959  {
1960  srcNorm8wt.clear();
1961  }
1962  }
1963  }
1964 
1965  // ----- Step 3: per-row bounds from HIGH-ORDER stencil -----------------
1966  // bounds(A, x): for each target row r, [lo, hi] = [min, max] of x over
1967  // A(r,:)'s nonzero columns. The proof in the reference paper requires
1968  // bounds to come from the larger (high-order) stencil so the constraint
1969  // set is provably nonempty.
1970  std::vector< double > lcl_lo( nTargetDofs, 1e308 );
1971  std::vector< double > lcl_hi( nTargetDofs, -1e308 );
1972 
1973  WeightMatrix& hiW = this->m_weightMatrix;
1974  for( size_t i = 0; i < nTargetDofs; i++ )
1975  {
1976  int r = row_dtoc_dofmap[i];
1977  if( r < 0 || r >= hiW.outerSize() ) continue;
1978  for( WeightMatrix::InnerIterator it( hiW, r ); it; ++it )
1979  {
1980  // it.col() is a matrix column index; map it to source vector index
1981  // by inverting col_dtoc_dofmap. For FV-FV with cell-based DOFs
1982  // and one-to-one mapping, this is the identity for owned columns.
1983  int mc = (int)it.col();
1984  // Search col_dtoc_dofmap[k]==mc; for typical FV cases the mapping
1985  // is dense and contiguous, so a linear scan over solSTagVals is
1986  // avoided by precomputing an inverse (below). For correctness we
1987  // fall back to scanning if the inverse is not available.
1988  // Build inverse once outside the loop (see below).
1989  (void)mc;
1990  }
1991  }
1992 
1993  // Precompute matrix-col -> source-vector-index inverse (cached per call;
1994  // O(nSourceDofs) construction). col_dtoc_dofmap has size nSourceDofs and
1995  // maps source-vector-index -> matrix-col.
1996  int maxMatCol = -1;
1997  for( size_t k = 0; k < nSourceDofs && k < col_dtoc_dofmap.size(); k++ )
1998  if( col_dtoc_dofmap[k] > maxMatCol ) maxMatCol = col_dtoc_dofmap[k];
1999  std::vector< int > col_inv( maxMatCol + 1, -1 );
2000  for( size_t k = 0; k < nSourceDofs && k < col_dtoc_dofmap.size(); k++ )
2001  if( col_dtoc_dofmap[k] >= 0 ) col_inv[col_dtoc_dofmap[k]] = (int)k;
2002 
2003  // Track whether any owned row's high-order stencil column failed to
2004  // resolve into the local coverage source vector. If that happens the
2005  // [lcl_lo, lcl_hi] bounds are computed over an INCOMPLETE stencil and
2006  // the CAAS clip + redistribute will produce decomposition-dependent
2007  // values — exactly the symptom seen as 1-2 ULP cross-rank-count drift
2008  // on file-loaded maps. Fail loudly with the offending coordinates so
2009  // the coverage layout (nghlay_cov in the calling code) can be widened.
2010  int bndsLocalErr = 0;
2011  int bndsFirstRowG = -1; // global target row id where the first failure happened
2012  int bndsFirstMc = -1; // matrix col index that failed to resolve
2013  double bndsFirstWgt = 0.0; // the dropped (nonzero) weight value
2014  int bndsFirstKind = 0; // 1 = mc out of maxMatCol; 2 = col_inv -> -1; 3 = srcIdx OOB
2015 
2016  for( size_t i = 0; i < nTargetDofs; i++ )
2017  {
2018  int r = row_dtoc_dofmap[i];
2019  if( r < 0 || r >= hiW.outerSize() ) continue;
2020  for( WeightMatrix::InnerIterator it( hiW, r ); it; ++it )
2021  {
2022  // Skip explicit-zero entries. Eigen's InnerIterator visits any
2023  // stored coefficient regardless of value; TempestRemap offline
2024  // maps routinely emit explicit zeros. MCT's
2025  // sMat_avMult_and_calc_bounds explicitly does
2026  // if (wgt == 0) cycle
2027  // before extending bounds (seq_nlmap_mod.F90:855). We can't use
2028  // an exact-zero compare here (FP-fragile), but 1e-50 is below
2029  // any physically meaningful map weight while still robust to
2030  // sign and denormal noise — entries this small can't shift the
2031  // per-row [lo, hi] enough to cross a clip threshold either.
2032  if( fabs( it.value() ) < 1e-50 ) continue;
2033  const int mc = (int)it.col();
2034 
2035  // Hard checks: a nonzero high-order weight at column mc means
2036  // this owned row genuinely depends on source-coverage column mc.
2037  // If we cannot resolve mc to a local source-vector index, the
2038  // 3-ring coverage on this rank is too narrow for the high-order
2039  // stencil. Either the caller asked for too few ghost layers,
2040  // or the map file references columns not present in any rank's
2041  // coverage (a catastrophic mismatch). Either way, silently
2042  // skipping corrupts the bounds and breaks BFB.
2043  if( mc < 0 || mc > maxMatCol )
2044  {
2045  if( !bndsLocalErr )
2046  {
2047  bndsLocalErr = 1;
2048  bndsFirstRowG = (r >= 0 && r < (int)row_gdofmap.size()) ? (int)row_gdofmap[r] : -1;
2049  bndsFirstMc = mc;
2050  bndsFirstWgt = it.value();
2051  bndsFirstKind = 1;
2052  }
2053  continue;
2054  }
2055  const int srcIdx = col_inv[mc];
2056  if( srcIdx < 0 )
2057  {
2058  if( !bndsLocalErr )
2059  {
2060  bndsLocalErr = 1;
2061  bndsFirstRowG = (r >= 0 && r < (int)row_gdofmap.size()) ? (int)row_gdofmap[r] : -1;
2062  bndsFirstMc = mc;
2063  bndsFirstWgt = it.value();
2064  bndsFirstKind = 2;
2065  }
2066  continue;
2067  }
2068  if( srcIdx >= (int)nSourceDofs )
2069  {
2070  if( !bndsLocalErr )
2071  {
2072  bndsLocalErr = 1;
2073  bndsFirstRowG = (r >= 0 && r < (int)row_gdofmap.size()) ? (int)row_gdofmap[r] : -1;
2074  bndsFirstMc = mc;
2075  bndsFirstWgt = it.value();
2076  bndsFirstKind = 3;
2077  }
2078  continue;
2079  }
2080  // solSTagVals[srcIdx] holds (frac * x) when the driver pre-normed
2081  // (hasNorm8wt true); divide by frac to recover x for bounds, and
2082  // skip the source cell when frac == 0 (matches MCT
2083  // seq_nlmap_mod.F90:857 "if xPrimeAV(natt+1,col) == 0 cycle").
2084  // When hasNorm8wt is false, solSTagVals already holds raw x.
2085  double v = solSTagVals[srcIdx];
2086  if( hasNorm8wt )
2087  {
2088  const double n = srcNorm8wt[srcIdx];
2089  if( fabs(n) < 1E-20 ) continue;
2090  v /= n;
2091  }
2092  if( v < lcl_lo[i] ) lcl_lo[i] = v;
2093  if( v > lcl_hi[i] ) lcl_hi[i] = v;
2094  }
2095  // If row had no nonzero columns in the high-order stencil, set bounds
2096  // to 0 (matching MCT's sMat_avMult_and_calc_bounds: "lop = 0; hip = 0").
2097  // Together with the y_lo == 0 masking step below, this forces the cell
2098  // to 0 — a "rare, local reduction in order to one" per the reference.
2099  if( lcl_lo[i] > lcl_hi[i] )
2100  {
2101  lcl_lo[i] = 0.0;
2102  lcl_hi[i] = 0.0;
2103  }
2104  }
2105 
2106  // Globalize: any rank with bndsLocalErr triggers a collective failure.
2107 #ifdef MOAB_HAVE_MPI
2108  {
2109  MPI_Comm comm = m_pcomm ? m_pcomm->comm() : MPI_COMM_SELF;
2110  int bndsGlobalErr = 0;
2111  MPI_Allreduce( &bndsLocalErr, &bndsGlobalErr, 1, MPI_INT, MPI_MAX, comm );
2112  if( bndsGlobalErr )
2113  {
2114  int myRank = 0;
2115  MPI_Comm_rank( comm, &myRank );
2116  if( bndsLocalErr )
2117  {
2118  static const char* kindStr[4] = { "?", "mc>maxMatCol", "col_inv[mc]==-1", "srcIdx>=nSourceDofs" };
2119  fprintf( stderr,
2120  "FATAL: ApplyWeightsWithDualMap bounds extraction dropped a nonzero "
2121  "high-order stencil column on rank %d.\n"
2122  " global_target_row=%d matrix_col=%d weight=%.17e reason=%s\n"
2123  " This means the source coverage on this rank does NOT contain a "
2124  "column the owned high-order row references — the 3-ring (or whatever) "
2125  "ghost layer setting is too narrow, or the map file was generated against "
2126  "a different mesh. Bounds computed over an incomplete stencil break BFB; "
2127  "aborting rather than silently producing wrong CAAS output.\n",
2128  myRank, bndsFirstRowG, bndsFirstMc, bndsFirstWgt, kindStr[bndsFirstKind] );
2129  fflush( stderr );
2130  }
2131  MPI_Abort( comm, 1 );
2132  }
2133  }
2134 #else
2135  if( bndsLocalErr )
2136  {
2137  static const char* kindStr[4] = { "?", "mc>maxMatCol", "col_inv[mc]==-1", "srcIdx>=nSourceDofs" };
2138  fprintf( stderr,
2139  "FATAL: ApplyWeightsWithDualMap bounds extraction dropped a nonzero "
2140  "high-order stencil column.\n"
2141  " global_target_row=%d matrix_col=%d weight=%.17e reason=%s\n",
2142  bndsFirstRowG, bndsFirstMc, bndsFirstWgt, kindStr[bndsFirstKind] );
2143  fflush( stderr );
2144  return moab::MB_FAILURE;
2145  }
2146 #endif
2147 
2148  // ----- Step 4: mask -- where y_lo == 0, zero out y_hi and bounds ------
2149  // (Per reference: "An exact 0 in the low-order field will mask the
2150  // high-order field unnecessarily, but that's OK: it's a rare, local
2151  // reduction in order to one, not a wrong value.")
2152  for( size_t i = 0; i < nTargetDofs; i++ )
2153  {
2154  if( yLow[i] == 0.0 )
2155  {
2156  solTTagVals[i] = 0.0;
2157  lcl_lo[i] = 0.0;
2158  lcl_hi[i] = 0.0;
2159  }
2160  }
2161 
2162  // ----- Step 4b: compute UNSCALED global extrema for the final safety
2163  // clip (Item 4). MCT's seq_nlmap_avNormArr clips the redistributed result
2164  // against gmins/gmaxs = global min/max of the per-row (post-mask) bounds,
2165  // not against the per-row bounds themselves. Snapshot here, BEFORE the
2166  // bounds get scaled by the mapped norm in Step 4d below.
2167  double g_lo = 1e308, g_hi = -1e308;
2168  for( size_t i = 0; i < nTargetDofs; i++ )
2169  {
2170  int r = row_dtoc_dofmap[i];
2171  if( r < 0 || r >= (int)row_gdofmap.size() ) continue; // not owned
2172  if( lcl_lo[i] < g_lo ) g_lo = lcl_lo[i];
2173  if( lcl_hi[i] > g_hi ) g_hi = lcl_hi[i];
2174  }
2175 #ifdef MOAB_HAVE_MPI
2176  {
2177  MPI_Comm comm = m_pcomm ? m_pcomm->comm() : MPI_COMM_SELF;
2178  double tmp_min = g_lo, tmp_max = g_hi;
2179  MPI_Allreduce( &tmp_min, &g_lo, 1, MPI_DOUBLE, MPI_MIN, comm );
2180  MPI_Allreduce( &tmp_max, &g_hi, 1, MPI_DOUBLE, MPI_MAX, comm );
2181  }
2182 #endif
2183 
2184  // ----- Step 4c: compute mapped norm8wt = low-order map applied to a
2185  // source-norm8wt vector. For target row i this equals
2186  // sum_l w_lo[i,l] * srcNorm8wt(l)
2187  // — equivalently, the value MCT carries in the natt+1 column of avp_o
2188  // after mct_sMat_avMult is applied to avp_i (whose norm8wt slot holds
2189  // frac post-pre-norm). When no "norm8wt" tag is available on the intx
2190  // side, fall back to applying the low-order map to a constant-1 vector
2191  // (equivalent to srcNorm8wt(l) == 1 everywhere — consistent with the
2192  // bounds-extraction fallback above).
2193  std::vector< double > mappedNorm8wt( nTargetDofs, 0.0 );
2194  if( hasNorm8wt )
2195  {
2196  MB_CHK_SET_ERR( loWeightMap->ApplyWeights( srcNorm8wt, mappedNorm8wt, false ),
2197  "Mapped-norm8wt computation (low-order on source norm8wt) failed" );
2198  }
2199  else
2200  {
2201  std::vector< double > srcOnes( nSourceDofs, 1.0 );
2202  MB_CHK_SET_ERR( loWeightMap->ApplyWeights( srcOnes, mappedNorm8wt, false ),
2203  "Mapped-norm8wt computation (low-order on ones) failed" );
2204  }
2205 
2206  // ----- Step 4d: scale per-row bounds by mapped norm8wt (Item 2).
2207  // MCT does this inside the CAAS loop:
2208  // if (lnorm) then
2209  // lo = lo*avp_o%rAttr(natt+1,j)
2210  // hi = hi*avp_o%rAttr(natt+1,j)
2211  // end if
2212  // Doing it once here propagates correctly into Step 5 (clipping) and
2213  // Step 9 (redistribution) which both use lcl_lo/lcl_hi. Note: g_lo/g_hi
2214  // were already snapshotted above and remain UNSCALED (matching MCT's
2215  // gmins/gmaxs which are the global min/max of the unscaled per-row bounds).
2216  for( size_t i = 0; i < nTargetDofs; i++ )
2217  {
2218  const double w = mappedNorm8wt[i];
2219  lcl_lo[i] *= w;
2220  lcl_hi[i] *= w;
2221  }
2222 
2223  // ----- Get target areas (per matrix-row) ------------------------------
2224  // For BFB with MCT we MUST use the same area values MCT does. MCT uses
2225  // 'aream' (= area_b from the netcdf map file, loaded once when the map
2226  // is read). iMOAB_LoadMapFile populates the 'aream' tag on the target
2227  // mesh from area_b when arearead != 0 (e.g. arearead=3 for F-maps).
2228  //
2229  // The CAAS path MUST NOT recompute spherical-polygon areas from mesh
2230  // geometry via lHuiller (or any other re-derivation): doing so differs
2231  // from area_b at FP precision and silently breaks BfB with MCT. If the
2232  // caller has not loaded an area-bearing map and there are no online
2233  // areas (m_dTargetAreas) either, fail the run loudly so the caller can
2234  // fix their map-load configuration instead of getting silent non-BfB
2235  // results.
2236  std::vector< double > tgtAreas( nTargetDofs, 0.0 );
2237  {
2238  std::vector< moab::EntityHandle > tentVec;
2239  tentVec.reserve( tents.size() );
2240  for( moab::Range::iterator it = tents.begin(); it != tents.end(); ++it )
2241  tentVec.push_back( *it );
2242 
2243  bool got_areas = false;
2244 
2245  // Preferred: pull the 'aream' tag from the target MOAB mesh — this
2246  // is the area_b value loaded by iMOAB_LoadMapFile and is byte-identical
2247  // to the 'aream' field MCT uses in seq_nlmap_avNormArr.
2248  moab::Tag aream_tag = nullptr;
2249  moab::ErrorCode rval = m_interface->tag_get_handle( "aream", aream_tag );
2250  if( MB_SUCCESS == rval && aream_tag != nullptr && !tentVec.empty() )
2251  {
2252  const size_t nents = std::min< size_t >( tentVec.size(), nTargetDofs );
2253  std::vector< double > aream_vals( nents, 0.0 );
2254  rval = m_interface->tag_get_data( aream_tag, &tentVec[0], (int)nents, &aream_vals[0] );
2255  if( MB_SUCCESS == rval )
2256  {
2257  for( size_t i = 0; i < nents; i++ ) tgtAreas[i] = aream_vals[i];
2258  got_areas = true;
2259  }
2260  }
2261 
2262  // Fallback: areas were computed online and live in OfflineMap's
2263  // m_dTargetAreas (indexed by matrix row). This is BFB with MCT only
2264  // when the same online-area code path is used on both couplers; it
2265  // is acceptable for runs that build the map online.
2266  if( !got_areas )
2267  {
2268  const DataArray1D< double >& dTargetAreas = this->GetTargetAreas();
2269  const size_t nRows = dTargetAreas.GetRows();
2270  if( nRows >= nTargetDofs )
2271  {
2272  for( size_t i = 0; i < nTargetDofs; i++ )
2273  {
2274  int r = row_dtoc_dofmap[i];
2275  if( r >= 0 && (size_t)r < nRows )
2276  tgtAreas[i] = dTargetAreas[r];
2277  }
2278  got_areas = true;
2279  }
2280  }
2281 
2282  // No fallback to lHuiller. Recomputing areas from mesh geometry is
2283  // not BFB with MCT and there is no safe silent default — abort.
2284  if( !got_areas )
2285  {
2286  MB_SET_ERR( moab::MB_FAILURE,
2287  "ApplyWeightsWithDualMap: no target-cell areas available. "
2288  "Neither the 'aream' tag (from iMOAB_LoadMapFile with "
2289  "arearead != 0) nor OfflineMap::GetTargetAreas() (from an "
2290  "online map build) provided areas. Recomputing areas from "
2291  "mesh geometry is not bit-for-bit with MCT and is no longer "
2292  "permitted in the CAAS path. Re-load the map file with an "
2293  "area-bearing arearead setting (e.g. arearead=3 for F-maps), "
2294  "or build the online map so target areas are populated." );
2295  }
2296  }
2297 
2298  // ----- Step 5: build per-cell CAAS weights ----------------------------
2299  // For BFB summation, we accumulate per-row (gid, value) pairs and reduce
2300  // them deterministically.
2301  std::vector< int > rowGids( nTargetDofs, -1 );
2302  std::vector< double > massLowPerRow( nTargetDofs, 0.0 );
2303  std::vector< double > massHiUnclippedPerRow( nTargetDofs, 0.0 ); // y_hi BEFORE clipping
2304  std::vector< double > clipDefectPerRow( nTargetDofs, 0.0 );
2305  std::vector< double > capLowPerRow( nTargetDofs, 0.0 );
2306  std::vector< double > capHighPerRow( nTargetDofs, 0.0 );
2307 
2308  for( size_t i = 0; i < nTargetDofs; i++ )
2309  {
2310  int r = row_dtoc_dofmap[i];
2311  if( r < 0 || r >= (int)row_gdofmap.size() )
2312  rowGids[i] = -1; // not owned by this rank
2313  else
2314  rowGids[i] = (int)row_gdofmap[r];
2315 
2316  const double area = tgtAreas[i];
2317  const double y = solTTagVals[i]; // y_hi (pre-clip)
2318  const double lo = lcl_lo[i];
2319  const double hi = lcl_hi[i];
2320  double yc = y; // clipped value
2321  double dm = 0.0;
2322  if( y < lo )
2323  {
2324  yc = lo;
2325  dm = ( y - lo ) * area; // negative: cell exceeded below
2326  }
2327  else if( y > hi )
2328  {
2329  yc = hi;
2330  dm = ( y - hi ) * area; // positive: cell exceeded above
2331  }
2332  clipDefectPerRow[i] = dm;
2333  capLowPerRow[i] = ( yc - lo ) * area; // room to subtract
2334  capHighPerRow[i] = ( hi - yc ) * area; // room to add
2335  massLowPerRow[i] = yLow[i] * area;
2336  // Per-row mass of the UNCLIPPED high-order projection. MCT reduces
2337  // exactly this quantity to obtain glbl_masses(natt+k) (M_hi_unclipped),
2338  // and then computes dM_total = dM_clip + (M_low - M_hi_unclipped) in
2339  // that 2-step order. We store the unclipped y here (rather than the
2340  // clipped yc as before) so MOAB's reprosum byte-matches MCT's, which
2341  // in turn lets the MCT-matching dM_total formula below produce the
2342  // same last bits as MCT.
2343  massHiUnclippedPerRow[i] = y * area;
2344  // Update solTTagVals to the clipped value for the next stage
2345  solTTagVals[i] = yc;
2346  }
2347 
2348 
2349  // ----- Step 6: BFB-deterministic global reductions --------------------
2350 
2351  // Reproducible global reductions via Worley's integer-vector algorithm
2352  // (moab::IntegerReprosum) — bit-identical to MCT's shr_reprosum_int
2353  // regardless of MPI rank count, mesh decomposition, or local iteration
2354  // order. This is MCT's default reprosum path (the namelist default
2355  // repro_sum_use_ddpdd=.false. on the MCT side). The integer-vector
2356  // algorithm is order-independent by construction (MPI_Allreduce with
2357  // MPI_SUM on int64), which eliminates the cross-rank-count ULP drift
2358  // that an order-sensitive reducer (e.g. Kahan or DDPDD) would otherwise
2359  // leak into the CAAS bounds and mass totals.
2360 #ifdef MOAB_HAVE_MPI
2361  MPI_Comm reduce_comm = m_pcomm ? m_pcomm->comm() : MPI_COMM_SELF;
2362 #else
2363  int reduce_comm = 0; // serial build: comm unused but kept for API symmetry
2364 #endif
2365 #ifdef MOAB_HAVE_MPI
2366  moab::IntegerReprosum repro( reduce_comm );
2367 #else
2368  moab::IntegerReprosum repro;
2369 #endif
2370  // Build the ownership mask once (rowGids[i] >= 0 ↔ owned).
2371  const std::vector< int >& reduce_mask = rowGids;
2372  // Batched reduction: one MPI_Allreduce for the per-field metadata
2373  // (gmax_exp / gmin_exp / max_nsummands across the 5 fields) and one
2374  // MPI_Allreduce for the concatenated integer-vector encoding of all 5
2375  // fields. Bit-for-bit identical to calling sum_masked() five times
2376  // separately (each field still uses its own per-field metadata and
2377  // decode pass), but goes from 10 collective calls to 2.
2378  const std::vector< std::vector< double > > caasFields = {
2379  massLowPerRow, massHiUnclippedPerRow, clipDefectPerRow,
2380  capLowPerRow, capHighPerRow };
2381  std::vector< double > caasGsums;
2382  repro.sum_masked_batch( caasFields, reduce_mask, caasGsums );
2383  const double M_low = caasGsums[0];
2384  const double M_hi_unclipped = caasGsums[1];
2385  const double dM_clip = caasGsums[2];
2386  const double cap_low_g = caasGsums[3];
2387  const double cap_high_g = caasGsums[4];
2388 
2389  // ----- Step 8: total mass deficit between low-order and clipped high-order
2390  // The redistribution must drive the (clipped) high-order solution back to
2391  // the low-order mass. MCT's seq_nlmap_avNormArr (line 616) computes this
2392  // in EXACTLY the following 2-step form, and floating-point rounding makes
2393  // it FP-different from the algebraically-equivalent (M_low - M_hi_clip):
2394  //
2395  // ! MCT (Fortran array assignment, evaluated element-wise)
2396  // gwts(k) = gwts(k) ! gwts(k) holds dM_clip after reprosum
2397  // + (glbl_masses(k) ! M_low
2398  // - glbl_masses(natt+k)) ! M_hi_unclipped
2399  //
2400  // Reproducing MCT's exact bit pattern requires:
2401  // (a) reducing the UNCLIPPED per-cell high-order mass directly via
2402  // reprosum, NOT deriving it as M_hi_clip + dM_clip — that derivation
2403  // drops 1-2 ULP because reprosum is exact only on its inputs.
2404  // => see massHiUnclippedPerRow above.
2405  // (b) computing dM_total in MCT's order: subtract first, then add.
2406  //
2407  // Sign: dM_total > 0 means low-order carries more mass than the clipped
2408  // high-order, so we need to ADD mass; dM_total < 0 means we need to REMOVE.
2409  //
2410  const double diff = M_low - M_hi_unclipped; // step 1: subtraction
2411  const double dM_total = dM_clip + diff; // step 2: addition (MCT order)
2412 
2413  // ----- Step 9: redistribute -------------------------------------------
2414  // For BfB with MCT seq_nlmap_avNormArr we MUST match its FP operation
2415  // order exactly. MCT does, per cell:
2416  // y = max(lo, min(hi, nl_avp_o(k,j))) ! re-clip
2417  // nl_avp_o(k,j) = y + ((hi - y)/tmp)*gwts(k) ! line 648 / 662
2418  // i.e. divide-then-multiply, with the loop-invariant denominator
2419  // (cap_high_g or cap_low_g) and numerator (dM_total) NOT precomputed
2420  // into a single `scale = dM_total/cap_g`. Doing so introduces ULP-level
2421  // per-cell differences (a/b*c reorders to (c/b)*a). Similarly the
2422  // earlier MOAB pattern computed `room = (hi-yc)*area` and then
2423  // `room/area`, which doesn't algebraically cancel in FP and added two
2424  // extra roundings per cell. The straightforward `(hi - yc)/cap_g *
2425  // dM_total` form below matches MCT bit-for-bit.
2426  if( dM_total > 0.0 && cap_high_g > 0.0 )
2427  {
2428  for( size_t i = 0; i < nTargetDofs; i++ )
2429  {
2430  const double area = tgtAreas[i];
2431  const double yc = solTTagVals[i];
2432  if( area > 0.0 )
2433  solTTagVals[i] = yc + ( ( lcl_hi[i] - yc ) / cap_high_g ) * dM_total;
2434  }
2435  }
2436  else if( dM_total < 0.0 && cap_low_g > 0.0 )
2437  {
2438  for( size_t i = 0; i < nTargetDofs; i++ )
2439  {
2440  const double area = tgtAreas[i];
2441  const double yc = solTTagVals[i];
2442  if( area > 0.0 )
2443  solTTagVals[i] = yc + ( ( yc - lcl_lo[i] ) / cap_low_g ) * dM_total;
2444  }
2445  }
2446 
2447  // Final hard clip for floating-point safety, against UNSCALED global
2448  // extrema (Item 4). MCT's seq_nlmap_avNormArr does:
2449  // if (avp_o(k,j) == 0) cycle ! 0-mask skip
2450  // nl_avp_o%rAttr(k,j) = max(gmins(k), min(gmaxs(k), nl_avp_o%rAttr(k,j)))
2451  // Per-row bounds (lcl_lo/lcl_hi) are now SCALED by mapped_norm8wt and so
2452  // would be a tighter clip than MCT applies; using global g_lo/g_hi keeps
2453  // the safety net loose, as the reference algorithm intends. The 0-mask
2454  // skip is critical: without it, target cells that were zeroed in Step 4
2455  // (yLow == 0) get bumped from 0 up to g_lo when g_lo > 0 (e.g.
2456  // positive-only fields like temperature/pressure), and the post-norm
2457  // divide in the driver then amplifies that wrong value by 1/wghts at
2458  // coastal coverage cells where wghts is tiny but nonzero.
2459  for( size_t i = 0; i < nTargetDofs; i++ )
2460  {
2461  if( fabs(yLow[i]) < 1E-40 ) continue;
2462  if( solTTagVals[i] < g_lo ) solTTagVals[i] = g_lo;
2463  if( solTTagVals[i] > g_hi ) solTTagVals[i] = g_hi;
2464  }
2465 
2466  // Store result back to the target tag
2467  MB_CHK_SET_ERR( m_interface->tag_set_data( tgtSolutionTag, tents, &solTTagVals[0] ),
2468  "Setting target tag data failed" );
2469 
2470  return moab::MB_SUCCESS;
2471 }

References ApplyWeights(), moab::Range::begin(), moab::Remapper::CoveringMesh, moab::E, moab::Range::end(), ErrorCode, MB_CHK_SET_ERR, MB_SET_ERR, MB_SUCCESS, moab::Range::size(), moab::IntegerReprosum::sum_masked_batch(), and moab::Remapper::TargetMesh.

◆ CAASLimiter()

void moab::TempestOnlineMap::CAASLimiter ( std::vector< double > &  dataCorrectedField,
std::vector< double > &  dataLowerBound,
std::vector< double > &  dataUpperBound,
double &  dMass 
)
private

Definition at line 540 of file TempestLinearRemap.cpp.

544 {
545  const size_t nrows = dataCorrectedField.size();
546  double dMassL = 0.0;
547  double dMassU = 0.0;
548  std::vector< double > dataCorrection( nrows );
549  const DataArray1D< double >& dTargetAreas = this->m_remapper->m_target->vecFaceArea;
550  double dMassDiff = dMass;
551  double dLMinusU = fabs( dataUpperBound[0] - dataLowerBound[0] );
552  double dMassCorrectU = 0.0;
553  double dMassCorrectL = 0.0;
554  for( size_t i = 0; i < nrows; i++ )
555  {
556  dataCorrection[i] = fmax( dataLowerBound[i], fmin( dataUpperBound[i], 0.0 ) );
557  dMassL += dTargetAreas[i] * dataLowerBound[i];
558  dMassU += dTargetAreas[i] * dataUpperBound[i];
559  dMassDiff -= dTargetAreas[i] * dataCorrection[i];
560  dLMinusU = fmax( dLMinusU, fabs( dataUpperBound[i] - dataLowerBound[i] ) );
561  dMassCorrectL += dTargetAreas[i] * ( dataCorrection[i] - dataLowerBound[i] );
562  dMassCorrectU += dTargetAreas[i] * ( dataUpperBound[i] - dataCorrection[i] );
563  }
564 
565 #ifdef MOAB_HAVE_MPI
566  std::vector< double > localDefects( 5, 0.0 ), globalDefects( 5, 0.0 );
567  localDefects[0] = dMassL;
568  localDefects[1] = dMassU;
569  localDefects[2] = dMassDiff;
570  localDefects[3] = dMassCorrectL;
571  localDefects[4] = dMassCorrectU;
572 
573  MPI_Allreduce( localDefects.data(), globalDefects.data(), 5, MPI_DOUBLE, MPI_SUM, m_pcomm->comm() );
574 
575  dMassL = globalDefects[0];
576  dMassU = globalDefects[1];
577  dMassDiff = globalDefects[2];
578  dMassCorrectL = globalDefects[3];
579  dMassCorrectU = globalDefects[4];
580 #endif
581 
582  //If the upper and lower bounds are too close together, just clip
583  if( fabs( dMassDiff ) < 1e-15 || fabs( dLMinusU ) < 1e-15 )
584  {
585  for( size_t i = 0; i < nrows; i++ )
586  dataCorrectedField[i] += dataCorrection[i];
587  return;
588  }
589  else
590  {
591  if( dMassL > dMassDiff )
592  {
593  Announce( "%d: Lower bound mass exceeds target mass by %1.15e: CAAS will need another iteration", rank,
594  dMassL - dMassDiff );
595  dMassDiff = dMassL;
596  dMass -= dMassL;
597  }
598  else if( dMassU < dMassDiff )
599  {
600  Announce( "%d: Target mass exceeds upper bound mass by %1.15e: CAAS will need another iteration", rank,
601  dMassDiff - dMassU );
602  dMassDiff = dMassU;
603  dMass -= dMassU;
604  }
605 
606  // TODO: optimize away dataMassVec by a simple transient double within the loop
607  DataArray1D< double > dataMassVec( nrows ); //vector of mass redistribution
608  if( dMassDiff > 0.0 )
609  {
610  for( size_t i = 0; i < nrows; i++ )
611  {
612  dataMassVec[i] = ( dataUpperBound[i] - dataCorrection[i] ) / dMassCorrectU;
613  dataCorrection[i] += dMassDiff * dataMassVec[i];
614  }
615  }
616  else
617  {
618  for( size_t i = 0; i < nrows; i++ )
619  {
620  dataMassVec[i] = ( dataCorrection[i] - dataLowerBound[i] ) / dMassCorrectL;
621  dataCorrection[i] += dMassDiff * dataMassVec[i];
622  }
623  }
624 
625  for( size_t i = 0; i < nrows; i++ )
626  dataCorrectedField[i] += dataCorrection[i];
627  }
628 
629  return;
630 }

◆ ComputeAdjacencyRelations()

void moab::TempestOnlineMap::ComputeAdjacencyRelations ( std::vector< std::unordered_set< int > > &  vecAdjFaces,
int  nrings,
const Range &  entities,
bool  useMOABAdjacencies = true,
Mesh *  trMesh = nullptr 
)
Parameters
vecAdjFaces
nrings
entities
useMOABAdjacencies
trMesh
Returns

Vector storing adjacent Faces.

Definition at line 1553 of file TempestOnlineMap.cpp.

1558 {
1559  assert( nrings > 0 );
1560  assert( useMOABAdjacencies || trMesh != nullptr );
1561 
1562  const size_t nrows = vecAdjFaces.size();
1564  for( size_t index = 0; index < nrows; index++ )
1565  {
1566  vecAdjFaces[index].insert( index ); // add self target face first
1567  {
1568  // Compute the adjacent faces to the target face
1569  if( useMOABAdjacencies )
1570  {
1571  moab::Range ents;
1572  // ents.insert( entities.index( entities[index] ) );
1573  ents.insert( entities[index] );
1574  moab::Range adjEnts;
1575  moab::ErrorCode rval = mtu.get_bridge_adjacencies( ents, 0, 2, adjEnts, nrings );MB_CHK_SET_ERR_CONT( rval, "Failed to get adjacent faces" );
1576  for( moab::Range::iterator it = adjEnts.begin(); it != adjEnts.end(); ++it )
1577  {
1578  // int adjIndex = m_interface->id_from_handle(*it)-1;
1579  int adjIndex = entities.index( *it );
1580  // printf("rank: %d, Element %lu, entity: %lu, adjIndex %d\n", rank, index, *it, adjIndex);
1581  if( adjIndex >= 0 ) vecAdjFaces[index].insert( adjIndex );
1582  }
1583  }
1584  else
1585  {
1586  /// Vector storing adjacent Faces.
1587  typedef std::pair< int, int > FaceDistancePair;
1588  typedef std::vector< FaceDistancePair > AdjacentFaceVector;
1589  AdjacentFaceVector adjFaces;
1590  Face& face = trMesh->faces[index];
1591  GetAdjacentFaceVectorByEdge( *trMesh, index, nrings * face.edges.size(), adjFaces );
1592 
1593  // Add the adjacent faces to the target face list
1594  for( auto adjFace : adjFaces )
1595  if( adjFace.first >= 0 )
1596  vecAdjFaces[index].insert( adjFace.first ); // map target face to source face
1597  }
1598  }
1599  }
1600 }

References moab::Range::begin(), moab::Range::end(), ErrorCode, moab::MeshTopoUtil::get_bridge_adjacencies(), moab::index, moab::Range::index(), moab::Range::insert(), and MB_CHK_SET_ERR_CONT.

◆ ComputeMetrics()

moab::ErrorCode moab::TempestOnlineMap::ComputeMetrics ( Remapper::IntersectionContext  ctx,
moab::Tag &  exactTag,
moab::Tag &  approxTag,
std::map< std::string, double > &  metrics,
bool  verbose = true 
)

Compute the error between a sampled (exact) solution and a projected solution in various error norms.

Definition at line 2839 of file TempestOnlineMap.cpp.

2844 {
2845  const bool outputEnabled = ( is_root );
2846  int discOrder;
2847  // DiscretizationType discMethod;
2848  // moab::EntityHandle meshset;
2849  moab::Range entities;
2850  // Mesh* trmesh;
2851  switch( ctx )
2852  {
2853  case Remapper::SourceMesh:
2854  // meshset = m_remapper->m_covering_source_set;
2855  // trmesh = m_remapper->m_covering_source;
2858  discOrder = m_nDofsPEl_Src;
2859  // discMethod = m_eInputType;
2860  break;
2861 
2862  case Remapper::TargetMesh:
2863  // meshset = m_remapper->m_target_set;
2864  // trmesh = m_remapper->m_target;
2865  entities =
2867  discOrder = m_nDofsPEl_Dest;
2868  // discMethod = m_eOutputType;
2869  break;
2870 
2871  default:
2872  if( outputEnabled )
2873  std::cout << "Invalid context specified for defining an analytical solution tag" << std::endl;
2874  return moab::MB_FAILURE;
2875  }
2876 
2877  // Let us create teh solution tag with appropriate information for name, discretization order
2878  // (DoF space)
2879  std::string exactTagName, projTagName;
2880  const int ntotsize = entities.size() * discOrder * discOrder;
2881  std::vector< double > exactSolution( ntotsize, 0.0 ), projSolution( ntotsize, 0.0 );
2882  MB_CHK_ERR( m_interface->tag_get_name( exactTag, exactTagName ) );
2883  MB_CHK_ERR( m_interface->tag_get_data( exactTag, entities, &exactSolution[0] ) );
2884  MB_CHK_ERR( m_interface->tag_get_name( approxTag, projTagName ) );
2885  MB_CHK_ERR( m_interface->tag_get_data( approxTag, entities, &projSolution[0] ) );
2886 
2887  const auto& ovents = m_remapper->m_overlap_entities;
2888 
2889  std::vector< double > errnorms( 4, 0.0 ), globerrnorms( 4, 0.0 ); // L1Err, L2Err, LinfErr
2890  double sumarea = 0.0;
2891  for( size_t i = 0; i < ovents.size(); ++i )
2892  {
2893  const int srcidx = m_remapper->m_overlap->vecSourceFaceIx[i];
2894  if( srcidx < 0 ) continue; // Skip non-overlapping entities
2895  const int tgtidx = m_remapper->m_overlap->vecTargetFaceIx[i];
2896  if( tgtidx < 0 ) continue; // skip ghost target faces
2897  const double ovarea = m_remapper->m_overlap->vecFaceArea[i];
2898  const double error = fabs( exactSolution[tgtidx] - projSolution[tgtidx] );
2899  errnorms[0] += ovarea * error;
2900  errnorms[1] += ovarea * error * error;
2901  errnorms[3] = ( error > errnorms[3] ? error : errnorms[3] );
2902  sumarea += ovarea;
2903  }
2904  errnorms[2] = sumarea;
2905 #ifdef MOAB_HAVE_MPI
2906  if( m_pcomm )
2907  {
2908  MPI_Reduce( &errnorms[0], &globerrnorms[0], 3, MPI_DOUBLE, MPI_SUM, 0, m_pcomm->comm() );
2909  MPI_Reduce( &errnorms[3], &globerrnorms[3], 1, MPI_DOUBLE, MPI_MAX, 0, m_pcomm->comm() );
2910  }
2911 #else
2912  for( int i = 0; i < 4; ++i )
2913  globerrnorms[i] = errnorms[i];
2914 #endif
2915 
2916  globerrnorms[0] = ( globerrnorms[0] / globerrnorms[2] );
2917  globerrnorms[1] = std::sqrt( globerrnorms[1] / globerrnorms[2] );
2918 
2919  metrics.clear();
2920  metrics["L1Error"] = globerrnorms[0];
2921  metrics["L2Error"] = globerrnorms[1];
2922  metrics["LinfError"] = globerrnorms[3];
2923 
2924  if( verbose && is_root )
2925  {
2926  std::cout << "Error metrics when comparing " << projTagName << " against " << exactTagName << std::endl;
2927  std::cout << "\t Total Intersection area = " << globerrnorms[2] << std::endl;
2928  std::cout << "\t L_1 error = " << globerrnorms[0] << std::endl;
2929  std::cout << "\t L_2 error = " << globerrnorms[1] << std::endl;
2930  std::cout << "\t L_inf error = " << globerrnorms[3] << std::endl;
2931  }
2932 
2933  return moab::MB_SUCCESS;
2934 }

References moab::error(), MB_CHK_ERR, MB_SUCCESS, moab::Range::size(), moab::Remapper::SourceMesh, moab::Remapper::TargetMesh, and verbose.

Referenced by main().

◆ CountAbsentColumns()

int moab::TempestOnlineMap::CountAbsentColumns ( int &  first_absent_gid) const

Count weight-matrix columns that the migrated coverage did not supply (i.e. no covering cell maps to them). For offline file maps this equals the set of source DoFs the map references but that are absent from the source mesh. Returns the count and, in first_absent_gid, one example 1-based source GID (or -1 if none).

Definition at line 479 of file TempestOnlineMap.cpp.

480 {
481  first_absent_gid = -1;
482  const int ncols = m_nTotDofs_SrcCov;
483  std::vector< bool > delivered;
484  compute_delivered_columns( ncols, col_dtoc_dofmap, delivered );
485  int cnt = 0;
486  for( int mc = 0; mc < ncols; mc++ )
487  {
488  if( !delivered[mc] )
489  {
490  cnt++;
491  if( first_absent_gid < 0 && mc < (int)col_gdofmap.size() )
492  first_absent_gid = (int)col_gdofmap[mc] + 1; // col_gdofmap is 0-based
493  }
494  }
495  return cnt;
496 }

References compute_delivered_columns().

◆ DefineAnalyticalSolution()

moab::ErrorCode moab::TempestOnlineMap::DefineAnalyticalSolution ( moab::Tag &  exactSolnTag,
const std::string &  solnName,
Remapper::IntersectionContext  ctx,
sample_function  testFunction,
moab::Tag *  clonedSolnTag = NULL,
std::string  cloneSolnName = "" 
)

Define an analytical solution over the given (source or target) mesh, as specificed in the context. This routine will define a tag that is compatible with the specified discretization method type and order and sample the solution exactly using the analytical function provided by the user.

Definition at line 2473 of file TempestOnlineMap.cpp.

2479 {
2480  const bool outputEnabled = ( is_root );
2481  int discOrder;
2482  DiscretizationType discMethod;
2483  // moab::EntityHandle meshset;
2484  moab::Range entities;
2485  Mesh* trmesh;
2486  switch( ctx )
2487  {
2488  case Remapper::SourceMesh:
2489  // meshset = m_remapper->m_covering_source_set;
2490  trmesh = m_remapper->m_covering_source;
2493  discOrder = m_nDofsPEl_Src;
2494  discMethod = m_eInputType;
2495  break;
2496 
2497  case Remapper::TargetMesh:
2498  // meshset = m_remapper->m_target_set;
2499  trmesh = m_remapper->m_target;
2500  entities =
2502  discOrder = m_nDofsPEl_Dest;
2503  discMethod = m_eOutputType;
2504  break;
2505 
2506  default:
2507  if( outputEnabled )
2508  std::cout << "Invalid context specified for defining an analytical solution tag" << std::endl;
2509  return moab::MB_FAILURE;
2510  }
2511 
2512  // Let us create teh solution tag with appropriate information for name, discretization order
2513  // (DoF space)
2514  MB_CHK_ERR( m_interface->tag_get_handle( solnName.c_str(), discOrder * discOrder, MB_TYPE_DOUBLE, solnTag,
2515  MB_TAG_DENSE | MB_TAG_CREAT ) );
2516  if( clonedSolnTag != nullptr )
2517  {
2518  if( cloneSolnName.size() == 0 )
2519  {
2520  cloneSolnName = solnName + std::string( "Cloned" );
2521  }
2522  MB_CHK_ERR( m_interface->tag_get_handle( cloneSolnName.c_str(), discOrder * discOrder, MB_TYPE_DOUBLE,
2523  *clonedSolnTag, MB_TAG_DENSE | MB_TAG_CREAT ) );
2524  }
2525 
2526  // Triangular quadrature rule
2527  const int TriQuadratureOrder = 10;
2528 
2529  if( outputEnabled ) std::cout << "Using triangular quadrature of order " << TriQuadratureOrder << std::endl;
2530 
2531  TriangularQuadratureRule triquadrule( TriQuadratureOrder );
2532 
2533  const int TriQuadraturePoints = triquadrule.GetPoints();
2534 
2535  const DataArray2D< double >& TriQuadratureG = triquadrule.GetG();
2536  const DataArray1D< double >& TriQuadratureW = triquadrule.GetW();
2537 
2538  // Output data
2539  DataArray1D< double > dVar;
2540  DataArray1D< double > dVarMB; // re-arranged local MOAB vector
2541 
2542  // Nodal geometric area
2543  DataArray1D< double > dNodeArea;
2544 
2545  // Calculate element areas
2546  // trmesh->CalculateFaceAreas(fContainsConcaveFaces);
2547 
2548  if( discMethod == DiscretizationType_CGLL || discMethod == DiscretizationType_DGLL )
2549  {
2550  /* Get the spectral points and sample the functionals accordingly */
2551  const bool fGLL = true;
2552  const bool fGLLIntegrate = false;
2553 
2554  // Generate grid metadata
2555  DataArray3D< int > dataGLLNodes;
2556  DataArray3D< double > dataGLLJacobian;
2557 
2558  GenerateMetaData( *trmesh, discOrder, false, dataGLLNodes, dataGLLJacobian );
2559 
2560  // Number of elements
2561  int nElements = trmesh->faces.size();
2562 
2563  // Verify all elements are quadrilaterals
2564  for( int k = 0; k < nElements; k++ )
2565  {
2566  const Face& face = trmesh->faces[k];
2567 
2568  if( face.edges.size() != 4 )
2569  {
2570  _EXCEPTIONT( "Non-quadrilateral face detected; "
2571  "incompatible with --gll" );
2572  }
2573  }
2574 
2575  // Number of unique nodes (CGLL) or total element-local DOFs (DGLL)
2576  const bool fDiscontinuous = ( discMethod == DiscretizationType_DGLL );
2577  int iMaxNode = 0;
2578  if( fDiscontinuous )
2579  {
2580  // DGLL: each element has independent DOFs
2581  iMaxNode = nElements * discOrder * discOrder;
2582  }
2583  else
2584  {
2585  // CGLL: shared nodes at element boundaries
2586  for( int i = 0; i < discOrder; i++ )
2587  for( int j = 0; j < discOrder; j++ )
2588  for( int k = 0; k < nElements; k++ )
2589  if( dataGLLNodes[i][j][k] > iMaxNode )
2590  iMaxNode = dataGLLNodes[i][j][k];
2591  }
2592 
2593  // Get Gauss-Lobatto quadrature nodes
2594  DataArray1D< double > dG;
2595  DataArray1D< double > dW;
2596 
2597  GaussLobattoQuadrature::GetPoints( discOrder, 0.0, 1.0, dG, dW );
2598 
2599  // Get Gauss quadrature nodes
2600  const int nGaussP = 10;
2601 
2602  DataArray1D< double > dGaussG;
2603  DataArray1D< double > dGaussW;
2604 
2605  GaussQuadrature::GetPoints( nGaussP, 0.0, 1.0, dGaussG, dGaussW );
2606 
2607  // Allocate data
2608  dVar.Allocate( iMaxNode );
2609  dVarMB.Allocate( discOrder * discOrder * nElements );
2610  dNodeArea.Allocate( iMaxNode );
2611 
2612  // Sample data
2613  for( int k = 0; k < nElements; k++ )
2614  {
2615  const Face& face = trmesh->faces[k];
2616 
2617  // Sample data at GLL nodes
2618  if( fGLL )
2619  {
2620  for( int i = 0; i < discOrder; i++ )
2621  {
2622  for( int j = 0; j < discOrder; j++ )
2623  {
2624 
2625  // Apply local map
2626  Node node;
2627  Node dDx1G;
2628  Node dDx2G;
2629 
2630  ApplyLocalMap( face, trmesh->nodes, dG[i], dG[j], node, dDx1G, dDx2G );
2631 
2632  // Sample data at this point
2633  double dNodeLon = atan2( node.y, node.x );
2634  if( dNodeLon < 0.0 )
2635  {
2636  dNodeLon += 2.0 * M_PI;
2637  }
2638  double dNodeLat = asin( node.z );
2639 
2640  double dSample = ( *testFunction )( dNodeLon, dNodeLat );
2641 
2642  if( fDiscontinuous )
2643  dVar[k * discOrder * discOrder + j * discOrder + i] = dSample;
2644  else
2645  dVar[dataGLLNodes[j][i][k] - 1] = dSample;
2646  }
2647  }
2648  // High-order Gaussian integration over basis function
2649  }
2650  else
2651  {
2652  DataArray2D< double > dCoeff( discOrder, discOrder );
2653 
2654  for( int p = 0; p < nGaussP; p++ )
2655  {
2656  for( int q = 0; q < nGaussP; q++ )
2657  {
2658 
2659  // Apply local map
2660  Node node;
2661  Node dDx1G;
2662  Node dDx2G;
2663 
2664  ApplyLocalMap( face, trmesh->nodes, dGaussG[p], dGaussG[q], node, dDx1G, dDx2G );
2665 
2666  // Cross product gives local Jacobian
2667  Node nodeCross = CrossProduct( dDx1G, dDx2G );
2668 
2669  double dJacobian =
2670  sqrt( nodeCross.x * nodeCross.x + nodeCross.y * nodeCross.y + nodeCross.z * nodeCross.z );
2671 
2672  // Find components of quadrature point in basis
2673  // of the first Face
2674  SampleGLLFiniteElement( 0, discOrder, dGaussG[p], dGaussG[q], dCoeff );
2675 
2676  // Sample data at this point
2677  double dNodeLon = atan2( node.y, node.x );
2678  if( dNodeLon < 0.0 )
2679  {
2680  dNodeLon += 2.0 * M_PI;
2681  }
2682  double dNodeLat = asin( node.z );
2683 
2684  double dSample = ( *testFunction )( dNodeLon, dNodeLat );
2685 
2686  // Integrate
2687  for( int i = 0; i < discOrder; i++ )
2688  {
2689  for( int j = 0; j < discOrder; j++ )
2690  {
2691 
2692  double dNodalArea = dCoeff[i][j] * dGaussW[p] * dGaussW[q] * dJacobian;
2693 
2694  dVar[dataGLLNodes[i][j][k] - 1] += dSample * dNodalArea;
2695 
2696  dNodeArea[dataGLLNodes[i][j][k] - 1] += dNodalArea;
2697  }
2698  }
2699  }
2700  }
2701  }
2702  }
2703 
2704  // Divide by area
2705  if( fGLLIntegrate )
2706  {
2707  for( size_t i = 0; i < dVar.GetRows(); i++ )
2708  {
2709  dVar[i] /= dNodeArea[i];
2710  }
2711  }
2712 
2713  // Let us rearrange the data based on DoF ID specification
2714  if( ctx == Remapper::SourceMesh )
2715  {
2716  for( unsigned j = 0; j < entities.size(); j++ )
2717  for( int p = 0; p < discOrder; p++ )
2718  for( int q = 0; q < discOrder; q++ )
2719  {
2720  const int offsetDOF = j * discOrder * discOrder + p * discOrder + q;
2721  dVarMB[offsetDOF] = dVar[col_dtoc_dofmap[offsetDOF]];
2722  }
2723  }
2724  else
2725  {
2726  for( unsigned j = 0; j < entities.size(); j++ )
2727  for( int p = 0; p < discOrder; p++ )
2728  for( int q = 0; q < discOrder; q++ )
2729  {
2730  const int offsetDOF = j * discOrder * discOrder + p * discOrder + q;
2731  dVarMB[offsetDOF] = dVar[row_dtoc_dofmap[offsetDOF]];
2732  }
2733  }
2734 
2735  // Set the tag data
2736  MB_CHK_ERR( m_interface->tag_set_data( solnTag, entities, &dVarMB[0] ) );
2737  }
2738  else
2739  {
2740  // assert( discOrder == 1 );
2741  if( discMethod == DiscretizationType_FV )
2742  {
2743  /* Compute an element-wise integral to store the sampled solution based on Quadrature
2744  * rules */
2745  // Resize the array
2746  dVar.Allocate( trmesh->faces.size() );
2747 
2748  std::vector< Node >& nodes = trmesh->nodes;
2749 
2750  // Loop through all Faces
2751  for( size_t i = 0; i < trmesh->faces.size(); i++ )
2752  {
2753  const Face& face = trmesh->faces[i];
2754 
2755  // Loop through all sub-triangles
2756  for( size_t j = 0; j < face.edges.size() - 2; j++ )
2757  {
2758 
2759  const Node& node0 = nodes[face[0]];
2760  const Node& node1 = nodes[face[j + 1]];
2761  const Node& node2 = nodes[face[j + 2]];
2762 
2763  // Triangle area
2764  Face faceTri( 3 );
2765  faceTri.SetNode( 0, face[0] );
2766  faceTri.SetNode( 1, face[j + 1] );
2767  faceTri.SetNode( 2, face[j + 2] );
2768 
2769  double dTriangleArea = CalculateFaceArea( faceTri, nodes );
2770 
2771  // Calculate the element average
2772  double dTotalSample = 0.0;
2773 
2774  // Loop through all quadrature points
2775  for( int k = 0; k < TriQuadraturePoints; k++ )
2776  {
2777  Node node( TriQuadratureG[k][0] * node0.x + TriQuadratureG[k][1] * node1.x +
2778  TriQuadratureG[k][2] * node2.x,
2779  TriQuadratureG[k][0] * node0.y + TriQuadratureG[k][1] * node1.y +
2780  TriQuadratureG[k][2] * node2.y,
2781  TriQuadratureG[k][0] * node0.z + TriQuadratureG[k][1] * node1.z +
2782  TriQuadratureG[k][2] * node2.z );
2783 
2784  double dMagnitude = node.Magnitude();
2785  node.x /= dMagnitude;
2786  node.y /= dMagnitude;
2787  node.z /= dMagnitude;
2788 
2789  double dLon = atan2( node.y, node.x );
2790  if( dLon < 0.0 )
2791  {
2792  dLon += 2.0 * M_PI;
2793  }
2794  double dLat = asin( node.z );
2795 
2796  double dSample = ( *testFunction )( dLon, dLat );
2797 
2798  dTotalSample += dSample * TriQuadratureW[k] * dTriangleArea;
2799  }
2800 
2801  dVar[i] += dTotalSample / trmesh->vecFaceArea[i];
2802  }
2803  }
2804  MB_CHK_ERR( m_interface->tag_set_data( solnTag, entities, &dVar[0] ) );
2805  }
2806  else /* discMethod == DiscretizationType_PCLOUD */
2807  {
2808  /* Get the coordinates of the vertices and sample the functionals accordingly */
2809  std::vector< Node >& nodes = trmesh->nodes;
2810 
2811  // Resize the array
2812  dVar.Allocate( nodes.size() );
2813 
2814  for( size_t j = 0; j < nodes.size(); j++ )
2815  {
2816  Node& node = nodes[j];
2817  double dMagnitude = node.Magnitude();
2818  node.x /= dMagnitude;
2819  node.y /= dMagnitude;
2820  node.z /= dMagnitude;
2821  double dLon = atan2( node.y, node.x );
2822  if( dLon < 0.0 )
2823  {
2824  dLon += 2.0 * M_PI;
2825  }
2826  double dLat = asin( node.z );
2827 
2828  double dSample = ( *testFunction )( dLon, dLat );
2829  dVar[j] = dSample;
2830  }
2831 
2832  MB_CHK_ERR( m_interface->tag_set_data( solnTag, entities, &dVar[0] ) );
2833  }
2834  }
2835 
2836  return moab::MB_SUCCESS;
2837 }

References MB_CHK_ERR, MB_SUCCESS, MB_TAG_CREAT, MB_TAG_DENSE, MB_TYPE_DOUBLE, moab::Range::size(), moab::Remapper::SourceMesh, and moab::Remapper::TargetMesh.

Referenced by main().

◆ DropAbsentColumns()

int moab::TempestOnlineMap::DropAbsentColumns ( )

Zero the weights of columns the migrated coverage did not supply, then compact the matrix. Use ONLY for columns proven globally absent (masked source grid): the projection already treats them as zero-source, so this is a BfB-safe drop that lets the dual-map CAAS bounds check skip them instead of aborting. Returns the number of columns dropped.

Definition at line 498 of file TempestOnlineMap.cpp.

499 {
500  const int ncols = m_nTotDofs_SrcCov;
501  std::vector< bool > delivered;
502  compute_delivered_columns( ncols, col_dtoc_dofmap, delivered );
503 
504  // Zero every stored coefficient whose column was not supplied by coverage.
505  // The projection already treats these columns as zero-source (ApplyWeights
506  // leaves m_colVector at 0 for them), so this changes no projected value; it
507  // only lets the dual-map CAAS bounds loop skip them via its |w|<1e-50 test.
508  int dropped = 0;
509  for( int r = 0; r < m_weightMatrix.outerSize(); r++ )
510  {
511  for( WeightMatrix::InnerIterator it( m_weightMatrix, r ); it; ++it )
512  {
513  const int mc = (int)it.col();
514  if( mc < 0 || mc >= ncols || !delivered[mc] )
515  {
516  if( it.value() != 0.0 ) dropped++;
517  it.valueRef() = 0.0;
518  }
519  }
520  }
521  // Remove the explicit zeros so iterators no longer visit them.
522  m_weightMatrix.prune( []( const Eigen::Index&, const Eigen::Index&, const double& v ) { return v != 0.0; } );
523  return dropped;
524 }

References compute_delivered_columns().

◆ fill_col_ids()

moab::ErrorCode moab::TempestOnlineMap::fill_col_ids ( std::vector< int > &  ids_of_interest)
inline

Definition at line 462 of file TempestOnlineMap.hpp.

463  {
464  ids_of_interest.reserve( col_gdofmap.size() );
465  // need to add 1
466  for( auto it = col_gdofmap.begin(); it != col_gdofmap.end(); it++ )
467  ids_of_interest.push_back( *it + 1 );
468  return moab::MB_SUCCESS;
469  }

References col_gdofmap, and MB_SUCCESS.

◆ GenerateRemappingWeights()

moab::ErrorCode moab::TempestOnlineMap::GenerateRemappingWeights ( std::string  strInputType,
std::string  strOutputType,
const GenerateOfflineMapAlgorithmOptions &  mapOptions,
const std::string &  srcDofTagName = "GLOBAL_ID",
const std::string &  tgtDofTagName = "GLOBAL_ID" 
)

Generate the offline map, given the source and target mesh and discretization details. This method generates the mapping between the two meshes based on the overlap and stores the result in the SparseMatrix.

the tag should be created already in the e3sm workflow; if not, create it here

Definition at line 527 of file TempestOnlineMap.cpp.

532 {
533 #ifdef MOAB_HAVE_NETCDF
534  NcError error( NcError::silent_nonfatal );
535 #endif
536 
537  moab::DebugOutput dbgprint( std::cout, rank, 0 );
538  dbgprint.set_prefix( "[TempestOnlineMap]: " );
539  moab::ErrorCode rval;
540 
541  const bool m_bPointCloudSource = ( m_remapper->point_cloud_source );
542  const bool m_bPointCloudTarget = ( m_remapper->point_cloud_target );
543  const bool m_bPointCloud = m_bPointCloudSource || m_bPointCloudTarget;
544 
545  // Build a matrix of source and target discretization so that we know how
546  // to assign the global DoFs in parallel for the mapping weights.
547  // For example,
548  // for FV->FV: the rows represented target DoFs and cols represent source DoFs
549  try
550  {
551  // Check command line parameters (data type arguments)
552  STLStringHelper::ToLower( strInputType );
553  STLStringHelper::ToLower( strOutputType );
554 
555  DiscretizationType eInputType;
556  DiscretizationType eOutputType;
557 
558  if( strInputType == "fv" )
559  {
560  eInputType = DiscretizationType_FV;
561  }
562  else if( strInputType == "cgll" )
563  {
564  eInputType = DiscretizationType_CGLL;
565  }
566  else if( strInputType == "dgll" )
567  {
568  eInputType = DiscretizationType_DGLL;
569  }
570  else if( strInputType == "pcloud" )
571  {
572  eInputType = DiscretizationType_PCLOUD;
573  }
574  else
575  {
576  _EXCEPTION1( "Invalid \"in_type\" value (%s), expected [fv|cgll|dgll]", strInputType.c_str() );
577  }
578 
579  if( strOutputType == "fv" )
580  {
581  eOutputType = DiscretizationType_FV;
582  }
583  else if( strOutputType == "cgll" )
584  {
585  eOutputType = DiscretizationType_CGLL;
586  }
587  else if( strOutputType == "dgll" )
588  {
589  eOutputType = DiscretizationType_DGLL;
590  }
591  else if( strOutputType == "pcloud" )
592  {
593  eOutputType = DiscretizationType_PCLOUD;
594  }
595  else
596  {
597  _EXCEPTION1( "Invalid \"out_type\" value (%s), expected [fv|cgll|dgll]", strOutputType.c_str() );
598  }
599 
600  // set all required input params
601  m_bConserved = !mapOptions.fNoConservation;
602  m_eInputType = eInputType;
603  m_eOutputType = eOutputType;
604 
605  // Method flags
606  std::string strMapAlgorithm( "" );
607  int nMonotoneType = ( mapOptions.fMonotone ) ? ( 1 ) : ( 0 );
608 
609  // Make an index of method arguments
610  std::set< std::string > setMethodStrings;
611  {
612  int iLast = 0;
613  for( size_t i = 0; i <= mapOptions.strMethod.length(); i++ )
614  {
615  if( ( i == mapOptions.strMethod.length() ) || ( mapOptions.strMethod[i] == ';' ) )
616  {
617  std::string strMethodString = mapOptions.strMethod.substr( iLast, i - iLast );
618  STLStringHelper::RemoveWhitespaceInPlace( strMethodString );
619  if( strMethodString.length() > 0 )
620  {
621  setMethodStrings.insert( strMethodString );
622  }
623  iLast = i + 1;
624  }
625  }
626  }
627 
628  for( const auto& it : setMethodStrings )
629  {
630  // Piecewise constant monotonicity
631  if( it == "mono2" )
632  {
634  {
635  _EXCEPTIONT( "--method \"mono2\" is only used when remapping to/from CGLL or DGLL grids" );
636  }
637  nMonotoneType = 2;
638 
639  // Piecewise linear monotonicity
640  }
641  else if( it == "mono3" )
642  {
644  {
645  _EXCEPTIONT( "--method \"mono3\" is only used when remapping to/from CGLL or DGLL grids" );
646  }
647  nMonotoneType = 3;
648 
649  // Volumetric remapping from FV to GLL
650  }
651  else if( it == "volumetric" )
652  {
654  {
655  _EXCEPTIONT( "--method \"volumetric\" may only be used for FV->CGLL or FV->DGLL remapping" );
656  }
657  strMapAlgorithm = "volumetric";
658 
659  // Inverse distance mapping
660  }
661  else if( it == "invdist" )
662  {
664  {
665  _EXCEPTIONT( "--method \"invdist\" may only be used for FV->FV remapping" );
666  }
667  strMapAlgorithm = "invdist";
668 
669  // Delaunay triangulation mapping
670  }
671  else if( it == "delaunay" )
672  {
674  {
675  _EXCEPTIONT( "--method \"delaunay\" may only be used for FV->FV remapping" );
676  }
677  strMapAlgorithm = "delaunay";
678 
679  // Bilinear
680  }
681  else if( it == "bilin" )
682  {
684  {
685  _EXCEPTIONT( "--method \"bilin\" may only be used for FV->FV remapping" );
686  }
687  strMapAlgorithm = "fvbilin";
688 
689  // Integrated bilinear (same as mono3 when source grid is CGLL/DGLL)
690  }
691  else if( it == "intbilin" )
692  {
694  {
695  _EXCEPTIONT( "--method \"intbilin\" may only be used when mapping to FV." );
696  }
698  {
699  strMapAlgorithm = "fvintbilin";
700  }
701  else
702  {
703  strMapAlgorithm = "mono3";
704  }
705 
706  // Integrated bilinear with generalized Barycentric coordinates
707  }
708  else if( it == "intbilingb" )
709  {
711  {
712  _EXCEPTIONT( "--method \"intbilingb\" may only be used for FV->FV remapping" );
713  }
714  strMapAlgorithm = "fvintbilingb";
715  }
716  else
717  {
718  _EXCEPTION1( "Invalid --method argument \"%s\"", it.c_str() );
719  }
720  }
721 
724  : mapOptions.nPin );
727  : mapOptions.nPout );
728 
729  // Set the source and target mesh objects
730  MB_CHK_ERR( SetDOFmapTags( srcDofTagName, tgtDofTagName ) );
731 
732  /// the tag should be created already in the e3sm workflow; if not, create it here
733  Tag areaTag;
734  rval = m_interface->tag_get_handle( "aream", 1, MB_TYPE_DOUBLE, areaTag,
736  if( MB_ALREADY_ALLOCATED == rval )
737  {
738  if( is_root ) dbgprint.printf( 0, "aream tag already defined \n" );
739  }
740 
741  double local_areas[3] = { 0.0, 0.0, 0.0 }, global_areas[3] = { 0.0, 0.0, 0.0 };
742  if( !m_bPointCloudSource )
743  {
744  // Calculate Input Mesh Face areas
745  if( is_root ) dbgprint.printf( 0, "Calculating input mesh Face areas\n" );
746  local_areas[0] = m_meshInput->CalculateFaceAreas( mapOptions.fSourceConcave );
747  // Set source element areas as tag on the source mesh
749 
750  // Update coverage source mesh areas as well.
751  m_meshInputCov->CalculateFaceAreas( mapOptions.fSourceConcave );
752  }
753 
754  if( !m_bPointCloudTarget )
755  {
756  // Calculate Output Mesh Face areas
757  if( is_root ) dbgprint.printf( 0, "Calculating output mesh Face areas\n" );
758  local_areas[1] = m_meshOutput->CalculateFaceAreas( mapOptions.fTargetConcave );
759  // Set target element areas as tag on the target mesh
760  MB_CHK_ERR(
761  m_interface->tag_set_data( areaTag, m_remapper->m_target_entities, m_meshOutput->vecFaceArea ) );
762  }
763 
764  if( !m_bPointCloud )
765  {
766  // Calculate Face areas
767  if (m_meshOverlap)
768  {
769  // Verify that overlap mesh is in the correct order (sanity check)
770  assert( m_meshOverlap->vecSourceFaceIx.size() == m_meshOverlap->vecTargetFaceIx.size() );
771 
772  if( is_root ) dbgprint.printf( 0, "Calculating overlap mesh Face areas\n" );
773  local_areas[2] =
774  m_meshOverlap->CalculateFaceAreas( mapOptions.fSourceConcave || mapOptions.fTargetConcave );
775 
776 #ifdef MOAB_HAVE_MPI
777  // CalculateFaceAreas() sums every face it holds; it has no notion of ownership.
778  // In parallel the overlap mesh also carries ghost elements -- intersections whose
779  // target cell is owned by another rank -- which ConvertOverlapMeshSourceOrdered()
780  // flags by setting vecTargetFaceIx to -1 so they are skipped when the weights are
781  // accumulated. Summing the raw total therefore counts those intersections twice
782  // (once here, once on the owning rank) and the reduced "Recovered Area" overshoots
783  // the sphere. Subtract the ghost contribution so the reported area matches the
784  // area the map is actually built from.
785  if( m_pcomm && is_parallel )
786  {
787  double ghost_area = 0.0;
788  for( size_t iover = 0; iover < m_meshOverlap->faces.size(); iover++ )
789  if( m_meshOverlap->vecTargetFaceIx[iover] < 0 ) ghost_area += m_meshOverlap->vecFaceArea[iover];
790  local_areas[2] -= ghost_area;
791  }
792 #endif
793  }
794 
795  // store it as global output for now - used later in reduction
796  std::copy( local_areas, local_areas + 3, global_areas );
797 #ifdef MOAB_HAVE_MPI
798  // reduce the local source, target and overlap mesh areas to global areas
799  if( m_pcomm && is_parallel )
800  MPI_Reduce( local_areas, global_areas, 3, MPI_DOUBLE, MPI_SUM, 0, m_pcomm->comm() );
801 #endif
802  if( is_root )
803  {
804  dbgprint.printf( 0, "Input Mesh Geometric Area: %1.15e\n", global_areas[0] );
805  dbgprint.printf( 0, "Output Mesh Geometric Area: %1.15e\n", global_areas[1] );
806  if (m_meshOverlap) dbgprint.printf( 0, "Overlap Mesh Recovered Area: %1.15e\n", global_areas[2] );
807  }
808 
809  // Correct areas to match the areas calculated in the overlap mesh
810  constexpr bool fCorrectAreas = true;
811  if( fCorrectAreas && m_meshOverlap ) // In MOAB-TempestRemap, we will always keep this to be true
812  {
813  if( is_root ) dbgprint.printf( 0, "Correcting source/target areas to overlap mesh areas\n" );
814  DataArray1D< double > dSourceArea( m_meshInputCov->faces.size() );
815  DataArray1D< double > dTargetArea( m_meshOutput->faces.size() );
816 
817  assert( m_meshOverlap->vecSourceFaceIx.size() == m_meshOverlap->faces.size() );
818  assert( m_meshOverlap->vecTargetFaceIx.size() == m_meshOverlap->faces.size() );
819  assert( m_meshOverlap->vecFaceArea.GetRows() == m_meshOverlap->faces.size() );
820 
821  assert( m_meshInputCov->vecFaceArea.GetRows() == m_meshInputCov->faces.size() );
822  assert( m_meshOutput->vecFaceArea.GetRows() == m_meshOutput->faces.size() );
823 
824  for( size_t i = 0; i < m_meshOverlap->faces.size(); i++ )
825  {
826  if( m_meshOverlap->vecSourceFaceIx[i] < 0 || m_meshOverlap->vecTargetFaceIx[i] < 0 )
827  continue; // skip this cell since it is ghosted
828 
829  // let us recompute the source/target areas based on overlap mesh areas
830  assert( static_cast< size_t >( m_meshOverlap->vecSourceFaceIx[i] ) < m_meshInputCov->faces.size() );
831  dSourceArea[m_meshOverlap->vecSourceFaceIx[i]] += m_meshOverlap->vecFaceArea[i];
832  assert( static_cast< size_t >( m_meshOverlap->vecTargetFaceIx[i] ) < m_meshOutput->faces.size() );
833  dTargetArea[m_meshOverlap->vecTargetFaceIx[i]] += m_meshOverlap->vecFaceArea[i];
834  }
835 
836  // How much disagreement between the accumulated overlap area and the
837  // geometric area we are willing to absorb.
838  //
839  // For meshes whose domains coincide, the two agree to roundoff and the
840  // 1e-10 test simply avoids replacing a good geometric area with a
841  // slightly noisier accumulated one.
842  //
843  // For a regionally refined mesh they legitimately differ: a cell on the
844  // edge of the other mesh's domain is only partly covered, so its overlap
845  // contributions sum to a fraction of its geometric area. The 1e-10 test
846  // then rejects the correction precisely where it is needed, leaving the
847  // cell with its full area while it receives only part of the overlap --
848  // which drives its map row sum below one. Accept the accumulated area
849  // for any cell that received a contribution, and leave cells with no
850  // coverage at their geometric area (they are outside the other domain;
851  // zeroing them would divide by zero downstream).
852  const bool regional = ( m_remapper != nullptr && m_remapper->IsRegionalMesh() );
853 
854  auto accept_area = [regional]( double accumulated, double geometric ) -> bool {
855  if( regional ) return accumulated > 0.0;
856  return fabs( accumulated - geometric ) < 1.0e-10;
857  };
858 
859  size_t nUncoveredTarget = 0;
860  for( size_t i = 0; i < m_meshInputCov->faces.size(); i++ )
861  {
862  if( accept_area( dSourceArea[i], m_meshInputCov->vecFaceArea[i] ) )
863  m_meshInputCov->vecFaceArea[i] = dSourceArea[i];
864  }
865  for( size_t i = 0; i < m_meshOutput->faces.size(); i++ )
866  {
867  if( accept_area( dTargetArea[i], m_meshOutput->vecFaceArea[i] ) )
868  m_meshOutput->vecFaceArea[i] = dTargetArea[i];
869  else if( regional && dTargetArea[i] <= 0.0 )
870  nUncoveredTarget++;
871  }
872 
873  if( regional )
874  {
875  // A regional remap legitimately leaves parts of the target uncovered.
876  // Report it: silently emitting empty rows is what made the previous
877  // behaviour so hard to diagnose.
878  //
879  // Only the target count is meaningful. m_meshInputCov is the
880  // *coverage* mesh, so in parallel it also holds cells owned by other
881  // ranks -- these have no overlap here by construction, and their
882  // number grows with the ghost-layer count rather than with the size
883  // of any hole. Reporting that as "uncovered" would be alarming and
884  // wrong, so it is left out.
885  size_t nUncovered = nUncoveredTarget;
886 #ifdef MOAB_HAVE_MPI
887  if( m_pcomm && is_parallel )
888  {
889  size_t local = nUncoveredTarget;
890  MPI_Reduce( &local, &nUncovered, 1, MPI_UNSIGNED_LONG, MPI_SUM, 0, m_pcomm->comm() );
891  }
892 #endif
893  if( is_root && nUncovered > 0 )
894  dbgprint.printf( 0,
895  "Regional mesh: %zu target cells have no overlap coverage; their areas "
896  "are left geometric and their map rows will be empty\n",
897  nUncovered );
898  }
899  }
900 
901  // Set source mesh areas in map
902  if( !m_bPointCloudSource && eInputType == DiscretizationType_FV )
903  {
904  this->SetSourceAreas( m_meshInputCov->vecFaceArea );
905  if( m_meshInputCov->vecMask.size() )
906  {
907  this->SetSourceMask( m_meshInputCov->vecMask );
908  }
909  }
910 
911  // Set target mesh areas in map
912  if( !m_bPointCloudTarget && eOutputType == DiscretizationType_FV )
913  {
914  this->SetTargetAreas( m_meshOutput->vecFaceArea );
915  if( m_meshOutput->vecMask.size() )
916  {
917  this->SetTargetMask( m_meshOutput->vecMask );
918  }
919  }
920 
921  // NOTE: Mesh::CalculateFaceAreasFromOverlap() looks like the natural helper
922  // for the sub-area case, but it is the wrong tool here on two counts: it
923  // indexes by vecSourceFaceIx, which refers to the *coverage* mesh rather
924  // than m_meshInput, and it zeroes every area before accumulating, which
925  // would discard the geometric area of cells the overlap never touches. The
926  // per-cell correction above does the same accumulation with the right
927  // indexing and only replaces areas it can justify.
928  }
929 
930  // Finite volume input / Finite volume output
931  if( ( eInputType == DiscretizationType_FV ) && ( eOutputType == DiscretizationType_FV ) )
932  {
933  // Generate reverse node array and edge map
934  if( m_meshInputCov->revnodearray.size() == 0 ) m_meshInputCov->ConstructReverseNodeArray();
935  if( m_meshInputCov->edgemap.size() == 0 ) m_meshInputCov->ConstructEdgeMap( false );
936 
937  // Initialize coordinates for map
938  this->InitializeSourceCoordinatesFromMeshFV( *m_meshInputCov );
939  this->InitializeTargetCoordinatesFromMeshFV( *m_meshOutput );
940 
941  this->m_pdataGLLNodesIn = nullptr;
942  this->m_pdataGLLNodesOut = nullptr;
943 
944  // Finite volume input / Finite element output
945  MB_CHK_ERR( this->SetDOFmapAssociation( eInputType, mapOptions.nPin, false, nullptr, nullptr, eOutputType,
946  mapOptions.nPout, false, nullptr ) );
947 
948  // Construct remap for FV-FV
949  if( is_root ) dbgprint.printf( 0, "Calculating remap weights\n" );
950 
951  // Construct OfflineMap
952  if( strMapAlgorithm == "invdist" )
953  {
954  if( m_meshInputCov->faces.size() )
955  {
956  if( is_root ) dbgprint.printf( 0, "Calculating map (invdist)\n" );
957  LinearRemapFVtoFVInvDist( *m_meshInputCov, *m_meshOutput, *m_meshOverlap, *this );
958  }
959  }
960  else if( strMapAlgorithm == "delaunay" ) // does not need intersection mesh
961  {
962  if( m_meshInputCov->faces.size() )
963  {
964  if( is_root ) dbgprint.printf( 0, "Calculating map (delaunay)\n" );
965  if (m_meshOverlap) LinearRemapTriangulation( *m_meshInputCov, *m_meshOutput, *m_meshOverlap, *this );
966  else
967  {
968  Mesh dummy;
969  LinearRemapTriangulation( *m_meshInputCov, *m_meshOutput, dummy, *this );
970  }
971  }
972  }
973  else if( strMapAlgorithm == "fvintbilin" )
974  {
975  if( m_meshInputCov->faces.size() )
976  {
977  if( is_root ) dbgprint.printf( 0, "Calculating map (intbilin)\n" );
978  LinearRemapIntegratedBilinear( *m_meshInputCov, *m_meshOutput, *m_meshOverlap, *this );
979  }
980  }
981  else if( strMapAlgorithm == "fvintbilingb" )
982  {
983  if( m_meshInputCov->faces.size() )
984  {
985  if( is_root ) dbgprint.printf( 0, "Calculating map (intbilingb)\n" );
986  LinearRemapIntegratedGeneralizedBarycentric( *m_meshInputCov, *m_meshOutput, *m_meshOverlap,
987  *this );
988  }
989  }
990  else if( strMapAlgorithm == "fvbilin" ) // does not need intersection mesh
991  {
992 #ifdef VERBOSE
993  if( is_root )
994  {
995  m_meshInputCov->Write( "SourceMeshMBTR.g" );
996  m_meshOutput->Write( "TargetMeshMBTR.g" );
997  }
998  else
999  {
1000  m_meshInputCov->Write( "SourceMeshMBTR" + std::to_string( rank ) + ".g" );
1001  m_meshOutput->Write( "TargetMeshMBTR" + std::to_string( rank ) + ".g" );
1002  }
1003 #endif
1004 
1005  if( m_meshInputCov->faces.size() )
1006  {
1007  if( is_root ) dbgprint.printf( 0, "Calculating map (bilin)\n" );
1008  if (m_meshOverlap) LinearRemapBilinear( *m_meshInputCov, *m_meshOutput, *m_meshOverlap, *this );
1009  else
1010  {
1011  Mesh dummy;
1012  LinearRemapBilinear( *m_meshInputCov, *m_meshOutput, dummy, *this );
1013  }
1014  }
1015  }
1016  else
1017  {
1018  if( is_root ) dbgprint.printf( 0, "Calculating conservative FV-FV map\n" );
1019  if( m_meshInputCov->faces.size() )
1020  {
1021 #ifdef USE_NATIVE_TEMPESTREMAP_ROUTINES
1022  LinearRemapFVtoFV( *m_meshInputCov, *m_meshOutput, *m_meshOverlap,
1023  ( mapOptions.fMonotone ) ? ( 1 ) : ( mapOptions.nPin ), *this );
1024 #else
1025  LinearRemapFVtoFV_Tempest_MOAB( ( mapOptions.fMonotone ? 1 : mapOptions.nPin ) );
1026 #endif
1027  }
1028  }
1029  }
1030  else if( eInputType == DiscretizationType_FV )
1031  {
1032  DataArray3D< double > dataGLLJacobian;
1033 
1034  if( is_root ) dbgprint.printf( 0, "Generating output mesh meta data\n" );
1035  double dNumericalArea_loc = GenerateMetaData( *m_meshOutput, mapOptions.nPout, mapOptions.fNoBubble,
1036  dataGLLNodesDest, dataGLLJacobian );
1037 
1038  double dNumericalArea = dNumericalArea_loc;
1039 #ifdef MOAB_HAVE_MPI
1040  if( m_pcomm )
1041  MPI_Reduce( &dNumericalArea_loc, &dNumericalArea, 1, MPI_DOUBLE, MPI_SUM, 0, m_pcomm->comm() );
1042 #endif
1043  if( is_root ) dbgprint.printf( 0, "Output Mesh Numerical Area: %1.15e\n", dNumericalArea );
1044 
1045  // Initialize coordinates for map
1046  this->InitializeSourceCoordinatesFromMeshFV( *m_meshInputCov );
1047  this->InitializeTargetCoordinatesFromMeshFE( *m_meshOutput, mapOptions.nPout, dataGLLNodesDest );
1048 
1049  this->m_pdataGLLNodesIn = nullptr;
1050  this->m_pdataGLLNodesOut = &dataGLLNodesDest;
1051 
1052  // Generate the continuous Jacobian
1053  bool fContinuous = ( eOutputType == DiscretizationType_CGLL );
1054 
1055  if( eOutputType == DiscretizationType_CGLL )
1056  {
1057  GenerateUniqueJacobian( dataGLLNodesDest, dataGLLJacobian, this->GetTargetAreas() );
1058  }
1059  else
1060  {
1061  GenerateDiscontinuousJacobian( dataGLLJacobian, this->GetTargetAreas() );
1062  }
1063 
1064  // Generate reverse node array and edge map
1065  if( m_meshInputCov->revnodearray.size() == 0 ) m_meshInputCov->ConstructReverseNodeArray();
1066  if( m_meshInputCov->edgemap.size() == 0 ) m_meshInputCov->ConstructEdgeMap( false );
1067 
1068  // Finite volume input / Finite element output
1069  MB_CHK_ERR( this->SetDOFmapAssociation( eInputType, mapOptions.nPin, false, nullptr, nullptr, eOutputType,
1070  mapOptions.nPout, ( eOutputType == DiscretizationType_CGLL ),
1071  &dataGLLNodesDest ) );
1072 
1073  // Generate remap weights
1074  if( strMapAlgorithm == "volumetric" )
1075  {
1076  if( is_root ) dbgprint.printf( 0, "Calculating remapping weights for FV->GLL (volumetric)\n" );
1077  LinearRemapFVtoGLL_Volumetric( *m_meshInputCov, *m_meshOutput, *m_meshOverlap, dataGLLNodesDest,
1078  dataGLLJacobian, this->GetTargetAreas(), mapOptions.nPin, *this,
1079  nMonotoneType, fContinuous, mapOptions.fNoConservation );
1080  }
1081  else
1082  {
1083  if( is_root ) dbgprint.printf( 0, "Calculating remapping weights for FV->GLL\n" );
1084  LinearRemapFVtoGLL( *m_meshInputCov, *m_meshOutput, *m_meshOverlap, dataGLLNodesDest, dataGLLJacobian,
1085  this->GetTargetAreas(), mapOptions.nPin, *this, nMonotoneType, fContinuous,
1086  mapOptions.fNoConservation );
1087  }
1088  }
1089  else if( ( eInputType == DiscretizationType_PCLOUD ) || ( eOutputType == DiscretizationType_PCLOUD ) )
1090  {
1091  DataArray3D< double > dataGLLJacobian;
1092  if( !m_bPointCloudSource )
1093  {
1094  // Generate reverse node array and edge map
1095  if( m_meshInputCov->revnodearray.size() == 0 ) m_meshInputCov->ConstructReverseNodeArray();
1096  if( m_meshInputCov->edgemap.size() == 0 ) m_meshInputCov->ConstructEdgeMap( false );
1097 
1098  // Initialize coordinates for map
1099  if( eInputType == DiscretizationType_FV )
1100  {
1101  this->InitializeSourceCoordinatesFromMeshFV( *m_meshInputCov );
1102  }
1103  else
1104  {
1105  if( is_root ) dbgprint.printf( 0, "Generating input mesh meta data\n" );
1106  DataArray3D< double > dataGLLJacobianSrc;
1107  GenerateMetaData( *m_meshInputCov, mapOptions.nPin, mapOptions.fNoBubble, dataGLLNodesSrcCov,
1108  dataGLLJacobian );
1109  GenerateMetaData( *m_meshInput, mapOptions.nPin, mapOptions.fNoBubble, dataGLLNodesSrc,
1110  dataGLLJacobianSrc );
1111  }
1112  }
1113  // else { /* Source is a point cloud dataset */ }
1114 
1115  if( !m_bPointCloudTarget )
1116  {
1117  // Generate reverse node array and edge map
1118  if( m_meshOutput->revnodearray.size() == 0 ) m_meshOutput->ConstructReverseNodeArray();
1119  if( m_meshOutput->edgemap.size() == 0 ) m_meshOutput->ConstructEdgeMap( false );
1120 
1121  // Initialize coordinates for map
1122  if( eOutputType == DiscretizationType_FV )
1123  {
1124  this->InitializeSourceCoordinatesFromMeshFV( *m_meshOutput );
1125  }
1126  else
1127  {
1128  if( is_root ) dbgprint.printf( 0, "Generating output mesh meta data\n" );
1129  GenerateMetaData( *m_meshOutput, mapOptions.nPout, mapOptions.fNoBubble, dataGLLNodesDest,
1130  dataGLLJacobian );
1131  }
1132  }
1133  // else { /* Target is a point cloud dataset */ }
1134 
1135  // Finite volume input / Finite element output
1137  eInputType, mapOptions.nPin, ( eInputType == DiscretizationType_CGLL ),
1138  ( m_bPointCloudSource || eInputType == DiscretizationType_FV ? nullptr : &dataGLLNodesSrcCov ),
1139  ( m_bPointCloudSource || eInputType == DiscretizationType_FV ? nullptr : &dataGLLNodesSrc ),
1140  eOutputType, mapOptions.nPout, ( eOutputType == DiscretizationType_CGLL ),
1141  ( m_bPointCloudTarget ? nullptr : &dataGLLNodesDest ) ) );
1142 
1143  // Construct remap
1144  if( is_root ) dbgprint.printf( 0, "Calculating remap weights with Nearest-Neighbor method\n" );
1145  MB_CHK_ERR( LinearRemapNN_MOAB( true /*use_GID_matching*/, false /*strict_check*/ ) );
1146  }
1147  else if( ( eInputType != DiscretizationType_FV ) && ( eOutputType == DiscretizationType_FV ) )
1148  {
1149  DataArray3D< double > dataGLLJacobianSrc, dataGLLJacobian;
1150 
1151  if( is_root ) dbgprint.printf( 0, "Generating input mesh meta data\n" );
1152  // generate metadata for the input meshes (both source and covering source)
1153  GenerateMetaData( *m_meshInput, mapOptions.nPin, mapOptions.fNoBubble, dataGLLNodesSrc,
1154  dataGLLJacobianSrc );
1155  GenerateMetaData( *m_meshInputCov, mapOptions.nPin, mapOptions.fNoBubble, dataGLLNodesSrcCov,
1156  dataGLLJacobian );
1157 
1158  if( dataGLLNodesSrcCov.GetSubColumns() != m_meshInputCov->faces.size() )
1159  {
1160  _EXCEPTIONT( "Number of element does not match between metadata and "
1161  "input mesh" );
1162  }
1163 
1164  // Initialize coordinates for map
1165  this->InitializeSourceCoordinatesFromMeshFE( *m_meshInputCov, mapOptions.nPin, dataGLLNodesSrcCov );
1166  this->InitializeTargetCoordinatesFromMeshFV( *m_meshOutput );
1167 
1168  // Generate the continuous Jacobian for input mesh
1169  bool fContinuousIn = ( eInputType == DiscretizationType_CGLL );
1170 
1171  if( eInputType == DiscretizationType_CGLL )
1172  {
1173  GenerateUniqueJacobian( dataGLLNodesSrcCov, dataGLLJacobian, this->GetSourceAreas() );
1174  }
1175  else
1176  {
1177  GenerateDiscontinuousJacobian( dataGLLJacobian, this->GetSourceAreas() );
1178  }
1179 
1180  // Finite element input / Finite volume output
1181  MB_CHK_ERR( this->SetDOFmapAssociation( eInputType, mapOptions.nPin,
1182  ( eInputType == DiscretizationType_CGLL ), &dataGLLNodesSrcCov,
1183  &dataGLLNodesSrc, eOutputType, mapOptions.nPout, false, nullptr ) );
1184 
1185  // Generate remap
1186  if( is_root ) dbgprint.printf( 0, "Calculating remap weights\n" );
1187 
1188  if( strMapAlgorithm == "volumetric" )
1189  {
1190  _EXCEPTIONT( "Unimplemented: Volumetric currently unavailable for"
1191  "GLL input mesh" );
1192  }
1193 
1194  this->m_pdataGLLNodesIn = &dataGLLNodesSrcCov;
1195  this->m_pdataGLLNodesOut = nullptr;
1196 
1197 #ifdef USE_NATIVE_TEMPESTREMAP_ROUTINES
1198  LinearRemapSE4( *m_meshInputCov, *m_meshOutput, *m_meshOverlap, dataGLLNodesSrcCov, dataGLLJacobian,
1199  nMonotoneType, fContinuousIn, mapOptions.fNoConservation, mapOptions.fSparseConstraints,
1200  *this );
1201 #else
1202  LinearRemapSE4_Tempest_MOAB( dataGLLNodesSrcCov, dataGLLJacobian, nMonotoneType, fContinuousIn,
1203  mapOptions.fNoConservation, mapOptions.fSparseConstraints );
1204 #endif
1205  }
1206  else if( ( eInputType != DiscretizationType_FV ) && ( eOutputType != DiscretizationType_FV ) )
1207  {
1208  DataArray3D< double > dataGLLJacobianIn, dataGLLJacobianSrc;
1209  DataArray3D< double > dataGLLJacobianOut;
1210 
1211  // Input metadata
1212  if( is_root ) dbgprint.printf( 0, "Generating input mesh meta data\n" );
1213  // generate metadata for the input meshes (both source and covering source)
1214  GenerateMetaData( *m_meshInput, mapOptions.nPin, mapOptions.fNoBubble, dataGLLNodesSrc,
1215  dataGLLJacobianSrc );
1216  // now coverage
1217  GenerateMetaData( *m_meshInputCov, mapOptions.nPin, mapOptions.fNoBubble, dataGLLNodesSrcCov,
1218  dataGLLJacobianIn );
1219  // Output metadata
1220  if( is_root ) dbgprint.printf( 0, "Generating output mesh meta data\n" );
1221  GenerateMetaData( *m_meshOutput, mapOptions.nPout, mapOptions.fNoBubble, dataGLLNodesDest,
1222  dataGLLJacobianOut );
1223 
1224  // Initialize coordinates for map
1225  this->InitializeSourceCoordinatesFromMeshFE( *m_meshInputCov, mapOptions.nPin, dataGLLNodesSrcCov );
1226  this->InitializeTargetCoordinatesFromMeshFE( *m_meshOutput, mapOptions.nPout, dataGLLNodesDest );
1227 
1228  // Generate the continuous Jacobian for input mesh
1229  bool fContinuousIn = ( eInputType == DiscretizationType_CGLL );
1230 
1231  if( eInputType == DiscretizationType_CGLL )
1232  {
1233  GenerateUniqueJacobian( dataGLLNodesSrcCov, dataGLLJacobianIn, this->GetSourceAreas() );
1234  }
1235  else
1236  {
1237  GenerateDiscontinuousJacobian( dataGLLJacobianIn, this->GetSourceAreas() );
1238  }
1239 
1240  // Generate the continuous Jacobian for output mesh
1241  bool fContinuousOut = ( eOutputType == DiscretizationType_CGLL );
1242 
1243  if( eOutputType == DiscretizationType_CGLL )
1244  {
1245  GenerateUniqueJacobian( dataGLLNodesDest, dataGLLJacobianOut, this->GetTargetAreas() );
1246  }
1247  else
1248  {
1249  GenerateDiscontinuousJacobian( dataGLLJacobianOut, this->GetTargetAreas() );
1250  }
1251 
1252  // Input Finite Element to Output Finite Element
1253  MB_CHK_ERR( this->SetDOFmapAssociation( eInputType, mapOptions.nPin,
1254  ( eInputType == DiscretizationType_CGLL ), &dataGLLNodesSrcCov,
1255  &dataGLLNodesSrc, eOutputType, mapOptions.nPout,
1256  ( eOutputType == DiscretizationType_CGLL ), &dataGLLNodesDest ) );
1257 
1258  this->m_pdataGLLNodesIn = &dataGLLNodesSrcCov;
1259  this->m_pdataGLLNodesOut = &dataGLLNodesDest;
1260 
1261  // Generate remap
1262  if( is_root ) dbgprint.printf( 0, "Calculating remap weights\n" );
1263 
1264 #ifdef USE_NATIVE_TEMPESTREMAP_ROUTINES
1265  LinearRemapGLLtoGLL_Integrated( *m_meshInputCov, *m_meshOutput, *m_meshOverlap, dataGLLNodesSrcCov,
1266  dataGLLJacobianIn, dataGLLNodesDest, dataGLLJacobianOut,
1267  this->GetTargetAreas(), mapOptions.nPin, mapOptions.nPout, nMonotoneType,
1268  fContinuousIn, fContinuousOut, mapOptions.fSparseConstraints, *this );
1269 #else
1270  LinearRemapGLLtoGLL2_MOAB( dataGLLNodesSrcCov, dataGLLJacobianIn, dataGLLNodesDest, dataGLLJacobianOut,
1271  this->GetTargetAreas(), mapOptions.nPin, mapOptions.nPout, nMonotoneType,
1272  fContinuousIn, fContinuousOut, mapOptions.fNoConservation );
1273 #endif
1274  }
1275  else
1276  {
1277  _EXCEPTIONT( "Not implemented" );
1278  }
1279 
1280 #ifdef MOAB_HAVE_EIGEN3
1281  copy_tempest_sparsemat_to_eigen3();
1282 #endif
1283 
1284 #ifdef MOAB_HAVE_MPI
1285  if (m_meshOverlap)
1286  {
1287  // Remove ghosted entities from overlap set
1288  moab::Range ghostedEnts;
1291  MB_CHK_SET_ERR( m_interface->remove_entities( m_meshOverlapSet, ghostedEnts ),
1292  "Deleting ghosted entities failed" );
1293  }
1294 #endif
1295  // Verify consistency, conservation and monotonicity, globally
1296  if( !mapOptions.fNoCheck )
1297  {
1298  if( is_root ) dbgprint.printf( 0, "Verifying map" );
1299  this->IsConsistent( 1.0e-8 );
1300  if( !mapOptions.fNoConservation ) this->IsConservative( 1.0e-8 );
1301 
1302  if( nMonotoneType != 0 )
1303  {
1304  this->IsMonotone( 1.0e-12 );
1305  }
1306  }
1307  }
1308  catch( Exception& e )
1309  {
1310  dbgprint.printf( 0, "%s", e.ToString().c_str() );
1311  return ( moab::MB_FAILURE );
1312  }
1313  catch( ... )
1314  {
1315  return ( moab::MB_FAILURE );
1316  }
1317  return moab::MB_SUCCESS;
1318 }

References dbgprint, moab::error(), ErrorCode, MB_ALREADY_ALLOCATED, MB_CHK_ERR, MB_CHK_SET_ERR, MB_SUCCESS, MB_TAG_CREAT, MB_TAG_DENSE, MB_TAG_EXCL, MB_TYPE_DOUBLE, and moab::Remapper::OverlapMesh.

Referenced by main().

◆ GetColDofMap()

const std::vector< int >& moab::TempestOnlineMap::GetColDofMap ( ) const
inline

Definition at line 502 of file TempestOnlineMap.hpp.

502 { return col_dtoc_dofmap; }

References col_dtoc_dofmap.

◆ GetColGlobalDoF()

int moab::TempestOnlineMap::GetColGlobalDoF ( int  localID) const
inline

Get the global Degrees-Of-Freedom ID on the source mesh.

Definition at line 621 of file TempestOnlineMap.hpp.

622 {
623  return col_gdofmap[localColID];
624 }

◆ GetDestinationGlobalNDofs()

int moab::TempestOnlineMap::GetDestinationGlobalNDofs ( )

Get the number of total Degrees-Of-Freedom defined on the destination mesh.

◆ GetDestinationLocalNDofs()

int moab::TempestOnlineMap::GetDestinationLocalNDofs ( )

Get the number of local Degrees-Of-Freedom defined on the destination mesh.

◆ GetDestinationNDofsPerElement()

int moab::TempestOnlineMap::GetDestinationNDofsPerElement ( )
inline

Get the number of Degrees-Of-Freedom per element on the destination mesh.

Definition at line 639 of file TempestOnlineMap.hpp.

640 {
641  return m_nDofsPEl_Dest;
642 }

◆ GetGlobalSourceAreas()

const DataArray1D< double >& moab::TempestOnlineMap::GetGlobalSourceAreas ( ) const

If we computed the reduction, get the vector representing the source areas for all entities in the mesh

◆ GetGlobalTargetAreas()

const DataArray1D< double >& moab::TempestOnlineMap::GetGlobalTargetAreas ( ) const

If we computed the reduction, get the vector representing the target areas for all entities in the mesh

◆ GetIndexOfColGlobalDoF()

int moab::TempestOnlineMap::GetIndexOfColGlobalDoF ( int  globalColDoF) const
inline

Get the index of globaColDoF.

Definition at line 626 of file TempestOnlineMap.hpp.

627 {
628  return globalColDoF + 1; // temporary
629 }

◆ GetIndexOfRowGlobalDoF()

int moab::TempestOnlineMap::GetIndexOfRowGlobalDoF ( int  globalRowDoF) const
inline

Get the index of globaRowDoF.

Definition at line 615 of file TempestOnlineMap.hpp.

616 {
617  return globalRowDoF + 1;
618 }

◆ GetRowDofMap()

const std::vector< int >& moab::TempestOnlineMap::GetRowDofMap ( ) const
inline

Read-only access to the matrix-row -> matrix-col DOF index maps. Used by callers (e.g. iMOAB diagnostic helpers) that need to translate matrix indices back into source/target tag-vector indices.

Definition at line 501 of file TempestOnlineMap.hpp.

501 { return row_dtoc_dofmap; }

References row_dtoc_dofmap.

◆ GetRowGlobalDoF()

int moab::TempestOnlineMap::GetRowGlobalDoF ( int  localID) const
inline

Get the global Degrees-Of-Freedom ID on the destination mesh.

Definition at line 610 of file TempestOnlineMap.hpp.

611 {
612  return row_gdofmap[localRowID];
613 }

◆ GetSourceGlobalNDofs()

int moab::TempestOnlineMap::GetSourceGlobalNDofs ( )

Get the number of total Degrees-Of-Freedom defined on the source mesh.

◆ GetSourceLocalNDofs()

int moab::TempestOnlineMap::GetSourceLocalNDofs ( )

Get the number of local Degrees-Of-Freedom defined on the source mesh.

◆ GetSourceNDofsPerElement()

int moab::TempestOnlineMap::GetSourceNDofsPerElement ( )
inline

Get the number of Degrees-Of-Freedom per element on the source mesh.

Definition at line 632 of file TempestOnlineMap.hpp.

633 {
634  return m_nDofsPEl_Src;
635 }

◆ GlobalSourceDofCount()

int moab::TempestOnlineMap::GlobalSourceDofCount ( ) const
inline

Global number of source DoFs declared by the map file (n_a), or -1 if unknown.

Definition at line 476 of file TempestOnlineMap.hpp.

476 { return m_nTotDofs_SrcGlobal; }

References m_nTotDofs_SrcGlobal.

◆ IsConservative()

int moab::TempestOnlineMap::IsConservative ( double  dTolerance)
virtual

Determine if the map is conservative.

Definition at line 1368 of file TempestOnlineMap.cpp.

1369 {
1370 #ifndef MOAB_HAVE_MPI
1371 
1372  return OfflineMap::IsConservative( dTolerance );
1373 
1374 #else
1375  // return OfflineMap::IsConservative(dTolerance);
1376 
1377  int ierr;
1378  // Get map entries
1379  DataArray1D< int > dataRows;
1380  DataArray1D< int > dataCols;
1381  DataArray1D< double > dataEntries;
1382  const DataArray1D< double >& dTargetAreas = this->GetTargetAreas();
1383  const DataArray1D< double >& dSourceAreas = this->GetSourceAreas();
1384 
1385  // Calculate column sums
1386  std::vector< int > dColumnsUnique;
1387  std::vector< double > dColumnSums;
1388 
1389  int nColumns = m_mapRemap.GetColumns();
1390  m_mapRemap.GetEntries( dataRows, dataCols, dataEntries );
1391  dColumnSums.resize( m_nTotDofs_SrcCov, 0.0 );
1392  dColumnsUnique.resize( m_nTotDofs_SrcCov, -1 );
1393 
1394  for( unsigned i = 0; i < dataEntries.GetRows(); i++ )
1395  {
1396  dColumnSums[dataCols[i]] += dataEntries[i] * dTargetAreas[dataRows[i]] / dSourceAreas[dataCols[i]];
1397 
1398  assert( dataCols[i] < m_nTotDofs_SrcCov );
1399 
1400  // GID for column DoFs: col_gdofmap[ col_ldofmap [ dataCols[i] ] ]
1401  int colGID = this->GetColGlobalDoF( dataCols[i] ); // col_gdofmap[ col_ldofmap [ dataCols[i] ] ];
1402  // int colGID = col_gdofmap[ col_ldofmap [ dataCols[i] ] ];
1403  dColumnsUnique[dataCols[i]] = colGID;
1404 
1405  // std::cout << "Column dataCols[i]=" << dataCols[i] << " with GID = " << colGID <<
1406  // std::endl;
1407  }
1408 
1409  int rootProc = 0;
1410  std::vector< int > nElementsInProc;
1411  const int nDATA = 3;
1412  nElementsInProc.resize( size * nDATA );
1413  int senddata[nDATA] = { nColumns, m_nTotDofs_SrcCov, m_nTotDofs_Src };
1414  ierr = MPI_Gather( senddata, nDATA, MPI_INT, nElementsInProc.data(), nDATA, MPI_INT, rootProc, m_pcomm->comm() );
1415  if( ierr != MPI_SUCCESS ) return -1;
1416 
1417  int nTotVals = 0, nTotColumns = 0; // nTotColumnsUnq = 0;
1418  std::vector< int > dColumnIndices;
1419  std::vector< double > dColumnSumsTotal;
1420  std::vector< int > displs, rcount;
1421  if( rank == rootProc )
1422  {
1423  displs.resize( size + 1, 0 );
1424  rcount.resize( size, 0 );
1425  int gsum = 0;
1426  for( int ir = 0; ir < size; ++ir )
1427  {
1428  nTotVals += nElementsInProc[ir * nDATA];
1429  nTotColumns += nElementsInProc[ir * nDATA + 1];
1430  // nTotColumnsUnq += nElementsInProc[ir * nDATA + 2];
1431 
1432  displs[ir] = gsum;
1433  rcount[ir] = nElementsInProc[ir * nDATA + 1];
1434  gsum += rcount[ir];
1435 
1436  // printf( "%d: nTotColumns: %d, Displs: %d, rcount: %d, gsum = %d\n", ir, nTotColumns, displs[ir], rcount[ir], gsum );
1437  }
1438 
1439  printf( "Total nnz: %d, global source elements = %d\n", nTotVals, gsum );
1440 
1441  dColumnIndices.resize( nTotColumns, -1 );
1442  dColumnSumsTotal.resize( nTotColumns, 0.0 );
1443  // dColumnSourceAreas.resize ( nTotColumns, 0.0 );
1444  }
1445 
1446  // Gather all ColumnSums to root process and accumulate
1447  // We expect that the sums of all columns equate to 1.0 within user specified tolerance
1448  // Need to do a gatherv here since different processes have different number of elements
1449  // MPI_Reduce(&dColumnSums[0], &dColumnSumsTotal[0], m_mapRemap.GetColumns(), MPI_DOUBLE,
1450  // MPI_SUM, 0, m_pcomm->comm());
1451  // Use .data() rather than &vec[0] -- on non-root ranks dColumnIndices /
1452  // dColumnSumsTotal are empty (only resized on root, see ~10 lines above),
1453  // and &vec[0] indexing into an empty vector is undefined behavior. The
1454  // .data() form returns nullptr for an empty vector, which MPI_Gatherv
1455  // ignores since recvcount on non-root paths is effectively zero.
1456  ierr = MPI_Gatherv( dColumnsUnique.data(), m_nTotDofs_SrcCov, MPI_INT, dColumnIndices.data(), rcount.data(),
1457  displs.data(), MPI_INT, rootProc, m_pcomm->comm() );
1458  if( ierr != MPI_SUCCESS ) return -1;
1459  ierr = MPI_Gatherv( dColumnSums.data(), m_nTotDofs_SrcCov, MPI_DOUBLE, dColumnSumsTotal.data(), rcount.data(),
1460  displs.data(), MPI_DOUBLE, rootProc, m_pcomm->comm() );
1461  if( ierr != MPI_SUCCESS ) return -1;
1462  // ierr = MPI_Gatherv ( &dSourceAreas[0], m_nTotDofs_SrcCov, MPI_DOUBLE, &dColumnSourceAreas[0],
1463  // rcount.data(), displs.data(), MPI_DOUBLE, rootProc, m_pcomm->comm() ); if ( ierr !=
1464  // MPI_SUCCESS ) return -1;
1465 
1466  // Clean out unwanted arrays now
1467  dColumnSums.clear();
1468  dColumnsUnique.clear();
1469 
1470  // Verify all column sums equal the input Jacobian
1471  int fConservative = 0;
1472  if( rank == rootProc )
1473  {
1474  displs[size] = ( nTotColumns );
1475  // std::vector<double> dColumnSumsOnRoot(nTotColumnsUnq, 0.0);
1476  std::map< int, double > dColumnSumsOnRoot;
1477  // std::map<int, double> dColumnSourceAreasOnRoot;
1478  for( int ir = 0; ir < size; ir++ )
1479  {
1480  for( int ips = displs[ir]; ips < displs[ir + 1]; ips++ )
1481  {
1482  if( dColumnIndices[ips] < 0 ) continue;
1483  // printf("%d, %d: dColumnIndices[ips]: %d\n", ir, ips, dColumnIndices[ips]);
1484  // assert( dColumnIndices[ips] < nTotColumnsUnq );
1485  dColumnSumsOnRoot[dColumnIndices[ips]] += dColumnSumsTotal[ips]; // / dColumnSourceAreas[ips];
1486  // dColumnSourceAreasOnRoot[ dColumnIndices[ips] ] = dColumnSourceAreas[ips];
1487  // dColumnSourceAreas[ dColumnIndices[ips] ]
1488  }
1489  }
1490 
1491  for( std::map< int, double >::iterator it = dColumnSumsOnRoot.begin(); it != dColumnSumsOnRoot.end(); ++it )
1492  {
1493  // if ( fabs ( it->second - dColumnSourceAreasOnRoot[it->first] ) > dTolerance )
1494  if( fabs( it->second - 1.0 ) > dTolerance )
1495  {
1496  fConservative++;
1497  Announce( "TempestOnlineMap is not conservative in column "
1498  // "%i (%1.15e)", it->first, it->second );
1499  "%i (%1.15e)",
1500  it->first, it->second /* / dColumnSourceAreasOnRoot[it->first] */ );
1501  }
1502  }
1503  }
1504 
1505  // TODO: Just do a broadcast from root instead of a reduction
1506  ierr = MPI_Bcast( &fConservative, 1, MPI_INT, rootProc, m_pcomm->comm() );
1507  if( ierr != MPI_SUCCESS ) return -1;
1508 
1509  return fConservative;
1510 #endif
1511 }

◆ IsConsistent()

int moab::TempestOnlineMap::IsConsistent ( double  dTolerance)
virtual

Determine if the map is first-order accurate.

Definition at line 1322 of file TempestOnlineMap.cpp.

1323 {
1324 #ifndef MOAB_HAVE_MPI
1325 
1326  return OfflineMap::IsConsistent( dTolerance );
1327 
1328 #else
1329 
1330  // Get map entries
1331  DataArray1D< int > dataRows;
1332  DataArray1D< int > dataCols;
1333  DataArray1D< double > dataEntries;
1334 
1335  // Calculate row sums
1336  DataArray1D< double > dRowSums;
1337  m_mapRemap.GetEntries( dataRows, dataCols, dataEntries );
1338  dRowSums.Allocate( m_mapRemap.GetRows() );
1339 
1340  for( unsigned i = 0; i < dataRows.GetRows(); i++ )
1341  {
1342  dRowSums[dataRows[i]] += dataEntries[i];
1343  }
1344 
1345  // Verify all row sums are equal to 1
1346  int fConsistent = 0;
1347  for( unsigned i = 0; i < dRowSums.GetRows(); i++ )
1348  {
1349  if( fabs( dRowSums[i] - 1.0 ) > dTolerance )
1350  {
1351  fConsistent++;
1352  int rowGID = row_gdofmap[i];
1353  Announce( "TempestOnlineMap is not consistent in row %i (%1.15e)", rowGID, dRowSums[i] );
1354  }
1355  }
1356 
1357  int ierr;
1358  int fConsistentGlobal = 0;
1359  ierr = MPI_Allreduce( &fConsistent, &fConsistentGlobal, 1, MPI_INT, MPI_SUM, m_pcomm->comm() );
1360  if( ierr != MPI_SUCCESS ) return -1;
1361 
1362  return fConsistentGlobal;
1363 #endif
1364 }

◆ IsMonotone()

int moab::TempestOnlineMap::IsMonotone ( double  dTolerance)
virtual

Determine if the map is monotone.

Definition at line 1515 of file TempestOnlineMap.cpp.

1516 {
1517 #ifndef MOAB_HAVE_MPI
1518 
1519  return OfflineMap::IsMonotone( dTolerance );
1520 
1521 #else
1522 
1523  // Get map entries
1524  DataArray1D< int > dataRows;
1525  DataArray1D< int > dataCols;
1526  DataArray1D< double > dataEntries;
1527 
1528  m_mapRemap.GetEntries( dataRows, dataCols, dataEntries );
1529 
1530  // Verify all entries are in the range [0,1]
1531  int fMonotone = 0;
1532  for( unsigned i = 0; i < dataRows.GetRows(); i++ )
1533  {
1534  if( ( dataEntries[i] < -dTolerance ) || ( dataEntries[i] > 1.0 + dTolerance ) )
1535  {
1536  fMonotone++;
1537 
1538  Announce( "TempestOnlineMap is not monotone in entry (%i): %1.15e", i, dataEntries[i] );
1539  }
1540  }
1541 
1542  int ierr;
1543  int fMonotoneGlobal = 0;
1544  ierr = MPI_Allreduce( &fMonotone, &fMonotoneGlobal, 1, MPI_INT, MPI_SUM, m_pcomm->comm() );
1545  if( ierr != MPI_SUCCESS ) return -1;
1546 
1547  return fMonotoneGlobal;
1548 #endif
1549 }

◆ LinearRemapFVtoFV_Tempest_MOAB()

void moab::TempestOnlineMap::LinearRemapFVtoFV_Tempest_MOAB ( int  nOrder)
private

Compute the remapping weights for a FV field defined on the source to a FV field defined on the target mesh.

Definition at line 121 of file TempestLinearRemap.cpp.

122 {
123  // Order of triangular quadrature rule
124  const int TriQuadRuleOrder = 4;
125 
126  // Verify ReverseNodeArray has been calculated
127  if( m_meshInputCov->faces.size() > 0 && m_meshInputCov->revnodearray.size() == 0 )
128  {
129  _EXCEPTIONT( "ReverseNodeArray has not been calculated for m_meshInputCov" );
130  }
131 
132  // Triangular quadrature rule
133  TriangularQuadratureRule triquadrule( TriQuadRuleOrder );
134 
135  // Number of coefficients needed at this order
136 #ifdef RECTANGULAR_TRUNCATION
137  int nCoefficients = nOrder * nOrder;
138 #endif
139 #ifdef TRIANGULAR_TRUNCATION
140  int nCoefficients = nOrder * ( nOrder + 1 ) / 2;
141 #endif
142 
143  // Number of faces you need
144  const int nRequiredFaceSetSize = nCoefficients;
145 
146  // Fit weight exponent
147  const int nFitWeightsExponent = nOrder + 2;
148 
149  // Announcements
150  moab::DebugOutput dbgprint( std::cout, this->rank, 0 );
151  dbgprint.set_prefix( "[LinearRemapFVtoFV_Tempest_MOAB]: " );
152  if( is_root )
153  {
154  dbgprint.printf( 0, "Finite Volume to Finite Volume Projection\n" );
155  dbgprint.printf( 0, "Triangular quadrature rule order %i\n", TriQuadRuleOrder );
156  dbgprint.printf( 0, "Number of coefficients: %i\n", nCoefficients );
157  dbgprint.printf( 0, "Required adjacency set size: %i\n", nRequiredFaceSetSize );
158  dbgprint.printf( 0, "Fit weights exponent: %i\n", nFitWeightsExponent );
159  }
160 
161  // Current overlap face
162  int ixOverlap = 0;
163 #ifdef VERBOSE
164  const unsigned outputFrequency = ( m_meshInputCov->faces.size() / 10 ) + 1;
165 #endif
166  DataArray2D< double > dIntArray;
167  DataArray1D< double > dConstraint( nCoefficients );
168 
169  // Loop through all faces on m_meshInputCov
170  for( size_t ixFirst = 0; ixFirst < m_meshInputCov->faces.size(); ixFirst++ )
171  {
172  // Output every 1000 elements
173 #ifdef VERBOSE
174  if( ixFirst % outputFrequency == 0 && is_root )
175  {
176  dbgprint.printf( 0, "Element %zu/%lu\n", ixFirst, m_meshInputCov->faces.size() );
177  }
178 #endif
179  // Find the set of Faces that overlap faceFirst
180  int ixOverlapBegin = ixOverlap;
181  unsigned ixOverlapEnd = ixOverlapBegin;
182 
183  for( ; ixOverlapEnd < m_meshOverlap->faces.size(); ixOverlapEnd++ )
184  {
185  if( ixFirst - m_meshOverlap->vecSourceFaceIx[ixOverlapEnd] != 0 ) break;
186  }
187 
188  unsigned nOverlapFaces = ixOverlapEnd - ixOverlapBegin;
189 
190  if( nOverlapFaces == 0 ) continue;
191 
192  // Build integration array
193  BuildIntegrationArray( *m_meshInputCov, *m_meshOverlap, triquadrule, ixFirst, ixOverlapBegin, ixOverlapEnd,
194  nOrder, dIntArray );
195 
196  // Set of Faces to use in building the reconstruction and associated
197  // distance metric.
198  AdjacentFaceVector vecAdjFaces;
199 
200  GetAdjacentFaceVectorByEdge( *m_meshInputCov, ixFirst, nRequiredFaceSetSize, vecAdjFaces );
201 
202  // Number of adjacent Faces
203  int nAdjFaces = vecAdjFaces.size();
204 
205  // Determine the conservative constraint equation
206  double dFirstArea = m_meshInputCov->vecFaceArea[ixFirst];
207  dConstraint.Zero();
208  for( int p = 0; p < nCoefficients; p++ )
209  {
210  for( unsigned j = 0; j < nOverlapFaces; j++ )
211  {
212  dConstraint[p] += dIntArray[p][j];
213  }
214  dConstraint[p] /= dFirstArea;
215  }
216 
217  // Build the fit array from the integration operator
218  DataArray2D< double > dFitArray;
219  DataArray1D< double > dFitWeights;
220  DataArray2D< double > dFitArrayPlus;
221 
222  BuildFitArray( *m_meshInputCov, triquadrule, ixFirst, vecAdjFaces, nOrder, nFitWeightsExponent, dConstraint,
223  dFitArray, dFitWeights );
224 
225  // Compute the inverse fit array
226  bool fSuccess = InvertFitArray_Corrected( dConstraint, dFitArray, dFitWeights, dFitArrayPlus );
227 
228  // Multiply integration array and fit array
229  DataArray2D< double > dComposedArray( nAdjFaces, nOverlapFaces );
230  if( fSuccess )
231  {
232  // Multiply integration array and inverse fit array
233  for( int i = 0; i < nAdjFaces; i++ )
234  {
235  for( size_t j = 0; j < nOverlapFaces; j++ )
236  {
237  for( int k = 0; k < nCoefficients; k++ )
238  {
239  dComposedArray( i, j ) += dIntArray( k, j ) * dFitArrayPlus( i, k );
240  }
241  }
242  }
243 
244  // Unable to invert fit array, drop to 1st order. In this case
245  // dFitArrayPlus(0,0) = 1 and all other entries are zero.
246  }
247  else
248  {
249  dComposedArray.Zero();
250  for( size_t j = 0; j < nOverlapFaces; j++ )
251  {
252  dComposedArray( 0, j ) += dIntArray( 0, j );
253  }
254  }
255 
256  // Put composed array into map
257  for( unsigned i = 0; i < vecAdjFaces.size(); i++ )
258  {
259  for( unsigned j = 0; j < nOverlapFaces; j++ )
260  {
261  int& ixFirstFaceLoc = vecAdjFaces[i].first;
262  int& ixSecondFaceLoc = m_meshOverlap->vecTargetFaceIx[ixOverlap + j];
263 
264  // signal to not participate, because it is a ghost target
265  if( ixSecondFaceLoc < 0 ) continue; // do not do anything
266 
267  m_mapRemap( ixSecondFaceLoc, ixFirstFaceLoc ) +=
268  dComposedArray[i][j] / m_meshOutput->vecFaceArea[ixSecondFaceLoc];
269  }
270  }
271 
272  // Increment the current overlap index
273  ixOverlap += nOverlapFaces;
274  }
275 
276  return;
277 }

References dbgprint.

◆ LinearRemapFVtoGLL_MOAB()

void moab::TempestOnlineMap::LinearRemapFVtoGLL_MOAB ( const DataArray3D< int > &  dataGLLNodes,
const DataArray3D< double > &  dataGLLJacobian,
const DataArray1D< double > &  dataGLLNodalArea,
int  nOrder,
int  nMonotoneType,
bool  fContinuous,
bool  fNoConservation 
)
private

Generate the OfflineMap for remapping from finite volumes to finite elements.

◆ LinearRemapGLLtoGLL2_MOAB()

void moab::TempestOnlineMap::LinearRemapGLLtoGLL2_MOAB ( const DataArray3D< int > &  dataGLLNodesIn,
const DataArray3D< double > &  dataGLLJacobianIn,
const DataArray3D< int > &  dataGLLNodesOut,
const DataArray3D< double > &  dataGLLJacobianOut,
const DataArray1D< double > &  dataNodalAreaOut,
int  nPin,
int  nPout,
int  nMonotoneType,
bool  fContinuousIn,
bool  fContinuousOut,
bool  fNoConservation 
)
private

Generate the OfflineMap for remapping from finite elements to finite elements.

Definition at line 1290 of file TempestLinearRemap.cpp.

1301 {
1302  // Triangular quadrature rule
1303  TriangularQuadratureRule triquadrule( 8 );
1304 
1305  const DataArray2D< double >& dG = triquadrule.GetG();
1306  const DataArray1D< double >& dW = triquadrule.GetW();
1307 
1308  // Get SparseMatrix represntation of the OfflineMap
1309  SparseMatrix< double >& smatMap = this->GetSparseMatrix();
1310 
1311  // Sample coefficients
1312  DataArray2D< double > dSampleCoeffIn( nPin, nPin );
1313  DataArray2D< double > dSampleCoeffOut( nPout, nPout );
1314 
1315  // Announcemnets
1316  moab::DebugOutput dbgprint( std::cout, this->rank, 0 );
1317  dbgprint.set_prefix( "[LinearRemapGLLtoGLL2_MOAB]: " );
1318  if( is_root )
1319  {
1320  dbgprint.printf( 0, "Finite Element to Finite Element Projection\n" );
1321  dbgprint.printf( 0, "Order of the input FE polynomial interpolant: %i\n", nPin );
1322  dbgprint.printf( 0, "Order of the output FE polynomial interpolant: %i\n", nPout );
1323  }
1324 
1325  // Build the integration array for each element on m_meshOverlap
1326  DataArray3D< double > dGlobalIntArray( nPin * nPin, m_meshOverlap->faces.size(), nPout * nPout );
1327 
1328  // Number of overlap Faces per source Face
1329  DataArray1D< int > nAllOverlapFaces( m_meshInputCov->faces.size() );
1330 
1331  int ixOverlap = 0;
1332  for( size_t ixFirst = 0; ixFirst < m_meshInputCov->faces.size(); ixFirst++ )
1333  {
1334  // Determine how many overlap Faces and triangles are present
1335  int nOverlapFaces = 0;
1336  size_t ixOverlapTemp = ixOverlap;
1337  for( ; ixOverlapTemp < m_meshOverlap->faces.size(); ixOverlapTemp++ )
1338  {
1339  // const Face & faceOverlap = m_meshOverlap->faces[ixOverlapTemp];
1340  if( ixFirst - m_meshOverlap->vecSourceFaceIx[ixOverlapTemp] != 0 )
1341  {
1342  break;
1343  }
1344 
1345  nOverlapFaces++;
1346  }
1347 
1348  nAllOverlapFaces[ixFirst] = nOverlapFaces;
1349 
1350  // Increment the current overlap index
1351  ixOverlap += nAllOverlapFaces[ixFirst];
1352  }
1353 
1354  // Geometric area of each output node
1355  DataArray2D< double > dGeometricOutputArea( m_meshOutput->faces.size(), nPout * nPout );
1356 
1357  // Area of each overlap element in the output basis
1358  DataArray2D< double > dOverlapOutputArea( m_meshOverlap->faces.size(), nPout * nPout );
1359 
1360  // Loop through all faces on m_meshInputCov
1361  ixOverlap = 0;
1362 #ifdef VERBOSE
1363  const unsigned outputFrequency = ( m_meshInputCov->faces.size() / 10 ) + 1;
1364 #endif
1365  if( is_root ) dbgprint.printf( 0, "Building conservative distribution maps\n" );
1366 
1367  // generic triangle used for area computation, for triangles around the center of overlap face;
1368  // used for overlap faces with more than 4 edges;
1369  // nodes array will be set for each triangle;
1370  // these triangles are not part of the mesh structure, they are just temporary during
1371  // aforementioned decomposition.
1372  Face faceTri( 3 );
1373  NodeVector nodes( 3 );
1374  faceTri.SetNode( 0, 0 );
1375  faceTri.SetNode( 1, 1 );
1376  faceTri.SetNode( 2, 2 );
1377 
1378  for( size_t ixFirst = 0; ixFirst < m_meshInputCov->faces.size(); ixFirst++ )
1379  {
1380 #ifdef VERBOSE
1381  // Announce computation progress
1382  if( ixFirst % outputFrequency == 0 && is_root )
1383  {
1384  dbgprint.printf( 0, "Element %zu/%lu\n", ixFirst, m_meshInputCov->faces.size() );
1385  }
1386 #endif
1387  // Quantities from the First Mesh
1388  const Face& faceFirst = m_meshInputCov->faces[ixFirst];
1389 
1390  const NodeVector& nodesFirst = m_meshInputCov->nodes;
1391 
1392  // Number of overlapping Faces and triangles
1393  int nOverlapFaces = nAllOverlapFaces[ixFirst];
1394 
1395  if( !nOverlapFaces ) continue;
1396 
1397  // // Calculate total element Jacobian
1398  // double dTotalJacobian = 0.0;
1399  // for (int s = 0; s < nPin; s++) {
1400  // for (int t = 0; t < nPin; t++) {
1401  // dTotalJacobian += dataGLLJacobianIn[s][t][ixFirst];
1402  // }
1403  // }
1404 
1405  // Loop through all Overlap Faces
1406  for( int i = 0; i < nOverlapFaces; i++ )
1407  {
1408  // Quantities from the overlap Mesh
1409  const Face& faceOverlap = m_meshOverlap->faces[ixOverlap + i];
1410 
1411  const NodeVector& nodesOverlap = m_meshOverlap->nodes;
1412 
1413  // Quantities from the Second Mesh
1414  int ixSecond = m_meshOverlap->vecTargetFaceIx[ixOverlap + i];
1415 
1416  // signal to not participate, because it is a ghost target
1417  if( ixSecond < 0 ) continue; // do not do anything
1418 
1419  const NodeVector& nodesSecond = m_meshOutput->nodes;
1420 
1421  const Face& faceSecond = m_meshOutput->faces[ixSecond];
1422 
1423  int nbEdges = faceOverlap.edges.size();
1424  int nOverlapTriangles = 1;
1425  Node center; // not used if nbEdges == 3
1426  if( nbEdges > 3 )
1427  { // decompose from center in this case
1428  nOverlapTriangles = nbEdges;
1429  for( int k = 0; k < nbEdges; k++ )
1430  {
1431  const Node& node = nodesOverlap[faceOverlap[k]];
1432  center = center + node;
1433  }
1434  center = center / nbEdges;
1435  center = center.Normalized(); // project back on sphere of radius 1
1436  }
1437 
1438  Node node0, node1, node2;
1439  double dTriArea;
1440 
1441  // Loop over all sub-triangles of this Overlap Face
1442  for( int j = 0; j < nOverlapTriangles; j++ )
1443  {
1444  if( nbEdges == 3 ) // will come here only once, nOverlapTriangles == 1 in this case
1445  {
1446  node0 = nodesOverlap[faceOverlap[0]];
1447  node1 = nodesOverlap[faceOverlap[1]];
1448  node2 = nodesOverlap[faceOverlap[2]];
1449  dTriArea = CalculateFaceArea( faceOverlap, nodesOverlap );
1450  }
1451  else // decompose polygon in triangles around the center
1452  {
1453  node0 = center;
1454  node1 = nodesOverlap[faceOverlap[j]];
1455  int j1 = ( j + 1 ) % nbEdges;
1456  node2 = nodesOverlap[faceOverlap[j1]];
1457  nodes[0] = center;
1458  nodes[1] = node1;
1459  nodes[2] = node2;
1460  dTriArea = CalculateFaceArea( faceTri, nodes );
1461  }
1462 
1463  for( int k = 0; k < triquadrule.GetPoints(); k++ )
1464  {
1465  // Get the nodal location of this point
1466  double dX[3];
1467 
1468  dX[0] = dG( k, 0 ) * node0.x + dG( k, 1 ) * node1.x + dG( k, 2 ) * node2.x;
1469  dX[1] = dG( k, 0 ) * node0.y + dG( k, 1 ) * node1.y + dG( k, 2 ) * node2.y;
1470  dX[2] = dG( k, 0 ) * node0.z + dG( k, 1 ) * node1.z + dG( k, 2 ) * node2.z;
1471 
1472  double dMag = sqrt( dX[0] * dX[0] + dX[1] * dX[1] + dX[2] * dX[2] );
1473 
1474  dX[0] /= dMag;
1475  dX[1] /= dMag;
1476  dX[2] /= dMag;
1477 
1478  Node nodeQuadrature( dX[0], dX[1], dX[2] );
1479 
1480  // Find the components of this quadrature point in the basis
1481  // of the first Face.
1482  double dAlphaIn;
1483  double dBetaIn;
1484 
1485  ApplyInverseMap( faceFirst, nodesFirst, nodeQuadrature, dAlphaIn, dBetaIn );
1486 
1487  // Find the components of this quadrature point in the basis
1488  // of the second Face.
1489  double dAlphaOut;
1490  double dBetaOut;
1491 
1492  ApplyInverseMap( faceSecond, nodesSecond, nodeQuadrature, dAlphaOut, dBetaOut );
1493 
1494  /*
1495  // Check inverse map value
1496  if ((dAlphaIn < 0.0) || (dAlphaIn > 1.0) ||
1497  (dBetaIn < 0.0) || (dBetaIn > 1.0)
1498  ) {
1499  _EXCEPTION2("Inverse Map out of range (%1.5e %1.5e)",
1500  dAlphaIn, dBetaIn);
1501  }
1502 
1503  // Check inverse map value
1504  if ((dAlphaOut < 0.0) || (dAlphaOut > 1.0) ||
1505  (dBetaOut < 0.0) || (dBetaOut > 1.0)
1506  ) {
1507  _EXCEPTION2("Inverse Map out of range (%1.5e %1.5e)",
1508  dAlphaOut, dBetaOut);
1509  }
1510  */
1511  // Sample the First finite element at this point
1512  SampleGLLFiniteElement( nMonotoneType, nPin, dAlphaIn, dBetaIn, dSampleCoeffIn );
1513 
1514  // Sample the Second finite element at this point
1515  SampleGLLFiniteElement( nMonotoneType, nPout, dAlphaOut, dBetaOut, dSampleCoeffOut );
1516 
1517  // Overlap output area
1518  for( int s = 0; s < nPout; s++ )
1519  {
1520  for( int t = 0; t < nPout; t++ )
1521  {
1522  double dNodeArea = dSampleCoeffOut[s][t] * dW[k] * dTriArea;
1523 
1524  dOverlapOutputArea[ixOverlap + i][s * nPout + t] += dNodeArea;
1525 
1526  dGeometricOutputArea[ixSecond][s * nPout + t] += dNodeArea;
1527  }
1528  }
1529 
1530  // Compute overlap integral
1531  int ixp = 0;
1532  for( int p = 0; p < nPin; p++ )
1533  {
1534  for( int q = 0; q < nPin; q++ )
1535  {
1536  int ixs = 0;
1537  for( int s = 0; s < nPout; s++ )
1538  {
1539  for( int t = 0; t < nPout; t++ )
1540  {
1541  // Sample the Second finite element at this point
1542  dGlobalIntArray[ixp][ixOverlap + i][ixs] +=
1543  dSampleCoeffOut[s][t] * dSampleCoeffIn[p][q] * dW[k] * dTriArea;
1544 
1545  ixs++;
1546  }
1547  }
1548 
1549  ixp++;
1550  }
1551  }
1552  }
1553  }
1554  }
1555 
1556  // Coefficients
1557  DataArray2D< double > dCoeff( nOverlapFaces * nPout * nPout, nPin * nPin );
1558 
1559  for( int i = 0; i < nOverlapFaces; i++ )
1560  {
1561  // int ixSecondFace = m_meshOverlap->vecTargetFaceIx[ixOverlap + i];
1562 
1563  int ixp = 0;
1564  for( int p = 0; p < nPin; p++ )
1565  {
1566  for( int q = 0; q < nPin; q++ )
1567  {
1568  int ixs = 0;
1569  for( int s = 0; s < nPout; s++ )
1570  {
1571  for( int t = 0; t < nPout; t++ )
1572  {
1573  dCoeff[i * nPout * nPout + ixs][ixp] = dGlobalIntArray[ixp][ixOverlap + i][ixs] /
1574  dOverlapOutputArea[ixOverlap + i][s * nPout + t];
1575 
1576  ixs++;
1577  }
1578  }
1579 
1580  ixp++;
1581  }
1582  }
1583  }
1584 
1585  // Source areas
1586  DataArray1D< double > vecSourceArea( nPin * nPin );
1587 
1588  for( int p = 0; p < nPin; p++ )
1589  {
1590  for( int q = 0; q < nPin; q++ )
1591  {
1592  vecSourceArea[p * nPin + q] = dataGLLJacobianIn[p][q][ixFirst];
1593  }
1594  }
1595 
1596  // Target areas
1597  DataArray1D< double > vecTargetArea( nOverlapFaces * nPout * nPout );
1598 
1599  for( int i = 0; i < nOverlapFaces; i++ )
1600  {
1601  // int ixSecond = m_meshOverlap->vecTargetFaceIx[ixOverlap + i];
1602  int ixs = 0;
1603  for( int s = 0; s < nPout; s++ )
1604  {
1605  for( int t = 0; t < nPout; t++ )
1606  {
1607  vecTargetArea[i * nPout * nPout + ixs] = dOverlapOutputArea[ixOverlap + i][nPout * s + t];
1608 
1609  ixs++;
1610  }
1611  }
1612  }
1613 
1614  // Force consistency and conservation
1615  if( !fNoConservation )
1616  {
1617  ForceIntArrayConsistencyConservation( vecSourceArea, vecTargetArea, dCoeff, ( nMonotoneType != 0 ) );
1618  }
1619 
1620  // Update global coefficients
1621  for( int i = 0; i < nOverlapFaces; i++ )
1622  {
1623  int ixp = 0;
1624  for( int p = 0; p < nPin; p++ )
1625  {
1626  for( int q = 0; q < nPin; q++ )
1627  {
1628  int ixs = 0;
1629  for( int s = 0; s < nPout; s++ )
1630  {
1631  for( int t = 0; t < nPout; t++ )
1632  {
1633  dGlobalIntArray[ixp][ixOverlap + i][ixs] =
1634  dCoeff[i * nPout * nPout + ixs][ixp] * dOverlapOutputArea[ixOverlap + i][s * nPout + t];
1635 
1636  ixs++;
1637  }
1638  }
1639 
1640  ixp++;
1641  }
1642  }
1643  }
1644 
1645 #ifdef VVERBOSE
1646  // Check column sums (conservation)
1647  for( int i = 0; i < nPin * nPin; i++ )
1648  {
1649  double dColSum = 0.0;
1650  for( int j = 0; j < nOverlapFaces * nPout * nPout; j++ )
1651  {
1652  dColSum += dCoeff[j][i] * vecTargetArea[j];
1653  }
1654  printf( "Col %i: %1.15e\n", i, dColSum / vecSourceArea[i] );
1655  }
1656 
1657  // Check row sums (consistency)
1658  for( int j = 0; j < nOverlapFaces * nPout * nPout; j++ )
1659  {
1660  double dRowSum = 0.0;
1661  for( int i = 0; i < nPin * nPin; i++ )
1662  {
1663  dRowSum += dCoeff[j][i];
1664  }
1665  printf( "Row %i: %1.15e\n", j, dRowSum );
1666  }
1667 #endif
1668 
1669  // Increment the current overlap index
1670  ixOverlap += nOverlapFaces;
1671  }
1672 
1673  // Build redistribution map within target element
1674  if( is_root ) dbgprint.printf( 0, "Building redistribution maps on target mesh\n" );
1675  DataArray1D< double > dRedistSourceArea( nPout * nPout );
1676  DataArray1D< double > dRedistTargetArea( nPout * nPout );
1677  std::vector< DataArray2D< double > > dRedistributionMaps;
1678  dRedistributionMaps.resize( m_meshOutput->faces.size() );
1679 
1680  for( size_t ixSecond = 0; ixSecond < m_meshOutput->faces.size(); ixSecond++ )
1681  {
1682  dRedistributionMaps[ixSecond].Allocate( nPout * nPout, nPout * nPout );
1683 
1684  for( int i = 0; i < nPout * nPout; i++ )
1685  {
1686  dRedistributionMaps[ixSecond][i][i] = 1.0;
1687  }
1688 
1689  for( int s = 0; s < nPout * nPout; s++ )
1690  {
1691  dRedistSourceArea[s] = dGeometricOutputArea[ixSecond][s];
1692  }
1693 
1694  for( int s = 0; s < nPout * nPout; s++ )
1695  {
1696  dRedistTargetArea[s] = dataGLLJacobianOut[s / nPout][s % nPout][ixSecond];
1697  }
1698 
1699  if( !fNoConservation )
1700  {
1701  ForceIntArrayConsistencyConservation( dRedistSourceArea, dRedistTargetArea, dRedistributionMaps[ixSecond],
1702  ( nMonotoneType != 0 ) );
1703 
1704  for( int s = 0; s < nPout * nPout; s++ )
1705  {
1706  for( int t = 0; t < nPout * nPout; t++ )
1707  {
1708  dRedistributionMaps[ixSecond][s][t] *= dRedistTargetArea[s] / dRedistSourceArea[t];
1709  }
1710  }
1711  }
1712  }
1713 
1714  // Construct the total geometric area
1715  DataArray1D< double > dTotalGeometricArea( dataNodalAreaOut.GetRows() );
1716  for( size_t ixSecond = 0; ixSecond < m_meshOutput->faces.size(); ixSecond++ )
1717  {
1718  for( int s = 0; s < nPout; s++ )
1719  {
1720  for( int t = 0; t < nPout; t++ )
1721  {
1722  dTotalGeometricArea[dataGLLNodesOut[s][t][ixSecond] - 1] +=
1723  dGeometricOutputArea[ixSecond][s * nPout + t];
1724  }
1725  }
1726  }
1727 
1728  // Compose the integration operator with the output map
1729  ixOverlap = 0;
1730 
1731  if( is_root ) dbgprint.printf( 0, "Assembling map\n" );
1732 
1733  // Map from source DOFs to target DOFs with redistribution applied
1734  DataArray2D< double > dRedistributedOp( nPin * nPin, nPout * nPout );
1735 
1736  for( size_t ixFirst = 0; ixFirst < m_meshInputCov->faces.size(); ixFirst++ )
1737  {
1738 #ifdef VERBOSE
1739  // Announce computation progress
1740  if( ixFirst % outputFrequency == 0 && is_root )
1741  {
1742  dbgprint.printf( 0, "Element %zu/%lu\n", ixFirst, m_meshInputCov->faces.size() );
1743  }
1744 #endif
1745  // Number of overlapping Faces and triangles
1746  int nOverlapFaces = nAllOverlapFaces[ixFirst];
1747 
1748  if( !nOverlapFaces ) continue;
1749 
1750  // Put composed array into map
1751  for( int j = 0; j < nOverlapFaces; j++ )
1752  {
1753  int ixSecondFace = m_meshOverlap->vecTargetFaceIx[ixOverlap + j];
1754 
1755  // signal to not participate, because it is a ghost target
1756  if( ixSecondFace < 0 ) continue; // do not do anything
1757 
1758  dRedistributedOp.Zero();
1759  for( int p = 0; p < nPin * nPin; p++ )
1760  {
1761  for( int s = 0; s < nPout * nPout; s++ )
1762  {
1763  for( int t = 0; t < nPout * nPout; t++ )
1764  {
1765  dRedistributedOp[p][s] +=
1766  dRedistributionMaps[ixSecondFace][s][t] * dGlobalIntArray[p][ixOverlap + j][t];
1767  }
1768  }
1769  }
1770 
1771  int ixp = 0;
1772  for( int p = 0; p < nPin; p++ )
1773  {
1774  for( int q = 0; q < nPin; q++ )
1775  {
1776  int ixFirstNode;
1777  if( fContinuousIn )
1778  {
1779  ixFirstNode = dataGLLNodesIn[p][q][ixFirst] - 1;
1780  }
1781  else
1782  {
1783  ixFirstNode = ixFirst * nPin * nPin + p * nPin + q;
1784  }
1785 
1786  int ixs = 0;
1787  for( int s = 0; s < nPout; s++ )
1788  {
1789  for( int t = 0; t < nPout; t++ )
1790  {
1791  int ixSecondNode;
1792  if( fContinuousOut )
1793  {
1794  ixSecondNode = dataGLLNodesOut[s][t][ixSecondFace] - 1;
1795 
1796  if( !fNoConservation )
1797  {
1798  smatMap( ixSecondNode, ixFirstNode ) +=
1799  dRedistributedOp[ixp][ixs] / dataNodalAreaOut[ixSecondNode];
1800  }
1801  else
1802  {
1803  smatMap( ixSecondNode, ixFirstNode ) +=
1804  dRedistributedOp[ixp][ixs] / dTotalGeometricArea[ixSecondNode];
1805  }
1806  }
1807  else
1808  {
1809  ixSecondNode = ixSecondFace * nPout * nPout + s * nPout + t;
1810 
1811  if( !fNoConservation )
1812  {
1813  smatMap( ixSecondNode, ixFirstNode ) +=
1814  dRedistributedOp[ixp][ixs] / dataGLLJacobianOut[s][t][ixSecondFace];
1815  }
1816  else
1817  {
1818  smatMap( ixSecondNode, ixFirstNode ) +=
1819  dRedistributedOp[ixp][ixs] / dGeometricOutputArea[ixSecondFace][s * nPout + t];
1820  }
1821  }
1822 
1823  ixs++;
1824  }
1825  }
1826 
1827  ixp++;
1828  }
1829  }
1830  }
1831 
1832  // Increment the current overlap index
1833  ixOverlap += nOverlapFaces;
1834  }
1835 
1836  return;
1837 }

References center(), dbgprint, and ForceIntArrayConsistencyConservation().

◆ LinearRemapGLLtoGLL2_Pointwise_MOAB()

void moab::TempestOnlineMap::LinearRemapGLLtoGLL2_Pointwise_MOAB ( const DataArray3D< int > &  dataGLLNodesIn,
const DataArray3D< double > &  dataGLLJacobianIn,
const DataArray3D< int > &  dataGLLNodesOut,
const DataArray3D< double > &  dataGLLJacobianOut,
const DataArray1D< double > &  dataNodalAreaOut,
int  nPin,
int  nPout,
int  nMonotoneType,
bool  fContinuousIn,
bool  fContinuousOut 
)
private

Generate the OfflineMap for remapping from finite elements to finite elements (pointwise interpolation).

Definition at line 1841 of file TempestLinearRemap.cpp.

1851 {
1852  // Gauss-Lobatto quadrature within Faces
1853  DataArray1D< double > dGL;
1854  DataArray1D< double > dWL;
1855 
1856  GaussLobattoQuadrature::GetPoints( nPout, 0.0, 1.0, dGL, dWL );
1857 
1858  // Get SparseMatrix represntation of the OfflineMap
1859  SparseMatrix< double >& smatMap = this->GetSparseMatrix();
1860 
1861  // Sample coefficients
1862  DataArray2D< double > dSampleCoeffIn( nPin, nPin );
1863 
1864  // Announcemnets
1865  moab::DebugOutput dbgprint( std::cout, this->rank, 0 );
1866  dbgprint.set_prefix( "[LinearRemapGLLtoGLL2_Pointwise_MOAB]: " );
1867  if( is_root )
1868  {
1869  dbgprint.printf( 0, "Finite Element to Finite Element (Pointwise) Projection\n" );
1870  dbgprint.printf( 0, "Order of the input FE polynomial interpolant: %i\n", nPin );
1871  dbgprint.printf( 0, "Order of the output FE polynomial interpolant: %i\n", nPout );
1872  }
1873 
1874  // Number of overlap Faces per source Face
1875  DataArray1D< int > nAllOverlapFaces( m_meshInputCov->faces.size() );
1876 
1877  int ixOverlap = 0;
1878 
1879  for( size_t ixFirst = 0; ixFirst < m_meshInputCov->faces.size(); ixFirst++ )
1880  {
1881  size_t ixOverlapTemp = ixOverlap;
1882  for( ; ixOverlapTemp < m_meshOverlap->faces.size(); ixOverlapTemp++ )
1883  {
1884  // const Face & faceOverlap = m_meshOverlap->faces[ixOverlapTemp];
1885 
1886  if( ixFirst - m_meshOverlap->vecSourceFaceIx[ixOverlapTemp] != 0 ) break;
1887 
1888  nAllOverlapFaces[ixFirst]++;
1889  }
1890 
1891  // Increment the current overlap index
1892  ixOverlap += nAllOverlapFaces[ixFirst];
1893  }
1894 
1895  // Number of times this point was found
1896  DataArray1D< bool > fSecondNodeFound( dataNodalAreaOut.GetRows() );
1897 
1898  ixOverlap = 0;
1899 #ifdef VERBOSE
1900  const unsigned outputFrequency = ( m_meshInputCov->faces.size() / 10 ) + 1;
1901 #endif
1902  // Loop through all faces on m_meshInputCov
1903  for( size_t ixFirst = 0; ixFirst < m_meshInputCov->faces.size(); ixFirst++ )
1904  {
1905 #ifdef VERBOSE
1906  // Announce computation progress
1907  if( ixFirst % outputFrequency == 0 && is_root )
1908  {
1909  dbgprint.printf( 0, "Element %zu/%lu\n", ixFirst, m_meshInputCov->faces.size() );
1910  }
1911 #endif
1912  // Quantities from the First Mesh
1913  const Face& faceFirst = m_meshInputCov->faces[ixFirst];
1914 
1915  const NodeVector& nodesFirst = m_meshInputCov->nodes;
1916 
1917  // Number of overlapping Faces and triangles
1918  int nOverlapFaces = nAllOverlapFaces[ixFirst];
1919 
1920  // Loop through all Overlap Faces
1921  for( int i = 0; i < nOverlapFaces; i++ )
1922  {
1923  // Quantities from the Second Mesh
1924  int ixSecond = m_meshOverlap->vecTargetFaceIx[ixOverlap + i];
1925 
1926  // signal to not participate, because it is a ghost target
1927  if( ixSecond < 0 ) continue; // do not do anything
1928 
1929  const NodeVector& nodesSecond = m_meshOutput->nodes;
1930  const Face& faceSecond = m_meshOutput->faces[ixSecond];
1931 
1932  // Loop through all nodes on the second face
1933  for( int s = 0; s < nPout; s++ )
1934  {
1935  for( int t = 0; t < nPout; t++ )
1936  {
1937  size_t ixSecondNode;
1938  if( fContinuousOut )
1939  {
1940  ixSecondNode = dataGLLNodesOut[s][t][ixSecond] - 1;
1941  }
1942  else
1943  {
1944  ixSecondNode = ixSecond * nPout * nPout + s * nPout + t;
1945  }
1946 
1947  if( ixSecondNode >= fSecondNodeFound.GetRows() ) _EXCEPTIONT( "Logic error" );
1948 
1949  // Check if this node has been found already
1950  if( fSecondNodeFound[ixSecondNode] ) continue;
1951 
1952  // Check this node
1953  Node node;
1954  Node dDx1G;
1955  Node dDx2G;
1956 
1957  ApplyLocalMap( faceSecond, nodesSecond, dGL[t], dGL[s], node, dDx1G, dDx2G );
1958 
1959  // Find the components of this quadrature point in the basis
1960  // of the first Face.
1961  double dAlphaIn;
1962  double dBetaIn;
1963 
1964  ApplyInverseMap( faceFirst, nodesFirst, node, dAlphaIn, dBetaIn );
1965 
1966  // Check if this node is within the first Face
1967  if( ( dAlphaIn < -1.0e-10 ) || ( dAlphaIn > 1.0 + 1.0e-10 ) || ( dBetaIn < -1.0e-10 ) ||
1968  ( dBetaIn > 1.0 + 1.0e-10 ) )
1969  continue;
1970 
1971  // Node is within the overlap region, mark as found
1972  fSecondNodeFound[ixSecondNode] = true;
1973 
1974  // Sample the First finite element at this point
1975  SampleGLLFiniteElement( nMonotoneType, nPin, dAlphaIn, dBetaIn, dSampleCoeffIn );
1976 
1977  // Add to map
1978  for( int p = 0; p < nPin; p++ )
1979  {
1980  for( int q = 0; q < nPin; q++ )
1981  {
1982  int ixFirstNode;
1983  if( fContinuousIn )
1984  {
1985  ixFirstNode = dataGLLNodesIn[p][q][ixFirst] - 1;
1986  }
1987  else
1988  {
1989  ixFirstNode = ixFirst * nPin * nPin + p * nPin + q;
1990  }
1991 
1992  smatMap( ixSecondNode, ixFirstNode ) += dSampleCoeffIn[p][q];
1993  }
1994  }
1995  }
1996  }
1997  }
1998 
1999  // Increment the current overlap index
2000  ixOverlap += nOverlapFaces;
2001  }
2002 
2003  // Check for missing samples
2004  for( size_t i = 0; i < fSecondNodeFound.GetRows(); i++ )
2005  {
2006  if( !fSecondNodeFound[i] )
2007  {
2008  _EXCEPTION1( "Can't sample point %i", i );
2009  }
2010  }
2011 
2012  return;
2013 }

References dbgprint.

◆ LinearRemapNN_MOAB()

moab::ErrorCode moab::TempestOnlineMap::LinearRemapNN_MOAB ( bool  use_GID_matching = false,
bool  strict_check = false 
)
private

Compute the remapping weights as a permutation matrix that relates DoFs on the source mesh to DoFs on the target mesh.

Definition at line 58 of file TempestLinearRemap.cpp.

59 {
60  /* m_mapRemap size = (m_nTotDofs_Dest X m_nTotDofs_SrcCov) */
61 
62 #ifdef VVERBOSE
63  {
64  std::ofstream output_file( "rowcolindices.txt", std::ios::out );
65  output_file << m_nTotDofs_Dest << " " << m_nTotDofs_SrcCov << " " << row_gdofmap.size() << " "
66  << col_gdofmap.size() << "\n";
67  output_file << "Rows \n";
68  for( unsigned iv = 0; iv < row_gdofmap.size(); iv++ )
69  output_file << iv << " " << row_gdofmap[iv] << "\n";
70  output_file << "Cols \n";
71  for( unsigned iv = 0; iv < col_gdofmap.size(); iv++ )
72  output_file << iv << " " << col_gdofmap[iv] << "\n";
73  output_file.flush(); // required here
74  output_file.close();
75  }
76 #endif
77 
78  if( use_GID_matching )
79  {
80  std::map< unsigned, unsigned > src_gl;
81  for( unsigned it = 0; it < col_gdofmap.size(); ++it )
82  src_gl[col_gdofmap[it]] = it;
83 
84  std::map< unsigned, unsigned >::iterator iter;
85  for( unsigned it = 0; it < row_gdofmap.size(); ++it )
86  {
87  unsigned row = row_gdofmap[it];
88  iter = src_gl.find( row );
89  if( strict_check && iter == src_gl.end() )
90  {
91  std::cout << "Searching for global target DOF " << row
92  << " but could not find correspondence in source mesh.\n";
93  assert( false );
94  }
95  else if( iter == src_gl.end() )
96  {
97  continue;
98  }
99  else
100  {
101  unsigned icol = src_gl[row];
102  unsigned irow = it;
103 
104  // Set the permutation matrix in local space
105  m_mapRemap( irow, icol ) = 1.0;
106  }
107  }
108 
109  return moab::MB_SUCCESS;
110  }
111  else
112  {
113  /* Create a Kd-tree to perform local queries to find nearest neighbors */
114 
115  return moab::MB_FAILURE;
116  }
117 }

References col_gdofmap, m_nTotDofs_Dest, m_nTotDofs_SrcCov, MB_SUCCESS, and row_gdofmap.

◆ LinearRemapSE0_Tempest_MOAB()

void moab::TempestOnlineMap::LinearRemapSE0_Tempest_MOAB ( const DataArray3D< int > &  dataGLLNodes,
const DataArray3D< double > &  dataGLLJacobian 
)
private

Generate the OfflineMap for linear conserative element-average spectral element to element average remapping.

◆ LinearRemapSE4_Tempest_MOAB()

void moab::TempestOnlineMap::LinearRemapSE4_Tempest_MOAB ( const DataArray3D< int > &  dataGLLNodes,
const DataArray3D< double > &  dataGLLJacobian,
int  nMonotoneType,
bool  fContinuousIn,
bool  fNoConservation,
bool  fSparseConstraints 
)
private

Generate the OfflineMap for cubic conserative element-average spectral element to element average remapping.

Definition at line 873 of file TempestLinearRemap.cpp.

879 {
880  // Order of the polynomial interpolant
881  int nP = dataGLLNodes.GetRows();
882 
883  // Order of triangular quadrature rule
884  const int TriQuadRuleOrder = 4;
885 
886  // Triangular quadrature rule
887  TriangularQuadratureRule triquadrule( TriQuadRuleOrder );
888 
889  int TriQuadraturePoints = triquadrule.GetPoints();
890 
891  const DataArray2D< double >& TriQuadratureG = triquadrule.GetG();
892 
893  const DataArray1D< double >& TriQuadratureW = triquadrule.GetW();
894 
895  // Sample coefficients
896  DataArray2D< double > dSampleCoeff( nP, nP );
897 
898  // GLL Quadrature nodes on quadrilateral elements
899  DataArray1D< double > dG;
900  DataArray1D< double > dW;
901  GaussLobattoQuadrature::GetPoints( nP, 0.0, 1.0, dG, dW );
902 
903  // Announcements
904  moab::DebugOutput dbgprint( std::cout, this->rank, 0 );
905  dbgprint.set_prefix( "[LinearRemapSE4_Tempest_MOAB]: " );
906  if( is_root )
907  {
908  dbgprint.printf( 0, "Finite Element to Finite Volume Projection\n" );
909  dbgprint.printf( 0, "Triangular quadrature rule order %i\n", TriQuadRuleOrder );
910  dbgprint.printf( 0, "Order of the FE polynomial interpolant: %i\n", nP );
911  }
912 
913  // Get SparseMatrix represntation of the OfflineMap
914  SparseMatrix< double >& smatMap = this->GetSparseMatrix();
915 
916  // NodeVector from m_meshOverlap
917  const NodeVector& nodesOverlap = m_meshOverlap->nodes;
918  const NodeVector& nodesFirst = m_meshInputCov->nodes;
919 
920  // Vector of source areas
921  DataArray1D< double > vecSourceArea( nP * nP );
922 
923  DataArray1D< double > vecTargetArea;
924  DataArray2D< double > dCoeff;
925 
926 #ifdef VERBOSE
927  std::stringstream sstr;
928  sstr << "remapdata_" << rank << ".txt";
929  std::ofstream output_file( sstr.str() );
930 #endif
931 
932  // Current Overlap Face
933  int ixOverlap = 0;
934 #ifdef VERBOSE
935  const unsigned outputFrequency = ( m_meshInputCov->faces.size() / 10 ) + 1;
936 #endif
937  // generic triangle used for area computation, for triangles around the center of overlap face;
938  // used for overlap faces with more than 4 edges;
939  // nodes array will be set for each triangle;
940  // these triangles are not part of the mesh structure, they are just temporary during
941  // aforementioned decomposition.
942  Face faceTri( 3 );
943  NodeVector nodes( 3 );
944  faceTri.SetNode( 0, 0 );
945  faceTri.SetNode( 1, 1 );
946  faceTri.SetNode( 2, 2 );
947 
948  // Loop over all input Faces
949  for( size_t ixFirst = 0; ixFirst < m_meshInputCov->faces.size(); ixFirst++ )
950  {
951  const Face& faceFirst = m_meshInputCov->faces[ixFirst];
952 
953  if( faceFirst.edges.size() != 4 )
954  {
955  _EXCEPTIONT( "Only quadrilateral elements allowed for SE remapping" );
956  }
957 #ifdef VERBOSE
958  // Announce computation progress
959  if( ixFirst % outputFrequency == 0 && is_root )
960  {
961  dbgprint.printf( 0, "Element %zu/%lu\n", ixFirst, m_meshInputCov->faces.size() );
962  }
963 #endif
964  // Need to re-number the overlap elements such that vecSourceFaceIx[a:b] = 0, then 1 and so
965  // on wrt the input mesh data Then the overlap_end and overlap_begin will be correct.
966  // However, the relation with MOAB and Tempest will go out of the roof
967 
968  // Determine how many overlap Faces and triangles are present
969  int nOverlapFaces = 0;
970  size_t ixOverlapTemp = ixOverlap;
971  for( ; ixOverlapTemp < m_meshOverlap->faces.size(); ixOverlapTemp++ )
972  {
973  // if( m_meshOverlap->vecTargetFaceIx[ixOverlapTemp] < 0 ) continue; // skip ghost target faces
974  // const Face & faceOverlap = m_meshOverlap->faces[ixOverlapTemp];
975  if( ixFirst - m_meshOverlap->vecSourceFaceIx[ixOverlapTemp] != 0 ) break;
976 
977  nOverlapFaces++;
978  }
979 
980  // No overlaps
981  if( nOverlapFaces == 0 ) continue;
982 
983  // Allocate remap coefficients array for meshFirst Face
984  DataArray3D< double > dRemapCoeff( nP, nP, nOverlapFaces );
985 
986  // Find the local remap coefficients
987  for( int j = 0; j < nOverlapFaces; j++ )
988  {
989  const Face& faceOverlap = m_meshOverlap->faces[ixOverlap + j];
990  if( m_meshOverlap->vecFaceArea[ixOverlap + j] < std::numeric_limits<double>::epsilon() ) // machine precision
991  {
992  if (false) { // verbose detailed output about small overlap elements (near machine precision area)
993  Announce( "Very small overlap at index %i area polygon: (%1.10e )", ixOverlap + j,
994  m_meshOverlap->vecFaceArea[ixOverlap + j] );
995  int n = faceOverlap.edges.size();
996  Announce( "Number nodes: %d", n );
997  for( int k = 0; k < n; k++ )
998  {
999  Node nd = nodesOverlap[faceOverlap[k]];
1000  Announce( "Node %d %d : %1.10e %1.10e %1.10e ", k, faceOverlap[k], nd.x, nd.y, nd.z );
1001  }
1002  }
1003  continue;
1004  }
1005 
1006  int nbEdges = faceOverlap.edges.size();
1007  int nOverlapTriangles = 1;
1008  Node center; // not used if nbEdges == 3
1009  if( nbEdges > 3 )
1010  { // decompose from center in this case
1011  nOverlapTriangles = nbEdges;
1012  for( int k = 0; k < nbEdges; k++ )
1013  {
1014  const Node& node = nodesOverlap[faceOverlap[k]];
1015  center = center + node;
1016  }
1017  center = center / nbEdges;
1018  center = center.Normalized(); // project back on sphere of radius 1
1019  }
1020 
1021  Node node0, node1, node2;
1022  double dTriangleArea;
1023 
1024  // Loop over all sub-triangles of this Overlap Face
1025  for( int k = 0; k < nOverlapTriangles; k++ )
1026  {
1027  if( nbEdges == 3 ) // will come here only once, nOverlapTriangles == 1 in this case
1028  {
1029  node0 = nodesOverlap[faceOverlap[0]];
1030  node1 = nodesOverlap[faceOverlap[1]];
1031  node2 = nodesOverlap[faceOverlap[2]];
1032  dTriangleArea = CalculateFaceArea( faceOverlap, nodesOverlap );
1033  }
1034  else // decompose polygon in triangles around the center
1035  {
1036  node0 = center;
1037  node1 = nodesOverlap[faceOverlap[k]];
1038  int k1 = ( k + 1 ) % nbEdges;
1039  node2 = nodesOverlap[faceOverlap[k1]];
1040  nodes[0] = center;
1041  nodes[1] = node1;
1042  nodes[2] = node2;
1043  dTriangleArea = CalculateFaceArea( faceTri, nodes );
1044  }
1045  // Coordinates of quadrature Node
1046  for( int l = 0; l < TriQuadraturePoints; l++ )
1047  {
1048  Node nodeQuadrature;
1049  nodeQuadrature.x = TriQuadratureG[l][0] * node0.x + TriQuadratureG[l][1] * node1.x +
1050  TriQuadratureG[l][2] * node2.x;
1051 
1052  nodeQuadrature.y = TriQuadratureG[l][0] * node0.y + TriQuadratureG[l][1] * node1.y +
1053  TriQuadratureG[l][2] * node2.y;
1054 
1055  nodeQuadrature.z = TriQuadratureG[l][0] * node0.z + TriQuadratureG[l][1] * node1.z +
1056  TriQuadratureG[l][2] * node2.z;
1057 
1058  nodeQuadrature = nodeQuadrature.Normalized();
1059 
1060  // Find components of quadrature point in basis
1061  // of the first Face
1062  double dAlpha;
1063  double dBeta;
1064 
1065  ApplyInverseMap( faceFirst, nodesFirst, nodeQuadrature, dAlpha, dBeta );
1066 
1067  // Check inverse map value
1068  if( ( dAlpha < -1.0e-13 ) || ( dAlpha > 1.0 + 1.0e-13 ) || ( dBeta < -1.0e-13 ) ||
1069  ( dBeta > 1.0 + 1.0e-13 ) )
1070  {
1071  _EXCEPTION4( "Inverse Map for element %d and subtriangle %d out of range "
1072  "(%1.5e %1.5e)",
1073  j, l, dAlpha, dBeta );
1074  }
1075 
1076  // Sample the finite element at this point
1077  SampleGLLFiniteElement( nMonotoneType, nP, dAlpha, dBeta, dSampleCoeff );
1078 
1079  // Add sample coefficients to the map if m_meshOverlap->vecFaceArea[ixOverlap + j] > 0
1080  for( int p = 0; p < nP; p++ )
1081  {
1082  for( int q = 0; q < nP; q++ )
1083  {
1084  dRemapCoeff[p][q][j] += TriQuadratureW[l] * dTriangleArea * dSampleCoeff[p][q] /
1085  m_meshOverlap->vecFaceArea[ixOverlap + j];
1086  }
1087  }
1088  }
1089  }
1090  }
1091 
1092 #ifdef VERBOSE
1093  output_file << "[" << ( ixFirst < (int)col_gdofmap.size() ? (int)col_gdofmap[ixFirst] : -1 ) << "] \t";
1094  for( int j = 0; j < nOverlapFaces; j++ )
1095  {
1096  for( int p = 0; p < nP; p++ )
1097  {
1098  for( int q = 0; q < nP; q++ )
1099  {
1100  output_file << dRemapCoeff[p][q][j] << " ";
1101  }
1102  }
1103  }
1104  output_file << std::endl;
1105 #endif
1106 
1107  // Force consistency and conservation
1108  if( !fNoConservation )
1109  {
1110  double dTargetArea = 0.0;
1111  for( int j = 0; j < nOverlapFaces; j++ )
1112  {
1113  dTargetArea += m_meshOverlap->vecFaceArea[ixOverlap + j];
1114  }
1115 
1116  for( int p = 0; p < nP; p++ )
1117  {
1118  for( int q = 0; q < nP; q++ )
1119  {
1120  vecSourceArea[p * nP + q] = dataGLLJacobian[p][q][ixFirst];
1121  }
1122  }
1123 
1124  const double areaTolerance = 1e-10;
1125  // Source elements are completely covered by target volumes
1126  if( fabs( m_meshInputCov->vecFaceArea[ixFirst] - dTargetArea ) <= areaTolerance )
1127  {
1128  vecTargetArea.Allocate( nOverlapFaces );
1129  for( int j = 0; j < nOverlapFaces; j++ )
1130  {
1131  vecTargetArea[j] = m_meshOverlap->vecFaceArea[ixOverlap + j];
1132  }
1133 
1134  dCoeff.Allocate( nOverlapFaces, nP * nP );
1135 
1136  for( int j = 0; j < nOverlapFaces; j++ )
1137  {
1138  for( int p = 0; p < nP; p++ )
1139  {
1140  for( int q = 0; q < nP; q++ )
1141  {
1142  dCoeff[j][p * nP + q] = dRemapCoeff[p][q][j];
1143  }
1144  }
1145  }
1146 
1147  // Target volumes only partially cover source elements
1148  }
1149  else if( m_meshInputCov->vecFaceArea[ixFirst] - dTargetArea > areaTolerance )
1150  {
1151  double dExtraneousArea = m_meshInputCov->vecFaceArea[ixFirst] - dTargetArea;
1152 
1153  vecTargetArea.Allocate( nOverlapFaces + 1 );
1154  for( int j = 0; j < nOverlapFaces; j++ )
1155  {
1156  vecTargetArea[j] = m_meshOverlap->vecFaceArea[ixOverlap + j];
1157  }
1158  vecTargetArea[nOverlapFaces] = dExtraneousArea;
1159 
1160 #ifdef VERBOSE
1161  Announce( "Partial volume: %i (%1.10e / %1.10e)", ixFirst, dTargetArea,
1162  m_meshInputCov->vecFaceArea[ixFirst] );
1163 #endif
1164  if( dTargetArea > m_meshInputCov->vecFaceArea[ixFirst] )
1165  {
1166  _EXCEPTIONT( "Partial element area exceeds total element area" );
1167  }
1168 
1169  dCoeff.Allocate( nOverlapFaces + 1, nP * nP );
1170 
1171  for( int j = 0; j < nOverlapFaces; j++ )
1172  {
1173  for( int p = 0; p < nP; p++ )
1174  {
1175  for( int q = 0; q < nP; q++ )
1176  {
1177  dCoeff[j][p * nP + q] = dRemapCoeff[p][q][j];
1178  }
1179  }
1180  }
1181  for( int p = 0; p < nP; p++ )
1182  {
1183  for( int q = 0; q < nP; q++ )
1184  {
1185  dCoeff[nOverlapFaces][p * nP + q] = dataGLLJacobian[p][q][ixFirst];
1186  }
1187  }
1188  for( int j = 0; j < nOverlapFaces; j++ )
1189  {
1190  for( int p = 0; p < nP; p++ )
1191  {
1192  for( int q = 0; q < nP; q++ )
1193  {
1194  dCoeff[nOverlapFaces][p * nP + q] -=
1195  dRemapCoeff[p][q][j] * m_meshOverlap->vecFaceArea[ixOverlap + j];
1196  }
1197  }
1198  }
1199  for( int p = 0; p < nP; p++ )
1200  {
1201  for( int q = 0; q < nP; q++ )
1202  {
1203  dCoeff[nOverlapFaces][p * nP + q] /= dExtraneousArea;
1204  }
1205  }
1206 
1207  // Source elements only partially cover target volumes
1208  }
1209  else
1210  {
1211  Announce( "Coverage area: %1.10e, and target element area: %1.10e)", ixFirst,
1212  m_meshInputCov->vecFaceArea[ixFirst], dTargetArea );
1213  _EXCEPTIONT( "Target grid must be a subset of source grid" );
1214  }
1215 
1216  ForceConsistencyConservation3( vecSourceArea, vecTargetArea, dCoeff, ( nMonotoneType > 0 ),
1217  fSparseConstraints );
1218 
1219  for( int j = 0; j < nOverlapFaces; j++ )
1220  {
1221  for( int p = 0; p < nP; p++ )
1222  {
1223  for( int q = 0; q < nP; q++ )
1224  {
1225  dRemapCoeff[p][q][j] = dCoeff[j][p * nP + q];
1226  }
1227  }
1228  }
1229  }
1230 
1231 #ifdef VERBOSE
1232  // output_file << "[" << col_gdofmap[ixFirst] << "] \t";
1233  // for ( int j = 0; j < nOverlapFaces; j++ )
1234  // {
1235  // for ( int p = 0; p < nP; p++ )
1236  // {
1237  // for ( int q = 0; q < nP; q++ )
1238  // {
1239  // output_file << dRemapCoeff[p][q][j] << " ";
1240  // }
1241  // }
1242  // }
1243  // output_file << std::endl;
1244 #endif
1245 
1246  // Put these remap coefficients into the SparseMatrix map
1247  for( int j = 0; j < nOverlapFaces; j++ )
1248  {
1249  int ixSecondFace = m_meshOverlap->vecTargetFaceIx[ixOverlap + j];
1250 
1251  // signal to not participate, because it is a ghost target
1252  if( ixSecondFace < 0 ) continue; // do not do anything
1253 
1254  for( int p = 0; p < nP; p++ )
1255  {
1256  for( int q = 0; q < nP; q++ )
1257  {
1258  if( fContinuousIn )
1259  {
1260  int ixFirstNode = dataGLLNodes[p][q][ixFirst] - 1;
1261 
1262  smatMap( ixSecondFace, ixFirstNode ) += dRemapCoeff[p][q][j] *
1263  m_meshOverlap->vecFaceArea[ixOverlap + j] /
1264  m_meshOutput->vecFaceArea[ixSecondFace];
1265  }
1266  else
1267  {
1268  int ixFirstNode = ixFirst * nP * nP + p * nP + q;
1269 
1270  smatMap( ixSecondFace, ixFirstNode ) += dRemapCoeff[p][q][j] *
1271  m_meshOverlap->vecFaceArea[ixOverlap + j] /
1272  m_meshOutput->vecFaceArea[ixSecondFace];
1273  }
1274  }
1275  }
1276  }
1277  // Increment the current overlap index
1278  ixOverlap += nOverlapFaces;
1279  }
1280 #ifdef VERBOSE
1281  output_file.flush(); // required here
1282  output_file.close();
1283 #endif
1284 
1285  return;
1286 }

References center(), dbgprint, and ForceConsistencyConservation3().

◆ PrintMapStatistics()

void moab::TempestOnlineMap::PrintMapStatistics ( )

Print information and metadata about the remapping weights.

Definition at line 281 of file TempestLinearRemap.cpp.

282 {
283  int nrows = m_weightMatrix.rows(); // Number of rows
284  int ncols = m_weightMatrix.cols(); // Number of columns
285  int NNZ = m_weightMatrix.nonZeros(); // Number of non zero values
286 #ifdef MOAB_HAVE_MPI
287  // find out min/max for NNZ, ncols, nrows
288  // should work on std c++ 11
289  int arr3[6] = { NNZ, nrows, ncols, -NNZ, -nrows, -ncols };
290  int rarr3[6] = {0, 0, 0, 0, 0, 0};
291  MPI_Reduce( arr3, rarr3, 6, MPI_INT, MPI_MIN, 0, m_pcomm->comm() );
292 
293  int total[3] = {0, 0, 0};
294  MPI_Reduce( arr3, total, 3, MPI_INT, MPI_SUM, 0, m_pcomm->comm() );
295  if( !rank )
296  std::cout << "-> Rows (min/max/sum): (" << rarr3[1] << " / " << -rarr3[4] << " / " << total[1] << "), "
297  << " Cols (min/max/sum): (" << rarr3[2] << " / " << -rarr3[5] << " / " << total[2] << "), "
298  << " NNZ (min/max/sum): (" << rarr3[0] << " / " << -rarr3[3] << " / " << total[0] << ")\n";
299 #else
300  std::cout << "-> Rows: " << nrows << ", Cols: " << ncols << ", NNZ: " << NNZ << "\n";
301 #endif
302 }

Referenced by main().

◆ QLTLimiter()

double moab::TempestOnlineMap::QLTLimiter ( int  caasIteration,
std::vector< double > &  dataCorrectedField,
std::vector< double > &  dataLowerBound,
std::vector< double > &  dataUpperBound,
std::vector< double > &  dMassDefect 
)
private

Definition at line 382 of file TempestLinearRemap.cpp.

387 {
388  const size_t nrows = dataCorrectedField.size();
389  double dMassL = 0.0;
390  double dMassU = 0.0;
391  std::vector< double > dataCorrection( nrows );
392  double dMassDiffCum = 0.0;
393  double dLMinusU = fabs( dataUpperBound[0] - dataLowerBound[0] );
394  const DataArray1D< double >& dTargetAreas = this->m_remapper->m_target->vecFaceArea;
395 
396  // std::vector< size_t > sortedIdx = sort_indexes( dMassDefect );
397  std::vector< std::unordered_set< int > > vecAdjTargetFaces( nrows );
398  constexpr bool useMOABAdjacencies = true;
399 #ifdef USE_ComputeAdjacencyRelations
400  if( useMOABAdjacencies )
401  ComputeAdjacencyRelations( vecAdjTargetFaces, caasIteration, m_remapper->m_target_entities,
402  useMOABAdjacencies );
403  else
404  ComputeAdjacencyRelations( vecAdjTargetFaces, caasIteration, m_remapper->m_target_entities, useMOABAdjacencies,
405  this->m_remapper->m_target );
406 #else
408  ;
409 #endif
410 
411  for( size_t i = 0; i < nrows; i++ )
412  {
413  // size_t index = sortedIdx[i];
414  size_t index = i;
415  dataCorrection[index] = fmax( dataLowerBound[index], fmin( dataUpperBound[index], 0.0 ) );
416  // dMassDiff[index] = dMassDefect[index] - dTargetAreas[index] * dataCorrection[index];
417  // dMassDiff[index] = dMassDefect[index];
418 
419  dMassL += dTargetAreas[index] * dataLowerBound[index];
420  dMassU += dTargetAreas[index] * dataUpperBound[index];
421  dLMinusU = fmax( dLMinusU, fabs( dataUpperBound[index] - dataLowerBound[index] ) );
422  dMassDiffCum += dMassDefect[index] - dTargetAreas[index] * dataCorrection[index];
423 
424 #ifndef USE_ComputeAdjacencyRelations
425  vecAdjTargetFaces[index].insert( index ); // add self target face first
426  {
427  // Compute the adjacent faces to the target face
428  if( useMOABAdjacencies )
429  {
430  moab::Range ents;
431  // ents.insert( m_remapper->m_target_entities.index( m_remapper->m_target_entities[index] ) );
433  moab::Range adjEnts;
434  moab::ErrorCode rval = mtu.get_bridge_adjacencies( ents, 0, 2, adjEnts, caasIteration );MB_CHK_SET_ERR_CONT( rval, "Failed to get adjacent faces" );
435  for( moab::Range::iterator it = adjEnts.begin(); it != adjEnts.end(); ++it )
436  {
437  // int adjIndex = m_interface->id_from_handle(*it)-1;
438  int adjIndex = m_remapper->m_target_entities.index( *it );
439  // printf("rank: %d, Element %lu, entity: %lu, adjIndex %d\n", rank, index, *it, adjIndex);
440  if( adjIndex >= 0 ) vecAdjTargetFaces[index].insert( adjIndex );
441  }
442  }
443  else
444  {
445  AdjacentFaceVector vecAdjFaces;
446  GetAdjacentFaceVectorByEdge( *this->m_remapper->m_target, index,
447  ( m_output_order + 1 ) * ( m_output_order + 1 ) * ( m_output_order + 1 ),
448  // ( m_output_order + 1 ) * ( m_output_order + 1 ),
449  // ( 4 ) * ( m_output_order + 1 ) * ( m_output_order + 1 ),
450  vecAdjFaces );
451 
452  // Add the adjacent faces to the target face list
453  for( auto adjFace : vecAdjFaces )
454  if( adjFace.first >= 0 )
455  vecAdjTargetFaces[index].insert( adjFace.first ); // map target face to source face
456  }
457  }
458 #endif
459  }
460 
461 #ifdef MOAB_HAVE_MPI
462  std::vector< double > localDefects( 5, 0.0 ), globalDefects( 5, 0.0 );
463  localDefects[0] = dMassL;
464  localDefects[1] = dMassU;
465  localDefects[2] = dMassDiffCum;
466  localDefects[3] = dLMinusU;
467  // localDefects[4] = dMassCorrectU;
468 
469  MPI_Allreduce( localDefects.data(), globalDefects.data(), 4, MPI_DOUBLE, MPI_SUM, m_pcomm->comm() );
470 
471  dMassL = globalDefects[0];
472  dMassU = globalDefects[1];
473  dMassDiffCum = globalDefects[2];
474  dLMinusU = globalDefects[3];
475  // dMassCorrectU = globalDefects[4];
476 #endif
477 
478  //If the upper and lower bounds are too close together, just clip
479  if( fabs( dMassDiffCum ) < 1e-15 || dLMinusU < 1e-15 )
480  {
481  for( size_t i = 0; i < nrows; i++ )
482  dataCorrectedField[i] += dataCorrection[i];
483  return dMassDiffCum;
484  }
485  else
486  {
487  if( dMassL > dMassDiffCum )
488  {
489  Announce( "Lower bound mass exceeds target mass by %1.15e: CAAS will need another iteration",
490  dMassL - dMassDiffCum );
491  dMassDiffCum = dMassL;
492  // dMass -= dMassL;
493  }
494  else if( dMassU < dMassDiffCum )
495  {
496  Announce( "Target mass exceeds upper bound mass by %1.15e: CAAS will need another iteration",
497  dMassDiffCum - dMassU );
498  dMassDiffCum = dMassU;
499  // dMass -= dMassU;
500  }
501 
502  // TODO: optimize away dataMassVec by a simple transient double within the loop
503  // DataArray1D< double > dataMassVec( nrows ); //vector of mass redistribution
504  for( size_t i = 0; i < nrows; i++ )
505  {
506  // size_t index = sortedIdx[i];
507  size_t index = i;
508  const std::unordered_set< int >& neighbors = vecAdjTargetFaces[index];
509  if( dMassDefect[index] > 0.0 )
510  {
511  double dMassCorrectU = 0.0;
512  for( auto it : neighbors )
513  dMassCorrectU += dTargetAreas[it] * ( dataUpperBound[it] - dataCorrection[it] );
514 
515  // double dMassDiffCumOld = dMassDefect[index];
516  for( auto it : neighbors )
517  dataCorrection[it] +=
518  dMassDefect[index] * ( dataUpperBound[it] - dataCorrection[it] ) / dMassCorrectU;
519  }
520  else
521  {
522  double dMassCorrectL = 0.0;
523  for( auto it : neighbors )
524  dMassCorrectL += dTargetAreas[it] * ( dataCorrection[it] - dataLowerBound[it] );
525 
526  // double dMassDiffCumOld = dMassDefect[index];
527  for( auto it : neighbors )
528  dataCorrection[it] +=
529  dMassDefect[index] * ( dataCorrection[it] - dataLowerBound[it] ) / dMassCorrectL;
530  }
531  }
532 
533  for( size_t i = 0; i < nrows; i++ )
534  dataCorrectedField[i] += dataCorrection[i];
535  }
536 
537  return dMassDiffCum;
538 }

References moab::Range::begin(), moab::Range::end(), ErrorCode, moab::MeshTopoUtil::get_bridge_adjacencies(), moab::index, moab::Range::insert(), and MB_CHK_SET_ERR_CONT.

◆ ReadParallelMap()

moab::ErrorCode moab::TempestOnlineMap::ReadParallelMap ( const char *  strSource,
const std::vector< int > &  tgt_dof_ids,
int  arearead,
std::vector< double > &  areaA,
int &  nA,
std::vector< double > &  areaB,
int &  nB 
)

Generate the metadata associated with the offline map.

Read the OfflineMap from a NetCDF file.

Definition at line 1197 of file TempestOnlineMapIO.cpp.

1204 {
1205 #if !defined( MOAB_HAVE_NETCDF ) && !defined( MOAB_HAVE_PNETCDF )
1206 #error "Cannot enable SCRIP reading without NetCDF or PNetCDF interfaces"
1207 #endif
1208 
1209  const bool readAreaA = ( 1 == arearead || 3 == arearead );
1210  const bool readAreaB = ( 2 == arearead || 3 == arearead );
1211  int nS = 0;
1212 
1213  // =========================================================================
1214  // Phase 1: Read map dimensions (nA, nB, nS) and sparse matrix data.
1215  //
1216  // The read strategy is selected adaptively:
1217  // - Serial or buffered read: rank 0 opens the file with serial NcFile,
1218  // reads dimensions, and (for buffered mode) scatters data in chunks.
1219  // - Direct parallel read: all ranks open the file with PNetCDF or
1220  // NETCDFPAR and read their stripe directly.
1221  // =========================================================================
1222 
1223  std::vector< int > vecRow, vecCol;
1224  std::vector< double > vecS;
1225  int localSize = 0; // number of sparse matrix entries on this rank after read
1226 
1227  // ============ Phase 1: read the map through the MBNcDispatch layer (mbnc_*) ============
1228  // Detect the on-disk format, let the dispatch pick the backend (serial / parallel NetCDF /
1229  // PnetCDF / rank-0-buffered), and read a contiguous stripe of the sparse matrix on each
1230  // rank. row/col/S stripes are redistributed to their owners in Phase 2; area_a/area_b are
1231  // read as trivial per-rank slices, which is what the downstream aream code expects.
1232  {
1233  int fileFormat = NCFMT_UNKNOWN;
1234 #ifdef MOAB_HAVE_MPI
1235  if( rank == 0 ) fileFormat = mbnc_detect_format( strSource );
1236  MPI_Bcast( &fileFormat, 1, MPI_INT, 0, m_pcomm->comm() );
1237 #else
1238  fileFormat = mbnc_detect_format( strSource );
1239 #endif
1240  NcBackend rbackend = mbnc_choose_backend_for_read( fileFormat, (int)size );
1241  if( rbackend == NCB_NONE )
1242  _EXCEPTION1( "Cannot read map file \"%s\": unrecognized format, or NetCDF-4/HDF5 without libnetcdf",
1243  strSource );
1244 
1245  int ncid = -1;
1246 #ifdef MOAB_HAVE_MPI
1247  ERR_MBNC( mbnc_open_par( rbackend, m_pcomm->comm(), MPI_INFO_NULL, strSource, 0, &ncid ), "open map" );
1248 #else
1249  ERR_MBNC( mbnc_open( strSource, 0, &ncid ), "open map" );
1250 #endif
1251 
1252  int did = -1;
1253  size_t dlen = 0;
1254  ERR_MBNC( mbnc_inq_dimid( ncid, "n_a", &did ), "inq n_a" );
1255  ERR_MBNC( mbnc_inq_dimlen( ncid, did, &dlen ), "len n_a" );
1256  nA = (int)dlen;
1257  ERR_MBNC( mbnc_inq_dimid( ncid, "n_b", &did ), "inq n_b" );
1258  ERR_MBNC( mbnc_inq_dimlen( ncid, did, &dlen ), "len n_b" );
1259  nB = (int)dlen;
1260  ERR_MBNC( mbnc_inq_dimid( ncid, "n_s", &did ), "inq n_s" );
1261  ERR_MBNC( mbnc_inq_dimlen( ncid, did, &dlen ), "len n_s" );
1262  nS = (int)dlen;
1263 
1264  // Contiguous per-rank stripes (last rank takes the remainder).
1265  localSize = nS / size;
1266  size_t offsetRead = (size_t)rank * (size_t)localSize;
1267  if( rank == size - 1 ) localSize += nS % size;
1268  int localSizeA = nA / size;
1269  size_t offsetReadA = (size_t)rank * (size_t)localSizeA;
1270  if( rank == size - 1 ) localSizeA += nA % size;
1271  int localSizeB = nB / size;
1272  size_t offsetReadB = (size_t)rank * (size_t)localSizeB;
1273  if( rank == size - 1 ) localSizeB += nB % size;
1274 
1275  vecRow.resize( localSize );
1276  vecCol.resize( localSize );
1277  vecS.resize( localSize );
1278 
1279  int vid = -1;
1280  size_t st = offsetRead, ct = (size_t)localSize;
1281  ERR_MBNC( mbnc_inq_varid( ncid, "row", &vid ), "inq row" );
1282  ERR_MBNC( mbnc_get_vara_int( ncid, vid, &st, &ct, localSize ? vecRow.data() : NULL ), "get row" );
1283  ERR_MBNC( mbnc_inq_varid( ncid, "col", &vid ), "inq col" );
1284  ERR_MBNC( mbnc_get_vara_int( ncid, vid, &st, &ct, localSize ? vecCol.data() : NULL ), "get col" );
1285  ERR_MBNC( mbnc_inq_varid( ncid, "S", &vid ), "inq S" );
1286  ERR_MBNC( mbnc_get_vara_double( ncid, vid, &st, &ct, localSize ? vecS.data() : NULL ), "get S" );
1287 
1288  if( readAreaA )
1289  {
1290  vecAreaA.resize( localSizeA );
1291  size_t sa = offsetReadA, ca = (size_t)localSizeA;
1292  ERR_MBNC( mbnc_inq_varid( ncid, "area_a", &vid ), "inq area_a" );
1293  ERR_MBNC( mbnc_get_vara_double( ncid, vid, &sa, &ca, localSizeA ? vecAreaA.data() : NULL ), "get area_a" );
1294  }
1295  if( readAreaB )
1296  {
1297  vecAreaB.resize( localSizeB );
1298  size_t sb = offsetReadB, cb = (size_t)localSizeB;
1299  ERR_MBNC( mbnc_inq_varid( ncid, "area_b", &vid ), "inq area_b" );
1300  ERR_MBNC( mbnc_get_vara_double( ncid, vid, &sb, &cb, localSizeB ? vecAreaB.data() : NULL ), "get area_b" );
1301  }
1302 
1303  ERR_MBNC( mbnc_close( ncid ), "close" );
1304  }
1305 
1306  // =========================================================================
1307  // Phase 2: Redistribute sparse matrix entries to their final owning ranks.
1308  //
1309  // After Phase 1, each rank holds a portion of the sparse matrix entries
1310  // (either its owned rows from the buffered read, or a stripe from the
1311  // direct parallel read). The rows/cols are still 1-based (SCRIP format).
1312  //
1313  // This phase uses TupleList-based crystal router communication to send
1314  // entries to the rank that owns each row (trivial nB/size partitioning),
1315  // and optionally a second redistribution based on owned_dof_ids.
1316  // =========================================================================
1317 
1318 #ifdef MOAB_HAVE_EIGEN3
1319 
1320  typedef Eigen::Triplet< double > Triplet;
1321  std::vector< Triplet > tripletList;
1322 
1323 #ifdef MOAB_HAVE_MPI
1324  if( size > 1 )
1325  {
1326  // Trivial row partitioning for redistribution
1327  const int nPerPart = nB / size;
1328 
1329  moab::TupleList* tl = new moab::TupleList;
1330  unsigned numr = 1;
1331  tl->initialize( 3, 0, 0, numr, localSize ); // to_proc, row, col, value
1332  tl->enableWriteAccess();
1333 
1334  for( int i = 0; i < localSize; i++ )
1335  {
1336  int rowval = vecRow[i] - 1; // convert from 1-based (SCRIP) to 0-based
1337  int colval = vecCol[i] - 1;
1338  int to_proc = rowval / nPerPart;
1339  if( to_proc >= size ) to_proc = size - 1;
1340 
1341  int n = tl->get_n();
1342  tl->vi_wr[3 * n] = to_proc;
1343  tl->vi_wr[3 * n + 1] = rowval;
1344  tl->vi_wr[3 * n + 2] = colval;
1345  tl->vr_wr[n] = vecS[i];
1346  tl->inc_n();
1347  }
1348 
1349  // Crystal router: redistribute entries by row ownership
1350  ( m_pcomm->proc_config().crystal_router() )->gs_transfer( 1, *tl, 0 );
1351 
1352  if( owned_dof_ids.size() > 0 )
1353  {
1354  // we need to send desired dof to the rendezvous point
1355  moab::TupleList tl_re; //
1356  tl_re.initialize( 2, 0, 0, 0, owned_dof_ids.size() ); // to proc, value
1357  tl_re.enableWriteAccess();
1358  // send first to rendez_vous point, decided by trivial partitioning
1359 
1360  for( size_t i = 0; i < owned_dof_ids.size(); i++ )
1361  {
1362  int to_proc = -1;
1363  int dof_val = owned_dof_ids[i] - 1; // dofs are 1 based in the file, partition from 0 ?
1364  to_proc = dof_val / nPerPart;
1365  if( to_proc == size ) to_proc = size - 1;
1366 
1367  int n = tl_re.get_n();
1368  tl_re.vi_wr[2 * n] = to_proc;
1369  tl_re.vi_wr[2 * n + 1] = dof_val;
1370 
1371  tl_re.inc_n();
1372  }
1373  ( m_pcomm->proc_config().crystal_router() )->gs_transfer( 1, tl_re, 0 );
1374  // now we know in tl_re where do we need to send back dof_val
1375  moab::TupleList::buffer sort_buffer;
1376  sort_buffer.buffer_init( tl_re.get_n() );
1377  tl_re.sort( 1, &sort_buffer ); // so now we order by value
1378 
1379  //sort_buffer.buffer_init( tl->get_n() );
1380 
1381  std::map< int, int > startDofIndex, endDofIndex; // indices in tl_re for values we want
1382  int dofVal = -1;
1383  if( tl_re.get_n() > 0 )
1384  {
1385  dofVal = tl_re.vi_rd[1]; // first dof val on this rank tl_re.vi_rd[2 * 0 + 1];
1386 
1387  startDofIndex[dofVal] = 0;
1388  endDofIndex[dofVal] = 0; // start and end
1389  for( unsigned k = 1; k < tl_re.get_n(); k++ )
1390  {
1391  int newDof = tl_re.vi_rd[2 * k + 1];
1392  if( dofVal == newDof )
1393  {
1394  endDofIndex[dofVal] = k; // increment by 1 actually
1395  }
1396  else
1397  {
1398  dofVal = newDof;
1399  startDofIndex[dofVal] = k;
1400  endDofIndex[dofVal] = k;
1401  }
1402  }
1403  }
1404  // basically, for each value we are interested in, index in tl_re with those values are
1405  // tl_re.vi_rd[2*startDofIndex+1] == valDof == tl_re.vi_rd[2*endDofIndex+1]
1406  // so now we have ordered
1407  // tl_re shows to what proc do we need to send the tuple (row, col, val)
1408  moab::TupleList* tl_back = new moab::TupleList;
1409  unsigned numr = 1; //
1410  // localSize is a good guess, but maybe it should be bigger ?
1411  // this could be bigger for repeated dofs
1412  tl_back->initialize( 3, 0, 0, numr, tl->get_n() ); // to proc, row, col, value
1413  tl_back->enableWriteAccess();
1414  // now loop over tl and tl_re to see where to send
1415  // form the new tuple, which will contain the desired dofs per task, per row or column distribution
1416 
1417  for( unsigned k = 0; k < tl->get_n(); k++ )
1418  {
1419  int valDof = tl->vi_rd[3 * k + 1]; // 1 for row, 2 for column // first value, it should be
1420  if( startDofIndex.find( valDof ) == startDofIndex.end() ) continue;
1421  for( int ire = startDofIndex[valDof]; ire <= endDofIndex[valDof]; ire++ )
1422  {
1423  int to_proc = tl_re.vi_rd[2 * ire];
1424  int n = tl_back->get_n();
1425  tl_back->vi_wr[3 * n] = to_proc;
1426  tl_back->vi_wr[3 * n + 1] = tl->vi_rd[3 * k + 1]; // row
1427  tl_back->vi_wr[3 * n + 2] = tl->vi_rd[3 * k + 2]; // col
1428  tl_back->vr_wr[n] = tl->vr_rd[k];
1429  tl_back->inc_n();
1430  }
1431  }
1432 
1433  // now communicate to the desired tasks:
1434  ( m_pcomm->proc_config().crystal_router() )->gs_transfer( 1, *tl_back, 0 );
1435 
1436  tl_re.reset(); // clear memory, although this will go out of scope
1437  tl->reset();
1438  tl = tl_back;
1439  }
1440 
1441  // set of row and col used on this task
1442  std::set< int > rowSet;
1443  std::set< int > colSet;
1444  // populate the sparsematrix, using rowMap and colMap
1445  int n = tl->get_n();
1446  for( int i = 0; i < n; i++ )
1447  {
1448  const int vecRowValue = tl->vi_wr[3 * i + 1];
1449  const int vecColValue = tl->vi_wr[3 * i + 2];
1450  rowSet.insert( vecRowValue );
1451  colSet.insert( vecColValue );
1452  }
1453  int index = 0;
1454  row_gdofmap.resize( rowSet.size() );
1455  for( auto setIt : rowSet )
1456  {
1457  row_gdofmap[index] = setIt;
1458  rowMap[setIt] = index++;
1459  }
1461  index = 0;
1462  col_gdofmap.resize( colSet.size() );
1463  for( auto setIt : colSet )
1464  {
1465  col_gdofmap[index] = setIt;
1466  colMap[setIt] = index++;
1467  }
1469 
1470  tripletList.reserve( n );
1471  for( int i = 0; i < n; i++ )
1472  {
1473  const int vecRowValue = tl->vi_wr[3 * i + 1];
1474  const int vecColValue = tl->vi_wr[3 * i + 2];
1475  double value = tl->vr_wr[i];
1476  tripletList.emplace_back( rowMap[vecRowValue], colMap[vecColValue], value );
1477  }
1478  tl->reset();
1479  }
1480  else
1481 #endif
1482  {
1483  // set of row and col used on this task
1484  std::set< int > rowSet;
1485  std::set< int > colSet;
1486  // populate the sparsematrix, using rowMap and colMap
1487  for( int i = 0; i < nS; i++ )
1488  {
1489  const int vecRowValue = vecRow[i] - 1;
1490  const int vecColValue = vecCol[i] - 1;
1491  rowSet.insert( vecRowValue );
1492  colSet.insert( vecColValue );
1493  }
1494 
1495  int index = 0;
1496  row_gdofmap.resize( rowSet.size() );
1497  for( auto setIt : rowSet )
1498  {
1499  row_gdofmap[index] = setIt;
1500  rowMap[setIt] = index++;
1501  }
1503  index = 0;
1504  col_gdofmap.resize( colSet.size() );
1505  for( auto setIt : colSet )
1506  {
1507  col_gdofmap[index] = setIt;
1508  colMap[setIt] = index++;
1509  }
1511 
1512  tripletList.reserve( nS );
1513  for( int i = 0; i < nS; i++ )
1514  {
1515  const int vecRowValue = vecRow[i] - 1; // the rows, cols are 1 based in the file
1516  const int vecColValue = vecCol[i] - 1; // sparse matrix will be 0 based
1517  double value = vecS[i];
1518  tripletList.emplace_back( rowMap[vecRowValue], colMap[vecColValue], value );
1519  }
1520  }
1521 
1522  m_weightMatrix.resize( m_nTotDofs_Dest, m_nTotDofs_SrcCov );
1523  m_rowVector.resize( m_nTotDofs_Dest );
1524  m_colVector.resize( m_nTotDofs_SrcCov );
1525  m_nTotDofs_Src = m_nTotDofs_SrcCov; // do we need both?
1526  // Preserve the map file's global source-DoF count (n_a) so the migration
1527  // can tell a masked source mesh (fewer cells than n_a -> drop is BfB-safe)
1528  // from a complete one (== n_a but a column missing -> real error).
1529  m_nTotDofs_SrcGlobal = nA;
1530  m_weightMatrix.setFromTriplets( tripletList.begin(), tripletList.end() );
1531  // Reset the source and target data first
1532  m_rowVector.setZero();
1533  m_colVector.setZero();
1534 #ifdef VERBOSE
1535  serializeSparseMatrix( m_weightMatrix, "map_operator_" + std::to_string( rank ) + ".txt" );
1536 #endif
1537 // #ifdef MOAB_HAVE_EIGEN3
1538 #endif
1539  // TODO: make this flexible and read the order from map with help of metadata
1540  m_nDofsPEl_Src = 1; // always assume FV-FV maps are read from file
1541  m_nDofsPEl_Dest = 1; // always assume FV-FV maps are read from file
1542 
1543  return moab::MB_SUCCESS;
1544 }

References moab::TupleList::enableWriteAccess(), moab::TupleList::get_n(), moab::TupleList::inc_n(), moab::index, moab::TupleList::initialize(), MB_SUCCESS, moab::mbnc_choose_backend_for_read(), moab::mbnc_close(), moab::mbnc_detect_format(), moab::mbnc_get_vara_double(), moab::mbnc_get_vara_int(), moab::mbnc_inq_dimid(), moab::mbnc_inq_dimlen(), moab::mbnc_inq_varid(), moab::mbnc_open(), moab::NCB_NONE, moab::NCFMT_UNKNOWN, moab::TupleList::reset(), moab::TupleList::sort(), moab::TupleList::vi_rd, moab::TupleList::vi_wr, moab::TupleList::vr_rd, and moab::TupleList::vr_wr.

◆ serializeSparseMatrix()

template<typename SparseMatrixType >
void moab::TempestOnlineMap::serializeSparseMatrix ( const SparseMatrixType &  mat,
const std::string &  filename 
)
private

◆ set_col_dc_dofs()

moab::ErrorCode moab::TempestOnlineMap::set_col_dc_dofs ( std::vector< int > &  values_entities)

Definition at line 433 of file TempestOnlineMap.cpp.

434 {
435  // col_gdofmap has global dofs , that should be in the list of values, such that
436  // row_dtoc_dofmap[offsetDOF] = localDOF;
437  // we need to find col_dtoc_dofmap such that: col_gdofmap[ col_dtoc_dofmap[i] ] == values_entities [i];
438  // we know that col_gdofmap[0..(nbcols-1)] = global_col_dofs -> in values_entities
439  // form first inverse
440  //
441  // resize and initialize to -1 to signal that this value should not be used, if not set below
442  col_dtoc_dofmap.resize( values_entities.size(), -1 );
443  for( size_t j = 0; j < values_entities.size(); j++ )
444  {
445  // values are 1 based, but rowMap, colMap are not
446  const auto it = colMap.find( values_entities[j] - 1 );
447  if( it != colMap.end() ) col_dtoc_dofmap[j] = it->second;
448  }
449  return moab::MB_SUCCESS;
450 }

References MB_SUCCESS.

◆ set_row_dc_dofs()

moab::ErrorCode moab::TempestOnlineMap::set_row_dc_dofs ( std::vector< int > &  values_entities)

Definition at line 452 of file TempestOnlineMap.cpp.

453 {
454  // we need to find row_dtoc_dofmap such that: row_gdofmap[ row_dtoc_dofmap[i] ] == values_entities [i];
455  // resize and initialize to -1 to signal that this value should not be used, if not set below
456  row_dtoc_dofmap.resize( values_entities.size(), -1 );
457  for( size_t j = 0; j < values_entities.size(); j++ )
458  {
459  // values are 1 based, but rowMap, colMap are not
460  const auto it = rowMap.find( values_entities[j] - 1 );
461  if( it != rowMap.end() ) row_dtoc_dofmap[j] = it->second;
462  }
463  return moab::MB_SUCCESS;
464 }

References MB_SUCCESS.

◆ SetDestinationNDofsPerElement()

void moab::TempestOnlineMap::SetDestinationNDofsPerElement ( int  nt)
inline

Get the number of Degrees-Of-Freedom per element on the destination mesh.

Definition at line 650 of file TempestOnlineMap.hpp.

651 {
652  m_nDofsPEl_Dest = nt;
653 }

◆ SetDOFmapAssociation()

moab::ErrorCode moab::TempestOnlineMap::SetDOFmapAssociation ( DiscretizationType  srcType,
int  srcOrder,
bool  isSrcContinuous,
DataArray3D< int > *  srcdataGLLNodes,
DataArray3D< int > *  srcdataGLLNodesSrc,
DiscretizationType  destType,
int  destOrder,
bool  isTgtContinuous,
DataArray3D< int > *  tgtdataGLLNodes 
)

Compute the association between the solution tag global DoF numbering and the local matrix numbering so that matvec operations can be performed consistently.

Parameters
srcTypeThe discretization type of the source mesh
srcOrderThe order of the discretization on the source mesh
isSrcContinuousThe continuity of the discretization on the source mesh
srcdataGLLNodesThe GLL nodes on the source mesh
srcdataGLLNodesSrcThe GLL nodes on the source mesh
destTypeThe discretization type of the destination mesh
destOrderThe order of the discretization on the destination mesh
isTgtContinuousThe continuity of the discretization on the destination mesh
tgtdataGLLNodesThe GLL nodes on the destination mesh

Definition at line 172 of file TempestOnlineMap.cpp.

181 {
182  std::vector< bool > dgll_cgll_row_ldofmap, dgll_cgll_col_ldofmap, dgll_cgll_covcol_ldofmap;
183  std::vector< int > src_soln_gdofs, locsrc_soln_gdofs, tgt_soln_gdofs;
184 
185  // We are assuming that these are element based tags that are sized: np * np
186  m_srcDiscType = srcType;
187  m_destDiscType = destType;
188  m_input_order = srcOrder;
189  m_output_order = destOrder;
190 
191  bool vprint = is_root && false;
192 
193  // Compute and store the total number of source and target DoFs corresponding
194  // to number of rows and columns in the mapping.
195  // Now compute the mapping and store it for the covering mesh
196  int srcTagSize = ( m_eInputType == DiscretizationType_FV ? 1 : m_nDofsPEl_Src * m_nDofsPEl_Src );
198  {
199  assert( m_nDofsPEl_Src == 1 );
200  col_gdofmap.resize( m_remapper->m_covering_source_vertices.size(), UINT_MAX );
202  src_soln_gdofs.resize( m_remapper->m_covering_source_vertices.size(), -1 );
203  MB_CHK_ERR(
205  srcTagSize = 1;
206  }
207  else
208  {
209  col_gdofmap.resize( m_remapper->m_covering_source_entities.size() * srcTagSize, UINT_MAX );
210  col_dtoc_dofmap.resize( m_remapper->m_covering_source_entities.size() * srcTagSize, -1 );
211  src_soln_gdofs.resize( m_remapper->m_covering_source_entities.size() * srcTagSize, -1 );
212  MB_CHK_ERR(
214  }
215 
216  m_nTotDofs_SrcCov = 0;
217  if( srcdataGLLNodes == nullptr )
218  {
219  /* we only have a mapping for elements as DoFs */
220  for( unsigned i = 0; i < col_gdofmap.size(); ++i )
221  {
222  auto gdof = src_soln_gdofs[i];
223  assert( gdof > 0 );
224  col_gdofmap[i] = gdof - 1;
225  col_dtoc_dofmap[i] = i;
226  if( vprint ) std::cout << "Col: " << i << ", " << col_gdofmap[i] << "\n";
228  }
229  }
230  else
231  {
232  if( isSrcContinuous )
233  dgll_cgll_covcol_ldofmap.resize( m_remapper->m_covering_source_entities.size() * srcTagSize, false );
234  // Put these remap coefficients into the SparseMatrix map
235  for( unsigned j = 0; j < m_remapper->m_covering_source_entities.size(); j++ )
236  {
237  for( int p = 0; p < m_nDofsPEl_Src; p++ )
238  {
239  for( int q = 0; q < m_nDofsPEl_Src; q++ )
240  {
241  const int localDOF = ( *srcdataGLLNodes )[p][q][j] - 1;
242  const int offsetDOF = j * srcTagSize + p * m_nDofsPEl_Src + q;
243  if( isSrcContinuous && !dgll_cgll_covcol_ldofmap[localDOF] )
244  {
246  dgll_cgll_covcol_ldofmap[localDOF] = true;
247  }
248  if( !isSrcContinuous ) m_nTotDofs_SrcCov++;
249  assert( src_soln_gdofs[offsetDOF] > 0 );
250  // For CGLL: weight matrix uses localDOF (continuous shared node index)
251  // as column index → col_gdofmap must be indexed by localDOF
252  // For DGLL: weight matrix uses offsetDOF (= elem*nP*nP + p*nP + q)
253  // as column index → col_gdofmap must be indexed by offsetDOF
254  if( isSrcContinuous )
255  {
256  col_gdofmap[localDOF] = src_soln_gdofs[offsetDOF] - 1;
257  col_dtoc_dofmap[offsetDOF] = localDOF;
258  }
259  else
260  {
261  col_gdofmap[offsetDOF] = src_soln_gdofs[offsetDOF] - 1;
262  col_dtoc_dofmap[offsetDOF] = offsetDOF;
263  }
264  }
265  }
266  }
267  }
268 
270  {
271  assert( m_nDofsPEl_Src == 1 );
272  srccol_gdofmap.resize( m_remapper->m_source_vertices.size(), UINT_MAX );
274  locsrc_soln_gdofs.resize( m_remapper->m_source_vertices.size(), -1 );
276  }
277  else
278  {
279  srccol_gdofmap.resize( m_remapper->m_source_entities.size() * srcTagSize, UINT_MAX );
280  srccol_dtoc_dofmap.resize( m_remapper->m_source_entities.size() * srcTagSize, -1 );
281  locsrc_soln_gdofs.resize( m_remapper->m_source_entities.size() * srcTagSize, -1 );
283  }
284 
285  // Now compute the mapping and store it for the original source mesh
286  m_nTotDofs_Src = 0;
287  if( srcdataGLLNodesSrc == nullptr )
288  {
289  /* we only have a mapping for elements as DoFs */
290  for( unsigned i = 0; i < srccol_gdofmap.size(); ++i )
291  {
292  auto gdof = locsrc_soln_gdofs[i];
293  assert( gdof > 0 );
294  srccol_gdofmap[i] = gdof - 1;
295  srccol_dtoc_dofmap[i] = i;
296  m_nTotDofs_Src++;
297  }
298  }
299  else
300  {
301  if( isSrcContinuous ) dgll_cgll_col_ldofmap.resize( m_remapper->m_source_entities.size() * srcTagSize, false );
302  // Put these remap coefficients into the SparseMatrix map
303  for( unsigned j = 0; j < m_remapper->m_source_entities.size(); j++ )
304  {
305  for( int p = 0; p < m_nDofsPEl_Src; p++ )
306  {
307  for( int q = 0; q < m_nDofsPEl_Src; q++ )
308  {
309  const int localDOF = ( *srcdataGLLNodesSrc )[p][q][j] - 1;
310  const int offsetDOF = j * srcTagSize + p * m_nDofsPEl_Src + q;
311  if( isSrcContinuous && !dgll_cgll_col_ldofmap[localDOF] )
312  {
313  m_nTotDofs_Src++;
314  dgll_cgll_col_ldofmap[localDOF] = true;
315  }
316  if( !isSrcContinuous ) m_nTotDofs_Src++;
317  assert( locsrc_soln_gdofs[offsetDOF] > 0 );
318  if( isSrcContinuous )
319  {
320  srccol_gdofmap[localDOF] = locsrc_soln_gdofs[offsetDOF] - 1;
321  srccol_dtoc_dofmap[offsetDOF] = localDOF;
322  }
323  else
324  {
325  srccol_gdofmap[offsetDOF] = locsrc_soln_gdofs[offsetDOF] - 1;
326  srccol_dtoc_dofmap[offsetDOF] = offsetDOF;
327  }
328  }
329  }
330  }
331  }
332 
333  int tgtTagSize = ( m_eOutputType == DiscretizationType_FV ? 1 : m_nDofsPEl_Dest * m_nDofsPEl_Dest );
335  {
336  assert( m_nDofsPEl_Dest == 1 );
337  row_gdofmap.resize( m_remapper->m_target_vertices.size(), UINT_MAX );
339  tgt_soln_gdofs.resize( m_remapper->m_target_vertices.size(), -1 );
341  tgtTagSize = 1;
342  }
343  else
344  {
345  row_gdofmap.resize( m_remapper->m_target_entities.size() * tgtTagSize, UINT_MAX );
346  row_dtoc_dofmap.resize( m_remapper->m_target_entities.size() * tgtTagSize, -1 );
347  tgt_soln_gdofs.resize( m_remapper->m_target_entities.size() * tgtTagSize, -1 );
349  }
350 
351  // Now compute the mapping and store it for the target mesh
352  // To access the GID for each row: row_gdofmap [ row_ldofmap [ 0 : local_ndofs ] ] = GDOF
353  m_nTotDofs_Dest = 0;
354  if( tgtdataGLLNodes == nullptr )
355  {
356  /* we only have a mapping for elements as DoFs */
357  for( unsigned i = 0; i < row_gdofmap.size(); ++i )
358  {
359  auto gdof = tgt_soln_gdofs[i];
360  assert( gdof > 0 );
361  row_gdofmap[i] = gdof - 1;
362  row_dtoc_dofmap[i] = i;
363  if( vprint ) std::cout << "Row: " << i << ", " << row_gdofmap[i] << "\n";
364  m_nTotDofs_Dest++;
365  }
366  }
367  else
368  {
369  if( isTgtContinuous ) dgll_cgll_row_ldofmap.resize( m_remapper->m_target_entities.size() * tgtTagSize, false );
370  // Put these remap coefficients into the SparseMatrix map
371  for( unsigned j = 0; j < m_remapper->m_target_entities.size(); j++ )
372  {
373  for( int p = 0; p < m_nDofsPEl_Dest; p++ )
374  {
375  for( int q = 0; q < m_nDofsPEl_Dest; q++ )
376  {
377  const int localDOF = ( *tgtdataGLLNodes )[p][q][j] - 1;
378  const int offsetDOF = j * tgtTagSize + p * m_nDofsPEl_Dest + q;
379  if( isTgtContinuous && !dgll_cgll_row_ldofmap[localDOF] )
380  {
381  m_nTotDofs_Dest++;
382  dgll_cgll_row_ldofmap[localDOF] = true;
383  }
384  if( !isTgtContinuous ) m_nTotDofs_Dest++;
385  assert( tgt_soln_gdofs[offsetDOF] > 0 );
386  if( isTgtContinuous )
387  {
388  row_gdofmap[localDOF] = tgt_soln_gdofs[offsetDOF] - 1;
389  row_dtoc_dofmap[offsetDOF] = localDOF;
390  }
391  else
392  {
393  row_gdofmap[offsetDOF] = tgt_soln_gdofs[offsetDOF] - 1;
394  row_dtoc_dofmap[offsetDOF] = offsetDOF;
395  }
396  if( vprint )
397  std::cout << "Row: " << offsetDOF << ", " << localDOF << ", " << row_gdofmap[offsetDOF] << ", "
398  << m_nTotDofs_Dest << "\n";
399  }
400  }
401  }
402  }
403 
404  // Let us also allocate the local representation of the sparse matrix
405 #if defined( MOAB_HAVE_EIGEN3 ) && defined( VERBOSE )
406  if( is_root )
407  {
408  std::cout << "[" << rank << "] DoFs: row = " << m_nTotDofs_Dest << " (gdofmap.size=" << row_gdofmap.size()
409  << "), col_src = " << m_nTotDofs_Src << ", col_cov = " << m_nTotDofs_SrcCov
410  << " (gdofmap.size=" << col_gdofmap.size() << ")\n";
411  }
412 #endif
413 
414  // check monotonicity of row_gdofmap and col_gdofmap
415 #ifdef CHECK_INCREASING_DOF
416  for( size_t i = 0; i < row_gdofmap.size() - 1; i++ )
417  {
418  if( row_gdofmap[i] > row_gdofmap[i + 1] )
419  std::cout << " on rank " << rank << " in row_gdofmap[" << i << "]=" << row_gdofmap[i] << " > row_gdofmap["
420  << i + 1 << "]=" << row_gdofmap[i + 1] << " \n";
421  }
422  for( size_t i = 0; i < col_gdofmap.size() - 1; i++ )
423  {
424  if( col_gdofmap[i] > col_gdofmap[i + 1] )
425  std::cout << " on rank " << rank << " in col_gdofmap[" << i << "]=" << col_gdofmap[i] << " > col_gdofmap["
426  << i + 1 << "]=" << col_gdofmap[i + 1] << " \n";
427  }
428 #endif
429 
430  return moab::MB_SUCCESS;
431 }

References MB_CHK_ERR, and MB_SUCCESS.

◆ SetDOFmapTags()

moab::ErrorCode moab::TempestOnlineMap::SetDOFmapTags ( const std::string  srcDofTagName,
const std::string  tgtDofTagName 
)

Store the tag names associated with global DoF ids for source and target meshes to be used for mapping.

Parameters
srcDofTagNameThe tag name associated with global DoF ids for the source mesh
tgtDofTagNameThe tag name associated with global DoF ids for the target mesh

Definition at line 140 of file TempestOnlineMap.cpp.

142 {
143  moab::ErrorCode rval;
144 
145  int tagSize = 0;
147  rval =
148  m_interface->tag_get_handle( srcDofTagName.c_str(), tagSize, MB_TYPE_INTEGER, this->m_dofTagSrc, MB_TAG_ANY );
149 
151  {
152  MB_CHK_SET_ERR( MB_FAILURE, "DoF tag is not set correctly for source mesh." );
153  }
154  else
155  MB_CHK_ERR( rval );
156 
158  rval =
159  m_interface->tag_get_handle( tgtDofTagName.c_str(), tagSize, MB_TYPE_INTEGER, this->m_dofTagDest, MB_TAG_ANY );
161  {
162  MB_CHK_SET_ERR( MB_FAILURE, "DoF tag is not set correctly for target mesh." );
163  }
164  else
165  MB_CHK_ERR( rval );
166 
167  return moab::MB_SUCCESS;
168 }

References ErrorCode, MB_CHK_ERR, MB_CHK_SET_ERR, MB_SUCCESS, MB_TAG_ANY, MB_TAG_NOT_FOUND, and MB_TYPE_INTEGER.

◆ SetMeshInput()

void moab::TempestOnlineMap::SetMeshInput ( Mesh *  imesh)
inline

Definition at line 493 of file TempestOnlineMap.hpp.

494  {
495  m_meshInput = imesh;
496  };

References m_meshInput.

◆ SetSourceNDofsPerElement()

void moab::TempestOnlineMap::SetSourceNDofsPerElement ( int  ns)
inline

Set the number of Degrees-Of-Freedom per element on the source mesh.

Definition at line 645 of file TempestOnlineMap.hpp.

646 {
647  m_nDofsPEl_Src = ns;
648 }

◆ setup_sizes_dimensions()

void moab::TempestOnlineMap::setup_sizes_dimensions ( )
private

Definition at line 102 of file TempestOnlineMap.cpp.

103 {
104  if( m_meshInputCov )
105  {
106  std::vector< std::string > dimNames;
107  std::vector< int > dimSizes;
108  dimNames.push_back( "num_elem" );
109  dimSizes.push_back( m_meshInputCov->faces.size() );
110 
111  this->InitializeSourceDimensions( dimNames, dimSizes );
112  }
113 
114  if( m_meshOutput )
115  {
116  std::vector< std::string > dimNames;
117  std::vector< int > dimSizes;
118  dimNames.push_back( "num_elem" );
119  dimSizes.push_back( m_meshOutput->faces.size() );
120 
121  this->InitializeTargetDimensions( dimNames, dimSizes );
122  }
123 }

Referenced by TempestOnlineMap().

◆ WriteHDF5MapFile()

moab::ErrorCode moab::TempestOnlineMap::WriteHDF5MapFile ( const std::string &  filename)
private

Parallel I/O with NetCDF to write out the SCRIP file from multiple processors.

Need to get the global maximum of number of vertices per element Key issue is that when calling InitializeCoordinatesFromMeshFV, the allocation for dVertexLon/dVertexLat are made based on the maximum vertices in the current process. However, when writing this out, other processes may have a different size for the same array. This is hence a mess to consolidate in h5mtoscrip eventually.

Definition at line 785 of file TempestOnlineMapIO.cpp.

786 {
787  /**
788  * Need to get the global maximum of number of vertices per element
789  * Key issue is that when calling InitializeCoordinatesFromMeshFV, the allocation for
790  *dVertexLon/dVertexLat are made based on the maximum vertices in the current process. However,
791  *when writing this out, other processes may have a different size for the same array. This is
792  *hence a mess to consolidate in h5mtoscrip eventually.
793  **/
794 
795  /* Let us compute all relevant data for the current original source mesh on the process */
796  DataArray1D< double > vecSourceFaceArea, vecTargetFaceArea;
797  DataArray1D< double > dSourceCenterLon, dSourceCenterLat, dTargetCenterLon, dTargetCenterLat;
798  DataArray2D< double > dSourceVertexLon, dSourceVertexLat, dTargetVertexLon, dTargetVertexLat;
800  {
801  this->InitializeCoordinatesFromMeshFV(
802  *m_meshInput, dSourceCenterLon, dSourceCenterLat, dSourceVertexLon, dSourceVertexLat,
803  ( this->m_remapper->m_source_type == moab::TempestRemapper::RLL ) /* fLatLon = false */,
805 
806  vecSourceFaceArea.Allocate( m_meshInput->vecFaceArea.GetRows() );
807  for( unsigned i = 0; i < m_meshInput->vecFaceArea.GetRows(); ++i )
808  vecSourceFaceArea[i] = m_meshInput->vecFaceArea[i];
809  }
810  else
811  {
812  DataArray3D< double > dataGLLJacobianSrc;
813  this->InitializeCoordinatesFromMeshFE( *m_meshInput, m_nDofsPEl_Src, dataGLLNodesSrc, dSourceCenterLon,
814  dSourceCenterLat, dSourceVertexLon, dSourceVertexLat );
815 
816  // Generate the continuous Jacobian for input mesh
817  GenerateMetaData( *m_meshInput, m_nDofsPEl_Src, false /* fBubble */, dataGLLNodesSrc, dataGLLJacobianSrc );
818 
820  {
821  GenerateUniqueJacobian( dataGLLNodesSrc, dataGLLJacobianSrc, vecSourceFaceArea );
822  }
823  else
824  {
825  GenerateDiscontinuousJacobian( dataGLLJacobianSrc, vecSourceFaceArea );
826  }
827  }
828 
830  {
831  this->InitializeCoordinatesFromMeshFV(
832  *m_meshOutput, dTargetCenterLon, dTargetCenterLat, dTargetVertexLon, dTargetVertexLat,
833  ( this->m_remapper->m_target_type == moab::TempestRemapper::RLL ) /* fLatLon = false */,
835 
836  vecTargetFaceArea.Allocate( m_meshOutput->vecFaceArea.GetRows() );
837  for( unsigned i = 0; i < m_meshOutput->vecFaceArea.GetRows(); ++i )
838  vecTargetFaceArea[i] = m_meshOutput->vecFaceArea[i];
839  }
840  else
841  {
842  DataArray3D< double > dataGLLJacobianDest;
843  this->InitializeCoordinatesFromMeshFE( *m_meshOutput, m_nDofsPEl_Dest, dataGLLNodesDest, dTargetCenterLon,
844  dTargetCenterLat, dTargetVertexLon, dTargetVertexLat );
845 
846  // Generate the continuous Jacobian for input mesh
847  GenerateMetaData( *m_meshOutput, m_nDofsPEl_Dest, false /* fBubble */, dataGLLNodesDest, dataGLLJacobianDest );
848 
850  {
851  GenerateUniqueJacobian( dataGLLNodesDest, dataGLLJacobianDest, vecTargetFaceArea );
852  }
853  else
854  {
855  GenerateDiscontinuousJacobian( dataGLLJacobianDest, vecTargetFaceArea );
856  }
857  }
858 
859  moab::EntityHandle& m_meshOverlapSet = m_remapper->m_overlap_set;
860  int tot_src_ents = m_remapper->m_source_entities.size();
861  int tot_tgt_ents = m_remapper->m_target_entities.size();
862  int tot_src_size = dSourceCenterLon.GetRows();
863  int tot_tgt_size = m_dTargetCenterLon.GetRows();
864  int tot_vsrc_size = dSourceVertexLon.GetRows() * dSourceVertexLon.GetColumns();
865  int tot_vtgt_size = m_dTargetVertexLon.GetRows() * m_dTargetVertexLon.GetColumns();
866 
867  const int weightMatNNZ = m_weightMatrix.nonZeros();
868  moab::Tag tagMapMetaData, tagMapIndexRow, tagMapIndexCol, tagMapValues, srcEleIDs, tgtEleIDs;
869  MB_CHK_SET_ERR( m_interface->tag_get_handle( "SMAT_DATA", 13, moab::MB_TYPE_INTEGER, tagMapMetaData,
871  "Retrieving tag handles failed" );
872  MB_CHK_SET_ERR( m_interface->tag_get_handle( "SMAT_ROWS", weightMatNNZ, moab::MB_TYPE_INTEGER, tagMapIndexRow,
874  "Retrieving tag handles failed" );
875  MB_CHK_SET_ERR( m_interface->tag_get_handle( "SMAT_COLS", weightMatNNZ, moab::MB_TYPE_INTEGER, tagMapIndexCol,
877  "Retrieving tag handles failed" );
878  MB_CHK_SET_ERR( m_interface->tag_get_handle( "SMAT_VALS", weightMatNNZ, moab::MB_TYPE_DOUBLE, tagMapValues,
880  "Retrieving tag handles failed" );
881  MB_CHK_SET_ERR( m_interface->tag_get_handle( "SourceGIDS", tot_src_size, moab::MB_TYPE_INTEGER, srcEleIDs,
883  "Retrieving tag handles failed" );
884  MB_CHK_SET_ERR( m_interface->tag_get_handle( "TargetGIDS", tot_tgt_size, moab::MB_TYPE_INTEGER, tgtEleIDs,
886  "Retrieving tag handles failed" );
887  moab::Tag srcAreaValues, tgtAreaValues;
888  MB_CHK_SET_ERR( m_interface->tag_get_handle( "SourceAreas", tot_src_size, moab::MB_TYPE_DOUBLE, srcAreaValues,
890  "Retrieving tag handles failed" );
891  MB_CHK_SET_ERR( m_interface->tag_get_handle( "TargetAreas", tot_tgt_size, moab::MB_TYPE_DOUBLE, tgtAreaValues,
893  "Retrieving tag handles failed" );
894  moab::Tag tagSrcCoordsCLon, tagSrcCoordsCLat, tagTgtCoordsCLon, tagTgtCoordsCLat;
895  MB_CHK_SET_ERR( m_interface->tag_get_handle( "SourceCoordCenterLon", tot_src_size, moab::MB_TYPE_DOUBLE,
896  tagSrcCoordsCLon,
898  "Retrieving tag handles failed" );
899  MB_CHK_SET_ERR( m_interface->tag_get_handle( "SourceCoordCenterLat", tot_src_size, moab::MB_TYPE_DOUBLE,
900  tagSrcCoordsCLat,
902  "Retrieving tag handles failed" );
903  MB_CHK_SET_ERR( m_interface->tag_get_handle( "TargetCoordCenterLon", tot_tgt_size, moab::MB_TYPE_DOUBLE,
904  tagTgtCoordsCLon,
906  "Retrieving tag handles failed" );
907  MB_CHK_SET_ERR( m_interface->tag_get_handle( "TargetCoordCenterLat", tot_tgt_size, moab::MB_TYPE_DOUBLE,
908  tagTgtCoordsCLat,
910  "Retrieving tag handles failed" );
911  moab::Tag tagSrcCoordsVLon, tagSrcCoordsVLat, tagTgtCoordsVLon, tagTgtCoordsVLat;
912  MB_CHK_SET_ERR( m_interface->tag_get_handle( "SourceCoordVertexLon", tot_vsrc_size, moab::MB_TYPE_DOUBLE,
913  tagSrcCoordsVLon,
915  "Retrieving tag handles failed" );
916  MB_CHK_SET_ERR( m_interface->tag_get_handle( "SourceCoordVertexLat", tot_vsrc_size, moab::MB_TYPE_DOUBLE,
917  tagSrcCoordsVLat,
919  "Retrieving tag handles failed" );
920  MB_CHK_SET_ERR( m_interface->tag_get_handle( "TargetCoordVertexLon", tot_vtgt_size, moab::MB_TYPE_DOUBLE,
921  tagTgtCoordsVLon,
923  "Retrieving tag handles failed" );
924  MB_CHK_SET_ERR( m_interface->tag_get_handle( "TargetCoordVertexLat", tot_vtgt_size, moab::MB_TYPE_DOUBLE,
925  tagTgtCoordsVLat,
927  "Retrieving tag handles failed" );
928  moab::Tag srcMaskValues, tgtMaskValues;
929  if( m_iSourceMask.IsAttached() )
930  {
931  MB_CHK_SET_ERR( m_interface->tag_get_handle( "SourceMask", m_iSourceMask.GetRows(), moab::MB_TYPE_INTEGER,
932  srcMaskValues,
934  "Retrieving tag handles failed" );
935  }
936  if( m_iTargetMask.IsAttached() )
937  {
938  MB_CHK_SET_ERR( m_interface->tag_get_handle( "TargetMask", m_iTargetMask.GetRows(), moab::MB_TYPE_INTEGER,
939  tgtMaskValues,
941  "Retrieving tag handles failed" );
942  }
943 
944  std::vector< int > smatrowvals( weightMatNNZ ), smatcolvals( weightMatNNZ );
945  std::vector< double > smatvals( weightMatNNZ );
946  // const double* smatvals = m_weightMatrix.valuePtr();
947  // Loop over the matrix entries and find the max global ID for rows and columns
948  for( int k = 0, offset = 0; k < m_weightMatrix.outerSize(); ++k )
949  {
950  for( moab::TempestOnlineMap::WeightMatrix::InnerIterator it( m_weightMatrix, k ); it; ++it, ++offset )
951  {
952  smatrowvals[offset] = this->GetRowGlobalDoF( it.row() );
953  smatcolvals[offset] = this->GetColGlobalDoF( it.col() );
954  smatvals[offset] = it.value();
955  }
956  }
957 
958  /* Set the global IDs for the DoFs */
959  ////
960  // col_gdofmap [ col_ldofmap [ 0 : local_ndofs ] ] = GDOF
961  // row_gdofmap [ row_ldofmap [ 0 : local_ndofs ] ] = GDOF
962  ////
963  int maxrow = 0, maxcol = 0;
964  std::vector< int > src_global_dofs( tot_src_size ), tgt_global_dofs( tot_tgt_size );
965  for( int i = 0; i < tot_src_size; ++i )
966  {
967  src_global_dofs[i] = srccol_gdofmap[i];
968  maxcol = ( src_global_dofs[i] > maxcol ) ? src_global_dofs[i] : maxcol;
969  }
970 
971  for( int i = 0; i < tot_tgt_size; ++i )
972  {
973  tgt_global_dofs[i] = row_gdofmap[i];
974  maxrow = ( tgt_global_dofs[i] > maxrow ) ? tgt_global_dofs[i] : maxrow;
975  }
976 
977  ///////////////////////////////////////////////////////////////////////////
978  // The metadata in H5M file contains the following data:
979  //
980  // 1. n_a: Total source entities: (number of elements in source mesh)
981  // 2. n_b: Total target entities: (number of elements in target mesh)
982  // 3. nv_a: Max edge size of elements in source mesh
983  // 4. nv_b: Max edge size of elements in target mesh
984  // 5. maxrows: Number of rows in remap weight matrix
985  // 6. maxcols: Number of cols in remap weight matrix
986  // 7. nnz: Number of total nnz in sparse remap weight matrix
987  // 8. np_a: The order of the field description on the source mesh: >= 1
988  // 9. np_b: The order of the field description on the target mesh: >= 1
989  // 10. method_a: The type of discretization for field on source mesh: [0 = FV, 1 = cGLL, 2 =
990  // dGLL]
991  // 11. method_b: The type of discretization for field on target mesh: [0 = FV, 1 = cGLL, 2 =
992  // dGLL]
993  // 12. conserved: Flag to specify whether the remap operator has conservation constraints: [0,
994  // 1]
995  // 13. monotonicity: Flags to specify whether the remap operator has monotonicity constraints:
996  // [0, 1, 2]
997  //
998  ///////////////////////////////////////////////////////////////////////////
999  int map_disc_details[6];
1000  map_disc_details[0] = m_nDofsPEl_Src;
1001  map_disc_details[1] = m_nDofsPEl_Dest;
1003  ? 0
1004  : ( m_srcDiscType == DiscretizationType_CGLL ? 1 : 2 ) );
1006  ? 0
1007  : ( m_destDiscType == DiscretizationType_CGLL ? 1 : 2 ) );
1008  map_disc_details[4] = ( m_bConserved ? 1 : 0 );
1009  map_disc_details[5] = m_iMonotonicity;
1010 
1011 #ifdef MOAB_HAVE_MPI
1012  int loc_smatmetadata[13] = { tot_src_ents,
1013  tot_tgt_ents,
1016  maxrow + 1,
1017  maxcol + 1,
1018  weightMatNNZ,
1019  map_disc_details[0],
1020  map_disc_details[1],
1021  map_disc_details[2],
1022  map_disc_details[3],
1023  map_disc_details[4],
1024  map_disc_details[5] };
1025  MB_CHK_SET_ERR( m_interface->tag_set_data( tagMapMetaData, &m_meshOverlapSet, 1, &loc_smatmetadata[0] ),
1026  "Setting local tag data failed" );
1027  int glb_smatmetadata[13] = { 0,
1028  0,
1029  0,
1030  0,
1031  0,
1032  0,
1033  0,
1034  map_disc_details[0],
1035  map_disc_details[1],
1036  map_disc_details[2],
1037  map_disc_details[3],
1038  map_disc_details[4],
1039  map_disc_details[5] };
1040  int loc_buf[7] = {
1041  tot_src_ents, tot_tgt_ents, weightMatNNZ, m_remapper->max_source_edges, m_remapper->max_target_edges,
1042  maxrow, maxcol };
1043  int glb_buf[4] = { 0, 0, 0, 0 };
1044  MPI_Reduce( &loc_buf[0], &glb_buf[0], 3, MPI_INT, MPI_SUM, 0, m_pcomm->comm() );
1045  glb_smatmetadata[0] = glb_buf[0];
1046  glb_smatmetadata[1] = glb_buf[1];
1047  glb_smatmetadata[6] = glb_buf[2];
1048  MPI_Reduce( &loc_buf[3], &glb_buf[0], 4, MPI_INT, MPI_MAX, 0, m_pcomm->comm() );
1049  glb_smatmetadata[2] = glb_buf[0];
1050  glb_smatmetadata[3] = glb_buf[1];
1051  glb_smatmetadata[4] = glb_buf[2];
1052  glb_smatmetadata[5] = glb_buf[3];
1053 #else
1054  int glb_smatmetadata[13] = { tot_src_ents,
1055  tot_tgt_ents,
1058  maxrow,
1059  maxcol,
1060  weightMatNNZ,
1061  map_disc_details[0],
1062  map_disc_details[1],
1063  map_disc_details[2],
1064  map_disc_details[3],
1065  map_disc_details[4],
1066  map_disc_details[5] };
1067 #endif
1068  // These values represent number of rows and columns. So should be 1-based.
1069  glb_smatmetadata[4]++;
1070  glb_smatmetadata[5]++;
1071 
1072  if( this->is_root )
1073  {
1074  std::cout << " " << this->rank << " Writing remap weights with size [" << glb_smatmetadata[4] << " X "
1075  << glb_smatmetadata[5] << "] and NNZ = " << glb_smatmetadata[6] << std::endl;
1076  EntityHandle root_set = 0;
1077  MB_CHK_SET_ERR( m_interface->tag_set_data( tagMapMetaData, &root_set, 1, &glb_smatmetadata[0] ),
1078  "Setting local tag data failed" );
1079  }
1080 
1081  int dsize;
1082  const int numval = weightMatNNZ;
1083  const void* smatrowvals_d = smatrowvals.data();
1084  const void* smatcolvals_d = smatcolvals.data();
1085  const void* smatvals_d = smatvals.data();
1086  MB_CHK_SET_ERR( m_interface->tag_set_by_ptr( tagMapIndexRow, &m_meshOverlapSet, 1, &smatrowvals_d, &numval ),
1087  "Setting local tag data failed" );
1088  MB_CHK_SET_ERR( m_interface->tag_set_by_ptr( tagMapIndexCol, &m_meshOverlapSet, 1, &smatcolvals_d, &numval ),
1089  "Setting local tag data failed" );
1090  MB_CHK_SET_ERR( m_interface->tag_set_by_ptr( tagMapValues, &m_meshOverlapSet, 1, &smatvals_d, &numval ),
1091  "Setting local tag data failed" );
1092 
1093  /* Set the global IDs for the DoFs */
1094  const void* srceleidvals_d = src_global_dofs.data();
1095  const void* tgteleidvals_d = tgt_global_dofs.data();
1096  dsize = src_global_dofs.size();
1097  MB_CHK_SET_ERR( m_interface->tag_set_by_ptr( srcEleIDs, &m_meshOverlapSet, 1, &srceleidvals_d, &dsize ),
1098  "Setting local tag data failed" );
1099  dsize = tgt_global_dofs.size();
1100  MB_CHK_SET_ERR( m_interface->tag_set_by_ptr( tgtEleIDs, &m_meshOverlapSet, 1, &tgteleidvals_d, &dsize ),
1101  "Setting local tag data failed" );
1102 
1103  /* Set the source and target areas */
1104  const void* srcareavals_d = vecSourceFaceArea;
1105  const void* tgtareavals_d = vecTargetFaceArea;
1106  dsize = tot_src_size;
1107  MB_CHK_SET_ERR( m_interface->tag_set_by_ptr( srcAreaValues, &m_meshOverlapSet, 1, &srcareavals_d, &dsize ),
1108  "Setting local tag data failed" );
1109  dsize = tot_tgt_size;
1110  MB_CHK_SET_ERR( m_interface->tag_set_by_ptr( tgtAreaValues, &m_meshOverlapSet, 1, &tgtareavals_d, &dsize ),
1111  "Setting local tag data failed" );
1112 
1113  /* Set the coordinates for source and target center vertices */
1114  const void* srccoordsclonvals_d = &dSourceCenterLon[0];
1115  const void* srccoordsclatvals_d = &dSourceCenterLat[0];
1116  dsize = dSourceCenterLon.GetRows();
1117  MB_CHK_SET_ERR( m_interface->tag_set_by_ptr( tagSrcCoordsCLon, &m_meshOverlapSet, 1, &srccoordsclonvals_d, &dsize ),
1118  "Setting local tag data failed" );
1119  MB_CHK_SET_ERR( m_interface->tag_set_by_ptr( tagSrcCoordsCLat, &m_meshOverlapSet, 1, &srccoordsclatvals_d, &dsize ),
1120  "Setting local tag data failed" );
1121  const void* tgtcoordsclonvals_d = &m_dTargetCenterLon[0];
1122  const void* tgtcoordsclatvals_d = &m_dTargetCenterLat[0];
1123  dsize = vecTargetFaceArea.GetRows();
1124  MB_CHK_SET_ERR( m_interface->tag_set_by_ptr( tagTgtCoordsCLon, &m_meshOverlapSet, 1, &tgtcoordsclonvals_d, &dsize ),
1125  "Setting local tag data failed" );
1126  MB_CHK_SET_ERR( m_interface->tag_set_by_ptr( tagTgtCoordsCLat, &m_meshOverlapSet, 1, &tgtcoordsclatvals_d, &dsize ),
1127  "Setting local tag data failed" );
1128 
1129  /* Set the coordinates for source and target element vertices */
1130  const void* srccoordsvlonvals_d = &( dSourceVertexLon[0][0] );
1131  const void* srccoordsvlatvals_d = &( dSourceVertexLat[0][0] );
1132  dsize = dSourceVertexLon.GetRows() * dSourceVertexLon.GetColumns();
1133  MB_CHK_SET_ERR( m_interface->tag_set_by_ptr( tagSrcCoordsVLon, &m_meshOverlapSet, 1, &srccoordsvlonvals_d, &dsize ),
1134  "Setting local tag data failed" );
1135  MB_CHK_SET_ERR( m_interface->tag_set_by_ptr( tagSrcCoordsVLat, &m_meshOverlapSet, 1, &srccoordsvlatvals_d, &dsize ),
1136  "Setting local tag data failed" );
1137  const void* tgtcoordsvlonvals_d = &( m_dTargetVertexLon[0][0] );
1138  const void* tgtcoordsvlatvals_d = &( m_dTargetVertexLat[0][0] );
1139  dsize = m_dTargetVertexLon.GetRows() * m_dTargetVertexLon.GetColumns();
1140  MB_CHK_SET_ERR( m_interface->tag_set_by_ptr( tagTgtCoordsVLon, &m_meshOverlapSet, 1, &tgtcoordsvlonvals_d, &dsize ),
1141  "Setting local tag data failed" );
1142  MB_CHK_SET_ERR( m_interface->tag_set_by_ptr( tagTgtCoordsVLat, &m_meshOverlapSet, 1, &tgtcoordsvlatvals_d, &dsize ),
1143  "Setting local tag data failed" );
1144 
1145  /* Set the masks for source and target meshes if available */
1146  if( m_iSourceMask.IsAttached() )
1147  {
1148  const void* srcmaskvals_d = m_iSourceMask;
1149  dsize = m_iSourceMask.GetRows();
1150  MB_CHK_SET_ERR( m_interface->tag_set_by_ptr( srcMaskValues, &m_meshOverlapSet, 1, &srcmaskvals_d, &dsize ),
1151  "Setting local tag data failed" );
1152  }
1153 
1154  if( m_iTargetMask.IsAttached() )
1155  {
1156  const void* tgtmaskvals_d = m_iTargetMask;
1157  dsize = m_iTargetMask.GetRows();
1158  MB_CHK_SET_ERR( m_interface->tag_set_by_ptr( tgtMaskValues, &m_meshOverlapSet, 1, &tgtmaskvals_d, &dsize ),
1159  "Setting local tag data failed" );
1160  }
1161 
1162 #ifdef MOAB_HAVE_MPI
1163  const char* writeOptions = ( this->size > 1 ? "PARALLEL=WRITE_PART" : "" );
1164 #else
1165  const char* writeOptions = "";
1166 #endif
1167 
1168  // EntityHandle sets[3] = {m_remapper->m_source_set, m_remapper->m_target_set, m_remapper->m_overlap_set};
1169  EntityHandle sets[1] = { m_remapper->m_overlap_set };
1170  MB_CHK_ERR( m_interface->write_file( strOutputFile.c_str(), NULL, writeOptions, sets, 1 ) );
1171 
1172 #ifdef WRITE_SCRIP_FILE
1173  sstr.str( "" );
1174  sstr << ctx.outFilename.substr( 0, lastindex ) << "_" << proc_id << ".nc";
1175  std::map< std::string, std::string > mapAttributes;
1176  mapAttributes["Creator"] = "MOAB mbtempest workflow";
1177  if( !ctx.proc_id ) std::cout << "Writing offline map to file: " << sstr.str() << std::endl;
1178  this->Write( strOutputFile.c_str(), mapAttributes, NcFile::Netcdf4 );
1179  sstr.str( "" );
1180 #endif
1181 
1182  return moab::MB_SUCCESS;
1183 }

References MB_CHK_ERR, MB_CHK_SET_ERR, MB_SUCCESS, MB_TAG_CREAT, MB_TAG_SPARSE, MB_TAG_VARLEN, MB_TYPE_DOUBLE, MB_TYPE_INTEGER, and moab::TempestRemapper::RLL.

◆ WriteParallelMap()

moab::ErrorCode moab::TempestOnlineMap::WriteParallelMap ( const std::string &  strTarget,
const std::map< std::string, std::string > &  attrMap 
)

Write the TempestOnlineMap to a parallel NetCDF file.

Definition at line 202 of file TempestOnlineMapIO.cpp.

204 {
205  size_t lastindex = strFilename.find_last_of( "." );
206  std::string extension = strFilename.substr( lastindex + 1, strFilename.size() );
207 
208  // Write the map file to disk in parallel
209  if( extension == "nc" )
210  {
211 #if !defined( MOAB_HAVE_NETCDFPAR ) && !defined( MOAB_HAVE_PNETCDF )
212  // Without a parallel SCRIP backend (parallel NetCDF or PnetCDF), the SCRIP writer
213  // cannot handle multiple MPI ranks writing to the same file.
214  if( this->size > 1 )
215  {
216 #if defined( MOAB_HAVE_HDF5 )
217  // Fall back to the HDF5 format with a .h5m extension; the map can be
218  // converted to SCRIP format offline if needed.
219  std::string h5mFilename = strFilename.substr( 0, lastindex ) + ".h5m";
220  if( !this->rank )
221  {
222  std::cout << " [WriteParallelMap]: Parallel NetCDF/PnetCDF not available; writing map to "
223  << "HDF5 format (" << h5mFilename << ") instead of SCRIP (.nc)\n";
224  }
225  MB_CHK_ERR( this->WriteHDF5MapFile( h5mFilename.c_str() ) );
226  return moab::MB_SUCCESS;
227 #else
228  MB_CHK_SET_ERR( moab::MB_FAILURE,
229  "Parallel SCRIP write requires NETCDFPAR or PnetCDF; HDF5 fallback unavailable" );
230 #endif
231  }
232 #endif
233  /* Invoke the actual call to write the parallel map to disk in SCRIP format */
234  MB_CHK_ERR( this->WriteSCRIPMapFile( strFilename.c_str(), attrMap ) );
235  }
236  else
237  {
238  /* Write to the parallel H5M format */
239  MB_CHK_ERR( this->WriteHDF5MapFile( strFilename.c_str() ) );
240  }
241 
242  return moab::MB_SUCCESS;
243 }

References MB_CHK_ERR, MB_CHK_SET_ERR, and MB_SUCCESS.

Referenced by main().

◆ WriteSCRIPMapFile()

moab::ErrorCode moab::TempestOnlineMap::WriteSCRIPMapFile ( const std::string &  strOutputFile,
const std::map< std::string, std::string > &  attrMap 
)
private

Copy the local matrix from Tempest SparseMatrix representation (ELL) to the parallel CSR Eigen Matrix for scalable application of matvec needed for projections.

Parallel I/O with HDF5 to write out the remapping weights from multiple processors.

Need to get the global maximum of number of vertices per element Key issue is that when calling InitializeCoordinatesFromMeshFV, the allocation for dVertexLon/dVertexLat are made based on the maximum vertices in the current process. However, when writing this out, other processes may have a different size for the same array. This is hence a mess to consolidate in h5mtoscrip eventually.

Definition at line 247 of file TempestOnlineMapIO.cpp.

249 {
250 #if !defined( MOAB_HAVE_NETCDF ) && !defined( MOAB_HAVE_PNETCDF )
251 #error "Cannot enable SCRIP writing without NetCDF or PNetCDF interfaces"
252 #endif
253  // The SCRIP map is written below through the MBNcDispatch layer (mbnc_*), which selects
254  // the NetCDF / parallel-NetCDF / PnetCDF backend at runtime from the target format. The
255  // file is created only after every buffer is computed (classic define-mode -> data-mode),
256  // so nothing is opened here.
257 
258  /**
259  * Need to get the global maximum of number of vertices per element
260  * Key issue is that when calling InitializeCoordinatesFromMeshFV, the allocation for
261  *dVertexLon/dVertexLat are made based on the maximum vertices in the current process. However,
262  *when writing this out, other processes may have a different size for the same array. This is
263  *hence a mess to consolidate in h5mtoscrip eventually.
264  **/
265 
266  /* Let us compute all relevant data for the current original source mesh on the process */
267  DataArray1D< double > vecSourceFaceArea, vecTargetFaceArea;
268  DataArray1D< double > dSourceCenterLon, dSourceCenterLat, dTargetCenterLon, dTargetCenterLat;
269  DataArray2D< double > dSourceVertexLon, dSourceVertexLat, dTargetVertexLon, dTargetVertexLat;
271  {
272  this->InitializeCoordinatesFromMeshFV(
273  *m_meshInput, dSourceCenterLon, dSourceCenterLat, dSourceVertexLon, dSourceVertexLat,
274  ( this->m_remapper->m_source_type == moab::TempestRemapper::RLL ), /* fLatLon = false */
276 
277  vecSourceFaceArea.Allocate( m_meshInput->vecFaceArea.GetRows() );
278  for( unsigned i = 0; i < m_meshInput->vecFaceArea.GetRows(); ++i )
279  vecSourceFaceArea[i] = m_meshInput->vecFaceArea[i];
280  }
281  else
282  {
283  DataArray3D< double > dataGLLJacobianSrc;
284  this->InitializeCoordinatesFromMeshFE( *m_meshInput, m_nDofsPEl_Src, dataGLLNodesSrc, dSourceCenterLon,
285  dSourceCenterLat, dSourceVertexLon, dSourceVertexLat );
286 
287  // Generate the continuous Jacobian for input mesh
288  GenerateMetaData( *m_meshInput, m_nDofsPEl_Src, false /* fBubble */, dataGLLNodesSrc, dataGLLJacobianSrc );
289 
291  {
292  GenerateUniqueJacobian( dataGLLNodesSrc, dataGLLJacobianSrc, vecSourceFaceArea );
293  }
294  else
295  {
296  GenerateDiscontinuousJacobian( dataGLLJacobianSrc, vecSourceFaceArea );
297  }
298  }
299 
301  {
302  this->InitializeCoordinatesFromMeshFV(
303  *m_meshOutput, dTargetCenterLon, dTargetCenterLat, dTargetVertexLon, dTargetVertexLat,
304  ( this->m_remapper->m_target_type == moab::TempestRemapper::RLL ), /* fLatLon = false */
306 
307  vecTargetFaceArea.Allocate( m_meshOutput->vecFaceArea.GetRows() );
308  for( unsigned i = 0; i < m_meshOutput->vecFaceArea.GetRows(); ++i )
309  {
310  vecTargetFaceArea[i] = m_meshOutput->vecFaceArea[i];
311  }
312  }
313  else
314  {
315  DataArray3D< double > dataGLLJacobianDest;
316  this->InitializeCoordinatesFromMeshFE( *m_meshOutput, m_nDofsPEl_Dest, dataGLLNodesDest, dTargetCenterLon,
317  dTargetCenterLat, dTargetVertexLon, dTargetVertexLat );
318 
319  // Generate the continuous Jacobian for input mesh
320  GenerateMetaData( *m_meshOutput, m_nDofsPEl_Dest, false /* fBubble */, dataGLLNodesDest, dataGLLJacobianDest );
321 
323  {
324  GenerateUniqueJacobian( dataGLLNodesDest, dataGLLJacobianDest, vecTargetFaceArea );
325  }
326  else
327  {
328  GenerateDiscontinuousJacobian( dataGLLJacobianDest, vecTargetFaceArea );
329  }
330  }
331 
332  // Map dimensions
333  unsigned nA = ( vecSourceFaceArea.GetRows() );
334  unsigned nB = ( vecTargetFaceArea.GetRows() );
335 
336  std::vector< int > masksA, masksB;
337  MB_CHK_SET_ERR( m_remapper->GetIMasks( moab::Remapper::SourceMesh, masksA ), "Trouble getting masks for source" );
338  MB_CHK_SET_ERR( m_remapper->GetIMasks( moab::Remapper::TargetMesh, masksB ), "Trouble getting masks for target" );
339 
340  // Number of nodes per Face
341  int nSourceNodesPerFace = dSourceVertexLon.GetColumns();
342  int nTargetNodesPerFace = dTargetVertexLon.GetColumns();
343 
344  // if source or target cells have triangles at poles, center of those triangles need to come from
345  // the original quad, not from center in 3d, converted to 2d again
346  // start copy OnlineMap.cpp tempestremap
347  // right now, do this only for source mesh; copy the logic for target mesh
348  for( unsigned i = 0; i < nA; i++ )
349  {
350  const Face& face = m_meshInput->faces[i];
351 
352  int nNodes = face.edges.size();
353  int indexNodeAtPole = -1;
354  if( 3 == nNodes ) // check if one node at the poles
355  {
356  for( int j = 0; j < nNodes; j++ )
357  if( fabs( fabs( dSourceVertexLat[i][j] ) - 90.0 ) < 1.0e-12 )
358  {
359  indexNodeAtPole = j;
360  break;
361  }
362  }
363  if( indexNodeAtPole < 0 ) continue; // continue i loop, do nothing
364  // recompute center of cell, from 3d data; add one 2 nodes at pole, and average
365  int nodeAtPole = face[indexNodeAtPole]; // use the overloaded operator
366  Node nodePole = m_meshInput->nodes[nodeAtPole];
367  Node newCenter = nodePole * 2;
368  for( int j = 1; j < nNodes; j++ )
369  {
370  int indexi = ( indexNodeAtPole + j ) % nNodes; // nNodes is 3 !
371  const Node& node = m_meshInput->nodes[face[indexi]];
372  newCenter = newCenter + node;
373  }
374  newCenter = newCenter * 0.25;
375  newCenter = newCenter.Normalized();
376 
377 #ifdef VERBOSE
378  double iniLon = dSourceCenterLon[i], iniLat = dSourceCenterLat[i];
379 #endif
380  // dSourceCenterLon, dSourceCenterLat
381  XYZtoRLL_Deg( newCenter.x, newCenter.y, newCenter.z, dSourceCenterLon[i], dSourceCenterLat[i] );
382 #ifdef VERBOSE
383  std::cout << " modify center of triangle from " << iniLon << " " << iniLat << " to " << dSourceCenterLon[i]
384  << " " << dSourceCenterLat[i] << "\n";
385 #endif
386  }
387 
388  // first move data if in parallel
389 #if defined( MOAB_HAVE_MPI )
390  int max_row_dof, max_col_dof; // output; arrays will be re-distributed in chunks [maxdof/size]
391  // if (size > 1)
392  {
393  int ierr = rearrange_arrays_by_dofs( srccol_gdofmap, vecSourceFaceArea, dSourceCenterLon, dSourceCenterLat,
394  dSourceVertexLon, dSourceVertexLat, masksA, nA, nSourceNodesPerFace,
395  max_col_dof ); // now nA will be close to maxdof/size
396  if( ierr != 0 )
397  {
398  _EXCEPTION1( "Unable to arrange source data %d ", nA );
399  }
400  // rearrange target data: (nB)
401  //
402  ierr = rearrange_arrays_by_dofs( row_gdofmap, vecTargetFaceArea, dTargetCenterLon, dTargetCenterLat,
403  dTargetVertexLon, dTargetVertexLat, masksB, nB, nTargetNodesPerFace,
404  max_row_dof ); // now nA will be close to maxdof/size
405  if( ierr != 0 )
406  {
407  _EXCEPTION1( "Unable to arrange target data %d ", nB );
408  }
409  }
410 #endif
411 
412  // Number of non-zeros in the remap matrix operator
413  int nS = m_weightMatrix.nonZeros();
414 
415 #if defined( MOAB_HAVE_MPI )
416  int locbuf[5] = { (int)nA, (int)nB, nS, nSourceNodesPerFace, nTargetNodesPerFace };
417  int offbuf[3] = { 0, 0, 0 };
418  int globuf[5] = { 0, 0, 0, 0, 0 };
419  MPI_Scan( locbuf, offbuf, 3, MPI_INT, MPI_SUM, m_pcomm->comm() );
420  MPI_Allreduce( locbuf, globuf, 3, MPI_INT, MPI_SUM, m_pcomm->comm() );
421  MPI_Allreduce( &locbuf[3], &globuf[3], 2, MPI_INT, MPI_MAX, m_pcomm->comm() );
422 
423  // MPI_Scan is inclusive of data in current rank; modify accordingly.
424  offbuf[0] -= nA;
425  offbuf[1] -= nB;
426  offbuf[2] -= nS;
427 
428 #else
429  int offbuf[3] = { 0, 0, 0 };
430  int globuf[5] = { (int)nA, (int)nB, nS, nSourceNodesPerFace, nTargetNodesPerFace };
431 #endif
432 
433  std::vector< std::string > srcdimNames, tgtdimNames;
434  std::vector< int > srcdimSizes, tgtdimSizes;
435  {
437  {
438  srcdimNames.push_back( "lat" );
439  srcdimNames.push_back( "lon" );
440  srcdimSizes.resize( 2, 0 );
441  srcdimSizes[0] = m_remapper->m_source_metadata[0];
442  srcdimSizes[1] = m_remapper->m_source_metadata[1];
443  }
444  else
445  {
446  srcdimNames.push_back( "num_elem" );
447  srcdimSizes.push_back( globuf[0] );
448  }
449 
451  {
452  tgtdimNames.push_back( "lat" );
453  tgtdimNames.push_back( "lon" );
454  tgtdimSizes.resize( 2, 0 );
455  tgtdimSizes[0] = m_remapper->m_target_metadata[0];
456  tgtdimSizes[1] = m_remapper->m_target_metadata[1];
457  }
458  else
459  {
460  tgtdimNames.push_back( "num_elem" );
461  tgtdimSizes.push_back( globuf[1] );
462  }
463  }
464 
465  // Write output dimensions entries
466  unsigned nSrcGridDims = ( srcdimSizes.size() );
467  unsigned nDstGridDims = ( tgtdimSizes.size() );
468 
469  // Write SparseMatrix entries (backend-agnostic: fills vecRow/vecCol/vecS and
470  // computes the fractional-coverage arrays dFracA/dFracB via crystal-router comm)
471  DataArray1D< int > vecRow( nS );
472  DataArray1D< int > vecCol( nS );
473  DataArray1D< double > vecS( nS );
474  DataArray1D< double > dFracA( nA );
475  DataArray1D< double > dFracB( nB );
476 
477  moab::TupleList tlValRow, tlValCol;
478  unsigned numr = 1; //
479  // value has to be sent to processor row/nB for for fracA and col/nA for fracB
480  // vecTargetArea (indexRow ) has to be sent for fracA (index col?)
481  // vecTargetFaceArea will have to be sent to col index, with its index !
482  tlValRow.initialize( 2, 0, 0, numr, nS ); // to proc(row), global row , value
483  tlValCol.initialize( 3, 0, 0, numr, nS ); // to proc(col), global row / col, value
484  tlValRow.enableWriteAccess();
485  tlValCol.enableWriteAccess();
486  /*
487  dFracA[ col ] += val / vecSourceFaceArea[ col ] * vecTargetFaceArea[ row ];
488  dFracB[ row ] += val ;
489  */
490  int offset = 0;
491 #if defined( MOAB_HAVE_MPI )
492  int nAbase = ( max_col_dof + 1 ) / size; // it is nA, except last rank ( == size - 1 )
493  int nBbase = ( max_row_dof + 1 ) / size; // it is nB, except last rank ( == size - 1 )
494 #endif
495  for( int i = 0; i < m_weightMatrix.outerSize(); ++i )
496  {
497  for( WeightMatrix::InnerIterator it( m_weightMatrix, i ); it; ++it )
498  {
499  vecRow[offset] = 1 + this->GetRowGlobalDoF( it.row() ); // row index
500  vecCol[offset] = 1 + this->GetColGlobalDoF( it.col() ); // col index
501  vecS[offset] = it.value(); // value
502 
503 #if defined( MOAB_HAVE_MPI )
504  {
505  // value M(row, col) will contribute to procRow and procCol values for fracA and fracB
506  int procRow = ( vecRow[offset] - 1 ) / nBbase;
507  if( procRow >= size ) procRow = size - 1;
508  int procCol = ( vecCol[offset] - 1 ) / nAbase;
509  if( procCol >= size ) procCol = size - 1;
510  int nrInd = tlValRow.get_n();
511  tlValRow.vi_wr[2 * nrInd] = procRow;
512  tlValRow.vi_wr[2 * nrInd + 1] = vecRow[offset] - 1;
513  tlValRow.vr_wr[nrInd] = vecS[offset];
514  tlValRow.inc_n();
515  int ncInd = tlValCol.get_n();
516  tlValCol.vi_wr[3 * ncInd] = procCol;
517  tlValCol.vi_wr[3 * ncInd + 1] = vecRow[offset] - 1;
518  tlValCol.vi_wr[3 * ncInd + 2] = vecCol[offset] - 1; // this is column
519  tlValCol.vr_wr[ncInd] = vecS[offset];
520  tlValCol.inc_n();
521  }
522 
523 #endif
524  offset++;
525  }
526  }
527 #if defined( MOAB_HAVE_MPI )
528  // need to send values for their row and col processors, to compute fractions there
529  // now do the heavy communication
530  ( m_pcomm->proc_config().crystal_router() )->gs_transfer( 1, tlValCol, 0 );
531  ( m_pcomm->proc_config().crystal_router() )->gs_transfer( 1, tlValRow, 0 );
532 
533  // we have now, for example, dFracB[ row ] += val ;
534  // so we know that on current task, we received tlValRow
535  // reminder dFracA[ col ] += val / vecSourceFaceArea[ col ] * vecTargetFaceArea[ row ];
536  // dFracB[ row ] += val ;
537  for( unsigned i = 0; i < tlValRow.get_n(); i++ )
538  {
539  // int fromProc = tlValRow.vi_wr[2 * i];
540  int gRowInd = tlValRow.vi_wr[2 * i + 1];
541  int localIndexRow = gRowInd - nBbase * rank; // modulo nBbase rank is from 0 to size - 1;
542  double wgt = tlValRow.vr_wr[i];
543  assert( localIndexRow >= 0 );
544  assert( nB - localIndexRow > 0 );
545  dFracB[localIndexRow] += wgt;
546  }
547  // to compute dFracA we need vecTargetFaceArea[ row ]; we know the row, and we can get the proc we need it from
548 
549  std::set< int > neededRows;
550  for( unsigned i = 0; i < tlValCol.get_n(); i++ )
551  {
552  int rRowInd = tlValCol.vi_wr[3 * i + 1];
553  neededRows.insert( rRowInd );
554  // we need vecTargetFaceAreaGlobal[ rRowInd ]; this exists on proc procRow
555  }
556  moab::TupleList tgtAreaReq;
557  tgtAreaReq.initialize( 2, 0, 0, 0, neededRows.size() );
558  tgtAreaReq.enableWriteAccess();
559  for( std::set< int >::iterator sit = neededRows.begin(); sit != neededRows.end(); ++sit )
560  {
561  int neededRow = *sit;
562  int procRow = neededRow / nBbase;
563  if( procRow >= size ) procRow = size - 1;
564  int nr = tgtAreaReq.get_n();
565  tgtAreaReq.vi_wr[2 * nr] = procRow;
566  tgtAreaReq.vi_wr[2 * nr + 1] = neededRow;
567  tgtAreaReq.inc_n();
568  }
569 
570  ( m_pcomm->proc_config().crystal_router() )->gs_transfer( 1, tgtAreaReq, 0 );
571  // we need to send back the tgtArea corresponding to row
572  moab::TupleList tgtAreaInfo; // load it with tgtArea at row
573  tgtAreaInfo.initialize( 2, 0, 0, 1, tgtAreaReq.get_n() );
574  tgtAreaInfo.enableWriteAccess();
575  for( unsigned i = 0; i < tgtAreaReq.get_n(); i++ )
576  {
577  int from_proc = tgtAreaReq.vi_wr[2 * i];
578  int row = tgtAreaReq.vi_wr[2 * i + 1];
579  int locaIndexRow = row - rank * nBbase;
580  double areaToSend = vecTargetFaceArea[locaIndexRow];
581  // int remoteIndex = tgtAreaReq.vi_wr[3*i + 2] ;
582 
583  tgtAreaInfo.vi_wr[2 * i] = from_proc; // send back requested info
584  tgtAreaInfo.vi_wr[2 * i + 1] = row;
585  tgtAreaInfo.vr_wr[i] = areaToSend; // this will be tgt area at row
586  tgtAreaInfo.inc_n();
587  }
588  ( m_pcomm->proc_config().crystal_router() )->gs_transfer( 1, tgtAreaInfo, 0 );
589 
590  std::map< int, double > areaAtRow;
591  for( unsigned i = 0; i < tgtAreaInfo.get_n(); i++ )
592  {
593  // we have received from proc, value for row !
594  int row = tgtAreaInfo.vi_wr[2 * i + 1];
595  areaAtRow[row] = tgtAreaInfo.vr_wr[i];
596  }
597 
598  // we have now for rows the
599  // it is ordered by index, so:
600  // now compute reminder dFracA[ col ] += val / vecSourceFaceArea[ col ] * vecTargetFaceArea[ row ];
601  // tgtAreaInfo will have at index i the area we need (from row)
602  // there should be an easier way :(
603  for( unsigned i = 0; i < tlValCol.get_n(); i++ )
604  {
605  int rRowInd = tlValCol.vi_wr[3 * i + 1];
606  int colInd = tlValCol.vi_wr[3 * i + 2];
607  double val = tlValCol.vr_wr[i];
608  int localColInd = colInd - rank * nAbase; // < local nA
609  // we need vecTargetFaceAreaGlobal[ rRowInd ]; this exists on proc procRow
610  auto itMap = areaAtRow.find( rRowInd ); // it should be different from end
611  if( itMap != areaAtRow.end() )
612  {
613  double areaRow = itMap->second; // we fished a lot for this !
614  dFracA[localColInd] += val / vecSourceFaceArea[localColInd] * areaRow;
615  }
616  }
617 
618 #endif
619  // ============ Write the SCRIP map through the MBNcDispatch layer (mbnc_*) ============
620  // One code path for every backend (serial NetCDF, parallel NetCDF, PnetCDF). The map is
621  // written as classic CDF-5, which both libnetcdf and PnetCDF read. All dimensions,
622  // variables and attributes are defined first (classic define-mode), then written
623  // collectively with the per-rank hyperslab offsets computed above.
624  {
625  const int mapFormat = NCFMT_CLASSIC;
626  NcBackend wbackend = mbnc_choose_backend_for_write( mapFormat, (int)size );
627  if( wbackend == NCB_NONE )
628  _EXCEPTION1( "No NetCDF backend available to write SCRIP map \"%s\"", strFilename.c_str() );
629 
630  int ncid = -1;
631  const int cmode = NC_CLOBBER | NC_64BIT_DATA; // CDF-5
632 #ifdef MOAB_HAVE_MPI
633  ERR_MBNC( mbnc_create_par( wbackend, m_pcomm->comm(), MPI_INFO_NULL, strFilename.c_str(), cmode, &ncid ),
634  "create map file" );
635 #else
636  ERR_MBNC( mbnc_create( strFilename.c_str(), cmode, &ncid ), "create map file" );
637 #endif
638 
639  // ---- global attributes ----
640  for( std::map< std::string, std::string >::const_iterator ait = attrMap.begin(); ait != attrMap.end();
641  ++ait )
642  ERR_MBNC( mbnc_put_att_text( ncid, NC_GLOBAL, ait->first.c_str(), ait->second.size(), ait->second.c_str() ),
643  "global attribute" );
644 
645  // ---- dimensions ----
646  int dimSrcRank, dimDstRank, dimNAp, dimNBp, dimNVAp, dimNVBp, dimNSp;
647  ERR_MBNC( mbnc_def_dim( ncid, "src_grid_rank", nSrcGridDims, &dimSrcRank ), "def src_grid_rank" );
648  ERR_MBNC( mbnc_def_dim( ncid, "dst_grid_rank", nDstGridDims, &dimDstRank ), "def dst_grid_rank" );
649  ERR_MBNC( mbnc_def_dim( ncid, "n_a", (size_t)globuf[0], &dimNAp ), "def n_a" );
650  ERR_MBNC( mbnc_def_dim( ncid, "n_b", (size_t)globuf[1], &dimNBp ), "def n_b" );
651  ERR_MBNC( mbnc_def_dim( ncid, "nv_a", (size_t)globuf[3], &dimNVAp ), "def nv_a" );
652  ERR_MBNC( mbnc_def_dim( ncid, "nv_b", (size_t)globuf[4], &dimNVBp ), "def nv_b" );
653  ERR_MBNC( mbnc_def_dim( ncid, "n_s", (size_t)globuf[2], &dimNSp ), "def n_s" );
654 
655  // ---- variables ----
656  int vSrcGridDims, vDstGridDims, vYCA, vYCB, vXCA, vXCB, vYVA, vYVB, vXVA, vXVB, vMaskA, vMaskB, vAreaA, vAreaB,
657  vRow, vCol, vS, vFracA, vFracB;
658  int d1[1], d2[2];
659  d1[0] = dimSrcRank;
660  ERR_MBNC( mbnc_def_var( ncid, "src_grid_dims", NC_INT, 1, d1, &vSrcGridDims ), "def src_grid_dims" );
661  d1[0] = dimDstRank;
662  ERR_MBNC( mbnc_def_var( ncid, "dst_grid_dims", NC_INT, 1, d1, &vDstGridDims ), "def dst_grid_dims" );
663  d1[0] = dimNAp;
664  ERR_MBNC( mbnc_def_var( ncid, "yc_a", NC_DOUBLE, 1, d1, &vYCA ), "def yc_a" );
665  ERR_MBNC( mbnc_def_var( ncid, "xc_a", NC_DOUBLE, 1, d1, &vXCA ), "def xc_a" );
666  ERR_MBNC( mbnc_def_var( ncid, "mask_a", NC_INT, 1, d1, &vMaskA ), "def mask_a" );
667  ERR_MBNC( mbnc_def_var( ncid, "area_a", NC_DOUBLE, 1, d1, &vAreaA ), "def area_a" );
668  ERR_MBNC( mbnc_def_var( ncid, "frac_a", NC_DOUBLE, 1, d1, &vFracA ), "def frac_a" );
669  d1[0] = dimNBp;
670  ERR_MBNC( mbnc_def_var( ncid, "yc_b", NC_DOUBLE, 1, d1, &vYCB ), "def yc_b" );
671  ERR_MBNC( mbnc_def_var( ncid, "xc_b", NC_DOUBLE, 1, d1, &vXCB ), "def xc_b" );
672  ERR_MBNC( mbnc_def_var( ncid, "mask_b", NC_INT, 1, d1, &vMaskB ), "def mask_b" );
673  ERR_MBNC( mbnc_def_var( ncid, "area_b", NC_DOUBLE, 1, d1, &vAreaB ), "def area_b" );
674  ERR_MBNC( mbnc_def_var( ncid, "frac_b", NC_DOUBLE, 1, d1, &vFracB ), "def frac_b" );
675  d2[0] = dimNAp;
676  d2[1] = dimNVAp;
677  ERR_MBNC( mbnc_def_var( ncid, "yv_a", NC_DOUBLE, 2, d2, &vYVA ), "def yv_a" );
678  ERR_MBNC( mbnc_def_var( ncid, "xv_a", NC_DOUBLE, 2, d2, &vXVA ), "def xv_a" );
679  d2[0] = dimNBp;
680  d2[1] = dimNVBp;
681  ERR_MBNC( mbnc_def_var( ncid, "yv_b", NC_DOUBLE, 2, d2, &vYVB ), "def yv_b" );
682  ERR_MBNC( mbnc_def_var( ncid, "xv_b", NC_DOUBLE, 2, d2, &vXVB ), "def xv_b" );
683  d1[0] = dimNSp;
684  ERR_MBNC( mbnc_def_var( ncid, "row", NC_INT, 1, d1, &vRow ), "def row" );
685  ERR_MBNC( mbnc_def_var( ncid, "col", NC_INT, 1, d1, &vCol ), "def col" );
686  ERR_MBNC( mbnc_def_var( ncid, "S", NC_DOUBLE, 1, d1, &vS ), "def S" );
687 
688  // ---- variable attributes (reversed grid_dims name ordering preserved) ----
689  {
690  char szDim[64];
691  for( unsigned i = 0; i < srcdimSizes.size(); i++ )
692  {
693  snprintf( szDim, 64, "name%u", i );
694  const std::string& nm = srcdimNames[nSrcGridDims - i - 1];
695  ERR_MBNC( mbnc_put_att_text( ncid, vSrcGridDims, szDim, nm.size(), nm.c_str() ), "src_grid_dims name" );
696  }
697  for( unsigned i = 0; i < tgtdimSizes.size(); i++ )
698  {
699  snprintf( szDim, 64, "name%u", i );
700  const std::string& nm = tgtdimNames[nDstGridDims - i - 1];
701  ERR_MBNC( mbnc_put_att_text( ncid, vDstGridDims, szDim, nm.size(), nm.c_str() ), "dst_grid_dims name" );
702  }
703  const std::string deg( "degrees" );
704  const int vdeg[8] = { vYCA, vYCB, vXCA, vXCB, vYVA, vYVB, vXVA, vXVB };
705  for( int k = 0; k < 8; k++ )
706  ERR_MBNC( mbnc_put_att_text( ncid, vdeg[k], "units", deg.size(), deg.c_str() ), "units" );
707  const std::string faName( "fraction of target coverage of source dof" );
708  const std::string fbName( "fraction of source coverage of target dof" );
709  const std::string unitless( "unitless" );
710  ERR_MBNC( mbnc_put_att_text( ncid, vFracA, "name", faName.size(), faName.c_str() ), "frac_a name" );
711  ERR_MBNC( mbnc_put_att_text( ncid, vFracA, "units", unitless.size(), unitless.c_str() ), "frac_a units" );
712  ERR_MBNC( mbnc_put_att_text( ncid, vFracB, "name", fbName.size(), fbName.c_str() ), "frac_b name" );
713  ERR_MBNC( mbnc_put_att_text( ncid, vFracB, "units", unitless.size(), unitless.c_str() ), "frac_b units" );
714  }
715 
716  ERR_MBNC( mbnc_enddef( ncid ), "enddef" );
717 
718  // ---- collective data writes (every rank participates) ----
719  size_t sA = (size_t)offbuf[0], cA = (size_t)nA;
720  size_t sB = (size_t)offbuf[1], cB = (size_t)nB;
721  size_t sS = (size_t)offbuf[2], cS = (size_t)nS;
722  double* pYCA = ( nA > 0 ) ? &dSourceCenterLat[0] : NULL;
723  double* pXCA = ( nA > 0 ) ? &dSourceCenterLon[0] : NULL;
724  double* pYCB = ( nB > 0 ) ? &dTargetCenterLat[0] : NULL;
725  double* pXCB = ( nB > 0 ) ? &dTargetCenterLon[0] : NULL;
726  int* pMaskA = ( nA > 0 ) ? &masksA[0] : NULL;
727  int* pMaskB = ( nB > 0 ) ? &masksB[0] : NULL;
728  double* pAreaA = ( nA > 0 ) ? &vecSourceFaceArea[0] : NULL;
729  double* pAreaB = ( nB > 0 ) ? &vecTargetFaceArea[0] : NULL;
730  double* pFracA = ( nA > 0 ) ? &dFracA[0] : NULL;
731  double* pFracB = ( nB > 0 ) ? &dFracB[0] : NULL;
732  int* pRow = ( nS > 0 ) ? &vecRow[0] : NULL;
733  int* pCol = ( nS > 0 ) ? &vecCol[0] : NULL;
734  double* pS = ( nS > 0 ) ? &vecS[0] : NULL;
735 
736  ERR_MBNC( mbnc_put_vara_double( ncid, vYCA, &sA, &cA, pYCA ), "put yc_a" );
737  ERR_MBNC( mbnc_put_vara_double( ncid, vXCA, &sA, &cA, pXCA ), "put xc_a" );
738  ERR_MBNC( mbnc_put_vara_double( ncid, vYCB, &sB, &cB, pYCB ), "put yc_b" );
739  ERR_MBNC( mbnc_put_vara_double( ncid, vXCB, &sB, &cB, pXCB ), "put xc_b" );
740  ERR_MBNC( mbnc_put_vara_int( ncid, vMaskA, &sA, &cA, pMaskA ), "put mask_a" );
741  ERR_MBNC( mbnc_put_vara_int( ncid, vMaskB, &sB, &cB, pMaskB ), "put mask_b" );
742  ERR_MBNC( mbnc_put_vara_double( ncid, vAreaA, &sA, &cA, pAreaA ), "put area_a" );
743  ERR_MBNC( mbnc_put_vara_double( ncid, vAreaB, &sB, &cB, pAreaB ), "put area_b" );
744  ERR_MBNC( mbnc_put_vara_double( ncid, vFracA, &sA, &cA, pFracA ), "put frac_a" );
745  ERR_MBNC( mbnc_put_vara_double( ncid, vFracB, &sB, &cB, pFracB ), "put frac_b" );
746  ERR_MBNC( mbnc_put_vara_int( ncid, vRow, &sS, &cS, pRow ), "put row" );
747  ERR_MBNC( mbnc_put_vara_int( ncid, vCol, &sS, &cS, pCol ), "put col" );
748  ERR_MBNC( mbnc_put_vara_double( ncid, vS, &sS, &cS, pS ), "put S" );
749  {
750  size_t s2A[2] = { (size_t)offbuf[0], 0 }, c2A[2] = { (size_t)nA, (size_t)nSourceNodesPerFace };
751  double* pYVA = ( nA > 0 ) ? &dSourceVertexLat[0][0] : NULL;
752  double* pXVA = ( nA > 0 ) ? &dSourceVertexLon[0][0] : NULL;
753  ERR_MBNC( mbnc_put_vara_double( ncid, vYVA, s2A, c2A, pYVA ), "put yv_a" );
754  ERR_MBNC( mbnc_put_vara_double( ncid, vXVA, s2A, c2A, pXVA ), "put xv_a" );
755  size_t s2B[2] = { (size_t)offbuf[1], 0 }, c2B[2] = { (size_t)nB, (size_t)nTargetNodesPerFace };
756  double* pYVB = ( nB > 0 ) ? &dTargetVertexLat[0][0] : NULL;
757  double* pXVB = ( nB > 0 ) ? &dTargetVertexLon[0][0] : NULL;
758  ERR_MBNC( mbnc_put_vara_double( ncid, vYVB, s2B, c2B, pYVB ), "put yv_b" );
759  ERR_MBNC( mbnc_put_vara_double( ncid, vXVB, s2B, c2B, pXVB ), "put xv_b" );
760  }
761  {
762  // Small global grid_dims arrays: rank 0 writes the full array, others write 0.
763  size_t sg = 0;
764  size_t cgSrc = ( rank == 0 ) ? (size_t)nSrcGridDims : 0;
765  size_t cgDst = ( rank == 0 ) ? (size_t)nDstGridDims : 0;
766  ERR_MBNC( mbnc_put_vara_int( ncid, vSrcGridDims, &sg, &cgSrc, ( rank == 0 ) ? &srcdimSizes[0] : NULL ),
767  "put src_grid_dims" );
768  ERR_MBNC( mbnc_put_vara_int( ncid, vDstGridDims, &sg, &cgDst, ( rank == 0 ) ? &tgtdimSizes[0] : NULL ),
769  "put dst_grid_dims" );
770  }
771 
772  ERR_MBNC( mbnc_close( ncid ), "close" );
773  }
774 
775 
776 
777 #ifdef VERBOSE
778  serializeSparseMatrix( m_weightMatrix, "map_operator_" + std::to_string( rank ) + ".txt" );
779 #endif
780  return moab::MB_SUCCESS;
781 }

References moab::TupleList::enableWriteAccess(), moab::TupleList::get_n(), moab::TupleList::inc_n(), moab::TupleList::initialize(), MB_CHK_SET_ERR, MB_SUCCESS, moab::mbnc_choose_backend_for_write(), moab::mbnc_close(), moab::mbnc_create(), moab::mbnc_def_dim(), moab::mbnc_def_var(), moab::mbnc_enddef(), moab::mbnc_put_att_text(), moab::mbnc_put_vara_double(), moab::mbnc_put_vara_int(), moab::NCB_NONE, moab::NCFMT_CLASSIC, moab::TempestRemapper::RLL, moab::Remapper::SourceMesh, moab::Remapper::TargetMesh, moab::TupleList::vi_wr, and moab::TupleList::vr_wr.

Member Data Documentation

◆ col_dtoc_dofmap

std::vector< int > moab::TempestOnlineMap::col_dtoc_dofmap
private

Definition at line 576 of file TempestOnlineMap.hpp.

Referenced by ApplyWeights(), and GetColDofMap().

◆ col_gdofmap

std::vector< unsigned > moab::TempestOnlineMap::col_gdofmap
private

Definition at line 573 of file TempestOnlineMap.hpp.

Referenced by ApplyWeights(), fill_col_ids(), and LinearRemapNN_MOAB().

◆ colMap

std::map< int, int > moab::TempestOnlineMap::colMap
private

Definition at line 578 of file TempestOnlineMap.hpp.

◆ dataGLLNodesDest

DataArray3D< int > moab::TempestOnlineMap::dataGLLNodesDest
private

Definition at line 581 of file TempestOnlineMap.hpp.

◆ dataGLLNodesSrc

DataArray3D< int > moab::TempestOnlineMap::dataGLLNodesSrc
private

Definition at line 581 of file TempestOnlineMap.hpp.

◆ dataGLLNodesSrcCov

DataArray3D< int > moab::TempestOnlineMap::dataGLLNodesSrcCov
private

Definition at line 581 of file TempestOnlineMap.hpp.

◆ is_parallel

bool moab::TempestOnlineMap::is_parallel
private

Definition at line 601 of file TempestOnlineMap.hpp.

Referenced by TempestOnlineMap().

◆ is_root

bool moab::TempestOnlineMap::is_root
private

Definition at line 601 of file TempestOnlineMap.hpp.

Referenced by TempestOnlineMap().

◆ m_bConserved

bool moab::TempestOnlineMap::m_bConserved
private

Definition at line 593 of file TempestOnlineMap.hpp.

◆ m_destDiscType

DiscretizationType moab::TempestOnlineMap::m_destDiscType
private

Definition at line 582 of file TempestOnlineMap.hpp.

◆ m_dofTagDest

moab::Tag moab::TempestOnlineMap::m_dofTagDest
private

Definition at line 572 of file TempestOnlineMap.hpp.

◆ m_dofTagSrc

moab::Tag moab::TempestOnlineMap::m_dofTagSrc
private

The original tag data and local to global DoF mapping to associate matrix values to solution

Definition at line 572 of file TempestOnlineMap.hpp.

◆ m_eInputType

DiscretizationType moab::TempestOnlineMap::m_eInputType
private

Definition at line 592 of file TempestOnlineMap.hpp.

◆ m_eOutputType

DiscretizationType moab::TempestOnlineMap::m_eOutputType
private

Definition at line 592 of file TempestOnlineMap.hpp.

◆ m_iMonotonicity

int moab::TempestOnlineMap::m_iMonotonicity
private

Definition at line 594 of file TempestOnlineMap.hpp.

◆ m_input_order

int moab::TempestOnlineMap::m_input_order
private

Definition at line 579 of file TempestOnlineMap.hpp.

Referenced by TempestOnlineMap().

◆ m_interface

moab::Interface* moab::TempestOnlineMap::m_interface
private

The reference to the moab::Core object that contains source/target and overlap sets.

Definition at line 560 of file TempestOnlineMap.hpp.

Referenced by TempestOnlineMap().

◆ m_meshInput

Mesh* moab::TempestOnlineMap::m_meshInput
private

Definition at line 596 of file TempestOnlineMap.hpp.

Referenced by SetMeshInput(), and TempestOnlineMap().

◆ m_meshInputCov

Mesh* moab::TempestOnlineMap::m_meshInputCov
private

Definition at line 597 of file TempestOnlineMap.hpp.

Referenced by TempestOnlineMap().

◆ m_meshOutput

Mesh* moab::TempestOnlineMap::m_meshOutput
private

Definition at line 598 of file TempestOnlineMap.hpp.

Referenced by TempestOnlineMap().

◆ m_meshOverlap

Mesh* moab::TempestOnlineMap::m_meshOverlap
private

Definition at line 599 of file TempestOnlineMap.hpp.

Referenced by TempestOnlineMap().

◆ m_nDofsPEl_Dest

int moab::TempestOnlineMap::m_nDofsPEl_Dest
private

Definition at line 591 of file TempestOnlineMap.hpp.

◆ m_nDofsPEl_Src

int moab::TempestOnlineMap::m_nDofsPEl_Src
private

Definition at line 591 of file TempestOnlineMap.hpp.

◆ m_nTotDofs_Dest

int moab::TempestOnlineMap::m_nTotDofs_Dest
private

Definition at line 583 of file TempestOnlineMap.hpp.

Referenced by ApplyWeights(), and LinearRemapNN_MOAB().

◆ m_nTotDofs_Src

int moab::TempestOnlineMap::m_nTotDofs_Src
private

Definition at line 583 of file TempestOnlineMap.hpp.

◆ m_nTotDofs_SrcCov

int moab::TempestOnlineMap::m_nTotDofs_SrcCov
private

Definition at line 583 of file TempestOnlineMap.hpp.

Referenced by ApplyWeights(), and LinearRemapNN_MOAB().

◆ m_nTotDofs_SrcGlobal

int moab::TempestOnlineMap::m_nTotDofs_SrcGlobal
private

Definition at line 588 of file TempestOnlineMap.hpp.

Referenced by GlobalSourceDofCount(), and TempestOnlineMap().

◆ m_output_order

int moab::TempestOnlineMap::m_output_order
private

Definition at line 579 of file TempestOnlineMap.hpp.

Referenced by TempestOnlineMap().

◆ m_remapper

moab::TempestRemapper* moab::TempestOnlineMap::m_remapper
private

The fundamental remapping operator object.

Definition at line 546 of file TempestOnlineMap.hpp.

Referenced by TempestOnlineMap().

◆ m_srcDiscType

DiscretizationType moab::TempestOnlineMap::m_srcDiscType
private

Definition at line 582 of file TempestOnlineMap.hpp.

◆ rank

int moab::TempestOnlineMap::rank
private

Definition at line 602 of file TempestOnlineMap.hpp.

Referenced by ApplyWeights(), and TempestOnlineMap().

◆ row_dtoc_dofmap

std::vector< int > moab::TempestOnlineMap::row_dtoc_dofmap
private

Definition at line 576 of file TempestOnlineMap.hpp.

Referenced by ApplyWeights(), and GetRowDofMap().

◆ row_gdofmap

std::vector< unsigned > moab::TempestOnlineMap::row_gdofmap
private

Definition at line 573 of file TempestOnlineMap.hpp.

Referenced by ApplyWeights(), and LinearRemapNN_MOAB().

◆ rowMap

std::map< int, int > moab::TempestOnlineMap::rowMap
private

Definition at line 578 of file TempestOnlineMap.hpp.

◆ size

int moab::TempestOnlineMap::size
private

Definition at line 602 of file TempestOnlineMap.hpp.

Referenced by ApplyWeights(), and TempestOnlineMap().

◆ srccol_dtoc_dofmap

std::vector< int > moab::TempestOnlineMap::srccol_dtoc_dofmap
private

Definition at line 576 of file TempestOnlineMap.hpp.

◆ srccol_gdofmap

std::vector< unsigned > moab::TempestOnlineMap::srccol_gdofmap
private

Definition at line 573 of file TempestOnlineMap.hpp.


The documentation for this class was generated from the following files: