33#include < vtkh/filters/Recenter.hpp>
44
55// vtkm includes
6- #include < vtkm/cont/DeviceAdapter.h>
7- #include < vtkm/cont/Storage.h>
8- #include < vtkm/internal/Configure.h>
6+ #include < vtkm/filter/scalar_topology/ContourTreeUniformDistributed.h>
7+ #include < vtkm/filter/scalar_topology/DistributedBranchDecompositionFilter.h>
8+ #include < vtkm/filter/scalar_topology/SelectTopVolumeContoursFilter.h>
9+ #include < vtkm/filter/scalar_topology/worklet/branch_decomposition/HierarchicalVolumetricBranchDecomposer.h>
910#include < vtkm/filter/scalar_topology/worklet/contourtree_augmented/PrintVectors.h>
1011#include < vtkm/filter/scalar_topology/worklet/contourtree_augmented/ProcessContourTree.h>
11- #include < vtkm/filter/scalar_topology/worklet/contourtree_augmented/processcontourtree/Branch.h>
12- #include < vtkm/filter/scalar_topology/worklet/contourtree_augmented/processcontourtree/PiecewiseLinearFunction.h>
12+ #include < vtkm/filter/scalar_topology/worklet/contourtree_augmented/Types.h>
13+ #include < vtkm/filter/scalar_topology/worklet/contourtree_distributed/HierarchicalContourTree.h>
14+ #include < vtkm/filter/scalar_topology/worklet/contourtree_distributed/TreeCompiler.h>
1315
1416#include < vtkh/filters/GhostStripper.hpp>
1517
1618#include < fstream>
1719
1820namespace caugmented_ns = vtkm::worklet::contourtree_augmented;
1921
22+ #ifdef DEBUG
23+ void SaveToBDEM (const vtkm::cont::DataSet& ds,
24+ const std::string& fieldname,
25+ const std::string& filename)
26+ {
27+ std::cout << " Saving block to " << filename << std::endl;
28+ vtkm::Id3 blockSize;
29+ vtkm::Id3 globalSize;
30+ vtkm::Id3 pointIndexStart;
31+ vtkm::cont::CastAndCall (ds.GetCellSet (),
32+ vtkm::worklet::contourtree_augmented::GetLocalAndGlobalPointDimensions (),
33+ blockSize,
34+ globalSize,
35+ pointIndexStart);
36+ std::ofstream os (filename, std::ios::binary);
37+ os << " #GLOBAL_EXTENTS " << globalSize[1 ] << " " << globalSize[0 ];
38+ if (globalSize[2 ] > 1 )
39+ {
40+ os << " " << globalSize[2 ];
41+ }
42+ os << std::endl;
43+ os << " #OFFSET " << pointIndexStart[1 ] << " " << pointIndexStart[0 ];
44+ if (globalSize[2 ] > 1 )
45+ {
46+ os << " " << pointIndexStart[2 ];
47+ }
48+ os << std::endl;
49+ os << blockSize[1 ] << " " << blockSize[0 ];
50+ if (globalSize[2 ] > 1 )
51+ {
52+ os << " " << blockSize[2 ];
53+ }
54+ os << std::endl;
55+ try
56+ {
57+ auto dataArray =
58+ ds.GetField (fieldname).GetData ().AsArrayHandle <vtkm::cont::ArrayHandleBasic<vtkm::Float32>>();
59+ os.write (reinterpret_cast <const char *>(dataArray.GetReadPointer ()),
60+ dataArray.GetNumberOfValues () * sizeof (vtkm::Float32));
61+ }
62+ catch (vtkm::cont::ErrorBadType& e)
63+ {
64+ std::cerr << " Error: Cannot get as ArrayHandleBasic<vtkm::Float32>: " << e.GetMessage ()
65+ << std::endl;
66+ }
67+ }
68+
69+ void SaveToBDEM (const vtkm::cont::PartitionedDataSet& pds,
70+ const std::string& fieldname,
71+ const std::string& basefilename,
72+ int rank,
73+ int blocksPerRank)
74+ {
75+ for (vtkm::Id i = 0 ; i < pds.GetNumberOfPartitions (); ++i)
76+ {
77+ std::string filename = basefilename + ' _' + std::to_string (rank*blocksPerRank+i) + " .bdem" ;
78+ std::cout << " Saving partition " << i << " to " << filename << std::endl;
79+ SaveToBDEM (pds.GetPartition (i), fieldname, filename);
80+ }
81+ }
82+ #endif
83+
84+ #ifdef VTKH_PARALLEL
2085static void ShiftLogicalOriginToZero (vtkm::cont::PartitionedDataSet& pds)
2186{
2287 // Shift the logical origin (minimum of LocalPointIndexStart) to zero
@@ -118,7 +183,6 @@ static void ComputeGlobalPointSize(vtkm::cont::PartitionedDataSet& pds)
118183 }
119184}
120185
121- #ifdef VTKH_PARALLEL
122186#include < mpi.h>
123187
124188// This is from VTK-m diy mpi_cast.hpp. Need the make_DIY_MPI_Comm
@@ -198,14 +262,52 @@ void ContourTree::PostExecute()
198262 Filter::PostExecute ();
199263}
200264
265+ struct TopVolumeBranchSaddleIsoValueFunctor
266+ {
267+ vtkm::Id nSelectedBranches;
268+ std::vector<vtkm::Float64> dbranches;
269+
270+ public:
271+
272+ TopVolumeBranchSaddleIsoValueFunctor (vtkm::Id nSelectedBranches) : nSelectedBranches(nSelectedBranches) {
273+ }
274+ void operator ()(const vtkm::cont::ArrayHandle<vtkm::Float32> &arr)
275+ {
276+ auto topVolBranchSaddleIsoValue = arr.ReadPortal ();
277+
278+ for (vtkm::Id branch = 0 ; branch < nSelectedBranches; ++branch) {
279+ dbranches.push_back (topVolBranchSaddleIsoValue.Get (branch));
280+ }
281+ }
282+
283+ void operator ()(const vtkm::cont::ArrayHandle<vtkm::Float64> &arr)
284+ {
285+ auto topVolBranchSaddleIsoValue = arr.ReadPortal ();
286+
287+ for (vtkm::Id branch = 0 ; branch < nSelectedBranches; ++branch) {
288+ dbranches.push_back (topVolBranchSaddleIsoValue.Get (branch));
289+ }
290+ }
291+ template <typename T>
292+ void operator ()(const T&)
293+ {
294+ throw vtkm::cont::ErrorBadValue (" TopVolumeBranchSaddleIsoValueFunctor Expected Float32 or Float64!" );
295+ }
296+
297+ vtkm::Float64 Get (vtkm::Id branch)
298+ {
299+ return dbranches[branch];
300+ }
301+ };
302+
201303struct AnalyzerFunctor
202304{
203- vtkm::filter::scalar_topology::ContourTreeAugmented& filter;
305+ vtkm::filter::scalar_topology::ContourTreeAugmented* filter;
204306 vtkh::ContourTree& contourTree;
205307 bool dataFieldIsSorted;
206308
207309 public:
208- AnalyzerFunctor (vtkh::ContourTree& contourTree, vtkm::filter::scalar_topology::ContourTreeAugmented& filter): contourTree(contourTree), filter(filter) {
310+ AnalyzerFunctor (vtkh::ContourTree& contourTree, vtkm::filter::scalar_topology::ContourTreeAugmented* filter): contourTree(contourTree), filter(filter) {
209311 }
210312
211313 void SetDataFieldIsSorted (bool dataFieldIsSorted) {
@@ -214,12 +316,12 @@ struct AnalyzerFunctor
214316
215317 void operator ()(const vtkm::cont::ArrayHandle<vtkm::Float32> &arr) const
216318 {
217- contourTree.analysis <vtkm::Float32>(filter, dataFieldIsSorted, arr);
319+ contourTree.analysis <vtkm::Float32>(* filter, dataFieldIsSorted, arr);
218320 }
219321
220322 void operator ()(const vtkm::cont::ArrayHandle<vtkm::Float64> &arr) const
221323 {
222- contourTree.analysis <vtkm::Float64>(filter, dataFieldIsSorted, arr);
324+ contourTree.analysis <vtkm::Float64>(* filter, dataFieldIsSorted, arr);
223325 }
224326
225327 template <typename T>
@@ -343,10 +445,77 @@ template<typename DataValueType> void ContourTree::analysis(vtkm::filter::scalar
343445 }
344446}
345447
448+ void ContourTree::distributed_analysis (const vtkm::cont::PartitionedDataSet& result, int mpi_rank) {
449+ vtkm::filter::scalar_topology::DistributedBranchDecompositionFilter bd_filter;
450+ auto bd_result = bd_filter.Execute (result);
451+
452+ #if 0
453+ for (vtkm::Id ds_no = 0; ds_no < result.GetNumberOfPartitions(); ++ds_no)
454+ {
455+ auto ds = bd_result.GetPartition(ds_no);
456+ std::string branchDecompositionFileName = std::string("BranchDecomposition_Rank_") +
457+ std::to_string(static_cast<int>(mpi_rank)) + std::string("_Block_") +
458+ std::to_string(static_cast<int>(ds_no)) + std::string(".txt");
459+
460+ std::ofstream treeStream(branchDecompositionFileName.c_str());
461+ treeStream
462+ << vtkm::filter::scalar_topology::HierarchicalVolumetricBranchDecomposer::PrintBranches(ds);
463+ }
464+ #endif
465+
466+ vtkm::filter::scalar_topology::SelectTopVolumeContoursFilter tp_filter;
467+ vtkm::Id numBranches = m_levels;
468+
469+ tp_filter.SetSavedBranches (numBranches);
470+ bd_result = tp_filter.Execute (bd_result);
471+
472+ for (vtkm::Id ds_no = 0 ; ds_no < result.GetNumberOfPartitions (); ++ds_no)
473+ {
474+ auto ds = bd_result.GetPartition (ds_no);
475+ auto topVolBranchGRId = ds.GetField (" TopVolumeBranchGlobalRegularIds" )
476+ .GetData ()
477+ .AsArrayHandle <vtkm::cont::ArrayHandle<vtkm::Id>>()
478+ .ReadPortal ();
479+
480+ auto topVolBranchSaddleEpsilon = ds.GetField (" TopVolumeBranchSaddleEpsilon" )
481+ .GetData ()
482+ .AsArrayHandle <vtkm::cont::ArrayHandle<vtkm::Id>>()
483+ .ReadPortal ();
484+
485+ vtkm::Id nSelectedBranches = topVolBranchGRId.GetNumberOfValues ();
486+
487+ TopVolumeBranchSaddleIsoValueFunctor iso_functor (nSelectedBranches);
488+
489+ vtkm::cont::CastAndCall (ds.GetField (" TopVolumeBranchSaddleIsoValue" ).GetData (), iso_functor);
490+
491+
492+ vtkm::Float32 eps = 0.00001 ;
493+
494+ for (vtkm::Id branch = 0 ; branch < nSelectedBranches; ++branch)
495+ {
496+ m_iso_values[branch] = iso_functor.Get (branch) + (eps * topVolBranchSaddleEpsilon.Get (branch));
497+ }
498+
499+ break ;
500+ }
501+
502+ std::sort (m_iso_values.begin (), m_iso_values.end ());
503+ }
504+
346505void ContourTree::DoExecute ()
506+ {
347507{
508+ #ifdef VTKH_PARALLEL
509+ int mpi_rank = 0 ;
510+
511+ MPI_Comm mpi_comm = MPI_Comm_f2c (vtkh::GetMPICommHandle ());
512+ vtkm::cont::EnvironmentTracker::SetCommunicator (vtkmdiy::mpi::communicator (vtkmdiy::mpi::make_DIY_MPI_Comm (mpi_comm)));
513+
514+ MPI_Comm_rank (mpi_comm, &mpi_rank);
515+ #endif
516+ }
517+
348518 vtkh::DataSet *old_input = this ->m_input ;
349- const int before_num_domains = this ->m_input ->GetNumberOfDomains ();
350519
351520 // make sure we have a node-centered field
352521 bool valid_field = false ;
@@ -388,7 +557,6 @@ void ContourTree::DoExecute()
388557 this ->m_output = new DataSet ();
389558
390559 const int num_domains = this ->m_input ->GetNumberOfDomains ();
391- assert (num_domains == 1 );
392560
393561#ifndef VTKH_PARALLEL
394562 vtkm::cont::DataSet inDataSet;
@@ -409,68 +577,85 @@ void ContourTree::DoExecute()
409577
410578 vtkm::cont::PartitionedDataSet inDataSet;
411579
412- vtkm::Id domain_id;
413- vtkm::cont::DataSet dom;
580+ for (int i = 0 ; i < num_domains; ++i)
581+ {
582+ vtkm::Id domain_id;
583+ vtkm::cont::DataSet dom;
414584
415- this ->m_input ->GetDomain (0 , dom, domain_id);
416- inDataSet.AppendPartition (dom);
585+ this ->m_input ->GetDomain (i, dom, domain_id);
586+ inDataSet.AppendPartition (dom);
587+ this ->m_output ->AddDomain (dom, domain_id);
588+ }
417589
590+ #ifdef DEBUG
418591 if ( mpi_size != 1 )
419592 {
420593 std::ostringstream ostr;
421594 ostr << " rank: " << mpi_rank
422595 << " coord system range: " << dom.GetCoordinateSystem (0 ).GetRange () << std::endl;
423596 std::cout << ostr.str ();
424597 }
425- #ifdef DEBUG
426598#endif
427599#endif // VTKH_PARALLEL
428600
601+ vtkm::filter::Filter *filterField;
602+
603+ #ifndef VTKH_PARALLEL
429604 bool useMarchingCubes = false ;
430605 // Compute the fully augmented contour tree.
431606 // This should always be true for now in order for the isovalue selection to work.
432607 bool computeRegularStructure = true ;
433608
434609 // Convert the mesh of values into contour tree, pairs of vertex ids
435610 vtkm::filter::scalar_topology::ContourTreeAugmented filter (useMarchingCubes, computeRegularStructure);
436-
437- filter.SetActiveField (m_field_name);
438-
439- #ifdef VTKH_PARALLEL
611+ #else // VTKH_PARALLEL
612+ // TODO SET VTKM to do vtkm::cont::LogLovel::Info
613+ // vtkm::filter::scalar_topology::ContourTreeUniformDistributed filter(vtkm::cont::LogLevel::Info, vtkm::cont::LogLevel::Info);
614+ vtkm::filter::scalar_topology::ContourTreeUniformDistributed filter;
615+ vtkm::filter::scalar_topology::ContourTreeAugmented aug_filter (false , true );
616+
617+ if (mpi_size == 1 ) {
618+ filterField = &aug_filter;
619+ } else {
620+ filter.SetUseBoundaryExtremaOnly (true );
621+ filter.SetUseMarchingCubes (false );
622+ filter.SetAugmentHierarchicalTree (true );
440623 ShiftLogicalOriginToZero (inDataSet);
441624 ComputeGlobalPointSize (inDataSet);
625+ filterField = &filter;
626+ }
627+
442628#endif // VTKH_PARALLEL
443629
444- auto result = filter. Execute (inDataSet );
630+ filterField-> SetActiveField (m_field_name );
445631
446- m_iso_values.resize (m_levels);
632+ #ifdef DEBUG
633+ SaveToBDEM (inDataSet, m_field_name, " debug-ascent" , mpi_rank, num_domains);
634+ #endif
447635
448- if (mpi_rank == 0 ) {
449- AnalyzerFunctor analyzerFunctor (*this , filter);
636+ auto result = filterField->Execute (inDataSet);
637+
638+ m_iso_values.resize (m_levels);
450639
451640#ifndef VTKH_PARALLEL
641+ if (mpi_rank == 0 ) {
642+ AnalyzerFunctor analyzerFunctor (*this , &filter);
452643 analyzerFunctor.SetDataFieldIsSorted (false );
453644 vtkm::cont::CastAndCall (inDataSet.GetField (m_field_name).GetData (), analyzerFunctor);
645+ } // mpi_rank == 0
454646#else
455- if (mpi_size == 1 )
456- {
457- analyzerFunctor.SetDataFieldIsSorted (false );
458- vtkm::cont::CastAndCall (inDataSet.GetPartitions ()[0 ].GetField (m_field_name).GetData (), analyzerFunctor);
459- } else {
460- analyzerFunctor.SetDataFieldIsSorted (true );
461-
462- /*
463- if( result.GetPartitions()[0].GetNumberOfFields() > 1 ) {
464- vtkm::cont::CastAndCall(result.GetPartitions()[0].GetField("values").GetData(), analyzerFunctor);
465- } else {
466- vtkm::cont::CastAndCall(result.GetPartitions()[0].GetField(0).GetData(), analyzerFunctor);
467- }*/
468-
469- // TODO TO BE REVISITED. Tested with: srun -n 8 ./t_vtk-h_contour_tree_par
470- vtkm::cont::CastAndCall (result.GetPartitions ()[0 ].GetField (" resultData" ).GetData (), analyzerFunctor);
471- }
647+ if (mpi_size > 1 )
648+ { // BranchDecomposition
649+ distributed_analysis (result, mpi_rank);
650+ }
651+ else
652+ {
653+ AnalyzerFunctor analyzerFunctor (*this , (vtkm::filter::scalar_topology::ContourTreeAugmented *) filterField);
654+
655+ analyzerFunctor.SetDataFieldIsSorted (false );
656+ vtkm::cont::CastAndCall (inDataSet.GetPartitions ()[0 ].GetField (m_field_name).GetData (), analyzerFunctor);
657+ }
472658#endif // VTKH_PARALLEL
473- } // mpi_rank == 0
474659
475660#ifdef VTKH_PARALLEL
476661 MPI_Bcast (&m_iso_values[0 ], m_levels, MPI_DOUBLE, 0 , mpi_comm);
0 commit comments