Repository navigation
Conversation
|
That looks great, Bertrand. Even though |
5b8df17 to
ffed9d2
Compare
joshua-white
left a comment
There was a problem hiding this comment.
Nice. Only suggestion might be to add a log note if a "large" mesh and kdtree are detected, suggesting user consider something else.
# Conflicts: # src/coreComponents/integrationTests/meshTests/testVTKImport.cpp # src/coreComponents/mesh/generators/VTKUtilities.cpp
|
Needs code owner review from @OmarDuran , @jafranc and/or @rrsettgast Thanks 🙏 |
jafranc
left a comment
There was a problem hiding this comment.
Looks good, but some part need to be moved to honor responsability logic at header level a bit more (and avoid too much spreading across the code base)
| // Identify which 3D global IDs we need from other ranks | ||
| SortedArray< int64_t > missingGlobalIds; | ||
| stdVector< int64_t > missingGlobalIdsVec; |
There was a problem hiding this comment.
Now that we are using c++20
auto missing = neighbors2Dto3D
| std::views::join
| std::views::filter( [&]( int64_t id ){ return !localPartitionMap.contains( id ); } );
stdVector< int64_t > missingGlobalIdsVec( missing.begin(), missing.end() ); // C++20 iterator-pair ctor
std::ranges::sort( missingGlobalIdsVec );
auto const tail = std::ranges::unique( missingGlobalIdsVec );
missingGlobalIdsVec.erase( tail.begin(), tail.end() );
sounds simpler.
| stdVector< stdArray< real64, 3 > > | ||
| computeCentroids( vtkDataSet & mesh ) | ||
| { | ||
| vtkIdType const n = mesh.GetNumberOfCells(); | ||
| stdVector< stdArray< real64, 3 > > centroids( n ); | ||
| vtkNew< vtkIdList > ptIds; | ||
|
|
||
| for( vtkIdType c = 0; c < n; ++c ) | ||
| { | ||
| mesh.GetCellPoints( c, ptIds ); | ||
| real64 cx = 0.0, cy = 0.0, cz = 0.0; | ||
| vtkIdType const nPts = ptIds->GetNumberOfIds(); | ||
| for( vtkIdType i = 0; i < nPts; ++i ) | ||
| { | ||
| real64 p[3]; | ||
| mesh.GetPoint( ptIds->GetId( i ), p ); | ||
| cx += p[0]; | ||
| cy += p[1]; | ||
| cz += p[2]; | ||
| } | ||
| if( nPts > 0 ) | ||
| { | ||
| real64 const inv = 1.0 / nPts; | ||
| centroids[c] = { cx * inv, cy * inv, cz * inv }; | ||
| } | ||
| else | ||
| { | ||
| centroids[c] = { 0.0, 0.0, 0.0 }; | ||
| } | ||
| } | ||
| return centroids; | ||
| } |
There was a problem hiding this comment.
can't we reuse something from ComputationalGeom here ? or as we are using VTK the canonical VTK function for that ?
There was a problem hiding this comment.
As for now, centroids can fall outside the cells.
| // Buffer serialization helpers | ||
| // ============================================================================ | ||
|
|
||
| void appendBytes( stdVector< char > & buf, void const * data, int64_t n ) |
There was a problem hiding this comment.
why won't these fit in mesh/BufferOps.hpp ?
| // MPI large-message helpers (handles buffers > 2 GB) | ||
| // ============================================================================ | ||
|
|
||
| constexpr int64_t MPI_CHUNK_SIZE = INT64_C( 1 ) << 30; // 1 GB |
There was a problem hiding this comment.
same here, why isn't it in MpiWrapper.hpp or even better directly a logic of MpiWrapper::send on the maximal chunck size and checkSum ?
| cells->ConvertToDefaultStorage(); | ||
|
|
||
| vtkDataArray * offsets = cells->GetOffsetsArray(); | ||
| vtkDataArray * conn = cells->GetConnectivityArray(); |
There was a problem hiding this comment.
Idk the status elsewhere in the code and if we should guard this with VTK version warning and throw otherwise or not
| vtkSmartPointer< vtkCellArray > cellArray = readCellArray( ptr ); | ||
| vtkSmartPointer< vtkCellArray > faceLocations = readCellArray( ptr ); | ||
| vtkSmartPointer< vtkCellArray > faces = readCellArray( ptr ); |
There was a problem hiding this comment.
| vtkSmartPointer< vtkCellArray > cellArray = readCellArray( ptr ); | |
| vtkSmartPointer< vtkCellArray > faceLocations = readCellArray( ptr ); | |
| vtkSmartPointer< vtkCellArray > faces = readCellArray( ptr ); | |
| //sweep ptr reading | |
| vtkSmartPointer< vtkCellArray > cellArray = readCellArray( ptr ); | |
| vtkSmartPointer< vtkCellArray > faceLocations = readCellArray( ptr ); | |
| vtkSmartPointer< vtkCellArray > faces = readCellArray( ptr ); |
There was a problem hiding this comment.
I would add an extra reading sweep function as this is implicit convention and will be hard to maintain.
| for( vtkIdType i = 0; i < static_cast< vtkIdType >( assignment.size() ); ++i ) | ||
| { | ||
| if( assignment[i] < mid ) | ||
| { | ||
| loCells.push_back( i ); | ||
| loAssign.push_back( assignment[i] ); | ||
| } | ||
| else | ||
| { | ||
| hiCells.push_back( i ); | ||
| hiAssign.push_back( assignment[i] ); | ||
| } | ||
| } |
There was a problem hiding this comment.
| for( vtkIdType i = 0; i < static_cast< vtkIdType >( assignment.size() ); ++i ) | |
| { | |
| if( assignment[i] < mid ) | |
| { | |
| loCells.push_back( i ); | |
| loAssign.push_back( assignment[i] ); | |
| } | |
| else | |
| { | |
| hiCells.push_back( i ); | |
| hiAssign.push_back( assignment[i] ); | |
| } | |
| } | |
| auto const n = static_cast< vtkIdType >( assignment.size() ); | |
| stdVector< vtkIdType > order( n ); | |
| std::iota( order.begin(), order.end(), vtkIdType{ 0 } ); | |
| auto const split = std::stable_partition( order.begin(), order.end(), | |
| [&]( vtkIdType i ) { return assignment[i] < mid; } ); | |
| loCells.assign( order.begin(), split ); | |
| hiCells.assign( split, order.end() ); | |
| auto const rankOf = [&]( vtkIdType i ) { return assignment[i]; }; | |
| std::ranges::transform( loCells, std::back_inserter( loAssign ), rankOf ); | |
| std::ranges::transform( hiCells, std::back_inserter( hiAssign ), rankOf ); |
| if( hiCells.size() <= loCells.size() ) | ||
| { | ||
| result.hiMesh = extract( mesh, hiCells ); | ||
| result.hiAssignment = std::move( hiAssign ); | ||
| result.loMesh = extract( mesh, loCells ); | ||
| result.loAssignment = std::move( loAssign ); | ||
| } | ||
| else | ||
| { | ||
| result.loMesh = extract( mesh, loCells ); | ||
| result.loAssignment = std::move( loAssign ); | ||
| result.hiMesh = extract( mesh, hiCells ); | ||
| result.hiAssignment = std::move( hiAssign ); | ||
| } |
There was a problem hiding this comment.
Maybe move this to the struct ctor would be cleaner ?
| std::function< void( vtkIdType, vtkIdType, integer, integer ) > bisect; | ||
| bisect = [&]( vtkIdType begin, vtkIdType end, integer rankLo, integer rankHi ) |
There was a problem hiding this comment.
| std::function< void( vtkIdType, vtkIdType, integer, integer ) > bisect; | |
| bisect = [&]( vtkIdType begin, vtkIdType end, integer rankLo, integer rankHi ) | |
| auto bisect = [&]( vtkIdType begin, vtkIdType end, integer rankLo, integer rankHi ) |
| // KdTree: legacy path using VTK's built-in redistribution | ||
| if( method == ScatterMethod::kdtree ) | ||
| { | ||
| constexpr vtkIdType largeMeshCellCount = 10000000; |
There was a problem hiding this comment.
looks like it should be a capital const
Problem
The current strategy to scatter the mesh initially loaded by rank 0 uses
vtkRedistributeDataSetFilter(kd-tree partitioning). This approach can take several minutes for large meshes (10s of millions of elements) when redistributed among 1000+ ranks.Recent example: A 136M cell mesh redistributed to 1024 ranks takes about 17 minutes with the current kd-tree approach.
Solution
This PR adds partitioning strategies that bypass the VTK library's bottleneck, available via the new
scatterMethodXML attribute.Available strategies
kdtreecontiguouscartesian-x/-y/-zpartition countsrcbRemark
The resulting partitions can be further refined and improved via calls to
ParMetisorPTScotchwithpartitionRefinement>0Benchmark
Rebaseline needed
67/78 failures are XML metadata diff:
Group has a child 'scatterMethod' in the file to compare but not the baseline file.11/78 are real partitioning changes in contact cases as we switched from Morton's algorithm to RCB