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)
 
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_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  // Initialize dimension information from file
92  this->setup_sizes_dimensions();
93 }

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

121 {
122  m_interface = nullptr;
123 #ifdef MOAB_HAVE_MPI
124  m_pcomm = nullptr;
125 #endif
126  m_meshInput = nullptr;
127  m_meshOutput = nullptr;
128  m_meshOverlap = nullptr;
129 }

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

1427 {
1428  std::vector< double > solSTagVals;
1429  std::vector< double > solTTagVals;
1430 
1431  moab::Range sents, tents;
1433  {
1435  {
1437  solSTagVals.resize( covSrcEnts.size(), default_projection );
1438  sents = covSrcEnts;
1439  }
1440  else
1441  {
1443  solSTagVals.resize( covSrcEnts.size() * this->GetSourceNDofsPerElement() * this->GetSourceNDofsPerElement(),
1444  default_projection );
1445  sents = covSrcEnts;
1446  }
1448  {
1450  solTTagVals.resize( tgtEnts.size(), default_projection );
1451  tents = tgtEnts;
1452  }
1453  else
1454  {
1456  solTTagVals.resize( tgtEnts.size() * this->GetDestinationNDofsPerElement() *
1457  this->GetDestinationNDofsPerElement(),
1458  default_projection );
1459  tents = tgtEnts;
1460  }
1461  }
1462  else
1463  {
1466  solSTagVals.resize( covSrcEnts.size() * this->GetSourceNDofsPerElement() * this->GetSourceNDofsPerElement(),
1467  default_projection );
1468  solTTagVals.resize( tgtEnts.size() * this->GetDestinationNDofsPerElement() *
1469  this->GetDestinationNDofsPerElement(),
1470  default_projection );
1471 
1472  sents = covSrcEnts;
1473  tents = tgtEnts;
1474  }
1475 
1476  // The tag data is np*np*n_el_src
1477  MB_CHK_SET_ERR( m_interface->tag_get_data( srcSolutionTag, sents, &solSTagVals[0] ),
1478  "Getting local tag data failed" );
1479 
1480  // Compute the application of weights on the suorce solution data and store it in the
1481  // destination solution vector data Optionally, can also perform the transpose application of
1482  // the weight matrix. Set the 3rd argument to true if this is needed
1483  MB_CHK_SET_ERR( this->ApplyWeights( solSTagVals, solTTagVals, transpose ),
1484  "Applying remap operator onto source vector data failed" );
1485 
1486  // The tag data is np*np*n_el_dest
1487  MB_CHK_SET_ERR( m_interface->tag_set_data( tgtSolutionTag, tents, &solTTagVals[0] ),
1488  "Setting target tag data failed" );
1489 
1490  if( caasType != CAAS_NONE )
1491  {
1492  std::string tgtSolutionTagName;
1493  MB_CHK_SET_ERR( m_interface->tag_get_name( tgtSolutionTag, tgtSolutionTagName ), "Getting tag name failed" );
1494 
1495  // Perform CAAS iterations iteratively until convergence
1496  constexpr int nmax_caas_iterations = 10;
1497  double mismatch = 1.0;
1498  int caasIteration = 0;
1499  double initialMismatch = 0.0;
1500  while( ( fabs( mismatch / initialMismatch ) > 1e-15 && fabs( mismatch ) > 1e-15 ) &&
1501  caasIteration++ < nmax_caas_iterations ) // iterate until convergence or a maximum of 5 iterations
1502  {
1503  double dMassDiffPostGlobal;
1504  std::pair< double, double > mDefect =
1505  this->ApplyBoundsLimiting( solSTagVals, solTTagVals, caasType, caasIteration, mismatch );
1506 #ifdef MOAB_HAVE_MPI
1507  double dMassDiffPost = mDefect.second;
1508  MPI_Allreduce( &dMassDiffPost, &dMassDiffPostGlobal, 1, MPI_DOUBLE, MPI_SUM, m_pcomm->comm() );
1509 #else
1510  dMassDiffPostGlobal = mDefect.second;
1511 #endif
1512  if( caasIteration == 1 ) initialMismatch = mDefect.first;
1513  if( m_remapper->verbose && is_root )
1514  {
1515  printf( "Field {%s} -> CAAS iteration: %d, mass defect: %3.4e, post-CAAS: %3.4e\n",
1516  tgtSolutionTagName.c_str(), caasIteration, mDefect.first, dMassDiffPostGlobal );
1517  }
1518  mismatch = dMassDiffPostGlobal;
1519 
1520  // The tag data is np*np*n_el_dest
1521  MB_CHK_SET_ERR( m_interface->tag_set_data( tgtSolutionTag, tents, &solTTagVals[0] ),
1522  "Setting local tag data failed" );
1523  }
1524  }
1525 
1526  return moab::MB_SUCCESS;
1527 }

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

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

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

1378 {
1379  assert( nrings > 0 );
1380  assert( useMOABAdjacencies || trMesh != nullptr );
1381 
1382  const size_t nrows = vecAdjFaces.size();
1384  for( size_t index = 0; index < nrows; index++ )
1385  {
1386  vecAdjFaces[index].insert( index ); // add self target face first
1387  {
1388  // Compute the adjacent faces to the target face
1389  if( useMOABAdjacencies )
1390  {
1391  moab::Range ents;
1392  // ents.insert( entities.index( entities[index] ) );
1393  ents.insert( entities[index] );
1394  moab::Range adjEnts;
1395  moab::ErrorCode rval = mtu.get_bridge_adjacencies( ents, 0, 2, adjEnts, nrings );MB_CHK_SET_ERR_CONT( rval, "Failed to get adjacent faces" );
1396  for( moab::Range::iterator it = adjEnts.begin(); it != adjEnts.end(); ++it )
1397  {
1398  // int adjIndex = m_interface->id_from_handle(*it)-1;
1399  int adjIndex = entities.index( *it );
1400  // printf("rank: %d, Element %lu, entity: %lu, adjIndex %d\n", rank, index, *it, adjIndex);
1401  if( adjIndex >= 0 ) vecAdjFaces[index].insert( adjIndex );
1402  }
1403  }
1404  else
1405  {
1406  /// Vector storing adjacent Faces.
1407  typedef std::pair< int, int > FaceDistancePair;
1408  typedef std::vector< FaceDistancePair > AdjacentFaceVector;
1409  AdjacentFaceVector adjFaces;
1410  Face& face = trMesh->faces[index];
1411  GetAdjacentFaceVectorByEdge( *trMesh, index, nrings * face.edges.size(), adjFaces );
1412 
1413  // Add the adjacent faces to the target face list
1414  for( auto adjFace : adjFaces )
1415  if( adjFace.first >= 0 )
1416  vecAdjFaces[index].insert( adjFace.first ); // map target face to source face
1417  }
1418  }
1419  }
1420 }

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

2659 {
2660  const bool outputEnabled = ( is_root );
2661  int discOrder;
2662  // DiscretizationType discMethod;
2663  // moab::EntityHandle meshset;
2664  moab::Range entities;
2665  // Mesh* trmesh;
2666  switch( ctx )
2667  {
2668  case Remapper::SourceMesh:
2669  // meshset = m_remapper->m_covering_source_set;
2670  // trmesh = m_remapper->m_covering_source;
2673  discOrder = m_nDofsPEl_Src;
2674  // discMethod = m_eInputType;
2675  break;
2676 
2677  case Remapper::TargetMesh:
2678  // meshset = m_remapper->m_target_set;
2679  // trmesh = m_remapper->m_target;
2680  entities =
2682  discOrder = m_nDofsPEl_Dest;
2683  // discMethod = m_eOutputType;
2684  break;
2685 
2686  default:
2687  if( outputEnabled )
2688  std::cout << "Invalid context specified for defining an analytical solution tag" << std::endl;
2689  return moab::MB_FAILURE;
2690  }
2691 
2692  // Let us create teh solution tag with appropriate information for name, discretization order
2693  // (DoF space)
2694  std::string exactTagName, projTagName;
2695  const int ntotsize = entities.size() * discOrder * discOrder;
2696  std::vector< double > exactSolution( ntotsize, 0.0 ), projSolution( ntotsize, 0.0 );
2697  MB_CHK_ERR( m_interface->tag_get_name( exactTag, exactTagName ) );
2698  MB_CHK_ERR( m_interface->tag_get_data( exactTag, entities, &exactSolution[0] ) );
2699  MB_CHK_ERR( m_interface->tag_get_name( approxTag, projTagName ) );
2700  MB_CHK_ERR( m_interface->tag_get_data( approxTag, entities, &projSolution[0] ) );
2701 
2702  const auto& ovents = m_remapper->m_overlap_entities;
2703 
2704  std::vector< double > errnorms( 4, 0.0 ), globerrnorms( 4, 0.0 ); // L1Err, L2Err, LinfErr
2705  double sumarea = 0.0;
2706  for( size_t i = 0; i < ovents.size(); ++i )
2707  {
2708  const int srcidx = m_remapper->m_overlap->vecSourceFaceIx[i];
2709  if( srcidx < 0 ) continue; // Skip non-overlapping entities
2710  const int tgtidx = m_remapper->m_overlap->vecTargetFaceIx[i];
2711  if( tgtidx < 0 ) continue; // skip ghost target faces
2712  const double ovarea = m_remapper->m_overlap->vecFaceArea[i];
2713  const double error = fabs( exactSolution[tgtidx] - projSolution[tgtidx] );
2714  errnorms[0] += ovarea * error;
2715  errnorms[1] += ovarea * error * error;
2716  errnorms[3] = ( error > errnorms[3] ? error : errnorms[3] );
2717  sumarea += ovarea;
2718  }
2719  errnorms[2] = sumarea;
2720 #ifdef MOAB_HAVE_MPI
2721  if( m_pcomm )
2722  {
2723  MPI_Reduce( &errnorms[0], &globerrnorms[0], 3, MPI_DOUBLE, MPI_SUM, 0, m_pcomm->comm() );
2724  MPI_Reduce( &errnorms[3], &globerrnorms[3], 1, MPI_DOUBLE, MPI_MAX, 0, m_pcomm->comm() );
2725  }
2726 #else
2727  for( int i = 0; i < 4; ++i )
2728  globerrnorms[i] = errnorms[i];
2729 #endif
2730 
2731  globerrnorms[0] = ( globerrnorms[0] / globerrnorms[2] );
2732  globerrnorms[1] = std::sqrt( globerrnorms[1] / globerrnorms[2] );
2733 
2734  metrics.clear();
2735  metrics["L1Error"] = globerrnorms[0];
2736  metrics["L2Error"] = globerrnorms[1];
2737  metrics["LinfError"] = globerrnorms[3];
2738 
2739  if( verbose && is_root )
2740  {
2741  std::cout << "Error metrics when comparing " << projTagName << " against " << exactTagName << std::endl;
2742  std::cout << "\t Total Intersection area = " << globerrnorms[2] << std::endl;
2743  std::cout << "\t L_1 error = " << globerrnorms[0] << std::endl;
2744  std::cout << "\t L_2 error = " << globerrnorms[1] << std::endl;
2745  std::cout << "\t L_inf error = " << globerrnorms[3] << std::endl;
2746  }
2747 
2748  return moab::MB_SUCCESS;
2749 }

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

Referenced by main().

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

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

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().

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

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

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

484 { 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 598 of file TempestOnlineMap.hpp.

599 {
600  return col_gdofmap[localColID];
601 }

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

617 {
618  return m_nDofsPEl_Dest;
619 }

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

604 {
605  return globalColDoF + 1; // temporary
606 }

◆ GetIndexOfRowGlobalDoF()

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

Get the index of globaRowDoF.

Definition at line 592 of file TempestOnlineMap.hpp.

593 {
594  return globalRowDoF + 1;
595 }

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

483 { 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 587 of file TempestOnlineMap.hpp.

588 {
589  return row_gdofmap[localRowID];
590 }

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

610 {
611  return m_nDofsPEl_Src;
612 }

◆ IsConservative()

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

Determine if the map is conservative.

Definition at line 1192 of file TempestOnlineMap.cpp.

1193 {
1194 #ifndef MOAB_HAVE_MPI
1195 
1196  return OfflineMap::IsConservative( dTolerance );
1197 
1198 #else
1199  // return OfflineMap::IsConservative(dTolerance);
1200 
1201  int ierr;
1202  // Get map entries
1203  DataArray1D< int > dataRows;
1204  DataArray1D< int > dataCols;
1205  DataArray1D< double > dataEntries;
1206  const DataArray1D< double >& dTargetAreas = this->GetTargetAreas();
1207  const DataArray1D< double >& dSourceAreas = this->GetSourceAreas();
1208 
1209  // Calculate column sums
1210  std::vector< int > dColumnsUnique;
1211  std::vector< double > dColumnSums;
1212 
1213  int nColumns = m_mapRemap.GetColumns();
1214  m_mapRemap.GetEntries( dataRows, dataCols, dataEntries );
1215  dColumnSums.resize( m_nTotDofs_SrcCov, 0.0 );
1216  dColumnsUnique.resize( m_nTotDofs_SrcCov, -1 );
1217 
1218  for( unsigned i = 0; i < dataEntries.GetRows(); i++ )
1219  {
1220  dColumnSums[dataCols[i]] += dataEntries[i] * dTargetAreas[dataRows[i]] / dSourceAreas[dataCols[i]];
1221 
1222  assert( dataCols[i] < m_nTotDofs_SrcCov );
1223 
1224  // GID for column DoFs: col_gdofmap[ col_ldofmap [ dataCols[i] ] ]
1225  int colGID = this->GetColGlobalDoF( dataCols[i] ); // col_gdofmap[ col_ldofmap [ dataCols[i] ] ];
1226  // int colGID = col_gdofmap[ col_ldofmap [ dataCols[i] ] ];
1227  dColumnsUnique[dataCols[i]] = colGID;
1228 
1229  // std::cout << "Column dataCols[i]=" << dataCols[i] << " with GID = " << colGID <<
1230  // std::endl;
1231  }
1232 
1233  int rootProc = 0;
1234  std::vector< int > nElementsInProc;
1235  const int nDATA = 3;
1236  nElementsInProc.resize( size * nDATA );
1237  int senddata[nDATA] = { nColumns, m_nTotDofs_SrcCov, m_nTotDofs_Src };
1238  ierr = MPI_Gather( senddata, nDATA, MPI_INT, nElementsInProc.data(), nDATA, MPI_INT, rootProc, m_pcomm->comm() );
1239  if( ierr != MPI_SUCCESS ) return -1;
1240 
1241  int nTotVals = 0, nTotColumns = 0; // nTotColumnsUnq = 0;
1242  std::vector< int > dColumnIndices;
1243  std::vector< double > dColumnSourceAreas;
1244  std::vector< double > dColumnSumsTotal;
1245  std::vector< int > displs, rcount;
1246  if( rank == rootProc )
1247  {
1248  displs.resize( size + 1, 0 );
1249  rcount.resize( size, 0 );
1250  int gsum = 0;
1251  for( int ir = 0; ir < size; ++ir )
1252  {
1253  nTotVals += nElementsInProc[ir * nDATA];
1254  nTotColumns += nElementsInProc[ir * nDATA + 1];
1255  // nTotColumnsUnq += nElementsInProc[ir * nDATA + 2];
1256 
1257  displs[ir] = gsum;
1258  rcount[ir] = nElementsInProc[ir * nDATA + 1];
1259  gsum += rcount[ir];
1260 
1261  // printf( "%d: nTotColumns: %d, Displs: %d, rcount: %d, gsum = %d\n", ir, nTotColumns, displs[ir], rcount[ir], gsum );
1262  }
1263 
1264  printf( "Total nnz: %d, global source elements = %d\n", nTotVals, gsum );
1265 
1266  dColumnIndices.resize( nTotColumns, -1 );
1267  dColumnSumsTotal.resize( nTotColumns, 0.0 );
1268  // dColumnSourceAreas.resize ( nTotColumns, 0.0 );
1269  }
1270 
1271  // Gather all ColumnSums to root process and accumulate
1272  // We expect that the sums of all columns equate to 1.0 within user specified tolerance
1273  // Need to do a gatherv here since different processes have different number of elements
1274  // MPI_Reduce(&dColumnSums[0], &dColumnSumsTotal[0], m_mapRemap.GetColumns(), MPI_DOUBLE,
1275  // MPI_SUM, 0, m_pcomm->comm());
1276  ierr = MPI_Gatherv( &dColumnsUnique[0], m_nTotDofs_SrcCov, MPI_INT, &dColumnIndices[0], rcount.data(),
1277  displs.data(), MPI_INT, rootProc, m_pcomm->comm() );
1278  if( ierr != MPI_SUCCESS ) return -1;
1279  ierr = MPI_Gatherv( &dColumnSums[0], m_nTotDofs_SrcCov, MPI_DOUBLE, &dColumnSumsTotal[0], rcount.data(),
1280  displs.data(), MPI_DOUBLE, rootProc, m_pcomm->comm() );
1281  if( ierr != MPI_SUCCESS ) return -1;
1282  // ierr = MPI_Gatherv ( &dSourceAreas[0], m_nTotDofs_SrcCov, MPI_DOUBLE, &dColumnSourceAreas[0],
1283  // rcount.data(), displs.data(), MPI_DOUBLE, rootProc, m_pcomm->comm() ); if ( ierr !=
1284  // MPI_SUCCESS ) return -1;
1285 
1286  // Clean out unwanted arrays now
1287  dColumnSums.clear();
1288  dColumnsUnique.clear();
1289 
1290  // Verify all column sums equal the input Jacobian
1291  int fConservative = 0;
1292  if( rank == rootProc )
1293  {
1294  displs[size] = ( nTotColumns );
1295  // std::vector<double> dColumnSumsOnRoot(nTotColumnsUnq, 0.0);
1296  std::map< int, double > dColumnSumsOnRoot;
1297  // std::map<int, double> dColumnSourceAreasOnRoot;
1298  for( int ir = 0; ir < size; ir++ )
1299  {
1300  for( int ips = displs[ir]; ips < displs[ir + 1]; ips++ )
1301  {
1302  if( dColumnIndices[ips] < 0 ) continue;
1303  // printf("%d, %d: dColumnIndices[ips]: %d\n", ir, ips, dColumnIndices[ips]);
1304  // assert( dColumnIndices[ips] < nTotColumnsUnq );
1305  dColumnSumsOnRoot[dColumnIndices[ips]] += dColumnSumsTotal[ips]; // / dColumnSourceAreas[ips];
1306  // dColumnSourceAreasOnRoot[ dColumnIndices[ips] ] = dColumnSourceAreas[ips];
1307  // dColumnSourceAreas[ dColumnIndices[ips] ]
1308  }
1309  }
1310 
1311  for( std::map< int, double >::iterator it = dColumnSumsOnRoot.begin(); it != dColumnSumsOnRoot.end(); ++it )
1312  {
1313  // if ( fabs ( it->second - dColumnSourceAreasOnRoot[it->first] ) > dTolerance )
1314  if( fabs( it->second - 1.0 ) > dTolerance )
1315  {
1316  fConservative++;
1317  Announce( "TempestOnlineMap is not conservative in column "
1318  // "%i (%1.15e)", it->first, it->second );
1319  "%i (%1.15e)",
1320  it->first, it->second /* / dColumnSourceAreasOnRoot[it->first] */ );
1321  }
1322  }
1323  }
1324 
1325  // TODO: Just do a broadcast from root instead of a reduction
1326  ierr = MPI_Bcast( &fConservative, 1, MPI_INT, rootProc, m_pcomm->comm() );
1327  if( ierr != MPI_SUCCESS ) return -1;
1328 
1329  return fConservative;
1330 #endif
1331 }

◆ IsConsistent()

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

Determine if the map is first-order accurate.

Definition at line 1146 of file TempestOnlineMap.cpp.

1147 {
1148 #ifndef MOAB_HAVE_MPI
1149 
1150  return OfflineMap::IsConsistent( dTolerance );
1151 
1152 #else
1153 
1154  // Get map entries
1155  DataArray1D< int > dataRows;
1156  DataArray1D< int > dataCols;
1157  DataArray1D< double > dataEntries;
1158 
1159  // Calculate row sums
1160  DataArray1D< double > dRowSums;
1161  m_mapRemap.GetEntries( dataRows, dataCols, dataEntries );
1162  dRowSums.Allocate( m_mapRemap.GetRows() );
1163 
1164  for( unsigned i = 0; i < dataRows.GetRows(); i++ )
1165  {
1166  dRowSums[dataRows[i]] += dataEntries[i];
1167  }
1168 
1169  // Verify all row sums are equal to 1
1170  int fConsistent = 0;
1171  for( unsigned i = 0; i < dRowSums.GetRows(); i++ )
1172  {
1173  if( fabs( dRowSums[i] - 1.0 ) > dTolerance )
1174  {
1175  fConsistent++;
1176  int rowGID = row_gdofmap[i];
1177  Announce( "TempestOnlineMap is not consistent in row %i (%1.15e)", rowGID, dRowSums[i] );
1178  }
1179  }
1180 
1181  int ierr;
1182  int fConsistentGlobal = 0;
1183  ierr = MPI_Allreduce( &fConsistent, &fConsistentGlobal, 1, MPI_INT, MPI_SUM, m_pcomm->comm() );
1184  if( ierr != MPI_SUCCESS ) return -1;
1185 
1186  return fConsistentGlobal;
1187 #endif
1188 }

◆ IsMonotone()

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

Determine if the map is monotone.

Definition at line 1335 of file TempestOnlineMap.cpp.

1336 {
1337 #ifndef MOAB_HAVE_MPI
1338 
1339  return OfflineMap::IsMonotone( dTolerance );
1340 
1341 #else
1342 
1343  // Get map entries
1344  DataArray1D< int > dataRows;
1345  DataArray1D< int > dataCols;
1346  DataArray1D< double > dataEntries;
1347 
1348  m_mapRemap.GetEntries( dataRows, dataCols, dataEntries );
1349 
1350  // Verify all entries are in the range [0,1]
1351  int fMonotone = 0;
1352  for( unsigned i = 0; i < dataRows.GetRows(); i++ )
1353  {
1354  if( ( dataEntries[i] < -dTolerance ) || ( dataEntries[i] > 1.0 + dTolerance ) )
1355  {
1356  fMonotone++;
1357 
1358  Announce( "TempestOnlineMap is not monotone in entry (%i): %1.15e", i, dataEntries[i] );
1359  }
1360  }
1361 
1362  int ierr;
1363  int fMonotoneGlobal = 0;
1364  ierr = MPI_Allreduce( &fMonotone, &fMonotoneGlobal, 1, MPI_INT, MPI_SUM, m_pcomm->comm() );
1365  if( ierr != MPI_SUCCESS ) return -1;
1366 
1367  return fMonotoneGlobal;
1368 #endif
1369 }

◆ 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  m_weightMatrix.setFromTriplets( tripletList.begin(), tripletList.end() );
1673  // Reset the source and target data first
1674  m_rowVector.setZero();
1675  m_colVector.setZero();
1676 #ifdef VERBOSE
1677  serializeSparseMatrix( m_weightMatrix, "map_operator_" + std::to_string( rank ) + ".txt" );
1678 #endif
1679 // #ifdef MOAB_HAVE_EIGEN3
1680 #endif
1681  // TODO: make this flexible and read the order from map with help of metadata
1682  m_nDofsPEl_Src = 1; // always assume FV-FV maps are read from file
1683  m_nDofsPEl_Dest = 1; // always assume FV-FV maps are read from file
1684 
1685  return moab::MB_SUCCESS;
1686 }

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

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

References MB_SUCCESS.

◆ set_row_dc_dofs()

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

Definition at line 420 of file TempestOnlineMap.cpp.

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

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

628 {
629  m_nDofsPEl_Dest = nt;
630 }

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

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

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

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

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

476  {
477  m_meshInput = imesh;
478  };

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

623 {
624  m_nDofsPEl_Src = ns;
625 }

◆ setup_sizes_dimensions()

void moab::TempestOnlineMap::setup_sizes_dimensions ( )
private

Definition at line 95 of file TempestOnlineMap.cpp.

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

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

Referenced by ApplyWeights(), and GetColDofMap().

◆ col_gdofmap

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

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

◆ dataGLLNodesDest

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

Definition at line 563 of file TempestOnlineMap.hpp.

◆ dataGLLNodesSrc

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

Definition at line 563 of file TempestOnlineMap.hpp.

◆ dataGLLNodesSrcCov

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

Definition at line 563 of file TempestOnlineMap.hpp.

◆ is_parallel

bool moab::TempestOnlineMap::is_parallel
private

Definition at line 578 of file TempestOnlineMap.hpp.

Referenced by TempestOnlineMap().

◆ is_root

bool moab::TempestOnlineMap::is_root
private

Definition at line 578 of file TempestOnlineMap.hpp.

Referenced by TempestOnlineMap().

◆ m_bConserved

bool moab::TempestOnlineMap::m_bConserved
private

Definition at line 570 of file TempestOnlineMap.hpp.

◆ m_destDiscType

DiscretizationType moab::TempestOnlineMap::m_destDiscType
private

Definition at line 564 of file TempestOnlineMap.hpp.

◆ m_dofTagDest

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

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

◆ m_eInputType

DiscretizationType moab::TempestOnlineMap::m_eInputType
private

Definition at line 569 of file TempestOnlineMap.hpp.

◆ m_eOutputType

DiscretizationType moab::TempestOnlineMap::m_eOutputType
private

Definition at line 569 of file TempestOnlineMap.hpp.

◆ m_iMonotonicity

int moab::TempestOnlineMap::m_iMonotonicity
private

Definition at line 571 of file TempestOnlineMap.hpp.

◆ m_input_order

int moab::TempestOnlineMap::m_input_order
private

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

Referenced by TempestOnlineMap().

◆ m_meshInput

Mesh* moab::TempestOnlineMap::m_meshInput
private

Definition at line 573 of file TempestOnlineMap.hpp.

Referenced by SetMeshInput(), and TempestOnlineMap().

◆ m_meshInputCov

Mesh* moab::TempestOnlineMap::m_meshInputCov
private

Definition at line 574 of file TempestOnlineMap.hpp.

Referenced by TempestOnlineMap().

◆ m_meshOutput

Mesh* moab::TempestOnlineMap::m_meshOutput
private

Definition at line 575 of file TempestOnlineMap.hpp.

Referenced by TempestOnlineMap().

◆ m_meshOverlap

Mesh* moab::TempestOnlineMap::m_meshOverlap
private

Definition at line 576 of file TempestOnlineMap.hpp.

Referenced by TempestOnlineMap().

◆ m_nDofsPEl_Dest

int moab::TempestOnlineMap::m_nDofsPEl_Dest
private

Definition at line 568 of file TempestOnlineMap.hpp.

◆ m_nDofsPEl_Src

int moab::TempestOnlineMap::m_nDofsPEl_Src
private

Definition at line 568 of file TempestOnlineMap.hpp.

◆ m_nTotDofs_Dest

int moab::TempestOnlineMap::m_nTotDofs_Dest
private

Definition at line 565 of file TempestOnlineMap.hpp.

Referenced by ApplyWeights(), and LinearRemapNN_MOAB().

◆ m_nTotDofs_Src

int moab::TempestOnlineMap::m_nTotDofs_Src
private

Definition at line 565 of file TempestOnlineMap.hpp.

◆ m_nTotDofs_SrcCov

int moab::TempestOnlineMap::m_nTotDofs_SrcCov
private

Definition at line 565 of file TempestOnlineMap.hpp.

Referenced by ApplyWeights(), and LinearRemapNN_MOAB().

◆ m_output_order

int moab::TempestOnlineMap::m_output_order
private

Definition at line 561 of file TempestOnlineMap.hpp.

Referenced by TempestOnlineMap().

◆ m_remapper

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

The fundamental remapping operator object.

Definition at line 528 of file TempestOnlineMap.hpp.

Referenced by TempestOnlineMap().

◆ m_srcDiscType

DiscretizationType moab::TempestOnlineMap::m_srcDiscType
private

Definition at line 564 of file TempestOnlineMap.hpp.

◆ rank

int moab::TempestOnlineMap::rank
private

Definition at line 579 of file TempestOnlineMap.hpp.

Referenced by ApplyWeights(), and TempestOnlineMap().

◆ row_dtoc_dofmap

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

Definition at line 558 of file TempestOnlineMap.hpp.

Referenced by ApplyWeights(), and GetRowDofMap().

◆ row_gdofmap

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

Definition at line 555 of file TempestOnlineMap.hpp.

Referenced by ApplyWeights(), and LinearRemapNN_MOAB().

◆ rowMap

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

Definition at line 560 of file TempestOnlineMap.hpp.

◆ size

int moab::TempestOnlineMap::size
private

Definition at line 579 of file TempestOnlineMap.hpp.

Referenced by ApplyWeights(), and TempestOnlineMap().

◆ srccol_dtoc_dofmap

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

Definition at line 558 of file TempestOnlineMap.hpp.

◆ srccol_gdofmap

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

Definition at line 555 of file TempestOnlineMap.hpp.


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