16 #include "DataArray3D.h"
17 #include "FiniteElementTools.h"
18 #include "TriangularQuadrature.h"
19 #include "GaussQuadrature.h"
20 #include "GaussLobattoQuadrature.h"
21 #include "SparseMatrix.h"
22 #include "STLStringHelper.h"
23 #include "LinearRemapFV.h"
33 #ifdef MOAB_HAVE_NETCDFPAR
36 #include "netcdfcpp.h"
47 #define MPI_CHK_ERR( err ) \
50 std::cout << "MPI Failure. ErrorCode (" << ( err ) << ") "; \
51 std::cout << "\nMPI Aborting... \n"; \
52 return moab::MB_FAILURE; \
60 m_pcomm =
m_remapper->get_parallel_communicator();
82 std::vector< std::string > dimNames;
83 std::vector< int > dimSizes;
84 dimNames.push_back(
"num_elem" );
85 dimSizes.push_back( m_meshInputCov->faces.size() );
87 this->InitializeSourceDimensions( dimNames, dimSizes );
92 std::vector< std::string > dimNames;
93 std::vector< int > dimSizes;
94 dimNames.push_back(
"num_elem" );
95 dimSizes.push_back( m_meshOutput->faces.size() );
97 this->InitializeTargetDimensions( dimNames, dimSizes );
111 m_meshOverlap = NULL;
117 const std::string tgtDofTagName )
122 tagSize = ( m_eInputType == DiscretizationType_FV ? 1 : m_nDofsPEl_Src * m_nDofsPEl_Src );
128 int ntot_elements = 0, nelements = m_remapper->m_source_entities.size();
130 int ierr = MPI_Allreduce( &nelements, &ntot_elements, 1, MPI_INT, MPI_SUM, m_pcomm->comm() );
131 if( ierr != 0 )
MB_CHK_SET_ERR( MB_FAILURE,
"MPI_Allreduce failed to get total source elements" );
133 ntot_elements = nelements;
136 rval = m_remapper->GenerateCSMeshMetadata( ntot_elements, m_remapper->m_covering_source_entities,
137 &m_remapper->m_source_entities, srcDofTagName, m_nDofsPEl_Src );
MB_CHK_ERR( rval );
139 rval = m_interface->tag_get_handle( srcDofTagName.c_str(), m_nDofsPEl_Src * m_nDofsPEl_Src,
MB_TYPE_INTEGER,
145 tagSize = ( m_eOutputType == DiscretizationType_FV ? 1 : m_nDofsPEl_Dest * m_nDofsPEl_Dest );
150 int ntot_elements = 0, nelements = m_remapper->m_target_entities.size();
152 int ierr = MPI_Allreduce( &nelements, &ntot_elements, 1, MPI_INT, MPI_SUM, m_pcomm->comm() );
153 if( ierr != 0 )
MB_CHK_SET_ERR( MB_FAILURE,
"MPI_Allreduce failed to get total source elements" );
155 ntot_elements = nelements;
158 rval = m_remapper->GenerateCSMeshMetadata( ntot_elements, m_remapper->m_target_entities, NULL, tgtDofTagName,
161 rval = m_interface->tag_get_handle( tgtDofTagName.c_str(), m_nDofsPEl_Dest * m_nDofsPEl_Dest,
MB_TYPE_INTEGER,
173 bool isSrcContinuous,
174 DataArray3D< int >* srcdataGLLNodes,
175 DataArray3D< int >* srcdataGLLNodesSrc,
177 bool isTgtContinuous,
178 DataArray3D< int >* tgtdataGLLNodes )
181 std::vector< bool > dgll_cgll_row_ldofmap, dgll_cgll_col_ldofmap, dgll_cgll_covcol_ldofmap;
182 std::vector< int > src_soln_gdofs, locsrc_soln_gdofs, tgt_soln_gdofs;
185 m_srcDiscType = srcType;
186 m_destDiscType = destType;
188 bool vprint = is_root &&
false;
194 src_soln_gdofs.resize( m_remapper->m_covering_source_entities.size() * m_nDofsPEl_Src * m_nDofsPEl_Src, -1 );
195 rval = m_interface->tag_get_data( m_dofTagSrc, m_remapper->m_covering_source_entities, &src_soln_gdofs[0] );
MB_CHK_ERR( rval );
196 locsrc_soln_gdofs.resize( m_remapper->m_source_entities.size() * m_nDofsPEl_Src * m_nDofsPEl_Src );
197 rval = m_interface->tag_get_data( m_dofTagSrc, m_remapper->m_source_entities, &locsrc_soln_gdofs[0] );
MB_CHK_ERR( rval );
198 tgt_soln_gdofs.resize( m_remapper->m_target_entities.size() * m_nDofsPEl_Dest * m_nDofsPEl_Dest );
199 rval = m_interface->tag_get_data( m_dofTagDest, m_remapper->m_target_entities, &tgt_soln_gdofs[0] );
MB_CHK_ERR( rval );
204 std::ofstream output_file(
"sourcecov-gids-0.txt" );
205 output_file <<
"I, GDOF\n";
206 for(
unsigned i = 0; i < src_soln_gdofs.size(); ++i )
207 output_file << i <<
", " << src_soln_gdofs[i] <<
"\n";
209 output_file <<
"ELEMID, IDOF, LDOF, GDOF, NDOF\n";
210 m_nTotDofs_SrcCov = 0;
211 if( isSrcContinuous )
212 dgll_cgll_covcol_ldofmap.resize(
213 m_remapper->m_covering_source_entities.size() * m_nDofsPEl_Src * m_nDofsPEl_Src,
false );
214 for(
unsigned j = 0; j < m_remapper->m_covering_source_entities.size(); j++ )
216 for(
int p = 0; p < m_nDofsPEl_Src; p++ )
218 for(
int q = 0; q < m_nDofsPEl_Src; q++ )
220 const int localDOF = ( *srcdataGLLNodes )[p][q][j] - 1;
221 const int offsetDOF = j * m_nDofsPEl_Src * m_nDofsPEl_Src + p * m_nDofsPEl_Src + q;
222 if( isSrcContinuous && !dgll_cgll_covcol_ldofmap[localDOF] )
225 dgll_cgll_covcol_ldofmap[localDOF] =
true;
227 output_file << m_remapper->lid_to_gid_covsrc[j] <<
", " << offsetDOF <<
", " << localDOF
228 <<
", " << src_soln_gdofs[offsetDOF] <<
", " << m_nTotDofs_SrcCov <<
"\n";
234 dgll_cgll_covcol_ldofmap.clear();
238 std::ofstream output_file(
"source-gids-0.txt" );
239 output_file <<
"I, GDOF\n";
240 for(
unsigned i = 0; i < locsrc_soln_gdofs.size(); ++i )
241 output_file << i <<
", " << locsrc_soln_gdofs[i] <<
"\n";
243 output_file <<
"ELEMID, IDOF, LDOF, GDOF, NDOF\n";
245 if( isSrcContinuous )
246 dgll_cgll_col_ldofmap.resize(
247 m_remapper->m_source_entities.size() * m_nDofsPEl_Src * m_nDofsPEl_Src,
false );
248 for(
unsigned j = 0; j < m_remapper->m_source_entities.size(); j++ )
250 for(
int p = 0; p < m_nDofsPEl_Src; p++ )
252 for(
int q = 0; q < m_nDofsPEl_Src; q++ )
254 const int localDOF = ( *srcdataGLLNodesSrc )[p][q][j] - 1;
255 const int offsetDOF = j * m_nDofsPEl_Src * m_nDofsPEl_Src + p * m_nDofsPEl_Src + q;
256 if( isSrcContinuous && !dgll_cgll_col_ldofmap[localDOF] )
259 dgll_cgll_col_ldofmap[localDOF] =
true;
261 output_file << m_remapper->lid_to_gid_src[j] <<
", " << offsetDOF <<
", " << localDOF
262 <<
", " << locsrc_soln_gdofs[offsetDOF] <<
", " << m_nTotDofs_Src <<
"\n";
268 dgll_cgll_col_ldofmap.clear();
272 std::ofstream output_file(
"target-gids-0.txt" );
273 output_file <<
"I, GDOF\n";
274 for(
unsigned i = 0; i < tgt_soln_gdofs.size(); ++i )
275 output_file << i <<
", " << tgt_soln_gdofs[i] <<
"\n";
277 output_file <<
"ELEMID, IDOF, GDOF, NDOF\n";
280 for(
unsigned i = 0; i < tgt_soln_gdofs.size(); ++i )
282 output_file << m_remapper->lid_to_gid_tgt[i] <<
", " << i <<
", " << tgt_soln_gdofs[i] <<
", "
283 << m_nTotDofs_Dest <<
"\n";
294 std::ofstream output_file(
"sourcecov-gids-1.txt" );
295 output_file <<
"I, GDOF\n";
296 for(
unsigned i = 0; i < src_soln_gdofs.size(); ++i )
297 output_file << i <<
", " << src_soln_gdofs[i] <<
"\n";
299 output_file <<
"ELEMID, IDOF, LDOF, GDOF, NDOF\n";
300 m_nTotDofs_SrcCov = 0;
301 if( isSrcContinuous )
302 dgll_cgll_covcol_ldofmap.resize(
303 m_remapper->m_covering_source_entities.size() * m_nDofsPEl_Src * m_nDofsPEl_Src,
false );
304 for(
unsigned j = 0; j < m_remapper->m_covering_source_entities.size(); j++ )
306 for(
int p = 0; p < m_nDofsPEl_Src; p++ )
308 for(
int q = 0; q < m_nDofsPEl_Src; q++ )
310 const int localDOF = ( *srcdataGLLNodes )[p][q][j] - 1;
311 const int offsetDOF = j * m_nDofsPEl_Src * m_nDofsPEl_Src + p * m_nDofsPEl_Src + q;
312 if( isSrcContinuous && !dgll_cgll_covcol_ldofmap[localDOF] )
315 dgll_cgll_covcol_ldofmap[localDOF] =
true;
317 output_file << m_remapper->lid_to_gid_covsrc[j] <<
", " << offsetDOF <<
", " << localDOF
318 <<
", " << src_soln_gdofs[offsetDOF] <<
", " << m_nTotDofs_SrcCov <<
"\n";
324 dgll_cgll_covcol_ldofmap.clear();
328 std::ofstream output_file(
"source-gids-1.txt" );
329 output_file <<
"I, GDOF\n";
330 for(
unsigned i = 0; i < locsrc_soln_gdofs.size(); ++i )
331 output_file << i <<
", " << locsrc_soln_gdofs[i] <<
"\n";
333 output_file <<
"ELEMID, IDOF, LDOF, GDOF, NDOF\n";
335 if( isSrcContinuous )
336 dgll_cgll_col_ldofmap.resize(
337 m_remapper->m_source_entities.size() * m_nDofsPEl_Src * m_nDofsPEl_Src,
false );
338 for(
unsigned j = 0; j < m_remapper->m_source_entities.size(); j++ )
340 for(
int p = 0; p < m_nDofsPEl_Src; p++ )
342 for(
int q = 0; q < m_nDofsPEl_Src; q++ )
344 const int localDOF = ( *srcdataGLLNodesSrc )[p][q][j] - 1;
345 const int offsetDOF = j * m_nDofsPEl_Src * m_nDofsPEl_Src + p * m_nDofsPEl_Src + q;
346 if( isSrcContinuous && !dgll_cgll_col_ldofmap[localDOF] )
349 dgll_cgll_col_ldofmap[localDOF] =
true;
351 output_file << m_remapper->lid_to_gid_src[j] <<
", " << offsetDOF <<
", " << localDOF
352 <<
", " << locsrc_soln_gdofs[offsetDOF] <<
", " << m_nTotDofs_Src <<
"\n";
358 dgll_cgll_col_ldofmap.clear();
362 std::ofstream output_file(
"target-gids-1.txt" );
363 output_file <<
"I, GDOF\n";
364 for(
unsigned i = 0; i < tgt_soln_gdofs.size(); ++i )
365 output_file << i <<
", " << tgt_soln_gdofs[i] <<
"\n";
367 output_file <<
"ELEMID, IDOF, GDOF, NDOF\n";
370 for(
unsigned i = 0; i < tgt_soln_gdofs.size(); ++i )
372 output_file << m_remapper->lid_to_gid_tgt[i] <<
", " << i <<
", " << tgt_soln_gdofs[i] <<
", "
373 << m_nTotDofs_Dest <<
"\n";
385 int srcTagSize = ( m_eInputType == DiscretizationType_FV ? 1 : m_nDofsPEl_Src * m_nDofsPEl_Src );
386 if( m_remapper->point_cloud_source )
388 assert( m_nDofsPEl_Src == 1 );
389 col_gdofmap.resize( m_remapper->m_covering_source_vertices.size(), UINT_MAX );
390 col_dtoc_dofmap.resize( m_remapper->m_covering_source_vertices.size(), UINT_MAX );
391 src_soln_gdofs.resize( m_remapper->m_covering_source_vertices.size(), UINT_MAX );
392 rval = m_interface->tag_get_data( m_dofTagSrc, m_remapper->m_covering_source_vertices, &src_soln_gdofs[0] );
MB_CHK_ERR( rval );
397 col_gdofmap.resize( m_remapper->m_covering_source_entities.size() * srcTagSize, UINT_MAX );
398 col_dtoc_dofmap.resize( m_remapper->m_covering_source_entities.size() * srcTagSize, UINT_MAX );
399 src_soln_gdofs.resize( m_remapper->m_covering_source_entities.size() * srcTagSize, UINT_MAX );
400 rval = m_interface->tag_get_data( m_dofTagSrc, m_remapper->m_covering_source_entities, &src_soln_gdofs[0] );
MB_CHK_ERR( rval );
406 #ifdef ALTERNATE_NUMBERING_IMPLEMENTATION
407 unsigned maxSrcIndx = 0;
410 std::vector< int > locdofs( srcTagSize );
411 std::map< Node, moab::EntityHandle > mapLocalMBNodes;
413 for(
unsigned iel = 0; iel < m_remapper->m_covering_source_entities.size(); ++iel )
415 EntityHandle eh = m_remapper->m_covering_source_entities[iel];
416 rval = m_interface->get_coords( &eh, 1, elcoords );
MB_CHK_ERR( rval );
417 Node elCentroid( elcoords[0], elcoords[1], elcoords[2] );
418 mapLocalMBNodes.insert( std::pair< Node, moab::EntityHandle >( elCentroid, eh ) );
421 const NodeVector& nodes = m_remapper->m_covering_source->nodes;
422 for(
unsigned j = 0; j < m_remapper->m_covering_source->faces.size(); j++ )
424 const Face&
face = m_remapper->m_covering_source->faces[j];
427 centroid.x = centroid.y = centroid.z = 0.0;
428 for(
unsigned l = 0; l <
face.edges.size(); ++l )
430 centroid.x += nodes[
face[l]].x;
431 centroid.y += nodes[
face[l]].y;
432 centroid.z += nodes[
face[l]].z;
434 const double factor = 1.0 /
face.edges.size();
435 centroid.x *= factor;
436 centroid.y *= factor;
437 centroid.z *= factor;
440 if( mapLocalMBNodes.find( centroid ) != mapLocalMBNodes.end() )
442 current_eh = mapLocalMBNodes[centroid];
445 rval = m_interface->tag_get_data( m_dofTagSrc, ¤t_eh, 1, &locdofs[0] );
MB_CHK_ERR( rval );
446 for(
int p = 0; p < m_nDofsPEl_Src; p++ )
448 for(
int q = 0; q < m_nDofsPEl_Src; q++ )
450 const int localDOF = ( *srcdataGLLNodes )[p][q][j] - 1;
451 const int offsetDOF = p * m_nDofsPEl_Src + q;
452 maxSrcIndx = ( localDOF > maxSrcIndx ? localDOF : maxSrcIndx );
453 std::cout <<
"Col: " << current_eh <<
", " << m_remapper->lid_to_gid_covsrc[j] <<
", " << offsetDOF
454 <<
", " << localDOF <<
", " << locdofs[offsetDOF] - 1 <<
", " << maxSrcIndx <<
"\n";
460 m_nTotDofs_SrcCov = 0;
461 if( srcdataGLLNodes == NULL )
463 for(
unsigned i = 0; i < col_gdofmap.size(); ++i )
465 assert( src_soln_gdofs[i] > 0 );
466 col_gdofmap[i] = src_soln_gdofs[i] - 1;
467 col_dtoc_dofmap[i] = i;
468 if( vprint ) std::cout <<
"Col: " << i <<
", " << col_gdofmap[i] <<
"\n";
474 if( isSrcContinuous )
475 dgll_cgll_covcol_ldofmap.resize( m_remapper->m_covering_source_entities.size() * srcTagSize,
false );
477 for(
unsigned j = 0; j < m_remapper->m_covering_source_entities.size(); j++ )
479 for(
int p = 0; p < m_nDofsPEl_Src; p++ )
481 for(
int q = 0; q < m_nDofsPEl_Src; q++ )
483 const int localDOF = ( *srcdataGLLNodes )[p][q][j] - 1;
484 const int offsetDOF = j * srcTagSize + p * m_nDofsPEl_Src + q;
485 if( isSrcContinuous && !dgll_cgll_covcol_ldofmap[localDOF] )
488 dgll_cgll_covcol_ldofmap[localDOF] =
true;
490 if( !isSrcContinuous ) m_nTotDofs_SrcCov++;
491 assert( src_soln_gdofs[offsetDOF] > 0 );
492 col_gdofmap[localDOF] = src_soln_gdofs[offsetDOF] - 1;
493 col_dtoc_dofmap[offsetDOF] = localDOF;
495 std::cout <<
"Col: " << m_remapper->lid_to_gid_covsrc[j] <<
", " << offsetDOF <<
", "
496 << localDOF <<
", " << col_gdofmap[offsetDOF] <<
", " << m_nTotDofs_SrcCov <<
"\n";
502 if( m_remapper->point_cloud_source )
504 assert( m_nDofsPEl_Src == 1 );
505 srccol_gdofmap.resize( m_remapper->m_source_vertices.size(), UINT_MAX );
506 srccol_dtoc_dofmap.resize( m_remapper->m_covering_source_vertices.size(), UINT_MAX );
507 locsrc_soln_gdofs.resize( m_remapper->m_source_vertices.size(), UINT_MAX );
508 rval = m_interface->tag_get_data( m_dofTagSrc, m_remapper->m_source_vertices, &locsrc_soln_gdofs[0] );
MB_CHK_ERR( rval );
512 srccol_gdofmap.resize( m_remapper->m_source_entities.size() * srcTagSize, UINT_MAX );
513 srccol_dtoc_dofmap.resize( m_remapper->m_source_entities.size() * srcTagSize, UINT_MAX );
514 locsrc_soln_gdofs.resize( m_remapper->m_source_entities.size() * srcTagSize, UINT_MAX );
515 rval = m_interface->tag_get_data( m_dofTagSrc, m_remapper->m_source_entities, &locsrc_soln_gdofs[0] );
MB_CHK_ERR( rval );
520 if( srcdataGLLNodesSrc == NULL )
522 for(
unsigned i = 0; i < srccol_gdofmap.size(); ++i )
524 assert( locsrc_soln_gdofs[i] > 0 );
525 srccol_gdofmap[i] = locsrc_soln_gdofs[i] - 1;
526 srccol_dtoc_dofmap[i] = i;
532 if( isSrcContinuous ) dgll_cgll_col_ldofmap.resize( m_remapper->m_source_entities.size() * srcTagSize,
false );
534 for(
unsigned j = 0; j < m_remapper->m_source_entities.size(); j++ )
536 for(
int p = 0; p < m_nDofsPEl_Src; p++ )
538 for(
int q = 0; q < m_nDofsPEl_Src; q++ )
540 const int localDOF = ( *srcdataGLLNodesSrc )[p][q][j] - 1;
541 const int offsetDOF = j * srcTagSize + p * m_nDofsPEl_Src + q;
542 if( isSrcContinuous && !dgll_cgll_col_ldofmap[localDOF] )
545 dgll_cgll_col_ldofmap[localDOF] =
true;
547 if( !isSrcContinuous ) m_nTotDofs_Src++;
548 assert( locsrc_soln_gdofs[offsetDOF] > 0 );
549 srccol_gdofmap[localDOF] = locsrc_soln_gdofs[offsetDOF] - 1;
550 srccol_dtoc_dofmap[offsetDOF] = localDOF;
556 int tgtTagSize = ( m_eOutputType == DiscretizationType_FV ? 1 : m_nDofsPEl_Dest * m_nDofsPEl_Dest );
557 if( m_remapper->point_cloud_target )
559 assert( m_nDofsPEl_Dest == 1 );
560 row_gdofmap.resize( m_remapper->m_target_vertices.size(), UINT_MAX );
561 row_dtoc_dofmap.resize( m_remapper->m_target_vertices.size(), UINT_MAX );
562 tgt_soln_gdofs.resize( m_remapper->m_target_vertices.size(), UINT_MAX );
563 rval = m_interface->tag_get_data( m_dofTagDest, m_remapper->m_target_vertices, &tgt_soln_gdofs[0] );
MB_CHK_ERR( rval );
568 row_gdofmap.resize( m_remapper->m_target_entities.size() * tgtTagSize, UINT_MAX );
569 row_dtoc_dofmap.resize( m_remapper->m_target_entities.size() * tgtTagSize, UINT_MAX );
570 tgt_soln_gdofs.resize( m_remapper->m_target_entities.size() * tgtTagSize, UINT_MAX );
571 rval = m_interface->tag_get_data( m_dofTagDest, m_remapper->m_target_entities, &tgt_soln_gdofs[0] );
MB_CHK_ERR( rval );
577 if( tgtdataGLLNodes == NULL )
579 for(
unsigned i = 0; i < row_gdofmap.size(); ++i )
581 assert( tgt_soln_gdofs[i] > 0 );
582 row_gdofmap[i] = tgt_soln_gdofs[i] - 1;
583 row_dtoc_dofmap[i] = i;
584 if( vprint ) std::cout <<
"Row: " << i <<
", " << row_gdofmap[i] <<
"\n";
590 if( isTgtContinuous ) dgll_cgll_row_ldofmap.resize( m_remapper->m_target_entities.size() * tgtTagSize,
false );
592 for(
unsigned j = 0; j < m_remapper->m_target_entities.size(); j++ )
594 for(
int p = 0; p < m_nDofsPEl_Dest; p++ )
596 for(
int q = 0; q < m_nDofsPEl_Dest; q++ )
598 const int localDOF = ( *tgtdataGLLNodes )[p][q][j] - 1;
599 const int offsetDOF = j * tgtTagSize + p * m_nDofsPEl_Dest + q;
600 if( isTgtContinuous && !dgll_cgll_row_ldofmap[localDOF] )
603 dgll_cgll_row_ldofmap[localDOF] =
true;
605 if( !isTgtContinuous ) m_nTotDofs_Dest++;
606 assert( tgt_soln_gdofs[offsetDOF] > 0 );
607 row_gdofmap[localDOF] = tgt_soln_gdofs[offsetDOF] - 1;
608 row_dtoc_dofmap[offsetDOF] = localDOF;
610 std::cout <<
"Row: " << m_remapper->lid_to_gid_tgt[j] <<
", " << offsetDOF <<
", " << localDOF
611 <<
", " << row_gdofmap[offsetDOF] <<
", " << m_nTotDofs_Dest <<
"\n";
618 #if defined( MOAB_HAVE_EIGEN3 ) && defined( VERBOSE )
621 std::cout <<
"[" << rank <<
"]"
622 <<
"DoFs: row = " << m_nTotDofs_Dest <<
", " << row_gdofmap.size() <<
", col = " << m_nTotDofs_Src
623 <<
", " << m_nTotDofs_SrcCov <<
", " << col_gdofmap.size() <<
"\n";
629 #ifdef CHECK_INCREASING_DOF
630 for(
size_t i = 0; i < row_gdofmap.size() - 1; i++ )
632 if( row_gdofmap[i] > row_gdofmap[i + 1] )
633 std::cout <<
" on rank " << rank <<
" in row_gdofmap[" << i <<
"]=" << row_gdofmap[i] <<
" > row_gdofmap["
634 << i + 1 <<
"]=" << row_gdofmap[i + 1] <<
" \n";
636 for(
size_t i = 0; i < col_gdofmap.size() - 1; i++ )
638 if( col_gdofmap[i] > col_gdofmap[i + 1] )
639 std::cout <<
" on rank " << rank <<
" in col_gdofmap[" << i <<
"]=" << col_gdofmap[i] <<
" > col_gdofmap["
640 << i + 1 <<
"]=" << col_gdofmap[i + 1] <<
" \n";
655 col_dtoc_dofmap.resize( values_entities.size() );
656 for(
int j = 0; j < (int)values_entities.size(); j++ )
658 if( colMap.find( values_entities[j] - 1 ) != colMap.end() )
659 col_dtoc_dofmap[j] = colMap[values_entities[j] - 1];
662 col_dtoc_dofmap[j] = -1;
675 row_dtoc_dofmap.resize( values_entities.size() );
676 for(
int j = 0; j < (int)values_entities.size(); j++ )
678 if( rowMap.find( values_entities[j] - 1 ) != rowMap.end() )
679 row_dtoc_dofmap[j] = rowMap[values_entities[j] - 1];
682 row_dtoc_dofmap[j] = -1;
692 std::string strOutputType,
693 const GenerateOfflineMapAlgorithmOptions& mapOptions,
694 const std::string& srcDofTagName,
695 const std::string& tgtDofTagName )
697 NcError
error( NcError::silent_nonfatal );
700 dbgprint.set_prefix(
"[TempestOnlineMap]: " );
703 const bool m_bPointCloudSource = ( m_remapper->point_cloud_source );
704 const bool m_bPointCloudTarget = ( m_remapper->point_cloud_target );
705 const bool m_bPointCloud = m_bPointCloudSource || m_bPointCloudTarget;
714 STLStringHelper::ToLower( strInputType );
715 STLStringHelper::ToLower( strOutputType );
720 if( strInputType ==
"fv" )
722 eInputType = DiscretizationType_FV;
724 else if( strInputType ==
"cgll" )
726 eInputType = DiscretizationType_CGLL;
728 else if( strInputType ==
"dgll" )
730 eInputType = DiscretizationType_DGLL;
732 else if( strInputType ==
"pcloud" )
734 eInputType = DiscretizationType_PCLOUD;
738 _EXCEPTION1(
"Invalid \"in_type\" value (%s), expected [fv|cgll|dgll]", strInputType.c_str() );
741 if( strOutputType ==
"fv" )
743 eOutputType = DiscretizationType_FV;
745 else if( strOutputType ==
"cgll" )
747 eOutputType = DiscretizationType_CGLL;
749 else if( strOutputType ==
"dgll" )
751 eOutputType = DiscretizationType_DGLL;
753 else if( strOutputType ==
"pcloud" )
755 eOutputType = DiscretizationType_PCLOUD;
759 _EXCEPTION1(
"Invalid \"out_type\" value (%s), expected [fv|cgll|dgll]", strOutputType.c_str() );
763 m_bConserved = !mapOptions.fNoConservation;
764 m_eInputType = eInputType;
765 m_eOutputType = eOutputType;
768 std::string strMapAlgorithm(
"" );
769 int nMonotoneType = ( mapOptions.fMonotone ) ? ( 1 ) : ( 0 );
772 std::set< std::string > setMethodStrings;
775 for(
size_t i = 0; i <= mapOptions.strMethod.length(); i++ )
777 if( ( i == mapOptions.strMethod.length() ) || ( mapOptions.strMethod[i] ==
';' ) )
779 std::string strMethodString = mapOptions.strMethod.substr( iLast, i - iLast );
780 STLStringHelper::RemoveWhitespaceInPlace( strMethodString );
781 if( strMethodString.length() > 0 )
783 setMethodStrings.insert( strMethodString );
790 for(
auto it : setMethodStrings )
795 if( nMonotoneType != 0 )
797 _EXCEPTIONT(
"Multiple monotonicity specifications found (--mono) or (--method \"mono#\")" );
799 if( ( m_eInputType == DiscretizationType_FV ) && ( m_eOutputType == DiscretizationType_FV ) )
801 _EXCEPTIONT(
"--method \"mono2\" is only used when remapping to/from CGLL or DGLL grids" );
807 else if( it ==
"mono3" )
809 if( nMonotoneType != 0 )
811 _EXCEPTIONT(
"Multiple monotonicity specifications found (--mono) or (--method \"mono#\")" );
813 if( ( m_eInputType == DiscretizationType_FV ) && ( m_eOutputType == DiscretizationType_FV ) )
815 _EXCEPTIONT(
"--method \"mono3\" is only used when remapping to/from CGLL or DGLL grids" );
821 else if( it ==
"volumetric" )
823 if( ( m_eInputType != DiscretizationType_FV ) || ( m_eOutputType == DiscretizationType_FV ) )
825 _EXCEPTIONT(
"--method \"volumetric\" may only be used for FV->CGLL or FV->DGLL remapping" );
827 strMapAlgorithm =
"volumetric";
831 else if( it ==
"invdist" )
833 if( ( m_eInputType != DiscretizationType_FV ) || ( m_eOutputType != DiscretizationType_FV ) )
835 _EXCEPTIONT(
"--method \"invdist\" may only be used for FV->FV remapping" );
837 strMapAlgorithm =
"invdist";
841 else if( it ==
"delaunay" )
843 if( ( m_eInputType != DiscretizationType_FV ) || ( m_eOutputType != DiscretizationType_FV ) )
845 _EXCEPTIONT(
"--method \"delaunay\" may only be used for FV->FV remapping" );
847 strMapAlgorithm =
"delaunay";
851 else if( it ==
"bilin" )
853 if( ( m_eInputType != DiscretizationType_FV ) || ( m_eOutputType != DiscretizationType_FV ) )
855 _EXCEPTIONT(
"--method \"bilin\" may only be used for FV->FV remapping" );
857 strMapAlgorithm =
"fvbilin";
861 else if( it ==
"intbilin" )
863 if( m_eOutputType != DiscretizationType_FV )
865 _EXCEPTIONT(
"--method \"intbilin\" may only be used when mapping to FV." );
867 if( m_eInputType == DiscretizationType_FV )
869 strMapAlgorithm =
"fvintbilin";
873 strMapAlgorithm =
"mono3";
878 else if( it ==
"intbilingb" )
880 if( ( m_eInputType != DiscretizationType_FV ) || ( m_eOutputType != DiscretizationType_FV ) )
882 _EXCEPTIONT(
"--method \"intbilingb\" may only be used for FV->FV remapping" );
884 strMapAlgorithm =
"fvintbilingb";
888 _EXCEPTION1(
"Invalid --method argument \"%s\"", it.c_str() );
893 ( m_eInputType == DiscretizationType_FV || m_eInputType == DiscretizationType_PCLOUD ? 1
896 ( m_eOutputType == DiscretizationType_FV || m_eOutputType == DiscretizationType_PCLOUD ? 1
897 : mapOptions.nPout );
899 rval = SetDOFmapTags( srcDofTagName, tgtDofTagName );
MB_CHK_ERR( rval );
903 rval = m_interface->tag_get_handle(
"aream", 1,
MB_TYPE_DOUBLE, areaTag,
907 if( is_root )
dbgprint.printf( 0,
"aream tag already defined \n" );
910 double dTotalAreaInput = 0.0, dTotalAreaOutput = 0.0;
911 if( !m_bPointCloudSource )
914 if( is_root )
dbgprint.printf( 0,
"Calculating input mesh Face areas\n" );
915 double dTotalAreaInput_loc = m_meshInput->CalculateFaceAreas( mapOptions.fSourceConcave );
916 rval = m_interface->tag_set_data( areaTag, m_remapper->m_source_entities, m_meshInput->vecFaceArea );
MB_CHK_ERR( rval );
917 dTotalAreaInput = dTotalAreaInput_loc;
920 MPI_Reduce( &dTotalAreaInput_loc, &dTotalAreaInput, 1, MPI_DOUBLE, MPI_SUM, 0, m_pcomm->comm() );
922 if( is_root )
dbgprint.printf( 0,
"Input Mesh Geometric Area: %1.15e\n", dTotalAreaInput );
925 m_meshInputCov->CalculateFaceAreas( mapOptions.fSourceConcave );
930 if( !m_bPointCloudTarget )
933 if( is_root )
dbgprint.printf( 0,
"Calculating output mesh Face areas\n" );
934 double dTotalAreaOutput_loc = m_meshOutput->CalculateFaceAreas( mapOptions.fTargetConcave );
935 dTotalAreaOutput = dTotalAreaOutput_loc;
938 MPI_Reduce( &dTotalAreaOutput_loc, &dTotalAreaOutput, 1, MPI_DOUBLE, MPI_SUM, 0, m_pcomm->comm() );
940 if( is_root )
dbgprint.printf( 0,
"Output Mesh Geometric Area: %1.15e\n", dTotalAreaOutput );
941 rval = m_interface->tag_set_data( areaTag, m_remapper->m_target_entities, m_meshOutput->vecFaceArea );
MB_CHK_ERR( rval );
949 int ixSourceFaceMax = ( -1 );
950 int ixTargetFaceMax = ( -1 );
952 if( m_meshOverlap->vecSourceFaceIx.size() != m_meshOverlap->vecTargetFaceIx.size() )
954 _EXCEPTIONT(
"Invalid overlap mesh:\n"
955 " Possible mesh file corruption?" );
958 for(
unsigned i = 0; i < m_meshOverlap->faces.size(); i++ )
960 if( m_meshOverlap->vecSourceFaceIx[i] + 1 > ixSourceFaceMax )
961 ixSourceFaceMax = m_meshOverlap->vecSourceFaceIx[i] + 1;
963 if( m_meshOverlap->vecTargetFaceIx[i] + 1 > ixTargetFaceMax )
964 ixTargetFaceMax = m_meshOverlap->vecTargetFaceIx[i] + 1;
968 if( m_meshInputCov->faces.size() - ixSourceFaceMax == 0 )
970 if( is_root )
dbgprint.printf( 0,
"Overlap mesh forward correspondence found\n" );
972 else if( m_meshOutput->faces.size() - ixSourceFaceMax == 0 )
974 if( is_root )
dbgprint.printf( 0,
"Overlap mesh reverse correspondence found (reversing)\n" );
977 m_meshOverlap->ExchangeFirstAndSecondMesh();
981 _EXCEPTION4(
"Invalid overlap mesh:\n No correspondence found with input and output meshes "
982 "(%i,%i) vs (%i,%i)",
983 m_meshInputCov->faces.size(), m_meshOutput->faces.size(), ixSourceFaceMax,
989 if( is_root )
dbgprint.printf( 0,
"Calculating overlap mesh Face areas\n" );
990 double dTotalAreaOverlap_loc = m_meshOverlap->CalculateFaceAreas(
false );
991 double dTotalAreaOverlap = dTotalAreaOverlap_loc;
994 MPI_Reduce( &dTotalAreaOverlap_loc, &dTotalAreaOverlap, 1, MPI_DOUBLE, MPI_SUM, 0, m_pcomm->comm() );
996 if( is_root )
dbgprint.printf( 0,
"Overlap Mesh Area: %1.15e\n", dTotalAreaOverlap );
1001 if( is_root )
dbgprint.printf( 0,
"Correcting source/target areas to overlap mesh areas\n" );
1002 DataArray1D< double > dSourceArea( m_meshInputCov->faces.size() );
1003 DataArray1D< double > dTargetArea( m_meshOutput->faces.size() );
1005 assert( m_meshOverlap->vecSourceFaceIx.size() == m_meshOverlap->faces.size() );
1006 assert( m_meshOverlap->vecTargetFaceIx.size() == m_meshOverlap->faces.size() );
1007 assert( m_meshOverlap->vecFaceArea.GetRows() == m_meshOverlap->faces.size() );
1009 assert( m_meshInputCov->vecFaceArea.GetRows() == m_meshInputCov->faces.size() );
1010 assert( m_meshOutput->vecFaceArea.GetRows() == m_meshOutput->faces.size() );
1012 for(
size_t i = 0; i < m_meshOverlap->faces.size(); i++ )
1014 if( m_meshOverlap->vecSourceFaceIx[i] < 0 || m_meshOverlap->vecTargetFaceIx[i] < 0 )
1018 assert(
static_cast< size_t >( m_meshOverlap->vecSourceFaceIx[i] ) < m_meshInputCov->faces.size() );
1019 dSourceArea[m_meshOverlap->vecSourceFaceIx[i]] += m_meshOverlap->vecFaceArea[i];
1020 assert(
static_cast< size_t >( m_meshOverlap->vecTargetFaceIx[i] ) < m_meshOutput->faces.size() );
1021 dTargetArea[m_meshOverlap->vecTargetFaceIx[i]] += m_meshOverlap->vecFaceArea[i];
1024 for(
size_t i = 0; i < m_meshInputCov->faces.size(); i++ )
1026 if( fabs( dSourceArea[i] - m_meshInputCov->vecFaceArea[i] ) < 1.0e-10 )
1028 m_meshInputCov->vecFaceArea[i] = dSourceArea[i];
1031 for(
size_t i = 0; i < m_meshOutput->faces.size(); i++ )
1033 if( fabs( dTargetArea[i] - m_meshOutput->vecFaceArea[i] ) < 1.0e-10 )
1035 m_meshOutput->vecFaceArea[i] = dTargetArea[i];
1041 if( !m_bPointCloudSource && eInputType == DiscretizationType_FV )
1043 this->SetSourceAreas( m_meshInputCov->vecFaceArea );
1044 if( m_meshInputCov->vecMask.size() )
1046 this->SetSourceMask( m_meshInputCov->vecMask );
1051 if( !m_bPointCloudTarget && eOutputType == DiscretizationType_FV )
1053 this->SetTargetAreas( m_meshOutput->vecFaceArea );
1054 if( m_meshOutput->vecMask.size() )
1056 this->SetTargetMask( m_meshOutput->vecMask );
1072 if( ( eInputType == DiscretizationType_FV ) && ( eOutputType == DiscretizationType_FV ) )
1075 if( m_meshInputCov->revnodearray.size() == 0 ) m_meshInputCov->ConstructReverseNodeArray();
1076 if( m_meshInputCov->edgemap.size() == 0 ) m_meshInputCov->ConstructEdgeMap(
false );
1079 this->InitializeSourceCoordinatesFromMeshFV( *m_meshInputCov );
1080 this->InitializeTargetCoordinatesFromMeshFV( *m_meshOutput );
1083 rval = this->SetDOFmapAssociation( eInputType,
false, NULL, NULL, eOutputType,
false, NULL );
MB_CHK_ERR( rval );
1086 if( is_root )
dbgprint.printf( 0,
"Calculating remap weights\n" );
1089 if( strMapAlgorithm ==
"invdist" )
1091 if( is_root ) AnnounceStartBlock(
"Calculating map (invdist)" );
1092 LinearRemapFVtoFVInvDist( *m_meshInputCov, *m_meshOutput, *m_meshOverlap, *
this );
1094 else if( strMapAlgorithm ==
"delaunay" )
1096 if( is_root ) AnnounceStartBlock(
"Calculating map (delaunay)" );
1097 LinearRemapTriangulation( *m_meshInputCov, *m_meshOutput, *m_meshOverlap, *
this );
1099 else if( strMapAlgorithm ==
"fvintbilin" )
1101 if( is_root ) AnnounceStartBlock(
"Calculating map (intbilin)" );
1102 LinearRemapIntegratedBilinear( *m_meshInputCov, *m_meshOutput, *m_meshOverlap, *
this );
1104 else if( strMapAlgorithm ==
"fvintbilingb" )
1106 if( is_root ) AnnounceStartBlock(
"Calculating map (intbilingb)" );
1107 LinearRemapIntegratedGeneralizedBarycentric( *m_meshInputCov, *m_meshOutput, *m_meshOverlap, *
this );
1109 else if( strMapAlgorithm ==
"fvbilin" )
1114 m_meshInputCov->Write(
"SourceMeshMBTR.g" );
1115 m_meshOutput->Write(
"TargetMeshMBTR.g" );
1119 m_meshInputCov->Write(
"SourceMeshMBTR" + std::to_string( rank ) +
".g" );
1120 m_meshOutput->Write(
"TargetMeshMBTR" + std::to_string( rank ) +
".g" );
1123 if( is_root ) AnnounceStartBlock(
"Calculating map (bilin)" );
1124 LinearRemapBilinear( *m_meshInputCov, *m_meshOutput, *m_meshOverlap, *
this );
1128 if( is_root ) AnnounceStartBlock(
"Calculating map (default)" );
1131 LinearRemapFVtoFV_Tempest_MOAB( ( mapOptions.fMonotone ? 1 : mapOptions.nPin ) );
1134 else if( eInputType == DiscretizationType_FV )
1136 DataArray3D< double > dataGLLJacobian;
1138 if( is_root )
dbgprint.printf( 0,
"Generating output mesh meta data\n" );
1139 double dNumericalArea_loc = GenerateMetaData( *m_meshOutput, mapOptions.nPout, mapOptions.fNoBubble,
1140 dataGLLNodesDest, dataGLLJacobian );
1142 double dNumericalArea = dNumericalArea_loc;
1143 #ifdef MOAB_HAVE_MPI
1145 MPI_Reduce( &dNumericalArea_loc, &dNumericalArea, 1, MPI_DOUBLE, MPI_SUM, 0, m_pcomm->comm() );
1147 if( is_root )
dbgprint.printf( 0,
"Output Mesh Numerical Area: %1.15e\n", dNumericalArea );
1150 this->InitializeSourceCoordinatesFromMeshFV( *m_meshInputCov );
1151 this->InitializeTargetCoordinatesFromMeshFE( *m_meshOutput, mapOptions.nPout, dataGLLNodesDest );
1154 bool fContinuous = ( eOutputType == DiscretizationType_CGLL );
1156 if( eOutputType == DiscretizationType_CGLL )
1158 GenerateUniqueJacobian( dataGLLNodesDest, dataGLLJacobian, this->GetTargetAreas() );
1162 GenerateDiscontinuousJacobian( dataGLLJacobian, this->GetTargetAreas() );
1166 if( m_meshInputCov->revnodearray.size() == 0 ) m_meshInputCov->ConstructReverseNodeArray();
1167 if( m_meshInputCov->edgemap.size() == 0 ) m_meshInputCov->ConstructEdgeMap(
false );
1170 rval = this->SetDOFmapAssociation( eInputType,
false, NULL, NULL, eOutputType,
1171 ( eOutputType == DiscretizationType_CGLL ), &dataGLLNodesDest );
MB_CHK_ERR( rval );
1174 if( strMapAlgorithm ==
"volumetric" )
1176 if( is_root )
dbgprint.printf( 0,
"Calculating remapping weights for FV->GLL (volumetric)\n" );
1177 LinearRemapFVtoGLL_Volumetric( *m_meshInputCov, *m_meshOutput, *m_meshOverlap, dataGLLNodesDest,
1178 dataGLLJacobian, this->GetTargetAreas(), mapOptions.nPin, *
this,
1179 nMonotoneType, fContinuous, mapOptions.fNoConservation );
1183 if( is_root )
dbgprint.printf( 0,
"Calculating remapping weights for FV->GLL\n" );
1184 LinearRemapFVtoGLL( *m_meshInputCov, *m_meshOutput, *m_meshOverlap, dataGLLNodesDest, dataGLLJacobian,
1185 this->GetTargetAreas(), mapOptions.nPin, *
this, nMonotoneType, fContinuous,
1186 mapOptions.fNoConservation );
1189 else if( ( eInputType == DiscretizationType_PCLOUD ) || ( eOutputType == DiscretizationType_PCLOUD ) )
1191 DataArray3D< double > dataGLLJacobian;
1192 if( !m_bPointCloudSource )
1195 if( m_meshInputCov->revnodearray.size() == 0 ) m_meshInputCov->ConstructReverseNodeArray();
1196 if( m_meshInputCov->edgemap.size() == 0 ) m_meshInputCov->ConstructEdgeMap(
false );
1199 if( eInputType == DiscretizationType_FV )
1201 this->InitializeSourceCoordinatesFromMeshFV( *m_meshInputCov );
1205 if( is_root )
dbgprint.printf( 0,
"Generating input mesh meta data\n" );
1206 DataArray3D< double > dataGLLJacobianSrc;
1207 GenerateMetaData( *m_meshInputCov, mapOptions.nPin, mapOptions.fNoBubble, dataGLLNodesSrcCov,
1209 GenerateMetaData( *m_meshInput, mapOptions.nPin, mapOptions.fNoBubble, dataGLLNodesSrc,
1210 dataGLLJacobianSrc );
1215 if( !m_bPointCloudTarget )
1218 if( m_meshOutput->revnodearray.size() == 0 ) m_meshOutput->ConstructReverseNodeArray();
1219 if( m_meshOutput->edgemap.size() == 0 ) m_meshOutput->ConstructEdgeMap(
false );
1222 if( eOutputType == DiscretizationType_FV )
1224 this->InitializeSourceCoordinatesFromMeshFV( *m_meshOutput );
1228 if( is_root )
dbgprint.printf( 0,
"Generating output mesh meta data\n" );
1229 GenerateMetaData( *m_meshOutput, mapOptions.nPout, mapOptions.fNoBubble, dataGLLNodesDest,
1236 rval = this->SetDOFmapAssociation(
1237 eInputType, ( eInputType == DiscretizationType_CGLL ),
1238 ( m_bPointCloudSource || eInputType == DiscretizationType_FV ? NULL : &dataGLLNodesSrcCov ),
1239 ( m_bPointCloudSource || eInputType == DiscretizationType_FV ? NULL : &dataGLLNodesSrc ), eOutputType,
1240 ( eOutputType == DiscretizationType_CGLL ), ( m_bPointCloudTarget ? NULL : &dataGLLNodesDest ) );
MB_CHK_ERR( rval );
1243 if( is_root )
dbgprint.printf( 0,
"Calculating remap weights with Nearest-Neighbor method\n" );
1244 rval = LinearRemapNN_MOAB(
true ,
false );
MB_CHK_ERR( rval );
1246 else if( ( eInputType != DiscretizationType_FV ) && ( eOutputType == DiscretizationType_FV ) )
1248 DataArray3D< double > dataGLLJacobianSrc, dataGLLJacobian;
1250 if( is_root )
dbgprint.printf( 0,
"Generating input mesh meta data\n" );
1252 GenerateMetaData( *m_meshInputCov, mapOptions.nPin, mapOptions.fNoBubble, dataGLLNodesSrcCov,
1255 double dNumericalArea_loc = GenerateMetaData( *m_meshInput, mapOptions.nPin, mapOptions.fNoBubble,
1256 dataGLLNodesSrc, dataGLLJacobianSrc );
1262 double dNumericalArea = dNumericalArea_loc;
1263 #ifdef MOAB_HAVE_MPI
1265 MPI_Reduce( &dNumericalArea_loc, &dNumericalArea, 1, MPI_DOUBLE, MPI_SUM, 0, m_pcomm->comm() );
1269 dbgprint.printf( 0,
"Input Mesh Numerical Area: %1.15e\n", dNumericalArea );
1271 if( fabs( dNumericalArea - dTotalAreaInput ) > 1.0e-12 )
1273 dbgprint.printf( 0,
"WARNING: Significant mismatch between input mesh "
1274 "numerical area and geometric area\n" );
1278 if( dataGLLNodesSrcCov.GetSubColumns() != m_meshInputCov->faces.size() )
1280 _EXCEPTIONT(
"Number of element does not match between metadata and "
1285 this->InitializeSourceCoordinatesFromMeshFE( *m_meshInputCov, mapOptions.nPin, dataGLLNodesSrcCov );
1286 this->InitializeTargetCoordinatesFromMeshFV( *m_meshOutput );
1289 bool fContinuousIn = ( eInputType == DiscretizationType_CGLL );
1291 if( eInputType == DiscretizationType_CGLL )
1293 GenerateUniqueJacobian( dataGLLNodesSrcCov, dataGLLJacobian, this->GetSourceAreas() );
1297 GenerateDiscontinuousJacobian( dataGLLJacobian, this->GetSourceAreas() );
1301 rval = this->SetDOFmapAssociation( eInputType, ( eInputType == DiscretizationType_CGLL ),
1302 &dataGLLNodesSrcCov, &dataGLLNodesSrc, eOutputType,
false, NULL );
MB_CHK_ERR( rval );
1305 if( is_root )
dbgprint.printf( 0,
"Calculating remap weights\n" );
1307 if( strMapAlgorithm ==
"volumetric" )
1309 _EXCEPTIONT(
"Unimplemented: Volumetric currently unavailable for"
1313 LinearRemapSE4_Tempest_MOAB( dataGLLNodesSrcCov, dataGLLJacobian, nMonotoneType, fContinuousIn,
1314 mapOptions.fNoConservation );
1316 else if( ( eInputType != DiscretizationType_FV ) && ( eOutputType != DiscretizationType_FV ) )
1318 DataArray3D< double > dataGLLJacobianIn, dataGLLJacobianSrc;
1319 DataArray3D< double > dataGLLJacobianOut;
1322 if( is_root )
dbgprint.printf( 0,
"Generating input mesh meta data\n" );
1323 GenerateMetaData( *m_meshInputCov, mapOptions.nPin, mapOptions.fNoBubble, dataGLLNodesSrcCov,
1324 dataGLLJacobianIn );
1326 double dNumericalAreaSrc_loc = GenerateMetaData( *m_meshInput, mapOptions.nPin, mapOptions.fNoBubble,
1327 dataGLLNodesSrc, dataGLLJacobianSrc );
1328 double dNumericalAreaIn = dNumericalAreaSrc_loc;
1329 #ifdef MOAB_HAVE_MPI
1331 MPI_Reduce( &dNumericalAreaSrc_loc, &dNumericalAreaIn, 1, MPI_DOUBLE, MPI_SUM, 0, m_pcomm->comm() );
1335 dbgprint.printf( 0,
"Input Mesh Numerical Area: %1.15e\n", dNumericalAreaIn );
1337 if( fabs( dNumericalAreaIn - dTotalAreaInput ) > 1.0e-12 )
1339 dbgprint.printf( 0,
"WARNING: Significant mismatch between input mesh "
1340 "numerical area and geometric area\n" );
1345 if( is_root )
dbgprint.printf( 0,
"Generating output mesh meta data\n" );
1346 double dNumericalAreaOut_loc = GenerateMetaData( *m_meshOutput, mapOptions.nPout, mapOptions.fNoBubble,
1347 dataGLLNodesDest, dataGLLJacobianOut );
1348 double dNumericalAreaOut = dNumericalAreaOut_loc;
1349 #ifdef MOAB_HAVE_MPI
1351 MPI_Reduce( &dNumericalAreaOut_loc, &dNumericalAreaOut, 1, MPI_DOUBLE, MPI_SUM, 0, m_pcomm->comm() );
1355 dbgprint.printf( 0,
"Output Mesh Numerical Area: %1.15e\n", dNumericalAreaOut );
1357 if( fabs( dNumericalAreaOut - dTotalAreaOutput ) > 1.0e-12 )
1360 dbgprint.printf( 0,
"WARNING: Significant mismatch between output mesh "
1361 "numerical area and geometric area\n" );
1366 this->InitializeSourceCoordinatesFromMeshFE( *m_meshInputCov, mapOptions.nPin, dataGLLNodesSrcCov );
1367 this->InitializeTargetCoordinatesFromMeshFE( *m_meshOutput, mapOptions.nPout, dataGLLNodesDest );
1370 bool fContinuousIn = ( eInputType == DiscretizationType_CGLL );
1372 if( eInputType == DiscretizationType_CGLL )
1374 GenerateUniqueJacobian( dataGLLNodesSrcCov, dataGLLJacobianIn, this->GetSourceAreas() );
1378 GenerateDiscontinuousJacobian( dataGLLJacobianIn, this->GetSourceAreas() );
1382 bool fContinuousOut = ( eOutputType == DiscretizationType_CGLL );
1384 if( eOutputType == DiscretizationType_CGLL )
1386 GenerateUniqueJacobian( dataGLLNodesDest, dataGLLJacobianOut, this->GetTargetAreas() );
1390 GenerateDiscontinuousJacobian( dataGLLJacobianOut, this->GetTargetAreas() );
1394 rval = this->SetDOFmapAssociation( eInputType, ( eInputType == DiscretizationType_CGLL ),
1395 &dataGLLNodesSrcCov, &dataGLLNodesSrc, eOutputType,
1396 ( eOutputType == DiscretizationType_CGLL ), &dataGLLNodesDest );
MB_CHK_ERR( rval );
1399 if( is_root )
dbgprint.printf( 0,
"Calculating remap weights\n" );
1401 LinearRemapGLLtoGLL2_MOAB( dataGLLNodesSrcCov, dataGLLJacobianIn, dataGLLNodesDest, dataGLLJacobianOut,
1402 this->GetTargetAreas(), mapOptions.nPin, mapOptions.nPout, nMonotoneType,
1403 fContinuousIn, fContinuousOut, mapOptions.fNoConservation );
1407 _EXCEPTIONT(
"Not implemented" );
1410 #ifdef MOAB_HAVE_EIGEN3
1411 copy_tempest_sparsemat_to_eigen3();
1414 #ifdef MOAB_HAVE_MPI
1418 rval = m_remapper->GetOverlapAugmentedEntities( ghostedEnts );
MB_CHK_ERR( rval );
1420 rval = m_interface->remove_entities( m_meshOverlapSet, ghostedEnts );
MB_CHK_SET_ERR( rval,
"Deleting ghosted entities failed" );
1424 if( !mapOptions.fNoCheck )
1426 if( is_root )
dbgprint.printf( 0,
"Verifying map" );
1427 this->IsConsistent( 1.0e-8 );
1428 if( !mapOptions.fNoConservation ) this->IsConservative( 1.0e-8 );
1430 if( nMonotoneType != 0 )
1432 this->IsMonotone( 1.0e-12 );
1436 catch( Exception& e )
1438 dbgprint.printf( 0,
"%s", e.ToString().c_str() );
1439 return ( moab::MB_FAILURE );
1443 return ( moab::MB_FAILURE );
1452 #ifndef MOAB_HAVE_MPI
1454 return OfflineMap::IsConsistent( dTolerance );
1459 DataArray1D< int > dataRows;
1460 DataArray1D< int > dataCols;
1461 DataArray1D< double > dataEntries;
1464 DataArray1D< double > dRowSums;
1465 m_mapRemap.GetEntries( dataRows, dataCols, dataEntries );
1466 dRowSums.Allocate( m_mapRemap.GetRows() );
1468 for(
unsigned i = 0; i < dataRows.GetRows(); i++ )
1470 dRowSums[dataRows[i]] += dataEntries[i];
1474 int fConsistent = 0;
1475 for(
unsigned i = 0; i < dRowSums.GetRows(); i++ )
1477 if( fabs( dRowSums[i] - 1.0 ) > dTolerance )
1480 int rowGID = row_gdofmap[i];
1481 Announce(
"TempestOnlineMap is not consistent in row %i (%1.15e)", rowGID, dRowSums[i] );
1486 int fConsistentGlobal = 0;
1487 ierr = MPI_Allreduce( &fConsistent, &fConsistentGlobal, 1, MPI_INT, MPI_SUM, m_pcomm->comm() );
1488 if( ierr != MPI_SUCCESS )
return -1;
1490 return fConsistentGlobal;
1498 #ifndef MOAB_HAVE_MPI
1500 return OfflineMap::IsConservative( dTolerance );
1507 DataArray1D< int > dataRows;
1508 DataArray1D< int > dataCols;
1509 DataArray1D< double > dataEntries;
1510 const DataArray1D< double >& dTargetAreas = this->GetTargetAreas();
1511 const DataArray1D< double >& dSourceAreas = this->GetSourceAreas();
1514 std::vector< int > dColumnsUnique;
1515 std::vector< double > dColumnSums;
1517 int nColumns = m_mapRemap.GetColumns();
1518 m_mapRemap.GetEntries( dataRows, dataCols, dataEntries );
1519 dColumnSums.resize( m_nTotDofs_SrcCov, 0.0 );
1520 dColumnsUnique.resize( m_nTotDofs_SrcCov, -1 );
1522 for(
unsigned i = 0; i < dataEntries.GetRows(); i++ )
1524 dColumnSums[dataCols[i]] += dataEntries[i] * dTargetAreas[dataRows[i]] / dSourceAreas[dataCols[i]];
1526 assert( dataCols[i] < m_nTotDofs_SrcCov );
1529 int colGID = this->GetColGlobalDoF( dataCols[i] );
1531 dColumnsUnique[dataCols[i]] = colGID;
1538 std::vector< int > nElementsInProc;
1539 const int nDATA = 3;
1540 if( !rank ) nElementsInProc.resize(
size * nDATA );
1541 int senddata[nDATA] = { nColumns, m_nTotDofs_SrcCov, m_nTotDofs_Src };
1542 ierr = MPI_Gather( senddata, nDATA, MPI_INT, nElementsInProc.data(), nDATA, MPI_INT, rootProc, m_pcomm->comm() );
1543 if( ierr != MPI_SUCCESS )
return -1;
1545 int nTotVals = 0, nTotColumns = 0, nTotColumnsUnq = 0;
1546 std::vector< int > dColumnIndices;
1547 std::vector< double > dColumnSourceAreas;
1548 std::vector< double > dColumnSumsTotal;
1549 std::vector< int > displs, rcount;
1550 if( rank == rootProc )
1552 displs.resize(
size + 1, 0 );
1553 rcount.resize(
size, 0 );
1555 for(
int ir = 0; ir <
size; ++ir )
1557 nTotVals += nElementsInProc[ir * nDATA];
1558 nTotColumns += nElementsInProc[ir * nDATA + 1];
1559 nTotColumnsUnq += nElementsInProc[ir * nDATA + 2];
1562 rcount[ir] = nElementsInProc[ir * nDATA + 1];
1569 dColumnIndices.resize( nTotColumns, -1 );
1570 dColumnSumsTotal.resize( nTotColumns, 0.0 );
1579 ierr = MPI_Gatherv( &dColumnsUnique[0], m_nTotDofs_SrcCov, MPI_INT, &dColumnIndices[0], rcount.data(),
1580 displs.data(), MPI_INT, rootProc, m_pcomm->comm() );
1581 if( ierr != MPI_SUCCESS )
return -1;
1582 ierr = MPI_Gatherv( &dColumnSums[0], m_nTotDofs_SrcCov, MPI_DOUBLE, &dColumnSumsTotal[0], rcount.data(),
1583 displs.data(), MPI_DOUBLE, rootProc, m_pcomm->comm() );
1584 if( ierr != MPI_SUCCESS )
return -1;
1590 dColumnSums.clear();
1591 dColumnsUnique.clear();
1594 int fConservative = 0;
1595 if( rank == rootProc )
1597 displs[
size] = ( nTotColumns );
1599 std::map< int, double > dColumnSumsOnRoot;
1601 for(
int ir = 0; ir <
size; ir++ )
1603 for(
int ips = displs[ir]; ips < displs[ir + 1]; ips++ )
1605 if( dColumnIndices[ips] < 0 )
continue;
1607 assert( dColumnIndices[ips] < nTotColumnsUnq );
1608 dColumnSumsOnRoot[dColumnIndices[ips]] += dColumnSumsTotal[ips];
1614 for( std::map< int, double >::iterator it = dColumnSumsOnRoot.begin(); it != dColumnSumsOnRoot.end(); ++it )
1617 if( fabs( it->second - 1.0 ) > dTolerance )
1620 Announce(
"TempestOnlineMap is not conservative in column "
1623 it->first, it->second );
1629 ierr = MPI_Bcast( &fConservative, 1, MPI_INT, rootProc, m_pcomm->comm() );
1630 if( ierr != MPI_SUCCESS )
return -1;
1632 return fConservative;
1640 #ifndef MOAB_HAVE_MPI
1642 return OfflineMap::IsMonotone( dTolerance );
1647 DataArray1D< int > dataRows;
1648 DataArray1D< int > dataCols;
1649 DataArray1D< double > dataEntries;
1651 m_mapRemap.GetEntries( dataRows, dataCols, dataEntries );
1655 for(
unsigned i = 0; i < dataRows.GetRows(); i++ )
1657 if( ( dataEntries[i] < -dTolerance ) || ( dataEntries[i] > 1.0 + dTolerance ) )
1661 Announce(
"TempestOnlineMap is not monotone in entry (%i): %1.15e", i, dataEntries[i] );
1666 int fMonotoneGlobal = 0;
1667 ierr = MPI_Allreduce( &fMonotone, &fMonotoneGlobal, 1, MPI_INT, MPI_SUM, m_pcomm->comm() );
1668 if( ierr != MPI_SUCCESS )
return -1;
1670 return fMonotoneGlobal;
1682 std::vector< double > solSTagVals;
1683 std::vector< double > solTTagVals;
1686 if( m_remapper->point_cloud_source || m_remapper->point_cloud_target )
1688 if( m_remapper->point_cloud_source )
1691 solSTagVals.resize( covSrcEnts.
size(), -1.0 );
1697 solSTagVals.resize( covSrcEnts.
size() * this->GetSourceNDofsPerElement() * this->GetSourceNDofsPerElement(),
1701 if( m_remapper->point_cloud_target )
1704 solTTagVals.resize( tgtEnts.
size(), -1.0 );
1711 tgtEnts.
size() * this->GetDestinationNDofsPerElement() * this->GetDestinationNDofsPerElement(), -1.0 );
1719 solSTagVals.resize( covSrcEnts.
size() * this->GetSourceNDofsPerElement() * this->GetSourceNDofsPerElement(),
1722 tgtEnts.
size() * this->GetDestinationNDofsPerElement() * this->GetDestinationNDofsPerElement(), -1.0 );
1729 rval = m_interface->tag_get_data( srcSolutionTag, sents, &solSTagVals[0] );
MB_CHK_SET_ERR( rval,
"Getting local tag data failed" );
1734 rval = this->ApplyWeights( solSTagVals, solTTagVals, transpose );
MB_CHK_SET_ERR( rval,
"Applying remap operator onto source vector data failed" );
1737 rval = m_interface->tag_set_data( tgtSolutionTag, tents, &solTTagVals[0] );
MB_CHK_SET_ERR( rval,
"Setting local tag data failed" );
1743 const std::string& solnName,
1745 sample_function testFunction,
1747 std::string cloneSolnName )
1750 const bool outputEnabled = ( is_root );
1759 meshset = m_remapper->m_covering_source_set;
1760 trmesh = m_remapper->m_covering_source;
1761 entities = ( m_remapper->point_cloud_source ? m_remapper->m_covering_source_vertices
1762 : m_remapper->m_covering_source_entities );
1763 discOrder = m_nDofsPEl_Src;
1764 discMethod = m_eInputType;
1768 meshset = m_remapper->m_target_set;
1769 trmesh = m_remapper->m_target;
1771 ( m_remapper->point_cloud_target ? m_remapper->m_target_vertices : m_remapper->m_target_entities );
1772 discOrder = m_nDofsPEl_Dest;
1773 discMethod = m_eOutputType;
1778 std::cout <<
"Invalid context specified for defining an analytical solution tag" << std::endl;
1779 return moab::MB_FAILURE;
1784 rval = m_interface->tag_get_handle( solnName.c_str(), discOrder * discOrder,
MB_TYPE_DOUBLE, solnTag,
1786 if( clonedSolnTag != NULL )
1788 if( cloneSolnName.size() == 0 )
1790 cloneSolnName = solnName + std::string(
"Cloned" );
1792 rval = m_interface->tag_get_handle( cloneSolnName.c_str(), discOrder * discOrder,
MB_TYPE_DOUBLE,
1797 const int TriQuadratureOrder = 10;
1799 if( outputEnabled ) std::cout <<
"Using triangular quadrature of order " << TriQuadratureOrder << std::endl;
1801 TriangularQuadratureRule triquadrule( TriQuadratureOrder );
1803 const int TriQuadraturePoints = triquadrule.GetPoints();
1805 const DataArray2D< double >& TriQuadratureG = triquadrule.GetG();
1806 const DataArray1D< double >& TriQuadratureW = triquadrule.GetW();
1809 DataArray1D< double > dVar;
1810 DataArray1D< double > dVarMB;
1813 DataArray1D< double > dNodeArea;
1818 if( discMethod == DiscretizationType_CGLL || discMethod == DiscretizationType_DGLL )
1821 const bool fGLL =
true;
1822 const bool fGLLIntegrate =
false;
1825 DataArray3D< int > dataGLLNodes;
1826 DataArray3D< double > dataGLLJacobian;
1828 GenerateMetaData( *trmesh, discOrder,
false, dataGLLNodes, dataGLLJacobian );
1831 int nElements = trmesh->faces.size();
1834 for(
int k = 0; k < nElements; k++ )
1836 const Face&
face = trmesh->faces[k];
1838 if(
face.edges.size() != 4 )
1840 _EXCEPTIONT(
"Non-quadrilateral face detected; "
1841 "incompatible with --gll" );
1847 for(
int i = 0; i < discOrder; i++ )
1849 for(
int j = 0; j < discOrder; j++ )
1851 for(
int k = 0; k < nElements; k++ )
1853 if( dataGLLNodes[i][j][k] > iMaxNode )
1855 iMaxNode = dataGLLNodes[i][j][k];
1862 DataArray1D< double > dG;
1863 DataArray1D< double > dW;
1865 GaussLobattoQuadrature::GetPoints( discOrder, 0.0, 1.0, dG, dW );
1868 const int nGaussP = 10;
1870 DataArray1D< double > dGaussG;
1871 DataArray1D< double > dGaussW;
1873 GaussQuadrature::GetPoints( nGaussP, 0.0, 1.0, dGaussG, dGaussW );
1876 dVar.Allocate( iMaxNode );
1877 dVarMB.Allocate( discOrder * discOrder * nElements );
1878 dNodeArea.Allocate( iMaxNode );
1881 for(
int k = 0; k < nElements; k++ )
1883 const Face&
face = trmesh->faces[k];
1888 for(
int i = 0; i < discOrder; i++ )
1890 for(
int j = 0; j < discOrder; j++ )
1898 ApplyLocalMap(
face, trmesh->nodes, dG[i], dG[j], node, dDx1G, dDx2G );
1901 double dNodeLon = atan2( node.y, node.x );
1902 if( dNodeLon < 0.0 )
1904 dNodeLon += 2.0 * M_PI;
1906 double dNodeLat = asin( node.z );
1908 double dSample = ( *testFunction )( dNodeLon, dNodeLat );
1910 dVar[dataGLLNodes[j][i][k] - 1] = dSample;
1917 DataArray2D< double > dCoeff( discOrder, discOrder );
1919 for(
int p = 0; p < nGaussP; p++ )
1921 for(
int q = 0; q < nGaussP; q++ )
1929 ApplyLocalMap(
face, trmesh->nodes, dGaussG[p], dGaussG[q], node, dDx1G, dDx2G );
1932 Node nodeCross = CrossProduct( dDx1G, dDx2G );
1935 sqrt( nodeCross.x * nodeCross.x + nodeCross.y * nodeCross.y + nodeCross.z * nodeCross.z );
1939 SampleGLLFiniteElement( 0, discOrder, dGaussG[p], dGaussG[q], dCoeff );
1942 double dNodeLon = atan2( node.y, node.x );
1943 if( dNodeLon < 0.0 )
1945 dNodeLon += 2.0 * M_PI;
1947 double dNodeLat = asin( node.z );
1949 double dSample = ( *testFunction )( dNodeLon, dNodeLat );
1952 for(
int i = 0; i < discOrder; i++ )
1954 for(
int j = 0; j < discOrder; j++ )
1957 double dNodalArea = dCoeff[i][j] * dGaussW[p] * dGaussW[q] * dJacobian;
1959 dVar[dataGLLNodes[i][j][k] - 1] += dSample * dNodalArea;
1961 dNodeArea[dataGLLNodes[i][j][k] - 1] += dNodalArea;
1972 for(
size_t i = 0; i < dVar.GetRows(); i++ )
1974 dVar[i] /= dNodeArea[i];
1981 for(
unsigned j = 0; j <
entities.size(); j++ )
1982 for(
int p = 0; p < discOrder; p++ )
1983 for(
int q = 0; q < discOrder; q++ )
1985 const int offsetDOF = j * discOrder * discOrder + p * discOrder + q;
1986 dVarMB[offsetDOF] = dVar[col_dtoc_dofmap[offsetDOF]];
1991 for(
unsigned j = 0; j <
entities.size(); j++ )
1992 for(
int p = 0; p < discOrder; p++ )
1993 for(
int q = 0; q < discOrder; q++ )
1995 const int offsetDOF = j * discOrder * discOrder + p * discOrder + q;
1996 dVarMB[offsetDOF] = dVar[row_dtoc_dofmap[offsetDOF]];
2001 rval = m_interface->tag_set_data( solnTag,
entities, &dVarMB[0] );
MB_CHK_ERR( rval );
2006 if( discMethod == DiscretizationType_FV )
2011 dVar.Allocate( trmesh->faces.size() );
2013 std::vector< Node >& nodes = trmesh->nodes;
2016 for(
size_t i = 0; i < trmesh->faces.size(); i++ )
2018 const Face&
face = trmesh->faces[i];
2021 for(
size_t j = 0; j <
face.edges.size() - 2; j++ )
2024 const Node& node0 = nodes[
face[0]];
2025 const Node& node1 = nodes[
face[j + 1]];
2026 const Node& node2 = nodes[
face[j + 2]];
2030 faceTri.SetNode( 0,
face[0] );
2031 faceTri.SetNode( 1,
face[j + 1] );
2032 faceTri.SetNode( 2,
face[j + 2] );
2034 double dTriangleArea = CalculateFaceArea( faceTri, nodes );
2037 double dTotalSample = 0.0;
2040 for(
int k = 0; k < TriQuadraturePoints; k++ )
2042 Node node( TriQuadratureG[k][0] * node0.x + TriQuadratureG[k][1] * node1.x +
2043 TriQuadratureG[k][2] * node2.x,
2044 TriQuadratureG[k][0] * node0.y + TriQuadratureG[k][1] * node1.y +
2045 TriQuadratureG[k][2] * node2.y,
2046 TriQuadratureG[k][0] * node0.z + TriQuadratureG[k][1] * node1.z +
2047 TriQuadratureG[k][2] * node2.z );
2049 double dMagnitude = node.Magnitude();
2050 node.x /= dMagnitude;
2051 node.y /= dMagnitude;
2052 node.z /= dMagnitude;
2054 double dLon = atan2( node.y, node.x );
2059 double dLat = asin( node.z );
2061 double dSample = ( *testFunction )( dLon, dLat );
2063 dTotalSample += dSample * TriQuadratureW[k] * dTriangleArea;
2066 dVar[i] += dTotalSample / trmesh->vecFaceArea[i];
2074 std::vector< Node >& nodes = trmesh->nodes;
2077 dVar.Allocate( nodes.size() );
2079 for(
size_t j = 0; j < nodes.size(); j++ )
2081 Node& node = nodes[j];
2082 double dMagnitude = node.Magnitude();
2083 node.x /= dMagnitude;
2084 node.y /= dMagnitude;
2085 node.z /= dMagnitude;
2086 double dLon = atan2( node.y, node.x );
2091 double dLat = asin( node.z );
2093 double dSample = ( *testFunction )( dLon, dLat );
2107 std::map< std::string, double >& metrics,
2111 const bool outputEnabled = ( is_root );
2120 meshset = m_remapper->m_covering_source_set;
2121 trmesh = m_remapper->m_covering_source;
2122 entities = ( m_remapper->point_cloud_source ? m_remapper->m_covering_source_vertices
2123 : m_remapper->m_covering_source_entities );
2124 discOrder = m_nDofsPEl_Src;
2125 discMethod = m_eInputType;
2129 meshset = m_remapper->m_target_set;
2130 trmesh = m_remapper->m_target;
2132 ( m_remapper->point_cloud_target ? m_remapper->m_target_vertices : m_remapper->m_target_entities );
2133 discOrder = m_nDofsPEl_Dest;
2134 discMethod = m_eOutputType;
2139 std::cout <<
"Invalid context specified for defining an analytical solution tag" << std::endl;
2140 return moab::MB_FAILURE;
2145 std::string exactTagName, projTagName;
2146 const int ntotsize =
entities.size() * discOrder * discOrder;
2147 int ntotsize_glob = 0;
2148 std::vector< double > exactSolution( ntotsize, 0.0 ), projSolution( ntotsize, 0.0 );
2149 rval = m_interface->tag_get_name( exactTag, exactTagName );
MB_CHK_ERR( rval );
2150 rval = m_interface->tag_get_data( exactTag,
entities, &exactSolution[0] );
MB_CHK_ERR( rval );
2151 rval = m_interface->tag_get_name( approxTag, projTagName );
MB_CHK_ERR( rval );
2152 rval = m_interface->tag_get_data( approxTag,
entities, &projSolution[0] );
MB_CHK_ERR( rval );
2154 std::vector< double > errnorms( 3, 0.0 ), globerrnorms( 3, 0.0 );
2155 for(
int i = 0; i < ntotsize; ++i )
2157 const double error = fabs( exactSolution[i] - projSolution[i] );
2158 errnorms[0] +=
error;
2160 errnorms[2] = (
error > errnorms[2] ?
error : errnorms[2] );
2162 #ifdef MOAB_HAVE_MPI
2165 MPI_Reduce( &ntotsize, &ntotsize_glob, 1, MPI_INT, MPI_SUM, 0, m_pcomm->comm() );
2166 MPI_Reduce( &errnorms[0], &globerrnorms[0], 2, MPI_DOUBLE, MPI_SUM, 0, m_pcomm->comm() );
2167 MPI_Reduce( &errnorms[2], &globerrnorms[2], 1, MPI_DOUBLE, MPI_MAX, 0, m_pcomm->comm() );
2170 ntotsize_glob = ntotsize;
2171 globerrnorms = errnorms;
2173 globerrnorms[0] = ( globerrnorms[0] / ntotsize_glob );
2174 globerrnorms[1] = std::sqrt( globerrnorms[1] / ntotsize_glob );
2177 metrics[
"L1Error"] = globerrnorms[0];
2178 metrics[
"L2Error"] = globerrnorms[1];
2179 metrics[
"LinfError"] = globerrnorms[2];
2183 std::cout <<
"Error metrics when comparing " << projTagName <<
" against " << exactTagName << std::endl;
2184 std::cout <<
"\t L_1 error = " << globerrnorms[0] << std::endl;
2185 std::cout <<
"\t L_2 error = " << globerrnorms[1] << std::endl;
2186 std::cout <<
"\t L_inf error = " << globerrnorms[2] << std::endl;