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)
 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::TempestRemapperm_remapper
 The fundamental remapping operator object. More...
 
moab::Interfacem_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 436 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 66 of file TempestOnlineMap.cpp.

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

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 123 of file TempestOnlineMap.cpp.

124 {
125  m_interface = nullptr;
126 #ifdef MOAB_HAVE_MPI
127  m_pcomm = nullptr;
128 #endif
129  m_meshInput = nullptr;
130  m_meshOutput = nullptr;
131  m_meshOverlap = nullptr;
132 }

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 638 of file TempestLinearRemap.cpp.

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

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 1485 of file TempestOnlineMap.cpp.

1490 {
1491  std::vector< double > solSTagVals;
1492  std::vector< double > solTTagVals;
1493 
1494  moab::Range sents, tents;
1496  {
1498  {
1500  solSTagVals.resize( covSrcEnts.size(), default_projection );
1501  sents = covSrcEnts;
1502  }
1503  else
1504  {
1506  solSTagVals.resize( covSrcEnts.size() * this->GetSourceNDofsPerElement() * this->GetSourceNDofsPerElement(),
1507  default_projection );
1508  sents = covSrcEnts;
1509  }
1511  {
1513  solTTagVals.resize( tgtEnts.size(), default_projection );
1514  tents = tgtEnts;
1515  }
1516  else
1517  {
1519  solTTagVals.resize( tgtEnts.size() * this->GetDestinationNDofsPerElement() *
1520  this->GetDestinationNDofsPerElement(),
1521  default_projection );
1522  tents = tgtEnts;
1523  }
1524  }
1525  else
1526  {
1529  solSTagVals.resize( covSrcEnts.size() * this->GetSourceNDofsPerElement() * this->GetSourceNDofsPerElement(),
1530  default_projection );
1531  solTTagVals.resize( tgtEnts.size() * this->GetDestinationNDofsPerElement() *
1532  this->GetDestinationNDofsPerElement(),
1533  default_projection );
1534 
1535  sents = covSrcEnts;
1536  tents = tgtEnts;
1537  }
1538 
1539  // The tag data is np*np*n_el_src
1540  MB_CHK_SET_ERR( m_interface->tag_get_data( srcSolutionTag, sents, &solSTagVals[0] ),
1541  "Getting local tag data failed" );
1542 
1543  // Compute the application of weights on the suorce solution data and store it in the
1544  // destination solution vector data Optionally, can also perform the transpose application of
1545  // the weight matrix. Set the 3rd argument to true if this is needed
1546  MB_CHK_SET_ERR( this->ApplyWeights( solSTagVals, solTTagVals, transpose ),
1547  "Applying remap operator onto source vector data failed" );
1548 
1549  // The tag data is np*np*n_el_dest
1550  MB_CHK_SET_ERR( m_interface->tag_set_data( tgtSolutionTag, tents, &solTTagVals[0] ),
1551  "Setting target tag data failed" );
1552 
1553  if( caasType != CAAS_NONE )
1554  {
1555  std::string tgtSolutionTagName;
1556  MB_CHK_SET_ERR( m_interface->tag_get_name( tgtSolutionTag, tgtSolutionTagName ), "Getting tag name failed" );
1557 
1558  // Perform CAAS iterations iteratively until convergence
1559  constexpr int nmax_caas_iterations = 10;
1560  double mismatch = 1.0;
1561  int caasIteration = 0;
1562  double initialMismatch = 0.0;
1563  while( ( fabs( mismatch / initialMismatch ) > 1e-15 && fabs( mismatch ) > 1e-15 ) &&
1564  caasIteration++ < nmax_caas_iterations ) // iterate until convergence or a maximum of 5 iterations
1565  {
1566  double dMassDiffPostGlobal;
1567  std::pair< double, double > mDefect =
1568  this->ApplyBoundsLimiting( solSTagVals, solTTagVals, caasType, caasIteration, mismatch );
1569 #ifdef MOAB_HAVE_MPI
1570  double dMassDiffPost = mDefect.second;
1571  MPI_Allreduce( &dMassDiffPost, &dMassDiffPostGlobal, 1, MPI_DOUBLE, MPI_SUM, m_pcomm->comm() );
1572 #else
1573  dMassDiffPostGlobal = mDefect.second;
1574 #endif
1575  if( caasIteration == 1 ) initialMismatch = mDefect.first;
1576  if( m_remapper->verbose && is_root )
1577  {
1578  printf( "Field {%s} -> CAAS iteration: %d, mass defect: %3.4e, post-CAAS: %3.4e\n",
1579  tgtSolutionTagName.c_str(), caasIteration, mDefect.first, dMassDiffPostGlobal );
1580  }
1581  mismatch = dMassDiffPostGlobal;
1582 
1583  // The tag data is np*np*n_el_dest
1584  MB_CHK_SET_ERR( m_interface->tag_set_data( tgtSolutionTag, tents, &solTTagVals[0] ),
1585  "Setting local tag data failed" );
1586  }
1587  }
1588 
1589  return moab::MB_SUCCESS;
1590 }

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 1592 of file TempestOnlineMap.cpp.

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

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 546 of file TempestLinearRemap.cpp.

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

◆ 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 1436 of file TempestOnlineMap.cpp.

1441 {
1442  assert( nrings > 0 );
1443  assert( useMOABAdjacencies || trMesh != nullptr );
1444 
1445  const size_t nrows = vecAdjFaces.size();
1447  for( size_t index = 0; index < nrows; index++ )
1448  {
1449  vecAdjFaces[index].insert( index ); // add self target face first
1450  {
1451  // Compute the adjacent faces to the target face
1452  if( useMOABAdjacencies )
1453  {
1454  moab::Range ents;
1455  // ents.insert( entities.index( entities[index] ) );
1456  ents.insert( entities[index] );
1457  moab::Range adjEnts;
1458  moab::ErrorCode rval = mtu.get_bridge_adjacencies( ents, 0, 2, adjEnts, nrings );MB_CHK_SET_ERR_CONT( rval, "Failed to get adjacent faces" );
1459  for( moab::Range::iterator it = adjEnts.begin(); it != adjEnts.end(); ++it )
1460  {
1461  // int adjIndex = m_interface->id_from_handle(*it)-1;
1462  int adjIndex = entities.index( *it );
1463  // printf("rank: %d, Element %lu, entity: %lu, adjIndex %d\n", rank, index, *it, adjIndex);
1464  if( adjIndex >= 0 ) vecAdjFaces[index].insert( adjIndex );
1465  }
1466  }
1467  else
1468  {
1469  /// Vector storing adjacent Faces.
1470  typedef std::pair< int, int > FaceDistancePair;
1471  typedef std::vector< FaceDistancePair > AdjacentFaceVector;
1472  AdjacentFaceVector adjFaces;
1473  Face& face = trMesh->faces[index];
1474  GetAdjacentFaceVectorByEdge( *trMesh, index, nrings * face.edges.size(), adjFaces );
1475 
1476  // Add the adjacent faces to the target face list
1477  for( auto adjFace : adjFaces )
1478  if( adjFace.first >= 0 )
1479  vecAdjFaces[index].insert( adjFace.first ); // map target face to source face
1480  }
1481  }
1482  }
1483 }

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 2717 of file TempestOnlineMap.cpp.

2722 {
2723  const bool outputEnabled = ( is_root );
2724  int discOrder;
2725  // DiscretizationType discMethod;
2726  // moab::EntityHandle meshset;
2727  moab::Range entities;
2728  // Mesh* trmesh;
2729  switch( ctx )
2730  {
2731  case Remapper::SourceMesh:
2732  // meshset = m_remapper->m_covering_source_set;
2733  // trmesh = m_remapper->m_covering_source;
2736  discOrder = m_nDofsPEl_Src;
2737  // discMethod = m_eInputType;
2738  break;
2739 
2740  case Remapper::TargetMesh:
2741  // meshset = m_remapper->m_target_set;
2742  // trmesh = m_remapper->m_target;
2743  entities =
2745  discOrder = m_nDofsPEl_Dest;
2746  // discMethod = m_eOutputType;
2747  break;
2748 
2749  default:
2750  if( outputEnabled )
2751  std::cout << "Invalid context specified for defining an analytical solution tag" << std::endl;
2752  return moab::MB_FAILURE;
2753  }
2754 
2755  // Let us create teh solution tag with appropriate information for name, discretization order
2756  // (DoF space)
2757  std::string exactTagName, projTagName;
2758  const int ntotsize = entities.size() * discOrder * discOrder;
2759  std::vector< double > exactSolution( ntotsize, 0.0 ), projSolution( ntotsize, 0.0 );
2760  MB_CHK_ERR( m_interface->tag_get_name( exactTag, exactTagName ) );
2761  MB_CHK_ERR( m_interface->tag_get_data( exactTag, entities, &exactSolution[0] ) );
2762  MB_CHK_ERR( m_interface->tag_get_name( approxTag, projTagName ) );
2763  MB_CHK_ERR( m_interface->tag_get_data( approxTag, entities, &projSolution[0] ) );
2764 
2765  const auto& ovents = m_remapper->m_overlap_entities;
2766 
2767  std::vector< double > errnorms( 4, 0.0 ), globerrnorms( 4, 0.0 ); // L1Err, L2Err, LinfErr
2768  double sumarea = 0.0;
2769  for( size_t i = 0; i < ovents.size(); ++i )
2770  {
2771  const int srcidx = m_remapper->m_overlap->vecSourceFaceIx[i];
2772  if( srcidx < 0 ) continue; // Skip non-overlapping entities
2773  const int tgtidx = m_remapper->m_overlap->vecTargetFaceIx[i];
2774  if( tgtidx < 0 ) continue; // skip ghost target faces
2775  const double ovarea = m_remapper->m_overlap->vecFaceArea[i];
2776  const double error = fabs( exactSolution[tgtidx] - projSolution[tgtidx] );
2777  errnorms[0] += ovarea * error;
2778  errnorms[1] += ovarea * error * error;
2779  errnorms[3] = ( error > errnorms[3] ? error : errnorms[3] );
2780  sumarea += ovarea;
2781  }
2782  errnorms[2] = sumarea;
2783 #ifdef MOAB_HAVE_MPI
2784  if( m_pcomm )
2785  {
2786  MPI_Reduce( &errnorms[0], &globerrnorms[0], 3, MPI_DOUBLE, MPI_SUM, 0, m_pcomm->comm() );
2787  MPI_Reduce( &errnorms[3], &globerrnorms[3], 1, MPI_DOUBLE, MPI_MAX, 0, m_pcomm->comm() );
2788  }
2789 #else
2790  for( int i = 0; i < 4; ++i )
2791  globerrnorms[i] = errnorms[i];
2792 #endif
2793 
2794  globerrnorms[0] = ( globerrnorms[0] / globerrnorms[2] );
2795  globerrnorms[1] = std::sqrt( globerrnorms[1] / globerrnorms[2] );
2796 
2797  metrics.clear();
2798  metrics["L1Error"] = globerrnorms[0];
2799  metrics["L2Error"] = globerrnorms[1];
2800  metrics["LinfError"] = globerrnorms[3];
2801 
2802  if( verbose && is_root )
2803  {
2804  std::cout << "Error metrics when comparing " << projTagName << " against " << exactTagName << std::endl;
2805  std::cout << "\t Total Intersection area = " << globerrnorms[2] << std::endl;
2806  std::cout << "\t L_1 error = " << globerrnorms[0] << std::endl;
2807  std::cout << "\t L_2 error = " << globerrnorms[1] << std::endl;
2808  std::cout << "\t L_inf error = " << globerrnorms[3] << std::endl;
2809  }
2810 
2811  return moab::MB_SUCCESS;
2812 }

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 450 of file TempestOnlineMap.cpp.

451 {
452  first_absent_gid = -1;
453  const int ncols = m_nTotDofs_SrcCov;
454  std::vector< bool > delivered;
455  compute_delivered_columns( ncols, col_dtoc_dofmap, delivered );
456  int cnt = 0;
457  for( int mc = 0; mc < ncols; mc++ )
458  {
459  if( !delivered[mc] )
460  {
461  cnt++;
462  if( first_absent_gid < 0 && mc < (int)col_gdofmap.size() )
463  first_absent_gid = (int)col_gdofmap[mc] + 1; // col_gdofmap is 0-based
464  }
465  }
466  return cnt;
467 }

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 2356 of file TempestOnlineMap.cpp.

2362 {
2363  const bool outputEnabled = ( is_root );
2364  int discOrder;
2365  DiscretizationType discMethod;
2366  // moab::EntityHandle meshset;
2367  moab::Range entities;
2368  Mesh* trmesh;
2369  switch( ctx )
2370  {
2371  case Remapper::SourceMesh:
2372  // meshset = m_remapper->m_covering_source_set;
2373  trmesh = m_remapper->m_covering_source;
2376  discOrder = m_nDofsPEl_Src;
2377  discMethod = m_eInputType;
2378  break;
2379 
2380  case Remapper::TargetMesh:
2381  // meshset = m_remapper->m_target_set;
2382  trmesh = m_remapper->m_target;
2383  entities =
2385  discOrder = m_nDofsPEl_Dest;
2386  discMethod = m_eOutputType;
2387  break;
2388 
2389  default:
2390  if( outputEnabled )
2391  std::cout << "Invalid context specified for defining an analytical solution tag" << std::endl;
2392  return moab::MB_FAILURE;
2393  }
2394 
2395  // Let us create teh solution tag with appropriate information for name, discretization order
2396  // (DoF space)
2397  MB_CHK_ERR( m_interface->tag_get_handle( solnName.c_str(), discOrder * discOrder, MB_TYPE_DOUBLE, solnTag,
2398  MB_TAG_DENSE | MB_TAG_CREAT ) );
2399  if( clonedSolnTag != nullptr )
2400  {
2401  if( cloneSolnName.size() == 0 )
2402  {
2403  cloneSolnName = solnName + std::string( "Cloned" );
2404  }
2405  MB_CHK_ERR( m_interface->tag_get_handle( cloneSolnName.c_str(), discOrder * discOrder, MB_TYPE_DOUBLE,
2406  *clonedSolnTag, MB_TAG_DENSE | MB_TAG_CREAT ) );
2407  }
2408 
2409  // Triangular quadrature rule
2410  const int TriQuadratureOrder = 10;
2411 
2412  if( outputEnabled ) std::cout << "Using triangular quadrature of order " << TriQuadratureOrder << std::endl;
2413 
2414  TriangularQuadratureRule triquadrule( TriQuadratureOrder );
2415 
2416  const int TriQuadraturePoints = triquadrule.GetPoints();
2417 
2418  const DataArray2D< double >& TriQuadratureG = triquadrule.GetG();
2419  const DataArray1D< double >& TriQuadratureW = triquadrule.GetW();
2420 
2421  // Output data
2422  DataArray1D< double > dVar;
2423  DataArray1D< double > dVarMB; // re-arranged local MOAB vector
2424 
2425  // Nodal geometric area
2426  DataArray1D< double > dNodeArea;
2427 
2428  // Calculate element areas
2429  // trmesh->CalculateFaceAreas(fContainsConcaveFaces);
2430 
2431  if( discMethod == DiscretizationType_CGLL || discMethod == DiscretizationType_DGLL )
2432  {
2433  /* Get the spectral points and sample the functionals accordingly */
2434  const bool fGLL = true;
2435  const bool fGLLIntegrate = false;
2436 
2437  // Generate grid metadata
2438  DataArray3D< int > dataGLLNodes;
2439  DataArray3D< double > dataGLLJacobian;
2440 
2441  GenerateMetaData( *trmesh, discOrder, false, dataGLLNodes, dataGLLJacobian );
2442 
2443  // Number of elements
2444  int nElements = trmesh->faces.size();
2445 
2446  // Verify all elements are quadrilaterals
2447  for( int k = 0; k < nElements; k++ )
2448  {
2449  const Face& face = trmesh->faces[k];
2450 
2451  if( face.edges.size() != 4 )
2452  {
2453  _EXCEPTIONT( "Non-quadrilateral face detected; "
2454  "incompatible with --gll" );
2455  }
2456  }
2457 
2458  // Number of unique nodes
2459  int iMaxNode = 0;
2460  for( int i = 0; i < discOrder; i++ )
2461  {
2462  for( int j = 0; j < discOrder; j++ )
2463  {
2464  for( int k = 0; k < nElements; k++ )
2465  {
2466  if( dataGLLNodes[i][j][k] > iMaxNode )
2467  {
2468  iMaxNode = dataGLLNodes[i][j][k];
2469  }
2470  }
2471  }
2472  }
2473 
2474  // Get Gauss-Lobatto quadrature nodes
2475  DataArray1D< double > dG;
2476  DataArray1D< double > dW;
2477 
2478  GaussLobattoQuadrature::GetPoints( discOrder, 0.0, 1.0, dG, dW );
2479 
2480  // Get Gauss quadrature nodes
2481  const int nGaussP = 10;
2482 
2483  DataArray1D< double > dGaussG;
2484  DataArray1D< double > dGaussW;
2485 
2486  GaussQuadrature::GetPoints( nGaussP, 0.0, 1.0, dGaussG, dGaussW );
2487 
2488  // Allocate data
2489  dVar.Allocate( iMaxNode );
2490  dVarMB.Allocate( discOrder * discOrder * nElements );
2491  dNodeArea.Allocate( iMaxNode );
2492 
2493  // Sample data
2494  for( int k = 0; k < nElements; k++ )
2495  {
2496  const Face& face = trmesh->faces[k];
2497 
2498  // Sample data at GLL nodes
2499  if( fGLL )
2500  {
2501  for( int i = 0; i < discOrder; i++ )
2502  {
2503  for( int j = 0; j < discOrder; j++ )
2504  {
2505 
2506  // Apply local map
2507  Node node;
2508  Node dDx1G;
2509  Node dDx2G;
2510 
2511  ApplyLocalMap( face, trmesh->nodes, dG[i], dG[j], node, dDx1G, dDx2G );
2512 
2513  // Sample data at this point
2514  double dNodeLon = atan2( node.y, node.x );
2515  if( dNodeLon < 0.0 )
2516  {
2517  dNodeLon += 2.0 * M_PI;
2518  }
2519  double dNodeLat = asin( node.z );
2520 
2521  double dSample = ( *testFunction )( dNodeLon, dNodeLat );
2522 
2523  dVar[dataGLLNodes[j][i][k] - 1] = dSample;
2524  }
2525  }
2526  // High-order Gaussian integration over basis function
2527  }
2528  else
2529  {
2530  DataArray2D< double > dCoeff( discOrder, discOrder );
2531 
2532  for( int p = 0; p < nGaussP; p++ )
2533  {
2534  for( int q = 0; q < nGaussP; q++ )
2535  {
2536 
2537  // Apply local map
2538  Node node;
2539  Node dDx1G;
2540  Node dDx2G;
2541 
2542  ApplyLocalMap( face, trmesh->nodes, dGaussG[p], dGaussG[q], node, dDx1G, dDx2G );
2543 
2544  // Cross product gives local Jacobian
2545  Node nodeCross = CrossProduct( dDx1G, dDx2G );
2546 
2547  double dJacobian =
2548  sqrt( nodeCross.x * nodeCross.x + nodeCross.y * nodeCross.y + nodeCross.z * nodeCross.z );
2549 
2550  // Find components of quadrature point in basis
2551  // of the first Face
2552  SampleGLLFiniteElement( 0, discOrder, dGaussG[p], dGaussG[q], dCoeff );
2553 
2554  // Sample data at this point
2555  double dNodeLon = atan2( node.y, node.x );
2556  if( dNodeLon < 0.0 )
2557  {
2558  dNodeLon += 2.0 * M_PI;
2559  }
2560  double dNodeLat = asin( node.z );
2561 
2562  double dSample = ( *testFunction )( dNodeLon, dNodeLat );
2563 
2564  // Integrate
2565  for( int i = 0; i < discOrder; i++ )
2566  {
2567  for( int j = 0; j < discOrder; j++ )
2568  {
2569 
2570  double dNodalArea = dCoeff[i][j] * dGaussW[p] * dGaussW[q] * dJacobian;
2571 
2572  dVar[dataGLLNodes[i][j][k] - 1] += dSample * dNodalArea;
2573 
2574  dNodeArea[dataGLLNodes[i][j][k] - 1] += dNodalArea;
2575  }
2576  }
2577  }
2578  }
2579  }
2580  }
2581 
2582  // Divide by area
2583  if( fGLLIntegrate )
2584  {
2585  for( size_t i = 0; i < dVar.GetRows(); i++ )
2586  {
2587  dVar[i] /= dNodeArea[i];
2588  }
2589  }
2590 
2591  // Let us rearrange the data based on DoF ID specification
2592  if( ctx == Remapper::SourceMesh )
2593  {
2594  for( unsigned j = 0; j < entities.size(); j++ )
2595  for( int p = 0; p < discOrder; p++ )
2596  for( int q = 0; q < discOrder; q++ )
2597  {
2598  const int offsetDOF = j * discOrder * discOrder + p * discOrder + q;
2599  dVarMB[offsetDOF] = dVar[col_dtoc_dofmap[offsetDOF]];
2600  }
2601  }
2602  else
2603  {
2604  for( unsigned j = 0; j < entities.size(); j++ )
2605  for( int p = 0; p < discOrder; p++ )
2606  for( int q = 0; q < discOrder; q++ )
2607  {
2608  const int offsetDOF = j * discOrder * discOrder + p * discOrder + q;
2609  dVarMB[offsetDOF] = dVar[row_dtoc_dofmap[offsetDOF]];
2610  }
2611  }
2612 
2613  // Set the tag data
2614  MB_CHK_ERR( m_interface->tag_set_data( solnTag, entities, &dVarMB[0] ) );
2615  }
2616  else
2617  {
2618  // assert( discOrder == 1 );
2619  if( discMethod == DiscretizationType_FV )
2620  {
2621  /* Compute an element-wise integral to store the sampled solution based on Quadrature
2622  * rules */
2623  // Resize the array
2624  dVar.Allocate( trmesh->faces.size() );
2625 
2626  std::vector< Node >& nodes = trmesh->nodes;
2627 
2628  // Loop through all Faces
2629  for( size_t i = 0; i < trmesh->faces.size(); i++ )
2630  {
2631  const Face& face = trmesh->faces[i];
2632 
2633  // Loop through all sub-triangles
2634  for( size_t j = 0; j < face.edges.size() - 2; j++ )
2635  {
2636 
2637  const Node& node0 = nodes[face[0]];
2638  const Node& node1 = nodes[face[j + 1]];
2639  const Node& node2 = nodes[face[j + 2]];
2640 
2641  // Triangle area
2642  Face faceTri( 3 );
2643  faceTri.SetNode( 0, face[0] );
2644  faceTri.SetNode( 1, face[j + 1] );
2645  faceTri.SetNode( 2, face[j + 2] );
2646 
2647  double dTriangleArea = CalculateFaceArea( faceTri, nodes );
2648 
2649  // Calculate the element average
2650  double dTotalSample = 0.0;
2651 
2652  // Loop through all quadrature points
2653  for( int k = 0; k < TriQuadraturePoints; k++ )
2654  {
2655  Node node( TriQuadratureG[k][0] * node0.x + TriQuadratureG[k][1] * node1.x +
2656  TriQuadratureG[k][2] * node2.x,
2657  TriQuadratureG[k][0] * node0.y + TriQuadratureG[k][1] * node1.y +
2658  TriQuadratureG[k][2] * node2.y,
2659  TriQuadratureG[k][0] * node0.z + TriQuadratureG[k][1] * node1.z +
2660  TriQuadratureG[k][2] * node2.z );
2661 
2662  double dMagnitude = node.Magnitude();
2663  node.x /= dMagnitude;
2664  node.y /= dMagnitude;
2665  node.z /= dMagnitude;
2666 
2667  double dLon = atan2( node.y, node.x );
2668  if( dLon < 0.0 )
2669  {
2670  dLon += 2.0 * M_PI;
2671  }
2672  double dLat = asin( node.z );
2673 
2674  double dSample = ( *testFunction )( dLon, dLat );
2675 
2676  dTotalSample += dSample * TriQuadratureW[k] * dTriangleArea;
2677  }
2678 
2679  dVar[i] += dTotalSample / trmesh->vecFaceArea[i];
2680  }
2681  }
2682  MB_CHK_ERR( m_interface->tag_set_data( solnTag, entities, &dVar[0] ) );
2683  }
2684  else /* discMethod == DiscretizationType_PCLOUD */
2685  {
2686  /* Get the coordinates of the vertices and sample the functionals accordingly */
2687  std::vector< Node >& nodes = trmesh->nodes;
2688 
2689  // Resize the array
2690  dVar.Allocate( nodes.size() );
2691 
2692  for( size_t j = 0; j < nodes.size(); j++ )
2693  {
2694  Node& node = nodes[j];
2695  double dMagnitude = node.Magnitude();
2696  node.x /= dMagnitude;
2697  node.y /= dMagnitude;
2698  node.z /= dMagnitude;
2699  double dLon = atan2( node.y, node.x );
2700  if( dLon < 0.0 )
2701  {
2702  dLon += 2.0 * M_PI;
2703  }
2704  double dLat = asin( node.z );
2705 
2706  double dSample = ( *testFunction )( dLon, dLat );
2707  dVar[j] = dSample;
2708  }
2709 
2710  MB_CHK_ERR( m_interface->tag_set_data( solnTag, entities, &dVar[0] ) );
2711  }
2712  }
2713 
2714  return moab::MB_SUCCESS;
2715 }

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 469 of file TempestOnlineMap.cpp.

470 {
471  const int ncols = m_nTotDofs_SrcCov;
472  std::vector< bool > delivered;
473  compute_delivered_columns( ncols, col_dtoc_dofmap, delivered );
474 
475  // Zero every stored coefficient whose column was not supplied by coverage.
476  // The projection already treats these columns as zero-source (ApplyWeights
477  // leaves m_colVector at 0 for them), so this changes no projected value; it
478  // only lets the dual-map CAAS bounds loop skip them via its |w|<1e-50 test.
479  int dropped = 0;
480  for( int r = 0; r < m_weightMatrix.outerSize(); r++ )
481  {
482  for( WeightMatrix::InnerIterator it( m_weightMatrix, r ); it; ++it )
483  {
484  const int mc = (int)it.col();
485  if( mc < 0 || mc >= ncols || !delivered[mc] )
486  {
487  if( it.value() != 0.0 ) dropped++;
488  it.valueRef() = 0.0;
489  }
490  }
491  }
492  // Remove the explicit zeros so iterators no longer visit them.
493  m_weightMatrix.prune( []( const Eigen::Index&, const Eigen::Index&, const double& v ) { return v != 0.0; } );
494  return dropped;
495 }

References compute_delivered_columns().

◆ fill_col_ids()

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

Definition at line 461 of file TempestOnlineMap.hpp.

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

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 498 of file TempestOnlineMap.cpp.

503 {
504  NcError error( NcError::silent_nonfatal );
505 
506  moab::DebugOutput dbgprint( std::cout, rank, 0 );
507  dbgprint.set_prefix( "[TempestOnlineMap]: " );
508  moab::ErrorCode rval;
509 
510  const bool m_bPointCloudSource = ( m_remapper->point_cloud_source );
511  const bool m_bPointCloudTarget = ( m_remapper->point_cloud_target );
512  const bool m_bPointCloud = m_bPointCloudSource || m_bPointCloudTarget;
513 
514  // Build a matrix of source and target discretization so that we know how
515  // to assign the global DoFs in parallel for the mapping weights.
516  // For example,
517  // for FV->FV: the rows represented target DoFs and cols represent source DoFs
518  try
519  {
520  // Check command line parameters (data type arguments)
521  STLStringHelper::ToLower( strInputType );
522  STLStringHelper::ToLower( strOutputType );
523 
524  DiscretizationType eInputType;
525  DiscretizationType eOutputType;
526 
527  if( strInputType == "fv" )
528  {
529  eInputType = DiscretizationType_FV;
530  }
531  else if( strInputType == "cgll" )
532  {
533  eInputType = DiscretizationType_CGLL;
534  }
535  else if( strInputType == "dgll" )
536  {
537  eInputType = DiscretizationType_DGLL;
538  }
539  else if( strInputType == "pcloud" )
540  {
541  eInputType = DiscretizationType_PCLOUD;
542  }
543  else
544  {
545  _EXCEPTION1( "Invalid \"in_type\" value (%s), expected [fv|cgll|dgll]", strInputType.c_str() );
546  }
547 
548  if( strOutputType == "fv" )
549  {
550  eOutputType = DiscretizationType_FV;
551  }
552  else if( strOutputType == "cgll" )
553  {
554  eOutputType = DiscretizationType_CGLL;
555  }
556  else if( strOutputType == "dgll" )
557  {
558  eOutputType = DiscretizationType_DGLL;
559  }
560  else if( strOutputType == "pcloud" )
561  {
562  eOutputType = DiscretizationType_PCLOUD;
563  }
564  else
565  {
566  _EXCEPTION1( "Invalid \"out_type\" value (%s), expected [fv|cgll|dgll]", strOutputType.c_str() );
567  }
568 
569  // set all required input params
570  m_bConserved = !mapOptions.fNoConservation;
571  m_eInputType = eInputType;
572  m_eOutputType = eOutputType;
573 
574  // Method flags
575  std::string strMapAlgorithm( "" );
576  int nMonotoneType = ( mapOptions.fMonotone ) ? ( 1 ) : ( 0 );
577 
578  // Make an index of method arguments
579  std::set< std::string > setMethodStrings;
580  {
581  int iLast = 0;
582  for( size_t i = 0; i <= mapOptions.strMethod.length(); i++ )
583  {
584  if( ( i == mapOptions.strMethod.length() ) || ( mapOptions.strMethod[i] == ';' ) )
585  {
586  std::string strMethodString = mapOptions.strMethod.substr( iLast, i - iLast );
587  STLStringHelper::RemoveWhitespaceInPlace( strMethodString );
588  if( strMethodString.length() > 0 )
589  {
590  setMethodStrings.insert( strMethodString );
591  }
592  iLast = i + 1;
593  }
594  }
595  }
596 
597  for( auto it : setMethodStrings )
598  {
599  // Piecewise constant monotonicity
600  if( it == "mono2" )
601  {
602  if( nMonotoneType != 0 )
603  {
604  _EXCEPTIONT( "Multiple monotonicity specifications found (--mono) or (--method \"mono#\")" );
605  }
607  {
608  _EXCEPTIONT( "--method \"mono2\" is only used when remapping to/from CGLL or DGLL grids" );
609  }
610  nMonotoneType = 2;
611 
612  // Piecewise linear monotonicity
613  }
614  else if( it == "mono3" )
615  {
616  if( nMonotoneType != 0 )
617  {
618  _EXCEPTIONT( "Multiple monotonicity specifications found (--mono) or (--method \"mono#\")" );
619  }
621  {
622  _EXCEPTIONT( "--method \"mono3\" is only used when remapping to/from CGLL or DGLL grids" );
623  }
624  nMonotoneType = 3;
625 
626  // Volumetric remapping from FV to GLL
627  }
628  else if( it == "volumetric" )
629  {
631  {
632  _EXCEPTIONT( "--method \"volumetric\" may only be used for FV->CGLL or FV->DGLL remapping" );
633  }
634  strMapAlgorithm = "volumetric";
635 
636  // Inverse distance mapping
637  }
638  else if( it == "invdist" )
639  {
641  {
642  _EXCEPTIONT( "--method \"invdist\" may only be used for FV->FV remapping" );
643  }
644  strMapAlgorithm = "invdist";
645 
646  // Delaunay triangulation mapping
647  }
648  else if( it == "delaunay" )
649  {
651  {
652  _EXCEPTIONT( "--method \"delaunay\" may only be used for FV->FV remapping" );
653  }
654  strMapAlgorithm = "delaunay";
655 
656  // Bilinear
657  }
658  else if( it == "bilin" )
659  {
661  {
662  _EXCEPTIONT( "--method \"bilin\" may only be used for FV->FV remapping" );
663  }
664  strMapAlgorithm = "fvbilin";
665 
666  // Integrated bilinear (same as mono3 when source grid is CGLL/DGLL)
667  }
668  else if( it == "intbilin" )
669  {
671  {
672  _EXCEPTIONT( "--method \"intbilin\" may only be used when mapping to FV." );
673  }
675  {
676  strMapAlgorithm = "fvintbilin";
677  }
678  else
679  {
680  strMapAlgorithm = "mono3";
681  }
682 
683  // Integrated bilinear with generalized Barycentric coordinates
684  }
685  else if( it == "intbilingb" )
686  {
688  {
689  _EXCEPTIONT( "--method \"intbilingb\" may only be used for FV->FV remapping" );
690  }
691  strMapAlgorithm = "fvintbilingb";
692  }
693  else
694  {
695  _EXCEPTION1( "Invalid --method argument \"%s\"", it.c_str() );
696  }
697  }
698 
701  : mapOptions.nPin );
704  : mapOptions.nPout );
705 
706  // Set the source and target mesh objects
707  MB_CHK_ERR( SetDOFmapTags( srcDofTagName, tgtDofTagName ) );
708 
709  /// the tag should be created already in the e3sm workflow; if not, create it here
710  Tag areaTag;
711  rval = m_interface->tag_get_handle( "aream", 1, MB_TYPE_DOUBLE, areaTag,
713  if( MB_ALREADY_ALLOCATED == rval )
714  {
715  if( is_root ) dbgprint.printf( 0, "aream tag already defined \n" );
716  }
717 
718  double local_areas[3] = { 0.0, 0.0, 0.0 }, global_areas[3] = { 0.0, 0.0, 0.0 };
719  if( !m_bPointCloudSource )
720  {
721  // Calculate Input Mesh Face areas
722  if( is_root ) dbgprint.printf( 0, "Calculating input mesh Face areas\n" );
723  local_areas[0] = m_meshInput->CalculateFaceAreas( mapOptions.fSourceConcave );
724  // Set source element areas as tag on the source mesh
726 
727  // Update coverage source mesh areas as well.
728  m_meshInputCov->CalculateFaceAreas( mapOptions.fSourceConcave );
729  }
730 
731  if( !m_bPointCloudTarget )
732  {
733  // Calculate Output Mesh Face areas
734  if( is_root ) dbgprint.printf( 0, "Calculating output mesh Face areas\n" );
735  local_areas[1] = m_meshOutput->CalculateFaceAreas( mapOptions.fTargetConcave );
736  // Set target element areas as tag on the target mesh
737  MB_CHK_ERR(
738  m_interface->tag_set_data( areaTag, m_remapper->m_target_entities, m_meshOutput->vecFaceArea ) );
739  }
740 
741  if( !m_bPointCloud )
742  {
743  // Verify that overlap mesh is in the correct order (sanity check)
744  assert( m_meshOverlap->vecSourceFaceIx.size() == m_meshOverlap->vecTargetFaceIx.size() );
745 
746  // Calculate Face areas
747  if( is_root ) dbgprint.printf( 0, "Calculating overlap mesh Face areas\n" );
748  local_areas[2] =
749  m_meshOverlap->CalculateFaceAreas( mapOptions.fSourceConcave || mapOptions.fTargetConcave );
750 
751  // store it as global output for now - used later in reduction
752  std::copy( local_areas, local_areas + 3, global_areas );
753 #ifdef MOAB_HAVE_MPI
754  // reduce the local source, target and overlap mesh areas to global areas
755  if( m_pcomm && is_parallel )
756  MPI_Reduce( local_areas, global_areas, 3, MPI_DOUBLE, MPI_SUM, 0, m_pcomm->comm() );
757 #endif
758  if( is_root )
759  {
760  dbgprint.printf( 0, "Input Mesh Geometric Area: %1.15e\n", global_areas[0] );
761  dbgprint.printf( 0, "Output Mesh Geometric Area: %1.15e\n", global_areas[1] );
762  dbgprint.printf( 0, "Overlap Mesh Recovered Area: %1.15e\n", global_areas[2] );
763  }
764 
765  // Correct areas to match the areas calculated in the overlap mesh
766  constexpr bool fCorrectAreas = true;
767  if( fCorrectAreas ) // In MOAB-TempestRemap, we will always keep this to be true
768  {
769  if( is_root ) dbgprint.printf( 0, "Correcting source/target areas to overlap mesh areas\n" );
770  DataArray1D< double > dSourceArea( m_meshInputCov->faces.size() );
771  DataArray1D< double > dTargetArea( m_meshOutput->faces.size() );
772 
773  assert( m_meshOverlap->vecSourceFaceIx.size() == m_meshOverlap->faces.size() );
774  assert( m_meshOverlap->vecTargetFaceIx.size() == m_meshOverlap->faces.size() );
775  assert( m_meshOverlap->vecFaceArea.GetRows() == m_meshOverlap->faces.size() );
776 
777  assert( m_meshInputCov->vecFaceArea.GetRows() == m_meshInputCov->faces.size() );
778  assert( m_meshOutput->vecFaceArea.GetRows() == m_meshOutput->faces.size() );
779 
780  for( size_t i = 0; i < m_meshOverlap->faces.size(); i++ )
781  {
782  if( m_meshOverlap->vecSourceFaceIx[i] < 0 || m_meshOverlap->vecTargetFaceIx[i] < 0 )
783  continue; // skip this cell since it is ghosted
784 
785  // let us recompute the source/target areas based on overlap mesh areas
786  assert( static_cast< size_t >( m_meshOverlap->vecSourceFaceIx[i] ) < m_meshInputCov->faces.size() );
787  dSourceArea[m_meshOverlap->vecSourceFaceIx[i]] += m_meshOverlap->vecFaceArea[i];
788  assert( static_cast< size_t >( m_meshOverlap->vecTargetFaceIx[i] ) < m_meshOutput->faces.size() );
789  dTargetArea[m_meshOverlap->vecTargetFaceIx[i]] += m_meshOverlap->vecFaceArea[i];
790  }
791 
792  for( size_t i = 0; i < m_meshInputCov->faces.size(); i++ )
793  {
794  if( fabs( dSourceArea[i] - m_meshInputCov->vecFaceArea[i] ) < 1.0e-10 )
795  {
796  m_meshInputCov->vecFaceArea[i] = dSourceArea[i];
797  }
798  }
799  for( size_t i = 0; i < m_meshOutput->faces.size(); i++ )
800  {
801  if( fabs( dTargetArea[i] - m_meshOutput->vecFaceArea[i] ) < 1.0e-10 )
802  {
803  m_meshOutput->vecFaceArea[i] = dTargetArea[i];
804  }
805  }
806  }
807 
808  // Set source mesh areas in map
809  if( !m_bPointCloudSource && eInputType == DiscretizationType_FV )
810  {
811  this->SetSourceAreas( m_meshInputCov->vecFaceArea );
812  if( m_meshInputCov->vecMask.size() )
813  {
814  this->SetSourceMask( m_meshInputCov->vecMask );
815  }
816  }
817 
818  // Set target mesh areas in map
819  if( !m_bPointCloudTarget && eOutputType == DiscretizationType_FV )
820  {
821  this->SetTargetAreas( m_meshOutput->vecFaceArea );
822  if( m_meshOutput->vecMask.size() )
823  {
824  this->SetTargetMask( m_meshOutput->vecMask );
825  }
826  }
827 
828  /*
829  // Recalculate input mesh area from overlap mesh
830  if (fabs(dTotalAreaOverlap - dTotalAreaInput) > 1.0e-10) {
831  dbgprint.printf(0, "Overlap mesh only covers a sub-area of the sphere\n");
832  dbgprint.printf(0, "Recalculating source mesh areas\n");
833  dTotalAreaInput = m_meshInput->CalculateFaceAreasFromOverlap(m_meshOverlap);
834  dbgprint.printf(0, "New Input Mesh Geometric Area: %1.15e\n", dTotalAreaInput);
835  }
836  */
837  }
838 
839  // Finite volume input / Finite volume output
840  if( ( eInputType == DiscretizationType_FV ) && ( eOutputType == DiscretizationType_FV ) )
841  {
842  // Generate reverse node array and edge map
843  if( m_meshInputCov->revnodearray.size() == 0 ) m_meshInputCov->ConstructReverseNodeArray();
844  if( m_meshInputCov->edgemap.size() == 0 ) m_meshInputCov->ConstructEdgeMap( false );
845 
846  // Initialize coordinates for map
847  this->InitializeSourceCoordinatesFromMeshFV( *m_meshInputCov );
848  this->InitializeTargetCoordinatesFromMeshFV( *m_meshOutput );
849 
850  this->m_pdataGLLNodesIn = nullptr;
851  this->m_pdataGLLNodesOut = nullptr;
852 
853  // Finite volume input / Finite element output
854  MB_CHK_ERR( this->SetDOFmapAssociation( eInputType, mapOptions.nPin, false, nullptr, nullptr, eOutputType,
855  mapOptions.nPout, false, nullptr ) );
856 
857  // Construct remap for FV-FV
858  if( is_root ) dbgprint.printf( 0, "Calculating remap weights\n" );
859 
860  // Construct OfflineMap
861  if( strMapAlgorithm == "invdist" )
862  {
863  if( is_root ) dbgprint.printf( 0, "Calculating map (invdist)\n" );
864  if( m_meshInputCov->faces.size() )
865  LinearRemapFVtoFVInvDist( *m_meshInputCov, *m_meshOutput, *m_meshOverlap, *this );
866  }
867  else if( strMapAlgorithm == "delaunay" )
868  {
869  if( is_root ) dbgprint.printf( 0, "Calculating map (delaunay)\n" );
870  if( m_meshInputCov->faces.size() )
871  LinearRemapTriangulation( *m_meshInputCov, *m_meshOutput, *m_meshOverlap, *this );
872  }
873  else if( strMapAlgorithm == "fvintbilin" )
874  {
875  if( is_root ) dbgprint.printf( 0, "Calculating map (intbilin)\n" );
876  if( m_meshInputCov->faces.size() )
877  LinearRemapIntegratedBilinear( *m_meshInputCov, *m_meshOutput, *m_meshOverlap, *this );
878  }
879  else if( strMapAlgorithm == "fvintbilingb" )
880  {
881  if( is_root ) dbgprint.printf( 0, "Calculating map (intbilingb)\n" );
882  if( m_meshInputCov->faces.size() )
883  LinearRemapIntegratedGeneralizedBarycentric( *m_meshInputCov, *m_meshOutput, *m_meshOverlap,
884  *this );
885  }
886  else if( strMapAlgorithm == "fvbilin" )
887  {
888 #ifdef VERBOSE
889  if( is_root )
890  {
891  m_meshInputCov->Write( "SourceMeshMBTR.g" );
892  m_meshOutput->Write( "TargetMeshMBTR.g" );
893  }
894  else
895  {
896  m_meshInputCov->Write( "SourceMeshMBTR" + std::to_string( rank ) + ".g" );
897  m_meshOutput->Write( "TargetMeshMBTR" + std::to_string( rank ) + ".g" );
898  }
899 #endif
900  if( is_root ) dbgprint.printf( 0, "Calculating map (bilin)\n" );
901  if( m_meshInputCov->faces.size() )
902  LinearRemapBilinear( *m_meshInputCov, *m_meshOutput, *m_meshOverlap, *this );
903  }
904  else
905  {
906  if( is_root ) dbgprint.printf( 0, "Calculating conservative FV-FV map\n" );
907  if( m_meshInputCov->faces.size() )
908  {
909 #ifdef USE_NATIVE_TEMPESTREMAP_ROUTINES
910  LinearRemapFVtoFV( *m_meshInputCov, *m_meshOutput, *m_meshOverlap,
911  ( mapOptions.fMonotone ) ? ( 1 ) : ( mapOptions.nPin ), *this );
912 #else
913  LinearRemapFVtoFV_Tempest_MOAB( ( mapOptions.fMonotone ? 1 : mapOptions.nPin ) );
914 #endif
915  }
916  }
917  }
918  else if( eInputType == DiscretizationType_FV )
919  {
920  DataArray3D< double > dataGLLJacobian;
921 
922  if( is_root ) dbgprint.printf( 0, "Generating output mesh meta data\n" );
923  double dNumericalArea_loc = GenerateMetaData( *m_meshOutput, mapOptions.nPout, mapOptions.fNoBubble,
924  dataGLLNodesDest, dataGLLJacobian );
925 
926  double dNumericalArea = dNumericalArea_loc;
927 #ifdef MOAB_HAVE_MPI
928  if( m_pcomm )
929  MPI_Reduce( &dNumericalArea_loc, &dNumericalArea, 1, MPI_DOUBLE, MPI_SUM, 0, m_pcomm->comm() );
930 #endif
931  if( is_root ) dbgprint.printf( 0, "Output Mesh Numerical Area: %1.15e\n", dNumericalArea );
932 
933  // Initialize coordinates for map
934  this->InitializeSourceCoordinatesFromMeshFV( *m_meshInputCov );
935  this->InitializeTargetCoordinatesFromMeshFE( *m_meshOutput, mapOptions.nPout, dataGLLNodesDest );
936 
937  this->m_pdataGLLNodesIn = nullptr;
938  this->m_pdataGLLNodesOut = &dataGLLNodesDest;
939 
940  // Generate the continuous Jacobian
941  bool fContinuous = ( eOutputType == DiscretizationType_CGLL );
942 
943  if( eOutputType == DiscretizationType_CGLL )
944  {
945  GenerateUniqueJacobian( dataGLLNodesDest, dataGLLJacobian, this->GetTargetAreas() );
946  }
947  else
948  {
949  GenerateDiscontinuousJacobian( dataGLLJacobian, this->GetTargetAreas() );
950  }
951 
952  // Generate reverse node array and edge map
953  if( m_meshInputCov->revnodearray.size() == 0 ) m_meshInputCov->ConstructReverseNodeArray();
954  if( m_meshInputCov->edgemap.size() == 0 ) m_meshInputCov->ConstructEdgeMap( false );
955 
956  // Finite volume input / Finite element output
957  MB_CHK_ERR( this->SetDOFmapAssociation( eInputType, mapOptions.nPin, false, nullptr, nullptr, eOutputType,
958  mapOptions.nPout, ( eOutputType == DiscretizationType_CGLL ),
959  &dataGLLNodesDest ) );
960 
961  // Generate remap weights
962  if( strMapAlgorithm == "volumetric" )
963  {
964  if( is_root ) dbgprint.printf( 0, "Calculating remapping weights for FV->GLL (volumetric)\n" );
965  LinearRemapFVtoGLL_Volumetric( *m_meshInputCov, *m_meshOutput, *m_meshOverlap, dataGLLNodesDest,
966  dataGLLJacobian, this->GetTargetAreas(), mapOptions.nPin, *this,
967  nMonotoneType, fContinuous, mapOptions.fNoConservation );
968  }
969  else
970  {
971  if( is_root ) dbgprint.printf( 0, "Calculating remapping weights for FV->GLL\n" );
972  LinearRemapFVtoGLL( *m_meshInputCov, *m_meshOutput, *m_meshOverlap, dataGLLNodesDest, dataGLLJacobian,
973  this->GetTargetAreas(), mapOptions.nPin, *this, nMonotoneType, fContinuous,
974  mapOptions.fNoConservation );
975  }
976  }
977  else if( ( eInputType == DiscretizationType_PCLOUD ) || ( eOutputType == DiscretizationType_PCLOUD ) )
978  {
979  DataArray3D< double > dataGLLJacobian;
980  if( !m_bPointCloudSource )
981  {
982  // Generate reverse node array and edge map
983  if( m_meshInputCov->revnodearray.size() == 0 ) m_meshInputCov->ConstructReverseNodeArray();
984  if( m_meshInputCov->edgemap.size() == 0 ) m_meshInputCov->ConstructEdgeMap( false );
985 
986  // Initialize coordinates for map
987  if( eInputType == DiscretizationType_FV )
988  {
989  this->InitializeSourceCoordinatesFromMeshFV( *m_meshInputCov );
990  }
991  else
992  {
993  if( is_root ) dbgprint.printf( 0, "Generating input mesh meta data\n" );
994  DataArray3D< double > dataGLLJacobianSrc;
995  GenerateMetaData( *m_meshInputCov, mapOptions.nPin, mapOptions.fNoBubble, dataGLLNodesSrcCov,
996  dataGLLJacobian );
997  GenerateMetaData( *m_meshInput, mapOptions.nPin, mapOptions.fNoBubble, dataGLLNodesSrc,
998  dataGLLJacobianSrc );
999  }
1000  }
1001  // else { /* Source is a point cloud dataset */ }
1002 
1003  if( !m_bPointCloudTarget )
1004  {
1005  // Generate reverse node array and edge map
1006  if( m_meshOutput->revnodearray.size() == 0 ) m_meshOutput->ConstructReverseNodeArray();
1007  if( m_meshOutput->edgemap.size() == 0 ) m_meshOutput->ConstructEdgeMap( false );
1008 
1009  // Initialize coordinates for map
1010  if( eOutputType == DiscretizationType_FV )
1011  {
1012  this->InitializeSourceCoordinatesFromMeshFV( *m_meshOutput );
1013  }
1014  else
1015  {
1016  if( is_root ) dbgprint.printf( 0, "Generating output mesh meta data\n" );
1017  GenerateMetaData( *m_meshOutput, mapOptions.nPout, mapOptions.fNoBubble, dataGLLNodesDest,
1018  dataGLLJacobian );
1019  }
1020  }
1021  // else { /* Target is a point cloud dataset */ }
1022 
1023  // Finite volume input / Finite element output
1025  eInputType, mapOptions.nPin, ( eInputType == DiscretizationType_CGLL ),
1026  ( m_bPointCloudSource || eInputType == DiscretizationType_FV ? nullptr : &dataGLLNodesSrcCov ),
1027  ( m_bPointCloudSource || eInputType == DiscretizationType_FV ? nullptr : &dataGLLNodesSrc ),
1028  eOutputType, mapOptions.nPout, ( eOutputType == DiscretizationType_CGLL ),
1029  ( m_bPointCloudTarget ? nullptr : &dataGLLNodesDest ) ) );
1030 
1031  // Construct remap
1032  if( is_root ) dbgprint.printf( 0, "Calculating remap weights with Nearest-Neighbor method\n" );
1033  MB_CHK_ERR( LinearRemapNN_MOAB( true /*use_GID_matching*/, false /*strict_check*/ ) );
1034  }
1035  else if( ( eInputType != DiscretizationType_FV ) && ( eOutputType == DiscretizationType_FV ) )
1036  {
1037  DataArray3D< double > dataGLLJacobianSrc, dataGLLJacobian;
1038 
1039  if( is_root ) dbgprint.printf( 0, "Generating input mesh meta data\n" );
1040  // generate metadata for the input meshes (both source and covering source)
1041  GenerateMetaData( *m_meshInput, mapOptions.nPin, mapOptions.fNoBubble, dataGLLNodesSrc,
1042  dataGLLJacobianSrc );
1043  GenerateMetaData( *m_meshInputCov, mapOptions.nPin, mapOptions.fNoBubble, dataGLLNodesSrcCov,
1044  dataGLLJacobian );
1045 
1046  if( dataGLLNodesSrcCov.GetSubColumns() != m_meshInputCov->faces.size() )
1047  {
1048  _EXCEPTIONT( "Number of element does not match between metadata and "
1049  "input mesh" );
1050  }
1051 
1052  // Initialize coordinates for map
1053  this->InitializeSourceCoordinatesFromMeshFE( *m_meshInputCov, mapOptions.nPin, dataGLLNodesSrcCov );
1054  this->InitializeTargetCoordinatesFromMeshFV( *m_meshOutput );
1055 
1056  // Generate the continuous Jacobian for input mesh
1057  bool fContinuousIn = ( eInputType == DiscretizationType_CGLL );
1058 
1059  if( eInputType == DiscretizationType_CGLL )
1060  {
1061  GenerateUniqueJacobian( dataGLLNodesSrcCov, dataGLLJacobian, this->GetSourceAreas() );
1062  }
1063  else
1064  {
1065  GenerateDiscontinuousJacobian( dataGLLJacobian, this->GetSourceAreas() );
1066  }
1067 
1068  // Finite element input / Finite volume output
1069  MB_CHK_ERR( this->SetDOFmapAssociation( eInputType, mapOptions.nPin,
1070  ( eInputType == DiscretizationType_CGLL ), &dataGLLNodesSrcCov,
1071  &dataGLLNodesSrc, eOutputType, mapOptions.nPout, false, nullptr ) );
1072 
1073  // Generate remap
1074  if( is_root ) dbgprint.printf( 0, "Calculating remap weights\n" );
1075 
1076  if( strMapAlgorithm == "volumetric" )
1077  {
1078  _EXCEPTIONT( "Unimplemented: Volumetric currently unavailable for"
1079  "GLL input mesh" );
1080  }
1081 
1082  this->m_pdataGLLNodesIn = &dataGLLNodesSrcCov;
1083  this->m_pdataGLLNodesOut = nullptr;
1084 
1085 #ifdef USE_NATIVE_TEMPESTREMAP_ROUTINES
1086  LinearRemapSE4( *m_meshInputCov, *m_meshOutput, *m_meshOverlap, dataGLLNodesSrcCov, dataGLLJacobian,
1087  nMonotoneType, fContinuousIn, mapOptions.fNoConservation, mapOptions.fSparseConstraints,
1088  *this );
1089 #else
1090  LinearRemapSE4_Tempest_MOAB( dataGLLNodesSrcCov, dataGLLJacobian, nMonotoneType, fContinuousIn,
1091  mapOptions.fNoConservation );
1092 #endif
1093  }
1094  else if( ( eInputType != DiscretizationType_FV ) && ( eOutputType != DiscretizationType_FV ) )
1095  {
1096  DataArray3D< double > dataGLLJacobianIn, dataGLLJacobianSrc;
1097  DataArray3D< double > dataGLLJacobianOut;
1098 
1099  // Input metadata
1100  if( is_root ) dbgprint.printf( 0, "Generating input mesh meta data\n" );
1101  // generate metadata for the input meshes (both source and covering source)
1102  GenerateMetaData( *m_meshInput, mapOptions.nPin, mapOptions.fNoBubble, dataGLLNodesSrc,
1103  dataGLLJacobianSrc );
1104  // now coverage
1105  GenerateMetaData( *m_meshInputCov, mapOptions.nPin, mapOptions.fNoBubble, dataGLLNodesSrcCov,
1106  dataGLLJacobianIn );
1107  // Output metadata
1108  if( is_root ) dbgprint.printf( 0, "Generating output mesh meta data\n" );
1109  GenerateMetaData( *m_meshOutput, mapOptions.nPout, mapOptions.fNoBubble, dataGLLNodesDest,
1110  dataGLLJacobianOut );
1111 
1112  // Initialize coordinates for map
1113  this->InitializeSourceCoordinatesFromMeshFE( *m_meshInputCov, mapOptions.nPin, dataGLLNodesSrcCov );
1114  this->InitializeTargetCoordinatesFromMeshFE( *m_meshOutput, mapOptions.nPout, dataGLLNodesDest );
1115 
1116  // Generate the continuous Jacobian for input mesh
1117  bool fContinuousIn = ( eInputType == DiscretizationType_CGLL );
1118 
1119  if( eInputType == DiscretizationType_CGLL )
1120  {
1121  GenerateUniqueJacobian( dataGLLNodesSrcCov, dataGLLJacobianIn, this->GetSourceAreas() );
1122  }
1123  else
1124  {
1125  GenerateDiscontinuousJacobian( dataGLLJacobianIn, this->GetSourceAreas() );
1126  }
1127 
1128  // Generate the continuous Jacobian for output mesh
1129  bool fContinuousOut = ( eOutputType == DiscretizationType_CGLL );
1130 
1131  if( eOutputType == DiscretizationType_CGLL )
1132  {
1133  GenerateUniqueJacobian( dataGLLNodesDest, dataGLLJacobianOut, this->GetTargetAreas() );
1134  }
1135  else
1136  {
1137  GenerateDiscontinuousJacobian( dataGLLJacobianOut, this->GetTargetAreas() );
1138  }
1139 
1140  // Input Finite Element to Output Finite Element
1141  MB_CHK_ERR( this->SetDOFmapAssociation( eInputType, mapOptions.nPin,
1142  ( eInputType == DiscretizationType_CGLL ), &dataGLLNodesSrcCov,
1143  &dataGLLNodesSrc, eOutputType, mapOptions.nPout,
1144  ( eOutputType == DiscretizationType_CGLL ), &dataGLLNodesDest ) );
1145 
1146  this->m_pdataGLLNodesIn = &dataGLLNodesSrcCov;
1147  this->m_pdataGLLNodesOut = &dataGLLNodesDest;
1148 
1149  // Generate remap
1150  if( is_root ) dbgprint.printf( 0, "Calculating remap weights\n" );
1151 
1152 #ifdef USE_NATIVE_TEMPESTREMAP_ROUTINES
1153  LinearRemapGLLtoGLL_Integrated( *m_meshInputCov, *m_meshOutput, *m_meshOverlap, dataGLLNodesSrcCov,
1154  dataGLLJacobianIn, dataGLLNodesDest, dataGLLJacobianOut,
1155  this->GetTargetAreas(), mapOptions.nPin, mapOptions.nPout, nMonotoneType,
1156  fContinuousIn, fContinuousOut, mapOptions.fSparseConstraints, *this );
1157 #else
1158  LinearRemapGLLtoGLL2_MOAB( dataGLLNodesSrcCov, dataGLLJacobianIn, dataGLLNodesDest, dataGLLJacobianOut,
1159  this->GetTargetAreas(), mapOptions.nPin, mapOptions.nPout, nMonotoneType,
1160  fContinuousIn, fContinuousOut, mapOptions.fNoConservation );
1161 #endif
1162  }
1163  else
1164  {
1165  _EXCEPTIONT( "Not implemented" );
1166  }
1167 
1168 #ifdef MOAB_HAVE_EIGEN3
1169  copy_tempest_sparsemat_to_eigen3();
1170 #endif
1171 
1172 #ifdef MOAB_HAVE_MPI
1173  {
1174  // Remove ghosted entities from overlap set
1175  moab::Range ghostedEnts;
1178  MB_CHK_SET_ERR( m_interface->remove_entities( m_meshOverlapSet, ghostedEnts ),
1179  "Deleting ghosted entities failed" );
1180  }
1181 #endif
1182  // Verify consistency, conservation and monotonicity, globally
1183  if( !mapOptions.fNoCheck )
1184  {
1185  if( is_root ) dbgprint.printf( 0, "Verifying map" );
1186  this->IsConsistent( 1.0e-8 );
1187  if( !mapOptions.fNoConservation ) this->IsConservative( 1.0e-8 );
1188 
1189  if( nMonotoneType != 0 )
1190  {
1191  this->IsMonotone( 1.0e-12 );
1192  }
1193  }
1194  }
1195  catch( Exception& e )
1196  {
1197  dbgprint.printf( 0, "%s", e.ToString().c_str() );
1198  return ( moab::MB_FAILURE );
1199  }
1200  catch( ... )
1201  {
1202  return ( moab::MB_FAILURE );
1203  }
1204  return moab::MB_SUCCESS;
1205 }

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 501 of file TempestOnlineMap.hpp.

501 { 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 620 of file TempestOnlineMap.hpp.

621 {
622  return col_gdofmap[localColID];
623 }

◆ 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 638 of file TempestOnlineMap.hpp.

639 {
640  return m_nDofsPEl_Dest;
641 }

◆ 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 625 of file TempestOnlineMap.hpp.

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

◆ GetIndexOfRowGlobalDoF()

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

Get the index of globaRowDoF.

Definition at line 614 of file TempestOnlineMap.hpp.

615 {
616  return globalRowDoF + 1;
617 }

◆ 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 500 of file TempestOnlineMap.hpp.

500 { 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 609 of file TempestOnlineMap.hpp.

610 {
611  return row_gdofmap[localRowID];
612 }

◆ 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 631 of file TempestOnlineMap.hpp.

632 {
633  return m_nDofsPEl_Src;
634 }

◆ 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 475 of file TempestOnlineMap.hpp.

475 { 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 1255 of file TempestOnlineMap.cpp.

1256 {
1257 #ifndef MOAB_HAVE_MPI
1258 
1259  return OfflineMap::IsConservative( dTolerance );
1260 
1261 #else
1262  // return OfflineMap::IsConservative(dTolerance);
1263 
1264  int ierr;
1265  // Get map entries
1266  DataArray1D< int > dataRows;
1267  DataArray1D< int > dataCols;
1268  DataArray1D< double > dataEntries;
1269  const DataArray1D< double >& dTargetAreas = this->GetTargetAreas();
1270  const DataArray1D< double >& dSourceAreas = this->GetSourceAreas();
1271 
1272  // Calculate column sums
1273  std::vector< int > dColumnsUnique;
1274  std::vector< double > dColumnSums;
1275 
1276  int nColumns = m_mapRemap.GetColumns();
1277  m_mapRemap.GetEntries( dataRows, dataCols, dataEntries );
1278  dColumnSums.resize( m_nTotDofs_SrcCov, 0.0 );
1279  dColumnsUnique.resize( m_nTotDofs_SrcCov, -1 );
1280 
1281  for( unsigned i = 0; i < dataEntries.GetRows(); i++ )
1282  {
1283  dColumnSums[dataCols[i]] += dataEntries[i] * dTargetAreas[dataRows[i]] / dSourceAreas[dataCols[i]];
1284 
1285  assert( dataCols[i] < m_nTotDofs_SrcCov );
1286 
1287  // GID for column DoFs: col_gdofmap[ col_ldofmap [ dataCols[i] ] ]
1288  int colGID = this->GetColGlobalDoF( dataCols[i] ); // col_gdofmap[ col_ldofmap [ dataCols[i] ] ];
1289  // int colGID = col_gdofmap[ col_ldofmap [ dataCols[i] ] ];
1290  dColumnsUnique[dataCols[i]] = colGID;
1291 
1292  // std::cout << "Column dataCols[i]=" << dataCols[i] << " with GID = " << colGID <<
1293  // std::endl;
1294  }
1295 
1296  int rootProc = 0;
1297  std::vector< int > nElementsInProc;
1298  const int nDATA = 3;
1299  nElementsInProc.resize( size * nDATA );
1300  int senddata[nDATA] = { nColumns, m_nTotDofs_SrcCov, m_nTotDofs_Src };
1301  ierr = MPI_Gather( senddata, nDATA, MPI_INT, nElementsInProc.data(), nDATA, MPI_INT, rootProc, m_pcomm->comm() );
1302  if( ierr != MPI_SUCCESS ) return -1;
1303 
1304  int nTotVals = 0, nTotColumns = 0; // nTotColumnsUnq = 0;
1305  std::vector< int > dColumnIndices;
1306  std::vector< double > dColumnSourceAreas;
1307  std::vector< double > dColumnSumsTotal;
1308  std::vector< int > displs, rcount;
1309  if( rank == rootProc )
1310  {
1311  displs.resize( size + 1, 0 );
1312  rcount.resize( size, 0 );
1313  int gsum = 0;
1314  for( int ir = 0; ir < size; ++ir )
1315  {
1316  nTotVals += nElementsInProc[ir * nDATA];
1317  nTotColumns += nElementsInProc[ir * nDATA + 1];
1318  // nTotColumnsUnq += nElementsInProc[ir * nDATA + 2];
1319 
1320  displs[ir] = gsum;
1321  rcount[ir] = nElementsInProc[ir * nDATA + 1];
1322  gsum += rcount[ir];
1323 
1324  // printf( "%d: nTotColumns: %d, Displs: %d, rcount: %d, gsum = %d\n", ir, nTotColumns, displs[ir], rcount[ir], gsum );
1325  }
1326 
1327  printf( "Total nnz: %d, global source elements = %d\n", nTotVals, gsum );
1328 
1329  dColumnIndices.resize( nTotColumns, -1 );
1330  dColumnSumsTotal.resize( nTotColumns, 0.0 );
1331  // dColumnSourceAreas.resize ( nTotColumns, 0.0 );
1332  }
1333 
1334  // Gather all ColumnSums to root process and accumulate
1335  // We expect that the sums of all columns equate to 1.0 within user specified tolerance
1336  // Need to do a gatherv here since different processes have different number of elements
1337  // MPI_Reduce(&dColumnSums[0], &dColumnSumsTotal[0], m_mapRemap.GetColumns(), MPI_DOUBLE,
1338  // MPI_SUM, 0, m_pcomm->comm());
1339  ierr = MPI_Gatherv( &dColumnsUnique[0], m_nTotDofs_SrcCov, MPI_INT, &dColumnIndices[0], rcount.data(),
1340  displs.data(), MPI_INT, rootProc, m_pcomm->comm() );
1341  if( ierr != MPI_SUCCESS ) return -1;
1342  ierr = MPI_Gatherv( &dColumnSums[0], m_nTotDofs_SrcCov, MPI_DOUBLE, &dColumnSumsTotal[0], rcount.data(),
1343  displs.data(), MPI_DOUBLE, rootProc, m_pcomm->comm() );
1344  if( ierr != MPI_SUCCESS ) return -1;
1345  // ierr = MPI_Gatherv ( &dSourceAreas[0], m_nTotDofs_SrcCov, MPI_DOUBLE, &dColumnSourceAreas[0],
1346  // rcount.data(), displs.data(), MPI_DOUBLE, rootProc, m_pcomm->comm() ); if ( ierr !=
1347  // MPI_SUCCESS ) return -1;
1348 
1349  // Clean out unwanted arrays now
1350  dColumnSums.clear();
1351  dColumnsUnique.clear();
1352 
1353  // Verify all column sums equal the input Jacobian
1354  int fConservative = 0;
1355  if( rank == rootProc )
1356  {
1357  displs[size] = ( nTotColumns );
1358  // std::vector<double> dColumnSumsOnRoot(nTotColumnsUnq, 0.0);
1359  std::map< int, double > dColumnSumsOnRoot;
1360  // std::map<int, double> dColumnSourceAreasOnRoot;
1361  for( int ir = 0; ir < size; ir++ )
1362  {
1363  for( int ips = displs[ir]; ips < displs[ir + 1]; ips++ )
1364  {
1365  if( dColumnIndices[ips] < 0 ) continue;
1366  // printf("%d, %d: dColumnIndices[ips]: %d\n", ir, ips, dColumnIndices[ips]);
1367  // assert( dColumnIndices[ips] < nTotColumnsUnq );
1368  dColumnSumsOnRoot[dColumnIndices[ips]] += dColumnSumsTotal[ips]; // / dColumnSourceAreas[ips];
1369  // dColumnSourceAreasOnRoot[ dColumnIndices[ips] ] = dColumnSourceAreas[ips];
1370  // dColumnSourceAreas[ dColumnIndices[ips] ]
1371  }
1372  }
1373 
1374  for( std::map< int, double >::iterator it = dColumnSumsOnRoot.begin(); it != dColumnSumsOnRoot.end(); ++it )
1375  {
1376  // if ( fabs ( it->second - dColumnSourceAreasOnRoot[it->first] ) > dTolerance )
1377  if( fabs( it->second - 1.0 ) > dTolerance )
1378  {
1379  fConservative++;
1380  Announce( "TempestOnlineMap is not conservative in column "
1381  // "%i (%1.15e)", it->first, it->second );
1382  "%i (%1.15e)",
1383  it->first, it->second /* / dColumnSourceAreasOnRoot[it->first] */ );
1384  }
1385  }
1386  }
1387 
1388  // TODO: Just do a broadcast from root instead of a reduction
1389  ierr = MPI_Bcast( &fConservative, 1, MPI_INT, rootProc, m_pcomm->comm() );
1390  if( ierr != MPI_SUCCESS ) return -1;
1391 
1392  return fConservative;
1393 #endif
1394 }

◆ IsConsistent()

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

Determine if the map is first-order accurate.

Definition at line 1209 of file TempestOnlineMap.cpp.

1210 {
1211 #ifndef MOAB_HAVE_MPI
1212 
1213  return OfflineMap::IsConsistent( dTolerance );
1214 
1215 #else
1216 
1217  // Get map entries
1218  DataArray1D< int > dataRows;
1219  DataArray1D< int > dataCols;
1220  DataArray1D< double > dataEntries;
1221 
1222  // Calculate row sums
1223  DataArray1D< double > dRowSums;
1224  m_mapRemap.GetEntries( dataRows, dataCols, dataEntries );
1225  dRowSums.Allocate( m_mapRemap.GetRows() );
1226 
1227  for( unsigned i = 0; i < dataRows.GetRows(); i++ )
1228  {
1229  dRowSums[dataRows[i]] += dataEntries[i];
1230  }
1231 
1232  // Verify all row sums are equal to 1
1233  int fConsistent = 0;
1234  for( unsigned i = 0; i < dRowSums.GetRows(); i++ )
1235  {
1236  if( fabs( dRowSums[i] - 1.0 ) > dTolerance )
1237  {
1238  fConsistent++;
1239  int rowGID = row_gdofmap[i];
1240  Announce( "TempestOnlineMap is not consistent in row %i (%1.15e)", rowGID, dRowSums[i] );
1241  }
1242  }
1243 
1244  int ierr;
1245  int fConsistentGlobal = 0;
1246  ierr = MPI_Allreduce( &fConsistent, &fConsistentGlobal, 1, MPI_INT, MPI_SUM, m_pcomm->comm() );
1247  if( ierr != MPI_SUCCESS ) return -1;
1248 
1249  return fConsistentGlobal;
1250 #endif
1251 }

◆ IsMonotone()

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

Determine if the map is monotone.

Definition at line 1398 of file TempestOnlineMap.cpp.

1399 {
1400 #ifndef MOAB_HAVE_MPI
1401 
1402  return OfflineMap::IsMonotone( dTolerance );
1403 
1404 #else
1405 
1406  // Get map entries
1407  DataArray1D< int > dataRows;
1408  DataArray1D< int > dataCols;
1409  DataArray1D< double > dataEntries;
1410 
1411  m_mapRemap.GetEntries( dataRows, dataCols, dataEntries );
1412 
1413  // Verify all entries are in the range [0,1]
1414  int fMonotone = 0;
1415  for( unsigned i = 0; i < dataRows.GetRows(); i++ )
1416  {
1417  if( ( dataEntries[i] < -dTolerance ) || ( dataEntries[i] > 1.0 + dTolerance ) )
1418  {
1419  fMonotone++;
1420 
1421  Announce( "TempestOnlineMap is not monotone in entry (%i): %1.15e", i, dataEntries[i] );
1422  }
1423  }
1424 
1425  int ierr;
1426  int fMonotoneGlobal = 0;
1427  ierr = MPI_Allreduce( &fMonotone, &fMonotoneGlobal, 1, MPI_INT, MPI_SUM, m_pcomm->comm() );
1428  if( ierr != MPI_SUCCESS ) return -1;
1429 
1430  return fMonotoneGlobal;
1431 #endif
1432 }

◆ 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  // int ixFirstFaceGlob = m_remapper->GetGlobalID(moab::Remapper::SourceMesh,
264  // ixFirstFaceLoc); int ixSecondFaceGlob =
265  // m_remapper->GetGlobalID(moab::Remapper::TargetMesh, ixSecondFaceLoc);
266 
267  // signal to not participate, because it is a ghost target
268  if( ixSecondFaceLoc < 0 ) continue; // do not do anything
269 
270  m_mapRemap( ixSecondFaceLoc, ixFirstFaceLoc ) +=
271  dComposedArray[i][j] / m_meshOutput->vecFaceArea[ixSecondFaceLoc];
272  }
273  }
274 
275  // Increment the current overlap index
276  ixOverlap += nOverlapFaces;
277  }
278 
279  return;
280 }

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 1300 of file TempestLinearRemap.cpp.

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

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 1851 of file TempestLinearRemap.cpp.

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

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  << row_ldofmap.size() << " " << col_gdofmap.size() << " " << col_ldofmap.size() << "\n";
67  output_file << "Rows \n";
68  for( unsigned iv = 0; iv < row_gdofmap.size(); iv++ )
69  output_file << row_gdofmap[iv] << " " << row_dofmap[iv] << "\n";
70  output_file << "Cols \n";
71  for( unsigned iv = 0; iv < col_gdofmap.size(); iv++ )
72  output_file << col_gdofmap[iv] << " " << col_dofmap[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 
)
private

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

Definition at line 879 of file TempestLinearRemap.cpp.

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

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

◆ PrintMapStatistics()

void moab::TempestOnlineMap::PrintMapStatistics ( )

Print information and metadata about the remapping weights.

Definition at line 284 of file TempestLinearRemap.cpp.

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

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 388 of file TempestLinearRemap.cpp.

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

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 1248 of file TempestOnlineMapIO.cpp.

1255 {
1256  NcError error( NcError::silent_nonfatal );
1257 
1258  NcVar *varRow = NULL, *varCol = NULL, *varS = NULL;
1259  NcVar *varAreaA = NULL, *varAreaB = NULL;
1260  bool readAreaA = false;
1261  bool readAreaB = false;
1262  if( 1 == arearead || 3 == arearead ) readAreaA = true;
1263  if( 2 == arearead || 3 == arearead ) readAreaB = true;
1264  int nS = 0;
1265 #ifdef MOAB_HAVE_PNETCDF
1266  // some variables will be used just in the case netcdfpar reader fails
1267  int ncfile = -1;
1268  int ndims, nvars, ngatts, unlimited;
1269 #endif
1270 #ifdef MOAB_HAVE_NETCDFPAR
1271  bool is_independent = true;
1272  ParNcFile ncMap( m_pcomm->comm(), MPI_INFO_NULL, strSource, NcFile::ReadOnly, NcFile::Netcdf4 );
1273  // ParNcFile ncMap( m_pcomm->comm(), MPI_INFO_NULL, strFilename.c_str(), NcmpiFile::replace, NcmpiFile::classic5 );
1274 #else
1275  NcFile ncMap( strSource, NcFile::ReadOnly );
1276 #endif
1277 
1278 #define CHECK_EXCEPTION( obj, type, varstr ) \
1279  { \
1280  if( obj == nullptr ) \
1281  { \
1282  _EXCEPTION3( "Map file \"%s\" does not contain %s \"%s\"", strSource, type, varstr ); \
1283  } \
1284  }
1285 
1286  // Read SparseMatrix entries
1287 
1288  if( ncMap.is_valid() )
1289  {
1290  NcDim* dimNS = ncMap.get_dim( "n_s" );
1291  CHECK_EXCEPTION( dimNS, "dimension", "n_s" );
1292 
1293  NcDim* dimNA = ncMap.get_dim( "n_a" );
1294  CHECK_EXCEPTION( dimNA, "dimension", "n_a" );
1295 
1296  NcDim* dimNB = ncMap.get_dim( "n_b" );
1297  CHECK_EXCEPTION( dimNB, "dimension", "n_b" );
1298 
1299  // store total number of nonzeros
1300  nS = dimNS->size();
1301  nA = dimNA->size();
1302  nB = dimNB->size();
1303 
1304  varRow = ncMap.get_var( "row" );
1305  CHECK_EXCEPTION( varRow, "variable", "row" );
1306 
1307  varCol = ncMap.get_var( "col" );
1308  CHECK_EXCEPTION( varCol, "variable", "col" );
1309 
1310  varS = ncMap.get_var( "S" );
1311  CHECK_EXCEPTION( varS, "variable", "S" );
1312 
1313  if( readAreaA )
1314  {
1315  varAreaA = ncMap.get_var( "area_a" );
1316  CHECK_EXCEPTION( varAreaA, "variable", "area_a" );
1317  }
1318  if( readAreaB )
1319  {
1320  varAreaB = ncMap.get_var( "area_b" );
1321  CHECK_EXCEPTION( varAreaB, "variable", "area_b" );
1322  }
1323 
1324 #ifdef MOAB_HAVE_NETCDFPAR
1325  ncMap.enable_var_par_access( varRow, is_independent );
1326  ncMap.enable_var_par_access( varCol, is_independent );
1327  ncMap.enable_var_par_access( varS, is_independent );
1328  if( readAreaA ) ncMap.enable_var_par_access( varAreaA, is_independent );
1329  if( readAreaB ) ncMap.enable_var_par_access( varAreaB, is_independent );
1330 #endif
1331  }
1332  else
1333  {
1334 #ifdef MOAB_HAVE_PNETCDF
1335  // read the file using pnetcdf directly, in parallel; need to have MPI, we do not check that anymore
1336  // why build wth pnetcdf without MPI ?
1337  // ParNcFile ncMap( m_pcomm->comm(), MPI_INFO_NULL, strSource, NcFile::ReadOnly, NcFile::Netcdf4 );
1338  ERR_PARNC(
1339  ncmpi_open( m_pcomm->comm(), strSource, NC_NOWRITE, MPI_INFO_NULL, &ncfile ) ); // bail out completely
1340  ERR_PARNC( ncmpi_inq( ncfile, &ndims, &nvars, &ngatts, &unlimited ) );
1341  // find dimension ids for n_S
1342  int ins;
1343  ERR_PARNC( ncmpi_inq_dimid( ncfile, "n_s", &ins ) );
1344  MPI_Offset leng;
1345  ERR_PARNC( ncmpi_inq_dimlen( ncfile, ins, &leng ) );
1346  nS = (int)leng;
1347  ERR_PARNC( ncmpi_inq_dimid( ncfile, "n_a", &ins ) );
1348  ERR_PARNC( ncmpi_inq_dimlen( ncfile, ins, &leng ) );
1349  nA = (int)leng;
1350  ERR_PARNC( ncmpi_inq_dimid( ncfile, "n_b", &ins ) );
1351  ERR_PARNC( ncmpi_inq_dimlen( ncfile, ins, &leng ) );
1352  nB = (int)leng;
1353 #else
1354  _EXCEPTION1( "cannot read the file %s", strSource );
1355 #endif
1356  }
1357 
1358  // Let us declare the map object for every process
1359  // SparseMatrix< double >& sparseMatrix = this->GetSparseMatrix();
1360 
1361  int localSize = nS / size;
1362  long offsetRead = rank * localSize;
1363  // leftovers on last rank
1364  if( rank == size - 1 )
1365  {
1366  localSize += nS % size;
1367  }
1368 
1369  int localSizeA = nA / size;
1370  long offsetReadA = rank * localSizeA;
1371  // leftovers on last rank
1372  if( rank == size - 1 )
1373  {
1374  localSizeA += nA % size;
1375  }
1376 
1377  int localSizeB = nB / size;
1378  long offsetReadB = rank * localSizeB;
1379  // leftovers on last rank
1380  if( rank == size - 1 )
1381  {
1382  localSizeB += nB % size;
1383  }
1384 
1385  std::vector< int > vecRow, vecCol;
1386  std::vector< double > vecS;
1387  vecRow.resize( localSize );
1388  vecCol.resize( localSize );
1389  vecS.resize( localSize );
1390  if( readAreaA ) vecAreaA.resize( localSizeA );
1391  if( readAreaB ) vecAreaB.resize( localSizeB );
1392 
1393  if( ncMap.is_valid() )
1394  {
1395  varRow->set_cur( (long)( offsetRead ) );
1396  varRow->get( &( vecRow[0] ), localSize );
1397 
1398  varCol->set_cur( (long)( offsetRead ) );
1399  varCol->get( &( vecCol[0] ), localSize );
1400 
1401  varS->set_cur( (long)( offsetRead ) );
1402  varS->get( &( vecS[0] ), localSize );
1403 
1404  if( readAreaA )
1405  {
1406  varAreaA->set_cur( (long)( offsetReadA ) );
1407  varAreaA->get( &( vecAreaA[0] ), localSizeA );
1408  }
1409 
1410  if( readAreaB )
1411  {
1412  varAreaB->set_cur( (long)( offsetReadB ) );
1413  varAreaB->get( &( vecAreaB[0] ), localSizeB );
1414  }
1415 
1416  ncMap.close();
1417  }
1418  else
1419  {
1420 #ifdef MOAB_HAVE_PNETCDF
1421  // fill the local vectors with the variables from pnetcdf file; first inquire, then fill
1422  MPI_Offset start = (MPI_Offset)offsetRead;
1423  MPI_Offset count = (MPI_Offset)localSize;
1424  int varid;
1425  // get the sparse matrix values, row and column indices
1426  ERR_PARNC( ncmpi_inq_varid( ncfile, "S", &varid ) );
1427  ERR_PARNC( ncmpi_get_vara_double_all( ncfile, varid, &start, &count, &vecS[0] ) );
1428  ERR_PARNC( ncmpi_inq_varid( ncfile, "row", &varid ) );
1429  ERR_PARNC( ncmpi_get_vara_int_all( ncfile, varid, &start, &count, &vecRow[0] ) );
1430  ERR_PARNC( ncmpi_inq_varid( ncfile, "col", &varid ) );
1431  ERR_PARNC( ncmpi_get_vara_int_all( ncfile, varid, &start, &count, &vecCol[0] ) );
1432 
1433  if( readAreaA )
1434  {
1435  ERR_PARNC( ncmpi_inq_varid( ncfile, "area_a", &varid ) );
1436  MPI_Offset startA = (MPI_Offset)offsetReadA;
1437  MPI_Offset countA = (MPI_Offset)localSizeA;
1438  ERR_PARNC( ncmpi_get_vara_double_all( ncfile, varid, &startA, &countA, &vecAreaA[0] ) );
1439  }
1440  if( readAreaB )
1441  {
1442  ERR_PARNC( ncmpi_inq_varid( ncfile, "area_b", &varid ) );
1443  MPI_Offset startB = (MPI_Offset)offsetReadB;
1444  MPI_Offset countB = (MPI_Offset)localSizeB;
1445  ERR_PARNC( ncmpi_get_vara_double_all( ncfile, varid, &startB, &countB, &vecAreaB[0] ) );
1446  }
1447  ERR_PARNC( ncmpi_close( ncfile ) );
1448 #endif
1449  }
1450 
1451  // NOTE: we can check if the following workflow would help here.
1452  // Initial experiments did not show same index map.
1453  // Might be useful to understand why that is the case.
1454  // First, ensure that all local and global indices are computed
1455  // this->m_remapper->ComputeGlobalLocalMaps();
1456  // Next, reuse the global to local map
1457  // const auto& rowMap = m_remapper->gid_to_lid_tgt;
1458  // const auto& colMap = m_remapper->gid_to_lid_covsrc;
1459 #ifdef MOAB_HAVE_EIGEN3
1460 
1461  typedef Eigen::Triplet< double > Triplet;
1462  std::vector< Triplet > tripletList;
1463 
1464 #ifdef MOAB_HAVE_MPI
1465  // bother with tuple list only if size > 1
1466  // otherwise, just fill the sparse matrix
1467  if( size > 1 )
1468  {
1469  std::vector< int > ownership;
1470  // the default trivial partitioning scheme
1471  int nDofs = nB; // this is for row partitioning
1472 
1473  int nPerPart = nDofs / size;
1474  moab::TupleList* tl = new moab::TupleList;
1475  unsigned numr = 1; //
1476  tl->initialize( 3, 0, 0, numr, localSize ); // to proc, row, col, value
1477  tl->enableWriteAccess();
1478  // populate
1479  for( int i = 0; i < localSize; i++ )
1480  {
1481  int rowval = vecRow[i] - 1; // dofs are 1 based in the file; sparse matrix is 0 based
1482  int colval = vecCol[i] - 1;
1483  int to_proc = -1;
1484 
1485  to_proc = rowval / nPerPart;
1486  if( to_proc == size ) to_proc = size - 1;
1487 
1488  int n = tl->get_n();
1489  tl->vi_wr[3 * n] = to_proc;
1490  tl->vi_wr[3 * n + 1] = rowval;
1491  tl->vi_wr[3 * n + 2] = colval;
1492  tl->vr_wr[n] = vecS[i];
1493  tl->inc_n();
1494  }
1495  // heavy communication
1496  ( m_pcomm->proc_config().crystal_router() )->gs_transfer( 1, *tl, 0 );
1497 
1498  if( owned_dof_ids.size() > 0 )
1499  {
1500  // we need to send desired dof to the rendezvous point
1501  moab::TupleList tl_re; //
1502  tl_re.initialize( 2, 0, 0, 0, owned_dof_ids.size() ); // to proc, value
1503  tl_re.enableWriteAccess();
1504  // send first to rendez_vous point, decided by trivial partitioning
1505 
1506  for( size_t i = 0; i < owned_dof_ids.size(); i++ )
1507  {
1508  int to_proc = -1;
1509  int dof_val = owned_dof_ids[i] - 1; // dofs are 1 based in the file, partition from 0 ?
1510  to_proc = dof_val / nPerPart;
1511  if( to_proc == size ) to_proc = size - 1;
1512 
1513  int n = tl_re.get_n();
1514  tl_re.vi_wr[2 * n] = to_proc;
1515  tl_re.vi_wr[2 * n + 1] = dof_val;
1516 
1517  tl_re.inc_n();
1518  }
1519  ( m_pcomm->proc_config().crystal_router() )->gs_transfer( 1, tl_re, 0 );
1520  // now we know in tl_re where do we need to send back dof_val
1521  moab::TupleList::buffer sort_buffer;
1522  sort_buffer.buffer_init( tl_re.get_n() );
1523  tl_re.sort( 1, &sort_buffer ); // so now we order by value
1524 
1525  //sort_buffer.buffer_init( tl->get_n() );
1526 
1527  std::map< int, int > startDofIndex, endDofIndex; // indices in tl_re for values we want
1528  int dofVal = -1;
1529  if( tl_re.get_n() > 0 )
1530  {
1531  dofVal = tl_re.vi_rd[1]; // first dof val on this rank tl_re.vi_rd[2 * 0 + 1];
1532 
1533  startDofIndex[dofVal] = 0;
1534  endDofIndex[dofVal] = 0; // start and end
1535  for( unsigned k = 1; k < tl_re.get_n(); k++ )
1536  {
1537  int newDof = tl_re.vi_rd[2 * k + 1];
1538  if( dofVal == newDof )
1539  {
1540  endDofIndex[dofVal] = k; // increment by 1 actually
1541  }
1542  else
1543  {
1544  dofVal = newDof;
1545  startDofIndex[dofVal] = k;
1546  endDofIndex[dofVal] = k;
1547  }
1548  }
1549  }
1550  // basically, for each value we are interested in, index in tl_re with those values are
1551  // tl_re.vi_rd[2*startDofIndex+1] == valDof == tl_re.vi_rd[2*endDofIndex+1]
1552  // so now we have ordered
1553  // tl_re shows to what proc do we need to send the tuple (row, col, val)
1554  moab::TupleList* tl_back = new moab::TupleList;
1555  unsigned numr = 1; //
1556  // localSize is a good guess, but maybe it should be bigger ?
1557  // this could be bigger for repeated dofs
1558  tl_back->initialize( 3, 0, 0, numr, tl->get_n() ); // to proc, row, col, value
1559  tl_back->enableWriteAccess();
1560  // now loop over tl and tl_re to see where to send
1561  // form the new tuple, which will contain the desired dofs per task, per row or column distribution
1562 
1563  for( unsigned k = 0; k < tl->get_n(); k++ )
1564  {
1565  int valDof = tl->vi_rd[3 * k + 1]; // 1 for row, 2 for column // first value, it should be
1566  if( startDofIndex.find( valDof ) == startDofIndex.end() ) continue;
1567  for( int ire = startDofIndex[valDof]; ire <= endDofIndex[valDof]; ire++ )
1568  {
1569  int to_proc = tl_re.vi_rd[2 * ire];
1570  int n = tl_back->get_n();
1571  tl_back->vi_wr[3 * n] = to_proc;
1572  tl_back->vi_wr[3 * n + 1] = tl->vi_rd[3 * k + 1]; // row
1573  tl_back->vi_wr[3 * n + 2] = tl->vi_rd[3 * k + 2]; // col
1574  tl_back->vr_wr[n] = tl->vr_rd[k];
1575  tl_back->inc_n();
1576  }
1577  }
1578 
1579  // now communicate to the desired tasks:
1580  ( m_pcomm->proc_config().crystal_router() )->gs_transfer( 1, *tl_back, 0 );
1581 
1582  tl_re.reset(); // clear memory, although this will go out of scope
1583  tl->reset();
1584  tl = tl_back;
1585  }
1586 
1587  // set of row and col used on this task
1588  std::set< int > rowSet;
1589  std::set< int > colSet;
1590  // populate the sparsematrix, using rowMap and colMap
1591  int n = tl->get_n();
1592  for( int i = 0; i < n; i++ )
1593  {
1594  const int vecRowValue = tl->vi_wr[3 * i + 1];
1595  const int vecColValue = tl->vi_wr[3 * i + 2];
1596  rowSet.insert( vecRowValue );
1597  colSet.insert( vecColValue );
1598  }
1599  int index = 0;
1600  row_gdofmap.resize( rowSet.size() );
1601  for( auto setIt : rowSet )
1602  {
1603  row_gdofmap[index] = setIt;
1604  rowMap[setIt] = index++;
1605  }
1607  index = 0;
1608  col_gdofmap.resize( colSet.size() );
1609  for( auto setIt : colSet )
1610  {
1611  col_gdofmap[index] = setIt;
1612  colMap[setIt] = index++;
1613  }
1615 
1616  tripletList.reserve( n );
1617  for( int i = 0; i < n; i++ )
1618  {
1619  const int vecRowValue = tl->vi_wr[3 * i + 1];
1620  const int vecColValue = tl->vi_wr[3 * i + 2];
1621  double value = tl->vr_wr[i];
1622  tripletList.emplace_back( rowMap[vecRowValue], colMap[vecColValue], value );
1623  }
1624  tl->reset();
1625  }
1626  else
1627 #endif
1628  {
1629  // set of row and col used on this task
1630  std::set< int > rowSet;
1631  std::set< int > colSet;
1632  // populate the sparsematrix, using rowMap and colMap
1633  for( int i = 0; i < nS; i++ )
1634  {
1635  const int vecRowValue = vecRow[i] - 1;
1636  const int vecColValue = vecCol[i] - 1;
1637  rowSet.insert( vecRowValue );
1638  colSet.insert( vecColValue );
1639  }
1640 
1641  int index = 0;
1642  row_gdofmap.resize( rowSet.size() );
1643  for( auto setIt : rowSet )
1644  {
1645  row_gdofmap[index] = setIt;
1646  rowMap[setIt] = index++;
1647  }
1649  index = 0;
1650  col_gdofmap.resize( colSet.size() );
1651  for( auto setIt : colSet )
1652  {
1653  col_gdofmap[index] = setIt;
1654  colMap[setIt] = index++;
1655  }
1657 
1658  tripletList.reserve( nS );
1659  for( int i = 0; i < nS; i++ )
1660  {
1661  const int vecRowValue = vecRow[i] - 1; // the rows, cols are 1 based in the file
1662  const int vecColValue = vecCol[i] - 1; // sparse matrix will be 0 based
1663  double value = vecS[i];
1664  tripletList.emplace_back( rowMap[vecRowValue], colMap[vecColValue], value );
1665  }
1666  }
1667 
1668  m_weightMatrix.resize( m_nTotDofs_Dest, m_nTotDofs_SrcCov );
1669  m_rowVector.resize( m_nTotDofs_Dest );
1670  m_colVector.resize( m_nTotDofs_SrcCov );
1671  m_nTotDofs_Src = m_nTotDofs_SrcCov; // do we need both?
1672  // Preserve the map file's global source-DoF count (n_a) so the migration
1673  // can tell a masked source mesh (fewer cells than n_a -> drop is BfB-safe)
1674  // from a complete one (== n_a but a column missing -> real error).
1675  m_nTotDofs_SrcGlobal = nA;
1676  m_weightMatrix.setFromTriplets( tripletList.begin(), tripletList.end() );
1677  // Reset the source and target data first
1678  m_rowVector.setZero();
1679  m_colVector.setZero();
1680 #ifdef VERBOSE
1681  serializeSparseMatrix( m_weightMatrix, "map_operator_" + std::to_string( rank ) + ".txt" );
1682 #endif
1683 // #ifdef MOAB_HAVE_EIGEN3
1684 #endif
1685  // TODO: make this flexible and read the order from map with help of metadata
1686  m_nDofsPEl_Src = 1; // always assume FV-FV maps are read from file
1687  m_nDofsPEl_Dest = 1; // always assume FV-FV maps are read from file
1688 
1689  return moab::MB_SUCCESS;
1690 }

References CHECK_EXCEPTION, moab::TupleList::enableWriteAccess(), moab::error(), moab::TupleList::get_n(), moab::TupleList::inc_n(), moab::index, moab::TupleList::initialize(), MB_SUCCESS, 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 404 of file TempestOnlineMap.cpp.

405 {
406  // col_gdofmap has global dofs , that should be in the list of values, such that
407  // row_dtoc_dofmap[offsetDOF] = localDOF;
408  // we need to find col_dtoc_dofmap such that: col_gdofmap[ col_dtoc_dofmap[i] ] == values_entities [i];
409  // we know that col_gdofmap[0..(nbcols-1)] = global_col_dofs -> in values_entities
410  // form first inverse
411  //
412  // resize and initialize to -1 to signal that this value should not be used, if not set below
413  col_dtoc_dofmap.resize( values_entities.size(), -1 );
414  for( size_t j = 0; j < values_entities.size(); j++ )
415  {
416  // values are 1 based, but rowMap, colMap are not
417  const auto it = colMap.find( values_entities[j] - 1 );
418  if( it != colMap.end() ) col_dtoc_dofmap[j] = it->second;
419  }
420  return moab::MB_SUCCESS;
421 }

References MB_SUCCESS.

◆ set_row_dc_dofs()

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

Definition at line 423 of file TempestOnlineMap.cpp.

424 {
425  // we need to find row_dtoc_dofmap such that: row_gdofmap[ row_dtoc_dofmap[i] ] == values_entities [i];
426  // resize and initialize to -1 to signal that this value should not be used, if not set below
427  row_dtoc_dofmap.resize( values_entities.size(), -1 );
428  for( size_t j = 0; j < values_entities.size(); j++ )
429  {
430  // values are 1 based, but rowMap, colMap are not
431  const auto it = rowMap.find( values_entities[j] - 1 );
432  if( it != rowMap.end() ) row_dtoc_dofmap[j] = it->second;
433  }
434  return moab::MB_SUCCESS;
435 }

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 649 of file TempestOnlineMap.hpp.

650 {
651  m_nDofsPEl_Dest = nt;
652 }

◆ 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 168 of file TempestOnlineMap.cpp.

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

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 136 of file TempestOnlineMap.cpp.

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

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 492 of file TempestOnlineMap.hpp.

493  {
494  m_meshInput = imesh;
495  };

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 644 of file TempestOnlineMap.hpp.

645 {
646  m_nDofsPEl_Src = ns;
647 }

◆ setup_sizes_dimensions()

void moab::TempestOnlineMap::setup_sizes_dimensions ( )
private

Definition at line 98 of file TempestOnlineMap.cpp.

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

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 827 of file TempestOnlineMapIO.cpp.

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

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 193 of file TempestOnlineMapIO.cpp.

195 {
196  size_t lastindex = strFilename.find_last_of( "." );
197  std::string extension = strFilename.substr( lastindex + 1, strFilename.size() );
198 
199  // Write the map file to disk in parallel
200  if( extension == "nc" )
201  {
202  /* Invoke the actual call to write the parallel map to disk in SCRIP format */
203  MB_CHK_ERR( this->WriteSCRIPMapFile( strFilename.c_str(), attrMap ) );
204  }
205  else
206  {
207  /* Write to the parallel H5M format */
208  MB_CHK_ERR( this->WriteHDF5MapFile( strFilename.c_str() ) );
209  }
210 
211  return moab::MB_SUCCESS;
212 }

References MB_CHK_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 216 of file TempestOnlineMapIO.cpp.

218 {
219  NcError error( NcError::silent_nonfatal );
220 
221 #ifdef MOAB_HAVE_NETCDFPAR
222  bool is_independent = true;
223  ParNcFile ncMap( m_pcomm->comm(), MPI_INFO_NULL, strFilename.c_str(), NcFile::Replace, NcFile::Netcdf4 );
224  // ParNcFile ncMap( m_pcomm->comm(), MPI_INFO_NULL, strFilename.c_str(), NcmpiFile::replace, NcmpiFile::classic5 );
225 #else
226  NcFile ncMap( strFilename.c_str(), NcFile::Replace );
227 #endif
228 
229  if( !ncMap.is_valid() )
230  {
231  _EXCEPTION1( "Unable to open output map file \"%s\"", strFilename.c_str() );
232  }
233 
234  // Attributes
235  // ncMap.add_att( "Title", "MOAB-TempestRemap Online Regridding Weight Generator" );
236  auto it = attrMap.begin();
237  while( it != attrMap.end() )
238  {
239  // set the map attributes
240  ncMap.add_att( it->first.c_str(), it->second.c_str() );
241  // increment iterator
242  it++;
243  }
244 
245  /**
246  * Need to get the global maximum of number of vertices per element
247  * Key issue is that when calling InitializeCoordinatesFromMeshFV, the allocation for
248  *dVertexLon/dVertexLat are made based on the maximum vertices in the current process. However,
249  *when writing this out, other processes may have a different size for the same array. This is
250  *hence a mess to consolidate in h5mtoscrip eventually.
251  **/
252 
253  /* Let us compute all relevant data for the current original source mesh on the process */
254  DataArray1D< double > vecSourceFaceArea, vecTargetFaceArea;
255  DataArray1D< double > dSourceCenterLon, dSourceCenterLat, dTargetCenterLon, dTargetCenterLat;
256  DataArray2D< double > dSourceVertexLon, dSourceVertexLat, dTargetVertexLon, dTargetVertexLat;
258  {
259  this->InitializeCoordinatesFromMeshFV(
260  *m_meshInput, dSourceCenterLon, dSourceCenterLat, dSourceVertexLon, dSourceVertexLat,
261  ( this->m_remapper->m_source_type == moab::TempestRemapper::RLL ), /* fLatLon = false */
263 
264  vecSourceFaceArea.Allocate( m_meshInput->vecFaceArea.GetRows() );
265  for( unsigned i = 0; i < m_meshInput->vecFaceArea.GetRows(); ++i )
266  vecSourceFaceArea[i] = m_meshInput->vecFaceArea[i];
267  }
268  else
269  {
270  DataArray3D< double > dataGLLJacobianSrc;
271  this->InitializeCoordinatesFromMeshFE( *m_meshInput, m_nDofsPEl_Src, dataGLLNodesSrc, dSourceCenterLon,
272  dSourceCenterLat, dSourceVertexLon, dSourceVertexLat );
273 
274  // Generate the continuous Jacobian for input mesh
275  GenerateMetaData( *m_meshInput, m_nDofsPEl_Src, false /* fBubble */, dataGLLNodesSrc, dataGLLJacobianSrc );
276 
278  {
279  GenerateUniqueJacobian( dataGLLNodesSrc, dataGLLJacobianSrc, vecSourceFaceArea );
280  }
281  else
282  {
283  GenerateDiscontinuousJacobian( dataGLLJacobianSrc, vecSourceFaceArea );
284  }
285  }
286 
288  {
289  this->InitializeCoordinatesFromMeshFV(
290  *m_meshOutput, dTargetCenterLon, dTargetCenterLat, dTargetVertexLon, dTargetVertexLat,
291  ( this->m_remapper->m_target_type == moab::TempestRemapper::RLL ), /* fLatLon = false */
293 
294  vecTargetFaceArea.Allocate( m_meshOutput->vecFaceArea.GetRows() );
295  for( unsigned i = 0; i < m_meshOutput->vecFaceArea.GetRows(); ++i )
296  {
297  vecTargetFaceArea[i] = m_meshOutput->vecFaceArea[i];
298  }
299  }
300  else
301  {
302  DataArray3D< double > dataGLLJacobianDest;
303  this->InitializeCoordinatesFromMeshFE( *m_meshOutput, m_nDofsPEl_Dest, dataGLLNodesDest, dTargetCenterLon,
304  dTargetCenterLat, dTargetVertexLon, dTargetVertexLat );
305 
306  // Generate the continuous Jacobian for input mesh
307  GenerateMetaData( *m_meshOutput, m_nDofsPEl_Dest, false /* fBubble */, dataGLLNodesDest, dataGLLJacobianDest );
308 
310  {
311  GenerateUniqueJacobian( dataGLLNodesDest, dataGLLJacobianDest, vecTargetFaceArea );
312  }
313  else
314  {
315  GenerateDiscontinuousJacobian( dataGLLJacobianDest, vecTargetFaceArea );
316  }
317  }
318 
319  // Map dimensions
320  unsigned nA = ( vecSourceFaceArea.GetRows() );
321  unsigned nB = ( vecTargetFaceArea.GetRows() );
322 
323  std::vector< int > masksA, masksB;
324  MB_CHK_SET_ERR( m_remapper->GetIMasks( moab::Remapper::SourceMesh, masksA ), "Trouble getting masks for source" );
325  MB_CHK_SET_ERR( m_remapper->GetIMasks( moab::Remapper::TargetMesh, masksB ), "Trouble getting masks for target" );
326 
327  // Number of nodes per Face
328  int nSourceNodesPerFace = dSourceVertexLon.GetColumns();
329  int nTargetNodesPerFace = dTargetVertexLon.GetColumns();
330 
331  // if source or target cells have triangles at poles, center of those triangles need to come from
332  // the original quad, not from center in 3d, converted to 2d again
333  // start copy OnlineMap.cpp tempestremap
334  // right now, do this only for source mesh; copy the logic for target mesh
335  for( unsigned i = 0; i < nA; i++ )
336  {
337  const Face& face = m_meshInput->faces[i];
338 
339  int nNodes = face.edges.size();
340  int indexNodeAtPole = -1;
341  if( 3 == nNodes ) // check if one node at the poles
342  {
343  for( int j = 0; j < nNodes; j++ )
344  if( fabs( fabs( dSourceVertexLat[i][j] ) - 90.0 ) < 1.0e-12 )
345  {
346  indexNodeAtPole = j;
347  break;
348  }
349  }
350  if( indexNodeAtPole < 0 ) continue; // continue i loop, do nothing
351  // recompute center of cell, from 3d data; add one 2 nodes at pole, and average
352  int nodeAtPole = face[indexNodeAtPole]; // use the overloaded operator
353  Node nodePole = m_meshInput->nodes[nodeAtPole];
354  Node newCenter = nodePole * 2;
355  for( int j = 1; j < nNodes; j++ )
356  {
357  int indexi = ( indexNodeAtPole + j ) % nNodes; // nNodes is 3 !
358  const Node& node = m_meshInput->nodes[face[indexi]];
359  newCenter = newCenter + node;
360  }
361  newCenter = newCenter * 0.25;
362  newCenter = newCenter.Normalized();
363 
364 #ifdef VERBOSE
365  double iniLon = dSourceCenterLon[i], iniLat = dSourceCenterLat[i];
366 #endif
367  // dSourceCenterLon, dSourceCenterLat
368  XYZtoRLL_Deg( newCenter.x, newCenter.y, newCenter.z, dSourceCenterLon[i], dSourceCenterLat[i] );
369 #ifdef VERBOSE
370  std::cout << " modify center of triangle from " << iniLon << " " << iniLat << " to " << dSourceCenterLon[i]
371  << " " << dSourceCenterLat[i] << "\n";
372 #endif
373  }
374 
375  // first move data if in parallel
376 #if defined( MOAB_HAVE_MPI )
377  int max_row_dof, max_col_dof; // output; arrays will be re-distributed in chunks [maxdof/size]
378  // if (size > 1)
379  {
380  int ierr = rearrange_arrays_by_dofs( srccol_gdofmap, vecSourceFaceArea, dSourceCenterLon, dSourceCenterLat,
381  dSourceVertexLon, dSourceVertexLat, masksA, nA, nSourceNodesPerFace,
382  max_col_dof ); // now nA will be close to maxdof/size
383  if( ierr != 0 )
384  {
385  _EXCEPTION1( "Unable to arrange source data %d ", nA );
386  }
387  // rearrange target data: (nB)
388  //
389  ierr = rearrange_arrays_by_dofs( row_gdofmap, vecTargetFaceArea, dTargetCenterLon, dTargetCenterLat,
390  dTargetVertexLon, dTargetVertexLat, masksB, nB, nTargetNodesPerFace,
391  max_row_dof ); // now nA will be close to maxdof/size
392  if( ierr != 0 )
393  {
394  _EXCEPTION1( "Unable to arrange target data %d ", nB );
395  }
396  }
397 #endif
398 
399  // Number of non-zeros in the remap matrix operator
400  int nS = m_weightMatrix.nonZeros();
401 
402 #if defined( MOAB_HAVE_MPI ) && defined( MOAB_HAVE_NETCDFPAR )
403  int locbuf[5] = { (int)nA, (int)nB, nS, nSourceNodesPerFace, nTargetNodesPerFace };
404  int offbuf[3] = { 0, 0, 0 };
405  int globuf[5] = { 0, 0, 0, 0, 0 };
406  MPI_Scan( locbuf, offbuf, 3, MPI_INT, MPI_SUM, m_pcomm->comm() );
407  MPI_Allreduce( locbuf, globuf, 3, MPI_INT, MPI_SUM, m_pcomm->comm() );
408  MPI_Allreduce( &locbuf[3], &globuf[3], 2, MPI_INT, MPI_MAX, m_pcomm->comm() );
409 
410  // MPI_Scan is inclusive of data in current rank; modify accordingly.
411  offbuf[0] -= nA;
412  offbuf[1] -= nB;
413  offbuf[2] -= nS;
414 
415 #else
416  int offbuf[3] = { 0, 0, 0 };
417  int globuf[5] = { (int)nA, (int)nB, nS, nSourceNodesPerFace, nTargetNodesPerFace };
418 #endif
419 
420  std::vector< std::string > srcdimNames, tgtdimNames;
421  std::vector< int > srcdimSizes, tgtdimSizes;
422  {
424  {
425  srcdimNames.push_back( "lat" );
426  srcdimNames.push_back( "lon" );
427  srcdimSizes.resize( 2, 0 );
428  srcdimSizes[0] = m_remapper->m_source_metadata[0];
429  srcdimSizes[1] = m_remapper->m_source_metadata[1];
430  }
431  else
432  {
433  srcdimNames.push_back( "num_elem" );
434  srcdimSizes.push_back( globuf[0] );
435  }
436 
438  {
439  tgtdimNames.push_back( "lat" );
440  tgtdimNames.push_back( "lon" );
441  tgtdimSizes.resize( 2, 0 );
442  tgtdimSizes[0] = m_remapper->m_target_metadata[0];
443  tgtdimSizes[1] = m_remapper->m_target_metadata[1];
444  }
445  else
446  {
447  tgtdimNames.push_back( "num_elem" );
448  tgtdimSizes.push_back( globuf[1] );
449  }
450  }
451 
452  // Write output dimensions entries
453  unsigned nSrcGridDims = ( srcdimSizes.size() );
454  unsigned nDstGridDims = ( tgtdimSizes.size() );
455 
456  NcDim* dimSrcGridRank = ncMap.add_dim( "src_grid_rank", nSrcGridDims );
457  NcDim* dimDstGridRank = ncMap.add_dim( "dst_grid_rank", nDstGridDims );
458 
459  NcVar* varSrcGridDims = ncMap.add_var( "src_grid_dims", ncInt, dimSrcGridRank );
460  NcVar* varDstGridDims = ncMap.add_var( "dst_grid_dims", ncInt, dimDstGridRank );
461 
462 #ifdef MOAB_HAVE_NETCDFPAR
463  ncMap.enable_var_par_access( varSrcGridDims, is_independent );
464  ncMap.enable_var_par_access( varDstGridDims, is_independent );
465 #endif
466 
467  // write dimension names
468  {
469  char szDim[64];
470  for( unsigned i = 0; i < srcdimSizes.size(); i++ )
471  {
472  varSrcGridDims->set_cur( nSrcGridDims - i - 1 );
473  varSrcGridDims->put( &( srcdimSizes[nSrcGridDims - i - 1] ), 1 );
474  }
475 
476  for( unsigned i = 0; i < srcdimSizes.size(); i++ )
477  {
478  snprintf( szDim, 64, "name%i", i );
479  varSrcGridDims->add_att( szDim, srcdimNames[nSrcGridDims - i - 1].c_str() );
480  }
481 
482  for( unsigned i = 0; i < tgtdimSizes.size(); i++ )
483  {
484  varDstGridDims->set_cur( nDstGridDims - i - 1 );
485  varDstGridDims->put( &( tgtdimSizes[nDstGridDims - i - 1] ), 1 );
486  }
487 
488  for( unsigned i = 0; i < tgtdimSizes.size(); i++ )
489  {
490  snprintf( szDim, 64, "name%i", i );
491  varDstGridDims->add_att( szDim, tgtdimNames[nDstGridDims - i - 1].c_str() );
492  }
493  }
494 
495  // Source and Target mesh resolutions
496  NcDim* dimNA = ncMap.add_dim( "n_a", globuf[0] );
497  NcDim* dimNB = ncMap.add_dim( "n_b", globuf[1] );
498 
499  // Number of nodes per Face
500  NcDim* dimNVA = ncMap.add_dim( "nv_a", globuf[3] );
501  NcDim* dimNVB = ncMap.add_dim( "nv_b", globuf[4] );
502 
503  // Write coordinates
504  NcVar* varYCA = ncMap.add_var( "yc_a", ncDouble, dimNA );
505  NcVar* varYCB = ncMap.add_var( "yc_b", ncDouble, dimNB );
506 
507  NcVar* varXCA = ncMap.add_var( "xc_a", ncDouble, dimNA );
508  NcVar* varXCB = ncMap.add_var( "xc_b", ncDouble, dimNB );
509 
510  NcVar* varYVA = ncMap.add_var( "yv_a", ncDouble, dimNA, dimNVA );
511  NcVar* varYVB = ncMap.add_var( "yv_b", ncDouble, dimNB, dimNVB );
512 
513  NcVar* varXVA = ncMap.add_var( "xv_a", ncDouble, dimNA, dimNVA );
514  NcVar* varXVB = ncMap.add_var( "xv_b", ncDouble, dimNB, dimNVB );
515 
516  // Write masks
517  NcVar* varMaskA = ncMap.add_var( "mask_a", ncInt, dimNA );
518  NcVar* varMaskB = ncMap.add_var( "mask_b", ncInt, dimNB );
519 
520 #ifdef MOAB_HAVE_NETCDFPAR
521  ncMap.enable_var_par_access( varYCA, is_independent );
522  ncMap.enable_var_par_access( varYCB, is_independent );
523  ncMap.enable_var_par_access( varXCA, is_independent );
524  ncMap.enable_var_par_access( varXCB, is_independent );
525  ncMap.enable_var_par_access( varYVA, is_independent );
526  ncMap.enable_var_par_access( varYVB, is_independent );
527  ncMap.enable_var_par_access( varXVA, is_independent );
528  ncMap.enable_var_par_access( varXVB, is_independent );
529  ncMap.enable_var_par_access( varMaskA, is_independent );
530  ncMap.enable_var_par_access( varMaskB, is_independent );
531 #endif
532 
533  varYCA->add_att( "units", "degrees" );
534  varYCB->add_att( "units", "degrees" );
535 
536  varXCA->add_att( "units", "degrees" );
537  varXCB->add_att( "units", "degrees" );
538 
539  varYVA->add_att( "units", "degrees" );
540  varYVB->add_att( "units", "degrees" );
541 
542  varXVA->add_att( "units", "degrees" );
543  varXVB->add_att( "units", "degrees" );
544 
545  // Verify dimensionality
546  if( dSourceCenterLon.GetRows() != nA )
547  {
548  _EXCEPTIONT( "Mismatch between dSourceCenterLon and nA" );
549  }
550  if( dSourceCenterLat.GetRows() != nA )
551  {
552  _EXCEPTIONT( "Mismatch between dSourceCenterLat and nA" );
553  }
554  if( dTargetCenterLon.GetRows() != nB )
555  {
556  _EXCEPTIONT( "Mismatch between dTargetCenterLon and nB" );
557  }
558  if( dTargetCenterLat.GetRows() != nB )
559  {
560  _EXCEPTIONT( "Mismatch between dTargetCenterLat and nB" );
561  }
562  if( dSourceVertexLon.GetRows() != nA )
563  {
564  _EXCEPTIONT( "Mismatch between dSourceVertexLon and nA" );
565  }
566  if( dSourceVertexLat.GetRows() != nA )
567  {
568  _EXCEPTIONT( "Mismatch between dSourceVertexLat and nA" );
569  }
570  if( dTargetVertexLon.GetRows() != nB )
571  {
572  _EXCEPTIONT( "Mismatch between dTargetVertexLon and nB" );
573  }
574  if( dTargetVertexLat.GetRows() != nB )
575  {
576  _EXCEPTIONT( "Mismatch between dTargetVertexLat and nB" );
577  }
578 
579  varYCA->set_cur( (long)offbuf[0] );
580  varYCA->put( &( dSourceCenterLat[0] ), nA );
581  varYCB->set_cur( (long)offbuf[1] );
582  varYCB->put( &( dTargetCenterLat[0] ), nB );
583 
584  varXCA->set_cur( (long)offbuf[0] );
585  varXCA->put( &( dSourceCenterLon[0] ), nA );
586  varXCB->set_cur( (long)offbuf[1] );
587  varXCB->put( &( dTargetCenterLon[0] ), nB );
588 
589  varYVA->set_cur( (long)offbuf[0] );
590  varYVA->put( &( dSourceVertexLat[0][0] ), nA, nSourceNodesPerFace );
591  varYVB->set_cur( (long)offbuf[1] );
592  varYVB->put( &( dTargetVertexLat[0][0] ), nB, nTargetNodesPerFace );
593 
594  varXVA->set_cur( (long)offbuf[0] );
595  varXVA->put( &( dSourceVertexLon[0][0] ), nA, nSourceNodesPerFace );
596  varXVB->set_cur( (long)offbuf[1] );
597  varXVB->put( &( dTargetVertexLon[0][0] ), nB, nTargetNodesPerFace );
598 
599  varMaskA->set_cur( (long)offbuf[0] );
600  varMaskA->put( &( masksA[0] ), nA );
601  varMaskB->set_cur( (long)offbuf[1] );
602  varMaskB->put( &( masksB[0] ), nB );
603 
604  // Write areas
605  NcVar* varAreaA = ncMap.add_var( "area_a", ncDouble, dimNA );
606 #ifdef MOAB_HAVE_NETCDFPAR
607  ncMap.enable_var_par_access( varAreaA, is_independent );
608 #endif
609  varAreaA->set_cur( (long)offbuf[0] );
610  varAreaA->put( &( vecSourceFaceArea[0] ), nA );
611 
612  NcVar* varAreaB = ncMap.add_var( "area_b", ncDouble, dimNB );
613 #ifdef MOAB_HAVE_NETCDFPAR
614  ncMap.enable_var_par_access( varAreaB, is_independent );
615 #endif
616  varAreaB->set_cur( (long)offbuf[1] );
617  varAreaB->put( &( vecTargetFaceArea[0] ), nB );
618 
619  // Write SparseMatrix entries
620  DataArray1D< int > vecRow( nS );
621  DataArray1D< int > vecCol( nS );
622  DataArray1D< double > vecS( nS );
623  DataArray1D< double > dFracA( nA );
624  DataArray1D< double > dFracB( nB );
625 
626  moab::TupleList tlValRow, tlValCol;
627  unsigned numr = 1; //
628  // value has to be sent to processor row/nB for for fracA and col/nA for fracB
629  // vecTargetArea (indexRow ) has to be sent for fracA (index col?)
630  // vecTargetFaceArea will have to be sent to col index, with its index !
631  tlValRow.initialize( 2, 0, 0, numr, nS ); // to proc(row), global row , value
632  tlValCol.initialize( 3, 0, 0, numr, nS ); // to proc(col), global row / col, value
633  tlValRow.enableWriteAccess();
634  tlValCol.enableWriteAccess();
635  /*
636  dFracA[ col ] += val / vecSourceFaceArea[ col ] * vecTargetFaceArea[ row ];
637  dFracB[ row ] += val ;
638  */
639  int offset = 0;
640 #if defined( MOAB_HAVE_MPI )
641  int nAbase = ( max_col_dof + 1 ) / size; // it is nA, except last rank ( == size - 1 )
642  int nBbase = ( max_row_dof + 1 ) / size; // it is nB, except last rank ( == size - 1 )
643 #endif
644  for( int i = 0; i < m_weightMatrix.outerSize(); ++i )
645  {
646  for( WeightMatrix::InnerIterator it( m_weightMatrix, i ); it; ++it )
647  {
648  vecRow[offset] = 1 + this->GetRowGlobalDoF( it.row() ); // row index
649  vecCol[offset] = 1 + this->GetColGlobalDoF( it.col() ); // col index
650  vecS[offset] = it.value(); // value
651 
652 #if defined( MOAB_HAVE_MPI )
653  {
654  // value M(row, col) will contribute to procRow and procCol values for fracA and fracB
655  int procRow = ( vecRow[offset] - 1 ) / nBbase;
656  if( procRow >= size ) procRow = size - 1;
657  int procCol = ( vecCol[offset] - 1 ) / nAbase;
658  if( procCol >= size ) procCol = size - 1;
659  int nrInd = tlValRow.get_n();
660  tlValRow.vi_wr[2 * nrInd] = procRow;
661  tlValRow.vi_wr[2 * nrInd + 1] = vecRow[offset] - 1;
662  tlValRow.vr_wr[nrInd] = vecS[offset];
663  tlValRow.inc_n();
664  int ncInd = tlValCol.get_n();
665  tlValCol.vi_wr[3 * ncInd] = procCol;
666  tlValCol.vi_wr[3 * ncInd + 1] = vecRow[offset] - 1;
667  tlValCol.vi_wr[3 * ncInd + 2] = vecCol[offset] - 1; // this is column
668  tlValCol.vr_wr[ncInd] = vecS[offset];
669  tlValCol.inc_n();
670  }
671 
672 #endif
673  offset++;
674  }
675  }
676 #if defined( MOAB_HAVE_MPI )
677  // need to send values for their row and col processors, to compute fractions there
678  // now do the heavy communication
679  ( m_pcomm->proc_config().crystal_router() )->gs_transfer( 1, tlValCol, 0 );
680  ( m_pcomm->proc_config().crystal_router() )->gs_transfer( 1, tlValRow, 0 );
681 
682  // we have now, for example, dFracB[ row ] += val ;
683  // so we know that on current task, we received tlValRow
684  // reminder dFracA[ col ] += val / vecSourceFaceArea[ col ] * vecTargetFaceArea[ row ];
685  // dFracB[ row ] += val ;
686  for( unsigned i = 0; i < tlValRow.get_n(); i++ )
687  {
688  // int fromProc = tlValRow.vi_wr[2 * i];
689  int gRowInd = tlValRow.vi_wr[2 * i + 1];
690  int localIndexRow = gRowInd - nBbase * rank; // modulo nBbase rank is from 0 to size - 1;
691  double wgt = tlValRow.vr_wr[i];
692  assert( localIndexRow >= 0 );
693  assert( nB - localIndexRow > 0 );
694  dFracB[localIndexRow] += wgt;
695  }
696  // to compute dFracA we need vecTargetFaceArea[ row ]; we know the row, and we can get the proc we need it from
697 
698  std::set< int > neededRows;
699  for( unsigned i = 0; i < tlValCol.get_n(); i++ )
700  {
701  int rRowInd = tlValCol.vi_wr[3 * i + 1];
702  neededRows.insert( rRowInd );
703  // we need vecTargetFaceAreaGlobal[ rRowInd ]; this exists on proc procRow
704  }
705  moab::TupleList tgtAreaReq;
706  tgtAreaReq.initialize( 2, 0, 0, 0, neededRows.size() );
707  tgtAreaReq.enableWriteAccess();
708  for( std::set< int >::iterator sit = neededRows.begin(); sit != neededRows.end(); sit++ )
709  {
710  int neededRow = *sit;
711  int procRow = neededRow / nBbase;
712  if( procRow >= size ) procRow = size - 1;
713  int nr = tgtAreaReq.get_n();
714  tgtAreaReq.vi_wr[2 * nr] = procRow;
715  tgtAreaReq.vi_wr[2 * nr + 1] = neededRow;
716  tgtAreaReq.inc_n();
717  }
718 
719  ( m_pcomm->proc_config().crystal_router() )->gs_transfer( 1, tgtAreaReq, 0 );
720  // we need to send back the tgtArea corresponding to row
721  moab::TupleList tgtAreaInfo; // load it with tgtArea at row
722  tgtAreaInfo.initialize( 2, 0, 0, 1, tgtAreaReq.get_n() );
723  tgtAreaInfo.enableWriteAccess();
724  for( unsigned i = 0; i < tgtAreaReq.get_n(); i++ )
725  {
726  int from_proc = tgtAreaReq.vi_wr[2 * i];
727  int row = tgtAreaReq.vi_wr[2 * i + 1];
728  int locaIndexRow = row - rank * nBbase;
729  double areaToSend = vecTargetFaceArea[locaIndexRow];
730  // int remoteIndex = tgtAreaReq.vi_wr[3*i + 2] ;
731 
732  tgtAreaInfo.vi_wr[2 * i] = from_proc; // send back requested info
733  tgtAreaInfo.vi_wr[2 * i + 1] = row;
734  tgtAreaInfo.vr_wr[i] = areaToSend; // this will be tgt area at row
735  tgtAreaInfo.inc_n();
736  }
737  ( m_pcomm->proc_config().crystal_router() )->gs_transfer( 1, tgtAreaInfo, 0 );
738 
739  std::map< int, double > areaAtRow;
740  for( unsigned i = 0; i < tgtAreaInfo.get_n(); i++ )
741  {
742  // we have received from proc, value for row !
743  int row = tgtAreaInfo.vi_wr[2 * i + 1];
744  areaAtRow[row] = tgtAreaInfo.vr_wr[i];
745  }
746 
747  // we have now for rows the
748  // it is ordered by index, so:
749  // now compute reminder dFracA[ col ] += val / vecSourceFaceArea[ col ] * vecTargetFaceArea[ row ];
750  // tgtAreaInfo will have at index i the area we need (from row)
751  // there should be an easier way :(
752  for( unsigned i = 0; i < tlValCol.get_n(); i++ )
753  {
754  int rRowInd = tlValCol.vi_wr[3 * i + 1];
755  int colInd = tlValCol.vi_wr[3 * i + 2];
756  double val = tlValCol.vr_wr[i];
757  int localColInd = colInd - rank * nAbase; // < local nA
758  // we need vecTargetFaceAreaGlobal[ rRowInd ]; this exists on proc procRow
759  auto itMap = areaAtRow.find( rRowInd ); // it should be different from end
760  if( itMap != areaAtRow.end() )
761  {
762  double areaRow = itMap->second; // we fished a lot for this !
763  dFracA[localColInd] += val / vecSourceFaceArea[localColInd] * areaRow;
764  }
765  }
766 
767 #endif
768  // Load in data
769  NcDim* dimNS = ncMap.add_dim( "n_s", globuf[2] );
770 
771  NcVar* varRow = ncMap.add_var( "row", ncInt, dimNS );
772  NcVar* varCol = ncMap.add_var( "col", ncInt, dimNS );
773  NcVar* varS = ncMap.add_var( "S", ncDouble, dimNS );
774 #ifdef MOAB_HAVE_NETCDFPAR
775  ncMap.enable_var_par_access( varRow, is_independent );
776  ncMap.enable_var_par_access( varCol, is_independent );
777  ncMap.enable_var_par_access( varS, is_independent );
778 #endif
779 
780  varRow->set_cur( (long)offbuf[2] );
781  varRow->put( vecRow, nS );
782 
783  varCol->set_cur( (long)offbuf[2] );
784  varCol->put( vecCol, nS );
785 
786  varS->set_cur( (long)offbuf[2] );
787  varS->put( &( vecS[0] ), nS );
788 
789  // Calculate and write fractional coverage arrays
790  NcVar* varFracA = ncMap.add_var( "frac_a", ncDouble, dimNA );
791 #ifdef MOAB_HAVE_NETCDFPAR
792  ncMap.enable_var_par_access( varFracA, is_independent );
793 #endif
794  varFracA->add_att( "name", "fraction of target coverage of source dof" );
795  varFracA->add_att( "units", "unitless" );
796  varFracA->set_cur( (long)offbuf[0] );
797  varFracA->put( &( dFracA[0] ), nA );
798 
799  NcVar* varFracB = ncMap.add_var( "frac_b", ncDouble, dimNB );
800 #ifdef MOAB_HAVE_NETCDFPAR
801  ncMap.enable_var_par_access( varFracB, is_independent );
802 #endif
803  varFracB->add_att( "name", "fraction of source coverage of target dof" );
804  varFracB->add_att( "units", "unitless" );
805  varFracB->set_cur( (long)offbuf[1] );
806  varFracB->put( &( dFracB[0] ), nB );
807 
808  // Add global attributes
809  // std::map<std::string, std::string>::const_iterator iterAttributes =
810  // mapAttributes.begin();
811  // for (; iterAttributes != mapAttributes.end(); iterAttributes++) {
812  // ncMap.add_att(
813  // iterAttributes->first.c_str(),
814  // iterAttributes->second.c_str());
815  // }
816 
817  ncMap.close();
818 
819 #ifdef VERBOSE
820  serializeSparseMatrix( m_weightMatrix, "map_operator_" + std::to_string( rank ) + ".txt" );
821 #endif
822  return moab::MB_SUCCESS;
823 }

References moab::TupleList::enableWriteAccess(), moab::error(), moab::TupleList::get_n(), moab::TupleList::inc_n(), moab::TupleList::initialize(), MB_CHK_SET_ERR, MB_SUCCESS, 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 575 of file TempestOnlineMap.hpp.

Referenced by ApplyWeights(), and GetColDofMap().

◆ col_gdofmap

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

Definition at line 572 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 577 of file TempestOnlineMap.hpp.

◆ dataGLLNodesDest

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

Definition at line 580 of file TempestOnlineMap.hpp.

◆ dataGLLNodesSrc

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

Definition at line 580 of file TempestOnlineMap.hpp.

◆ dataGLLNodesSrcCov

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

Definition at line 580 of file TempestOnlineMap.hpp.

◆ is_parallel

bool moab::TempestOnlineMap::is_parallel
private

Definition at line 600 of file TempestOnlineMap.hpp.

Referenced by TempestOnlineMap().

◆ is_root

bool moab::TempestOnlineMap::is_root
private

Definition at line 600 of file TempestOnlineMap.hpp.

Referenced by TempestOnlineMap().

◆ m_bConserved

bool moab::TempestOnlineMap::m_bConserved
private

Definition at line 592 of file TempestOnlineMap.hpp.

◆ m_destDiscType

DiscretizationType moab::TempestOnlineMap::m_destDiscType
private

Definition at line 581 of file TempestOnlineMap.hpp.

◆ m_dofTagDest

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

Definition at line 571 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 571 of file TempestOnlineMap.hpp.

◆ m_eInputType

DiscretizationType moab::TempestOnlineMap::m_eInputType
private

Definition at line 591 of file TempestOnlineMap.hpp.

◆ m_eOutputType

DiscretizationType moab::TempestOnlineMap::m_eOutputType
private

Definition at line 591 of file TempestOnlineMap.hpp.

◆ m_iMonotonicity

int moab::TempestOnlineMap::m_iMonotonicity
private

Definition at line 593 of file TempestOnlineMap.hpp.

◆ m_input_order

int moab::TempestOnlineMap::m_input_order
private

Definition at line 578 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 559 of file TempestOnlineMap.hpp.

Referenced by TempestOnlineMap().

◆ m_meshInput

Mesh* moab::TempestOnlineMap::m_meshInput
private

Definition at line 595 of file TempestOnlineMap.hpp.

Referenced by SetMeshInput(), and TempestOnlineMap().

◆ m_meshInputCov

Mesh* moab::TempestOnlineMap::m_meshInputCov
private

Definition at line 596 of file TempestOnlineMap.hpp.

Referenced by TempestOnlineMap().

◆ m_meshOutput

Mesh* moab::TempestOnlineMap::m_meshOutput
private

Definition at line 597 of file TempestOnlineMap.hpp.

Referenced by TempestOnlineMap().

◆ m_meshOverlap

Mesh* moab::TempestOnlineMap::m_meshOverlap
private

Definition at line 598 of file TempestOnlineMap.hpp.

Referenced by TempestOnlineMap().

◆ m_nDofsPEl_Dest

int moab::TempestOnlineMap::m_nDofsPEl_Dest
private

Definition at line 590 of file TempestOnlineMap.hpp.

◆ m_nDofsPEl_Src

int moab::TempestOnlineMap::m_nDofsPEl_Src
private

Definition at line 590 of file TempestOnlineMap.hpp.

◆ m_nTotDofs_Dest

int moab::TempestOnlineMap::m_nTotDofs_Dest
private

Definition at line 582 of file TempestOnlineMap.hpp.

Referenced by ApplyWeights(), and LinearRemapNN_MOAB().

◆ m_nTotDofs_Src

int moab::TempestOnlineMap::m_nTotDofs_Src
private

Definition at line 582 of file TempestOnlineMap.hpp.

◆ m_nTotDofs_SrcCov

int moab::TempestOnlineMap::m_nTotDofs_SrcCov
private

Definition at line 582 of file TempestOnlineMap.hpp.

Referenced by ApplyWeights(), and LinearRemapNN_MOAB().

◆ m_nTotDofs_SrcGlobal

int moab::TempestOnlineMap::m_nTotDofs_SrcGlobal
private

Definition at line 587 of file TempestOnlineMap.hpp.

Referenced by GlobalSourceDofCount(), and TempestOnlineMap().

◆ m_output_order

int moab::TempestOnlineMap::m_output_order
private

Definition at line 578 of file TempestOnlineMap.hpp.

Referenced by TempestOnlineMap().

◆ m_remapper

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

The fundamental remapping operator object.

Definition at line 545 of file TempestOnlineMap.hpp.

Referenced by TempestOnlineMap().

◆ m_srcDiscType

DiscretizationType moab::TempestOnlineMap::m_srcDiscType
private

Definition at line 581 of file TempestOnlineMap.hpp.

◆ rank

int moab::TempestOnlineMap::rank
private

Definition at line 601 of file TempestOnlineMap.hpp.

Referenced by ApplyWeights(), and TempestOnlineMap().

◆ row_dtoc_dofmap

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

Definition at line 575 of file TempestOnlineMap.hpp.

Referenced by ApplyWeights(), and GetRowDofMap().

◆ row_gdofmap

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

Definition at line 572 of file TempestOnlineMap.hpp.

Referenced by ApplyWeights(), and LinearRemapNN_MOAB().

◆ rowMap

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

Definition at line 577 of file TempestOnlineMap.hpp.

◆ size

int moab::TempestOnlineMap::size
private

Definition at line 601 of file TempestOnlineMap.hpp.

Referenced by ApplyWeights(), and TempestOnlineMap().

◆ srccol_dtoc_dofmap

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

Definition at line 575 of file TempestOnlineMap.hpp.

◆ srccol_gdofmap

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

Definition at line 572 of file TempestOnlineMap.hpp.


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