//============================================================================ // Copyright (c) Kitware, Inc. // All rights reserved. // See LICENSE.txt for details. // // This software is distributed WITHOUT ANY WARRANTY; without even // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notice for more information. //============================================================================ // Copyright (c) 2018, The Regents of the University of California, through // Lawrence Berkeley National Laboratory (subject to receipt of any required approvals // from the U.S. Dept. of Energy). All rights reserved. // // Redistribution and use in source and binary forms, with or without modification, // are permitted provided that the following conditions are met: // // (1) Redistributions of source code must retain the above copyright notice, this // list of conditions and the following disclaimer. // // (2) Redistributions in binary form must reproduce the above copyright notice, // this list of conditions and the following disclaimer in the documentation // and/or other materials provided with the distribution. // // (3) Neither the name of the University of California, Lawrence Berkeley National // Laboratory, U.S. Dept. of Energy nor the names of its contributors may be // used to endorse or promote products derived from this software without // specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. // IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, // INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE // OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED // OF THE POSSIBILITY OF SUCH DAMAGE. // //======================================================================================= // // Parallel Peak Pruning v. 2.0 // // Started June 15, 2017 // // Copyright Hamish Carr, University of Leeds // // PrintGraph.h - routines for outputting dot files for debug purposes // //======================================================================================= // // COMMENTS: We have dot output scattered among multiple files, when it is primarily // used for debug. Moreover, our principal data classes are public so that // the algorithmic construction can be separated into factory classes. // So we can collect all of the code for dot output into one place and be // consistent about how we implement it // // We will want dot output for the following classes: // BoundaryTree // ContourTree // ContourTreeMesh // HierarchicalContourTree // InteriorForest / InteriorForest // // In addition, it may be useful to be able to print out: // MergeTree // // but as of the time of writing (30/07/2020) it is not priority for debug // // Factory classes will typically have multiple temporary arrays // and will therefore primarily be debugged by printing the arrays // // However, since these classes are often indexed on our the main data classes // we should also have a version of the graph output that shows a designated // piece of text on each node // // The general design (initially) will be to produce all-purpose illustrations // While these will probably be overkill, experience shows that confusion between // the different indices is common, so it is better to be absolutely explicit // and print out every piece of information possible in the graph. // However, for the ContourTree and HierarchicalContourTree, we also want to have // the option to show regular / super / hyper structure separately. // // NOTE ON CONST USAGE: // I had originally planned to use NULL vectors for parameters not being passed // but the compiler doesn't like the possibility of non-const temporary parameters // so the flags arrays must be const. // If I do the same with Mesh & ContourTree, I need to set functions elsewhere to // be const. I will not do that unilaterally. // All parameters of these print functions should on principal be const, except // the stream parameter // //======================================================================================= #ifndef vtk_m_worklet_contourtree_distributed_print_graph_h #define vtk_m_worklet_contourtree_distributed_print_graph_h #include #include #include #include #include #include #include #include namespace vtkm { namespace worklet { namespace contourtree_distributed { /// \brief Routines for printing various tree data structures in graphviz .dot format /// /// These routines are primarily for debug at the moment, and share a number of constants and /// software patterns. /// They are therefore collected in a single unit rather than distributed in each clase // Routines to print out a contour tree in regular and super/hyper format // The older code ended up with separate versions depending on mesh type, but there are now // accessor functions, and I will try to avoid this. However, this means templating everything // to do with contour trees // Routines for contour trees: // 1. Routine to print super structure // generic, with colour set according to iteration // with option to print grey / white for boundary / interior nodes (regular tree only) // with option to show BoundaryTree / Forest as different styles (super tree only) // with option to show an arbitrary additional value passed as an array // Routines for ContourTreeMesh: // 2. Simple routine to dump out nodes / edges for cross-checking // no options should be needed // Routines for BoundaryTree: // 3. All purpose routine to dump out the contents for comparison with contour tree // Includes option to show how the InteriorForest connects to it // Routines for HierarchicalContourTree: // 4. Routine to print regular/super/hyper structure with similar options to contour tree // Routines for contour trees: // 1. Routine to print super structure // generic, with colour set according to iteration // with option to print grey / white for boundary / interior nodes (regular tree only) // with option to show BoundaryTree / Forest as different styles (super tree only) // with option to show an arbitrary additional value passed as an array constexpr vtkm::Id INDEX_WIDTH = 6; constexpr vtkm::Id NO_PER_NODE_VALUES = 0; constexpr vtkm::Id PER_REGULAR_NODE_VALUES = 1; constexpr vtkm::Id PER_REGULAR_NODE_BOUNDARY_FLAGS = 2; constexpr vtkm::Id PER_SUPER_NODE_VALUES = 3; constexpr vtkm::Id PER_SUPER_NODE_BOUNDARY_FLAGS = 4; constexpr vtkm::Id PER_HYPER_NODE_VALUES = 5; constexpr vtkm::Id BAD_PER_NODE_VALUES = 6; constexpr vtkm::Id NODE_TYPE_REGULAR = 0; constexpr vtkm::Id NODE_TYPE_SUPER = 1; constexpr vtkm::Id NODE_TYPE_HYPER = 2; // bitflags for various components constexpr vtkm::Id SHOW_REGULAR_STRUCTURE = 0x00000001; constexpr vtkm::Id SHOW_SUPER_STRUCTURE = 0x00000002; constexpr vtkm::Id SHOW_HYPER_STRUCTURE = 0x00000004; constexpr vtkm::Id SHOW_BOUNDARY_NODES = 0x00000010; constexpr vtkm::Id SHOW_CRITICAL_BOUNDARY_NODES = 0x00000020; constexpr vtkm::Id SHOW_NECESSARY_SUPERNODES = 0x00000040; constexpr vtkm::Id SHOW_GLOBAL_ID = 0x00000100; constexpr vtkm::Id SHOW_DATA_VALUE = 0x00000200; constexpr vtkm::Id SHOW_MESH_REGULAR_ID = 0x00000400; constexpr vtkm::Id SHOW_MESH_SORT_ID = 0x00000800; constexpr vtkm::Id SHOW_NODE_ID = 0x00001000; constexpr vtkm::Id SHOW_SUPERPARENT = 0x00002000; constexpr vtkm::Id SHOW_ARC_ID = 0x00004000; constexpr vtkm::Id SHOW_EXTRA_DATA = 0x00008000; constexpr vtkm::Id SHOW_SUPERNODE_ID = 0x00010000; constexpr vtkm::Id SHOW_HYPERPARENT = 0x00020000; constexpr vtkm::Id SHOW_SUPERARC_ID = 0x0004000; constexpr vtkm::Id SHOW_ITERATION = 0x00080000; constexpr vtkm::Id SHOW_HYPERNODE_ID = 0x00100000; constexpr vtkm::Id SHOW_HYPERARC_ID = 0x00200000; // bit flags used for structures other than the contour tree // the BoundaryTree has a vertex index, but doesn't have the contour tree's nodes, so we will reuse that bit flag constexpr vtkm::Id SHOW_BOUNDARY_TREE_VERTEX_ID = SHOW_NODE_ID; // others are just relabelling the same ID flag constexpr vtkm::Id SHOW_BOUNDARY_TREE_GLOBAL_ID = SHOW_GLOBAL_ID; constexpr vtkm::Id SHOW_BOUNDARY_TREE_DATA_VALUE = SHOW_DATA_VALUE; constexpr vtkm::Id SHOW_BOUNDARY_TREE_MESH_REGULAR_ID = SHOW_MESH_REGULAR_ID; constexpr vtkm::Id SHOW_BOUNDARY_TREE_MESH_SORT_ID = SHOW_MESH_SORT_ID; constexpr vtkm::Id SHOW_BOUNDARY_TREE_ARC_ID = SHOW_ARC_ID; constexpr vtkm::Id SHOW_BOUNDARY_TREE_ALL = (SHOW_BOUNDARY_TREE_VERTEX_ID | SHOW_BOUNDARY_TREE_GLOBAL_ID | SHOW_BOUNDARY_TREE_DATA_VALUE | SHOW_BOUNDARY_TREE_MESH_REGULAR_ID | SHOW_BOUNDARY_TREE_MESH_SORT_ID | SHOW_BOUNDARY_TREE_ARC_ID); // Similarly, relabel the IDs for use with contour tree meshes constexpr vtkm::Id SHOW_CONTOUR_TREE_MESH_VERTEX_ID = SHOW_NODE_ID; // others are just relabelling the same ID flag constexpr vtkm::Id SHOW_CONTOUR_TREE_MESH_GLOBAL_ID = SHOW_GLOBAL_ID; constexpr vtkm::Id SHOW_CONTOUR_TREE_MESH_DATA_VALUE = SHOW_DATA_VALUE; constexpr vtkm::Id SHOW_CONTOUR_TREE_MESH_ALL = (SHOW_CONTOUR_TREE_MESH_VERTEX_ID | SHOW_CONTOUR_TREE_MESH_GLOBAL_ID | SHOW_CONTOUR_TREE_MESH_DATA_VALUE); // InteriorForest is slightly tricky, but wants to be use the same set as BoundaryTree constexpr vtkm::Id SHOW_INTERIOR_FOREST_VERTEX_ID = SHOW_SUPERNODE_ID; constexpr vtkm::Id SHOW_INTERIOR_FOREST_GLOBAL_ID = SHOW_BOUNDARY_TREE_GLOBAL_ID; constexpr vtkm::Id SHOW_INTERIOR_FOREST_DATA_VALUE = SHOW_BOUNDARY_TREE_DATA_VALUE; constexpr vtkm::Id SHOW_INTERIOR_FOREST_MESH_REGULAR_ID = SHOW_BOUNDARY_TREE_MESH_REGULAR_ID; constexpr vtkm::Id SHOW_INTERIOR_FOREST_MESH_SORT_ID = SHOW_BOUNDARY_TREE_MESH_SORT_ID; constexpr vtkm::Id SHOW_INTERIOR_FOREST_ALL = (SHOW_INTERIOR_FOREST_VERTEX_ID | SHOW_INTERIOR_FOREST_GLOBAL_ID | SHOW_INTERIOR_FOREST_DATA_VALUE | SHOW_INTERIOR_FOREST_MESH_REGULAR_ID | SHOW_INTERIOR_FOREST_MESH_SORT_ID); constexpr vtkm::Id SHOW_ALL_STRUCTURE = (SHOW_REGULAR_STRUCTURE | SHOW_SUPER_STRUCTURE | SHOW_HYPER_STRUCTURE); constexpr vtkm::Id SHOW_BASIC_IDS = (SHOW_DATA_VALUE | SHOW_MESH_SORT_ID); constexpr vtkm::Id SHOW_ALL_IDS = (SHOW_GLOBAL_ID | SHOW_DATA_VALUE | SHOW_MESH_REGULAR_ID | SHOW_MESH_SORT_ID | SHOW_NODE_ID | SHOW_SUPERPARENT | SHOW_ARC_ID); constexpr vtkm::Id SHOW_BASIC_SUPERIDS = (SHOW_SUPERNODE_ID | SHOW_ITERATION); constexpr vtkm::Id SHOW_ALL_SUPERIDS = (SHOW_SUPERNODE_ID | SHOW_HYPERPARENT | SHOW_ITERATION | SHOW_SUPERARC_ID); constexpr vtkm::Id SHOW_BASIC_HYPERIDS = SHOW_HYPERNODE_ID; constexpr vtkm::Id SHOW_ALL_HYPERIDS = (SHOW_HYPERNODE_ID | SHOW_HYPERARC_ID); constexpr vtkm::Id SHOW_REGULAR_SIMPLE = (SHOW_REGULAR_STRUCTURE | SHOW_BASIC_IDS); constexpr vtkm::Id SHOW_REGULAR_BOUNDARY = (SHOW_REGULAR_STRUCTURE | SHOW_BASIC_IDS | SHOW_BOUNDARY_NODES); constexpr vtkm::Id SHOW_REGULAR_CRITICAL_BOUNDARY = (SHOW_REGULAR_STRUCTURE | SHOW_BASIC_IDS | SHOW_CRITICAL_BOUNDARY_NODES); constexpr vtkm::Id SHOW_SUPER_SIMPLE = (SHOW_SUPER_STRUCTURE | SHOW_BASIC_IDS | SHOW_BASIC_SUPERIDS); constexpr vtkm::Id SHOW_BOUNDARY_INTERIOR_DIVISION = (SHOW_SUPER_STRUCTURE | SHOW_BASIC_IDS | SHOW_BASIC_SUPERIDS | SHOW_NECESSARY_SUPERNODES); constexpr vtkm::Id SHOW_SUPER_AND_HYPER_SIMPLE = (SHOW_SUPER_SIMPLE | SHOW_HYPER_STRUCTURE | SHOW_HYPERNODE_ID); constexpr vtkm::Id SHOW_ALL_STANDARD = (SHOW_ALL_STRUCTURE | SHOW_ALL_IDS | SHOW_ALL_SUPERIDS | SHOW_ALL_HYPERIDS); constexpr vtkm::Id SHOW_HIERARCHICAL_STANDARD = (SHOW_SUPER_STRUCTURE | SHOW_HYPER_STRUCTURE | SHOW_ALL_IDS | SHOW_ALL_SUPERIDS | SHOW_ALL_HYPERIDS); // 1. Routine for printing dot for contour tree regular / super / hyper structure VTKM_CONT template std::string ContourTreeDotGraphPrint( const std::string& label, // the label to use as title for the graph MeshType& mesh, // the underlying mesh for the contour tree const vtkm::worklet::contourtree_augmented::mesh_dem::IdRelabeler* localToGlobalIdRelabeler, // relabler needed to compute global ids const vtkm::cont::ArrayHandle& field, vtkm::worklet::contourtree_augmented::ContourTree& contourTree, // the contour tree itself const vtkm::Id showMask = SHOW_ALL_STANDARD, // mask with flags for what elements to show // const vtkm::worklet::contourtree_augmented::IdArrayType &necessaryFlags = vtkm::worklet::contourtree_augmented::IdArrayType(), // array with flags for "necessary" const VectorType& perNodeValues = VectorType()) // an arbitrary vector of values { // ContourTreeSuperDotGraphPrint() // initialise a string stream to capture the output std::stringstream outStream; // now grab portals to all the variables we will need auto nodesPortal = contourTree.Nodes.ReadPortal(); auto arcsPortal = contourTree.Arcs.ReadPortal(); auto superparentsPortal = contourTree.Superparents.ReadPortal(); auto supernodesPortal = contourTree.Supernodes.ReadPortal(); auto superarcsPortal = contourTree.Superarcs.ReadPortal(); auto hyperparentsPortal = contourTree.Hyperparents.ReadPortal(); auto whenTransferredPortal = contourTree.WhenTransferred.ReadPortal(); auto hypernodesPortal = contourTree.Hypernodes.ReadPortal(); auto hyperarcsPortal = contourTree.Hyperarcs.ReadPortal(); // auto necessaryFlagsPortal = necessaryFlags.ReadPortal(); auto perNodeValuesPortal = perNodeValues.ReadPortal(); // work out how long the computed value is int nodeValueType = vtkm::worklet::contourtree_distributed::BAD_PER_NODE_VALUES; vtkm::Id perNodeSize = perNodeValues.GetNumberOfValues(); if (perNodeSize == 0) nodeValueType = vtkm::worklet::contourtree_distributed::NO_PER_NODE_VALUES; else if (perNodeSize == contourTree.Nodes.GetNumberOfValues()) nodeValueType = vtkm::worklet::contourtree_distributed::PER_REGULAR_NODE_VALUES; else if (perNodeSize == contourTree.Supernodes.GetNumberOfValues()) nodeValueType = vtkm::worklet::contourtree_distributed::PER_SUPER_NODE_VALUES; else if (perNodeSize == contourTree.Hypernodes.GetNumberOfValues()) nodeValueType = vtkm::worklet::contourtree_distributed::PER_HYPER_NODE_VALUES; else { // error message outStream << "ERROR in ContourTreeDotGraphPrint().\n"; outStream << "Per node values array must be empty, or\n"; outStream << "Same length as regular nodes (" << std::setw(vtkm::worklet::contourtree_distributed::INDEX_WIDTH) << contourTree.Nodes.GetNumberOfValues() << "), or\n"; outStream << "Same length as super nodes (" << std::setw(vtkm::worklet::contourtree_distributed::INDEX_WIDTH) << contourTree.Supernodes.GetNumberOfValues() << "), or\n"; outStream << "Same length as hyper nodes (" << std::setw(vtkm::worklet::contourtree_distributed::INDEX_WIDTH) << contourTree.Hypernodes.GetNumberOfValues() << ")\n"; outStream << "Actual length was (" << std::setw(vtkm::worklet::contourtree_distributed::INDEX_WIDTH) << perNodeValues.GetNumberOfValues() << ")\n"; } // error message // print the header information outStream << "digraph ContourTree\n\t{\n"; outStream << "\tlabel=\"" << std::setw(1) << label << "\"\n\tlabelloc=t\n\tfontsize=30\n"; outStream << "\t// Nodes" << std::endl; auto meshSortOrderPortal = mesh.SortOrder.ReadPortal(); auto globalIds = mesh.GetGlobalIdsFromSortIndices(contourTree.Nodes, localToGlobalIdRelabeler); auto globalIdsPortal = globalIds.ReadPortal(); auto dataValuesPortal = field.ReadPortal(); // loop through all of the nodes in the regular list for (vtkm::Id node = 0; node < contourTree.Nodes.GetNumberOfValues(); node++) { // per node // the nodes array is actually sorted by superarc, but the superarcs array is not // so we ignore the nodes array and work directly with the node # vtkm::Id sortID = nodesPortal.Get(node); // retrieve the regular ID vtkm::Id regularID = meshSortOrderPortal.Get(sortID); // retrieve the global ID vtkm::Id globalID = globalIdsPortal.Get(sortID); // retrieve the values auto dataValue = dataValuesPortal.Get(regularID); // retrieve the superparent vtkm::Id superparent = superparentsPortal.Get(sortID); // and retrieve the iteration # vtkm::Id iteration = vtkm::worklet::contourtree_augmented::MaskedIndex(whenTransferredPortal.Get(superparent)); // work out the super ID & hyper ID vtkm::Id superID = vtkm::worklet::contourtree_augmented::NO_SUCH_ELEMENT; vtkm::Id hyperparent = vtkm::worklet::contourtree_augmented::NO_SUCH_ELEMENT; vtkm::Id hyperID = vtkm::worklet::contourtree_augmented::NO_SUCH_ELEMENT; vtkm::Id nodeType = vtkm::worklet::contourtree_distributed::NODE_TYPE_REGULAR; // test for super if (supernodesPortal.Get(superparent) == sortID) { // at least super // set super ID superID = superparent; // set hyperparent hyperparent = hyperparentsPortal.Get(superID); // set nodetype nodeType = NODE_TYPE_SUPER; // test for hyper if (hypernodesPortal.Get(hyperparent) == superID) { // hyper node nodeType = NODE_TYPE_HYPER; hyperID = hyperparent; } // hyper node } // at least super // now, if we don't want the regular nodes, we want to skip them entirely, so bool showNode = false; // regular structure always shows all nodes if (showMask & vtkm::worklet::contourtree_distributed::SHOW_REGULAR_STRUCTURE) showNode = true; // super structure shows super & hyper nodes only else if (showMask & vtkm::worklet::contourtree_distributed::SHOW_SUPER_STRUCTURE) showNode = (nodeType != vtkm::worklet::contourtree_distributed::NODE_TYPE_REGULAR); else if (showMask & vtkm::worklet::contourtree_distributed::SHOW_HYPER_STRUCTURE) showNode = (nodeType == vtkm::worklet::contourtree_distributed::NODE_TYPE_HYPER); // if we didn't set the flag, skip the node if (!showNode) continue; // print the vertex ID, which should be the sort ID & needs to be left-justified to work outStream << "\ts" << std::setw(1) << sortID; // print the style characteristics - node is filled and fixed size outStream << " [style=filled,fixedsize=true,fontname=\"Courier\",margin=\"0.02,0.02\""; // specify the style based on the type of node if (nodeType == vtkm::worklet::contourtree_distributed::NODE_TYPE_REGULAR) outStream << ",height=\"1.7in\",width=\"1.7in\",penwidth=5"; else if (nodeType == vtkm::worklet::contourtree_distributed::NODE_TYPE_SUPER) outStream << ",height=\"2.5in\",width=\"2.5in\",penwidth=10"; else if (nodeType == vtkm::worklet::contourtree_distributed::NODE_TYPE_HYPER) outStream << ",height=\"2.5in\",width=\"2.5in\",penwidth=15"; // shape should always be circular. outStream << ",shape=circle"; // fill colour is grey for boundary or necessary, if these are passed in bool isGrey = false; // TODO: Add liesOnBoundary and isNecessary so we can define the gray value /* if (showMask & vtkm::worklet::contourtree_distributed::SHOW_BOUNDARY_NODES) isGrey = (showMask & vtkm::worklet::contourtree_distributed::SHOW_REGULAR_STRUCTURE) && mesh.liesOnBoundary(regularID); else if (showMask & vtkm::worklet::contourtree_distributed::SHOW_CRITICAL_BOUNDARY_NODES) isGrey = (showMask & vtkm::worklet::contourtree_distributed::SHOW_REGULAR_STRUCTURE) && mesh.isNecessary(regularID); else if (showMask & vtkm::worklet::contourtree_distributed::SHOW_NECESSARY_SUPERNODES) isGrey = (showMask & vtkm::worklet::contourtree_distributed::SHOW_SUPER_STRUCTURE) // skip if superstructure not shown && !vtkm::worklet::contourtree_augmented::NoSuchElement(superID) // ignore non-super nodes && (necessaryFlags.GetNumberOfValues() == contourTree.Supernodes.GetNumberOfValues()) // skip if necessary flags array is wrong size && necessaryFlagsPortal.Get(superID); */ // after setting the flag, its easy outStream << (isGrey ? ",fillcolor=grey" : ",fillcolor=white"); // stroke colour depends on iteration outStream << ",color=" << vtkm::worklet::contourtree_augmented::NODE_COLORS [iteration % vtkm::worklet::contourtree_augmented::N_NODE_COLORS]; // start printing the label outStream << ",label=\""; // print the global ID if (showMask & vtkm::worklet::contourtree_distributed::SHOW_GLOBAL_ID) outStream << "g " << std::setw(vtkm::worklet::contourtree_distributed::INDEX_WIDTH) << globalID << "\\n"; // print the value if (showMask & vtkm::worklet::contourtree_distributed::SHOW_DATA_VALUE) outStream << "v " << std::setw(vtkm::worklet::contourtree_distributed::INDEX_WIDTH) << dataValue << "\\n"; // print the regular & sort IDs if (showMask & vtkm::worklet::contourtree_distributed::SHOW_MESH_REGULAR_ID) outStream << "r " << std::setw(vtkm::worklet::contourtree_distributed::INDEX_WIDTH) << regularID << "\\n"; if (showMask & vtkm::worklet::contourtree_distributed::SHOW_MESH_SORT_ID) outStream << "s " << std::setw(vtkm::worklet::contourtree_distributed::INDEX_WIDTH) << sortID << "\\n"; // and the node ID if (showMask & vtkm::worklet::contourtree_distributed::SHOW_NODE_ID) outStream << "n " << std::setw(vtkm::worklet::contourtree_distributed::INDEX_WIDTH) << node << "\\n"; // print the superparent if (showMask & vtkm::worklet::contourtree_distributed::SHOW_SUPERPARENT) outStream << "sp" << std::setw(vtkm::worklet::contourtree_distributed::INDEX_WIDTH) << superparent << "\\n"; // add arbitrary per node value if it is regular in nature if ((showMask & vtkm::worklet::contourtree_distributed::SHOW_EXTRA_DATA) && (nodeValueType == vtkm::worklet::contourtree_distributed::PER_REGULAR_NODE_VALUES)) outStream << "x " << std::setw(vtkm::worklet::contourtree_distributed::INDEX_WIDTH) << perNodeValuesPortal.Get(regularID) << "\\n"; // we now want to add labelling information specific to supernodes, but also present in hypernodes if (nodeType != vtkm::worklet::contourtree_distributed::NODE_TYPE_REGULAR) { // at least super // print the super node ID if (showMask & vtkm::worklet::contourtree_distributed::SHOW_SUPERNODE_ID) outStream << "SN" << std::setw(vtkm::worklet::contourtree_distributed::INDEX_WIDTH) << superID << "\\n"; // print the hyperparent as well if (showMask & vtkm::worklet::contourtree_distributed::SHOW_HYPERPARENT) outStream << "HP" << std::setw(vtkm::worklet::contourtree_distributed::INDEX_WIDTH) << hyperparent << "\\n"; if (showMask & vtkm::worklet::contourtree_distributed::SHOW_ITERATION) outStream << "IT" << std::setw(vtkm::worklet::contourtree_distributed::INDEX_WIDTH) << iteration << "\\n"; // add arbitrary per node value if it is super in nature if ((showMask & vtkm::worklet::contourtree_distributed::SHOW_EXTRA_DATA) && (nodeValueType == vtkm::worklet::contourtree_distributed::PER_SUPER_NODE_VALUES)) outStream << "X " << std::setw(vtkm::worklet::contourtree_distributed::INDEX_WIDTH) << perNodeValuesPortal.Get(superID) << "\\n"; } // at least super // now add even more for hypernodes if (nodeType == vtkm::worklet::contourtree_distributed::NODE_TYPE_HYPER) { // hyper node if (showMask & vtkm::worklet::contourtree_distributed::SHOW_HYPERNODE_ID) outStream << "HN" << std::setw(vtkm::worklet::contourtree_distributed::INDEX_WIDTH) << hyperID << "\\n"; // add arbitrary per node value if it is hyper in nature if ((showMask & vtkm::worklet::contourtree_distributed::SHOW_EXTRA_DATA) && (nodeValueType == vtkm::worklet::contourtree_distributed::PER_HYPER_NODE_VALUES)) outStream << "X " << std::setw(vtkm::worklet::contourtree_distributed::INDEX_WIDTH) << perNodeValuesPortal.Get(hyperID) << "\\n"; } // hyper node outStream << "\"]" << std::endl; } // per node // always show the null node outStream << "\t// Null Node" << std::endl; outStream << "\tNULL " "[style=filled,fixedsize=true,fontname=\"Courier\",margin=\"0.02,0.02\",height=\"0.5in\"," "width=\"0.5in\",penwidth=1,shape=circle,fillcolor=white,color=black,label=\"NULL\"]" << std::endl; // start the arcs outStream << "\t// Arcs" << std::endl; // now add regular arcs (if requested) if (showMask & vtkm::worklet::contourtree_distributed::SHOW_REGULAR_STRUCTURE) for (vtkm::Id node = 0; node < contourTree.Nodes.GetNumberOfValues(); node++) { // per node // retrieve the "to" end vtkm::Id to = arcsPortal.Get(node); // if "to" is NSE, it's the root node if (vtkm::worklet::contourtree_augmented::NoSuchElement(to)) outStream << "\ts" << std::setw(1) << node << " -> NULL [penwidth=2"; else { // actual node // mask out the flags to get the target node to = vtkm::worklet::contourtree_augmented::MaskedIndex(to); // since we're using sort IDs, we compare them if (node < to) outStream << "\ts" << std::setw(1) << to << " -> s" << std::setw(1) << node << " [dir=back,penwidth=3"; else outStream << "\ts" << std::setw(1) << node << " -> s" << std::setw(1) << to << " [penwidth=3"; } // actual node // set the color based on the from vertex // retrieve the superparent vtkm::Id superparent = superparentsPortal.Get(node); vtkm::Id iteration = vtkm::worklet::contourtree_augmented::MaskedIndex(whenTransferredPortal.Get(superparent)); outStream << ",color=" << vtkm::worklet::contourtree_augmented::NODE_COLORS [iteration % vtkm::worklet::contourtree_augmented::N_NODE_COLORS]; if (showMask & SHOW_ARC_ID) outStream << ",label=\"A" << node << "\""; outStream << "]" << std::endl; } // per node // show superarcs if requested if (showMask & vtkm::worklet::contourtree_distributed::SHOW_SUPER_STRUCTURE) for (vtkm::Id supernode = 0; supernode < contourTree.Supernodes.GetNumberOfValues(); supernode++) { // per supernode // retrieve the sort ID vtkm::Id from = supernodesPortal.Get(supernode); // retrieve the "to" end vtkm::Id toSuper = superarcsPortal.Get(supernode); // test for "NSE" if (vtkm::worklet::contourtree_augmented::NoSuchElement(toSuper)) outStream << "\ts" << std::setw(1) << from << " -> NULL [penwidth=4"; else { // supernode // mask out the ascending flag & convert to sort ID vtkm::Id to = supernodesPortal.Get(vtkm::worklet::contourtree_augmented::MaskedIndex(toSuper)); // now test for ascending with sort IDs as before if (from < to) outStream << "\ts" << std::setw(1) << to << " -> s" << std::setw(1) << from << " [dir=back,penwidth=7"; else outStream << "\ts" << std::setw(1) << from << " -> s" << std::setw(1) << to << " [penwidth=7"; } // supernode // set the color based on the from vertex vtkm::Id iteration = vtkm::worklet::contourtree_augmented::MaskedIndex(whenTransferredPortal.Get(supernode)); outStream << ",color=" << vtkm::worklet::contourtree_augmented::NODE_COLORS [iteration % vtkm::worklet::contourtree_augmented::N_NODE_COLORS]; if (showMask & vtkm::worklet::contourtree_distributed::SHOW_SUPERARC_ID) outStream << ",label=\"SA" << supernode << "\""; outStream << "]" << std::endl; } // per supernode // add hyper arcs if requested if (showMask & vtkm::worklet::contourtree_distributed::SHOW_HYPER_STRUCTURE) for (vtkm::Id hypernode = 0; hypernode < contourTree.Hypernodes.GetNumberOfValues(); hypernode++) { // per hypernode // retrieve the sort ID vtkm::Id fromSuper = hypernodesPortal.Get(hypernode); vtkm::Id from = supernodesPortal.Get(fromSuper); // retrieve the "to" end vtkm::Id toSuper = hyperarcsPortal.Get(hypernode); // test for "NSE" if (vtkm::worklet::contourtree_augmented::NoSuchElement(toSuper)) outStream << "\ts" << std::setw(1) << from << " -> NULL [penwidth=6"; else { // hypernode // mask out the ascending flag & convert to sort ID vtkm::Id to = supernodesPortal.Get(vtkm::worklet::contourtree_augmented::MaskedIndex(toSuper)); // now test for ascending with sort IDs as before if (from < to) outStream << "\ts" << std::setw(1) << to << " -> s" << std::setw(1) << from << " [dir=back,penwidth=12"; else outStream << "\ts" << std::setw(1) << from << " -> s" << std::setw(1) << to << " [penwidth=12"; } // hypernode // set the color based on the from vertex vtkm::Id iteration = vtkm::worklet::contourtree_augmented::MaskedIndex(whenTransferredPortal.Get(fromSuper)); outStream << ",color=" << vtkm::worklet::contourtree_augmented::NODE_COLORS [iteration % vtkm::worklet::contourtree_augmented::N_NODE_COLORS]; if (showMask & vtkm::worklet::contourtree_distributed::SHOW_HYPERARC_ID) outStream << ",label=\"HA" << hypernode << "\""; outStream << "]" << std::endl; } // per hypernode // print the footer information outStream << "\t}\n"; // now return the string return outStream.str(); } // ContourTreeSuperDotGraphPrint() // 2. Simple routine to dump out nodes / edges for cross-checking VTKM_CONT template std::string ContourTreeMeshDotGraphPrint( const std::string& label, // the label to use as title for the graph vtkm::worklet::contourtree_augmented::ContourTreeMesh& mesh, // the mesh itself const vtkm::Id showMask = SHOW_CONTOUR_TREE_MESH_ALL) // mask with flags for what elements to show { // ContourTreeMeshDotGraphPrint() // initialise a string stream to capture the output std::stringstream outStream; // now grab portals to all the variables we will need auto globalMeshIndexPortal = mesh.GlobalMeshIndex.ReadPortal(); auto meshSortedValuesPortal = mesh.SortedValues.ReadPortal(); auto meshNeighborConnectivityPortal = mesh.NeighborConnectivity.ReadPortal(); auto meshNeighborOffsetsPortal = mesh.NeighborOffsets.ReadPortal(); // print the header information outStream << "digraph ContourTreeMesh\n\t{\n"; outStream << "\tlabel=\"" << std::setw(1) << label << "\"\n\tlabelloc=t\n\tfontsize=30\n"; outStream << "\t// Nodes" << std::endl; // loop through all vertices for (vtkm::Id vertex = 0; vertex < mesh.GetNumberOfVertices(); vertex++) { // per vertex // work out the various ID's vtkm::Id globalID = globalMeshIndexPortal.Get(vertex); auto dataValue = meshSortedValuesPortal.Get(vertex); // print the vertex outStream << "\tr" << std::setw(1) << vertex; outStream << "[style=filled,fixedsize=true,fontname=\"Courier\",margin=\"0.02,0.02\",height=" "\"1.7in\",width=\"1.7in\",penwidth=5,shape=circle"; outStream << ",fillcolor=white"; outStream << ",label=\""; if (showMask & vtkm::worklet::contourtree_distributed::SHOW_CONTOUR_TREE_MESH_VERTEX_ID) outStream << "r " << std::setw(vtkm::worklet::contourtree_distributed::INDEX_WIDTH) << vertex << "\\n"; if (showMask & vtkm::worklet::contourtree_distributed::SHOW_CONTOUR_TREE_MESH_GLOBAL_ID) outStream << "g " << std::setw(vtkm::worklet::contourtree_distributed::INDEX_WIDTH) << globalID << "\\n"; if (showMask & vtkm::worklet::contourtree_distributed::SHOW_CONTOUR_TREE_MESH_DATA_VALUE) outStream << "v " << std::setw(vtkm::worklet::contourtree_distributed::INDEX_WIDTH) << dataValue << "\\n"; outStream << "\"];\n"; } // per vertex // now print out the edges for (vtkm::Id vertex = 0; vertex < mesh.NeighborOffsets.GetNumberOfValues(); vertex++) { // per vertex // find iterators for the block of edges for this vertex vtkm::Id neighboursBegin = meshNeighborOffsetsPortal.Get(vertex); vtkm::Id neighboursEnd = (vertex < mesh.GetNumberOfVertices() - 1) ? meshNeighborOffsetsPortal.Get(vertex + 1) : mesh.NeighborConnectivity.GetNumberOfValues(); // now loop through the neighbours for (vtkm::Id whichNbr = neighboursBegin; whichNbr != neighboursEnd; ++whichNbr) { // per neighbour vtkm::Id nbrID = meshNeighborConnectivityPortal.Get(whichNbr); // skip if the neighbour is higher (use sim. of simp.) if ((meshSortedValuesPortal.Get(nbrID) > meshSortedValuesPortal.Get(vertex)) || ((meshSortedValuesPortal.Get(nbrID) == meshSortedValuesPortal.Get(vertex)) && (nbrID > vertex))) // output the edge outStream << "\tr" << std::setw(1) << nbrID << " -> r" << std::setw(1) << vertex << " [penwidth=3]" << std::endl; else outStream << "\tr" << std::setw(1) << vertex << " -> r" << std::setw(1) << nbrID << " [dir=back,penwidth=3]" << std::endl; } // per neighbour } // per vertex // close the graph outStream << "\t}" << std::endl; // now return the string return outStream.str(); } // ContourTreeMeshDotGraphPrint() // 3. All purpose routine to dump out the contents for comparison with contour tree VTKM_CONT template std::string BoundaryTreeDotGraphPrint( const std::string& label, // the label to use as title for the graph MeshType& mesh, // the underlying mesh for the contour tree MeshBoundaryExecObjType& meshBoundaryExecutionObject, // the boundary description need to determin if a vertex is on the boundary vtkm::worklet::contourtree_distributed::BoundaryTree& boundaryTree, // the boundary tree itself const vtkm::worklet::contourtree_augmented::mesh_dem::IdRelabeler* localToGlobalIdRelabeler, // relabler needed to compute global ids const vtkm::cont::ArrayHandle& field, const vtkm::Id showMask = vtkm::worklet::contourtree_distributed:: SHOW_BOUNDARY_TREE_ALL, // mask with flags for what elements to show const bool printHeaderAndFooter = true) { // BoundaryTreeDotGraphPrint() // initialise a string stream to capture the output std::stringstream outStream; // now grab portals to all the variables we will need auto vertexIndexPortal = boundaryTree.VertexIndex.ReadPortal(); auto superarcsPortal = boundaryTree.Superarcs.ReadPortal(); // if requested if (printHeaderAndFooter) { // print header // print the header information outStream << "digraph BoundaryTree\n\t{\n"; outStream << "\tlabel=\"" << std::setw(1) << label << "\"\n\tlabelloc=t\n\tfontsize=30\n"; outStream << "\t// Nodes" << std::endl; } // print header // prercompute the mesh boundary // TODO: This should be done in parallel. We have the basic code but for printing this is fine for now vtkm::cont::ArrayHandle liesOnBoundary; { vtkm::worklet::contourtree_augmented::IdArrayType boundaryVertexArray; vtkm::worklet::contourtree_augmented::IdArrayType boundaryVertexSortIndexArray; mesh.GetBoundaryVertices(boundaryVertexArray, // output boundaryVertexSortIndexArray, // output &meshBoundaryExecutionObject //input ); // TODO Add option for boundary critical only auto boundaryVertexArrayPortal = boundaryVertexArray.ReadPortal(); // vtkm::cont::ArrayHandle rangeArray = vtkm::cont::ArrayRangeCompute(mesh.SortOrder); // vtkm::Id maxId = static_cast(rangeArray.ReadPortal().Get(0).Max) + 1; liesOnBoundary.Allocate(mesh.SortOrder.GetNumberOfValues()); auto liesOnBoundaryWritePortal = liesOnBoundary.WritePortal(); vtkm::cont::Algorithm::Copy( vtkm::cont::ArrayHandleConstant(false, liesOnBoundary.GetNumberOfValues()), liesOnBoundary); for (vtkm::Id i = 0; i < boundaryVertexArray.GetNumberOfValues(); ++i) { liesOnBoundaryWritePortal.Set(boundaryVertexArrayPortal.Get(i), true); } } auto liesOnBoundaryPortal = liesOnBoundary.ReadPortal(); // loop through all nodes auto meshSortOrderPortal = mesh.SortOrder.ReadPortal(); auto globalIds = mesh.GetGlobalIdsFromSortIndices(mesh.SortOrder, localToGlobalIdRelabeler); auto globalIdsPortal = globalIds.ReadPortal(); auto dataValuesPortal = field.ReadPortal(); for (vtkm::Id node = 0; node < boundaryTree.VertexIndex.GetNumberOfValues(); node++) { // per node // work out the node and it's value vtkm::Id sortID = vertexIndexPortal.Get(node); vtkm::Id regularID = meshSortOrderPortal.Get(sortID); // NOTE: globalIdsPortal already looked up by meshSortOrder so we need to // look up globalID now by node not sortID vtkm::Id globalID = globalIdsPortal.Get(node); auto dataValue = dataValuesPortal.Get(regularID); // print the vertex (using global ID to simplify things for the residue) outStream << "\tg" << std::setw(1) << globalID; outStream << "[style=filled,fixedsize=true,fontname=\"Courier\",margin=\"0.02,0.02\",height=" "\"1.7in\",width=\"1.7in\",penwidth=5,shape=circle"; outStream << ",fillcolor=" << (liesOnBoundaryPortal.Get(regularID) ? "grey" : "white"); outStream << ",label=\""; if (showMask & vtkm::worklet::contourtree_distributed::SHOW_BOUNDARY_TREE_VERTEX_ID) outStream << "b " << std::setw(vtkm::worklet::contourtree_distributed::INDEX_WIDTH) << node << "\\n"; if (showMask & vtkm::worklet::contourtree_distributed::SHOW_BOUNDARY_TREE_GLOBAL_ID) outStream << "g " << std::setw(vtkm::worklet::contourtree_distributed::INDEX_WIDTH) << globalID << "\\n"; if (showMask & vtkm::worklet::contourtree_distributed::SHOW_BOUNDARY_TREE_DATA_VALUE) outStream << "v " << std::setw(vtkm::worklet::contourtree_distributed::INDEX_WIDTH) << dataValue << "\\n"; if (showMask & vtkm::worklet::contourtree_distributed::SHOW_BOUNDARY_TREE_MESH_REGULAR_ID) outStream << "r " << std::setw(vtkm::worklet::contourtree_distributed::INDEX_WIDTH) << regularID << "\\n"; if (showMask & vtkm::worklet::contourtree_distributed::SHOW_BOUNDARY_TREE_MESH_SORT_ID) outStream << "s " << std::setw(vtkm::worklet::contourtree_distributed::INDEX_WIDTH) << sortID << "\\n"; outStream << "\"];\n"; } // per vertex // always show the null node outStream << "\t// Null Node" << std::endl; outStream << "\tNULL " "[style=filled,fixedsize=true,fontname=\"Courier\",margin=\"0.02,0.02\",height=\"0.5in\"," "width=\"0.5in\",penwidth=1,shape=circle,fillcolor=white,color=black,label=\"NULL\"]" << std::endl; // now print out the edges for (vtkm::Id node = 0; node < boundaryTree.Superarcs.GetNumberOfValues(); node++) { // per node // retrieve global ID of node vtkm::Id sortID = vertexIndexPortal.Get(node); vtkm::Id globalID = globalIdsPortal.Get(sortID); // retrieve ID of target supernode vtkm::Id to = superarcsPortal.Get(node); // if this is true, it is the last pruned vertex & is omitted if (vtkm::worklet::contourtree_augmented::NoSuchElement(to)) outStream << "\tg" << std::setw(1) << globalID << " -> NULL [penwidth=2"; else { // actual superarc vtkm::Id toSort = vertexIndexPortal.Get(to); vtkm::Id toGlobal = globalIdsPortal.Get(toSort); if (node < to) outStream << "\tg" << std::setw(1) << toGlobal << " -> g" << std::setw(1) << globalID << " [dir=back,penwidth=3"; else outStream << "\tg" << std::setw(1) << globalID << " -> g" << std::setw(1) << toGlobal << " [penwidth=3"; } // actual superarc // now tidy up if (showMask & vtkm::worklet::contourtree_distributed::SHOW_BOUNDARY_TREE_ARC_ID) outStream << ",label=\"BA" << node << "\""; outStream << "]" << std::endl; } // per node if (printHeaderAndFooter) { // print footer outStream << "\t}" << std::endl; } // print footer // now return the string return outStream.str(); } // BoundaryTreeDotGraphPrint() // Routines for InteriorForest: // 4. All purpose routine to dump out the contents for comparison with contour tree VTKM_CONT template std::string InteriorForestDotGraphPrint( const std::string& label, // the label to use as title for the graph vtkm::worklet::contourtree_distributed::InteriorForest& forest, // the forest in question vtkm::worklet::contourtree_augmented::ContourTree& contourTree, // the contour tree to which it belongs vtkm::worklet::contourtree_distributed::BoundaryTree& boundaryTree, // the boundary tree MeshType& mesh, // the underlying mesh for the contour tree MeshBoundaryExecObjType& meshBoundaryExecutionObject, // the boundary description need to determin if a vertex is on the boundary const vtkm::worklet::contourtree_augmented::mesh_dem::IdRelabeler* localToGlobalIdRelabeler, // relabler needed to compute global ids const vtkm::cont::ArrayHandle& field, const vtkm::Id& showMask = vtkm::worklet::contourtree_distributed::SHOW_INTERIOR_FOREST_ALL) // mask for what to show { // InteriorForestDotGraphPrint() // initialise a string stream to capture the output std::stringstream outStream; // now grab portals to all the variables we will need auto supernodesPortal = contourTree.Supernodes.ReadPortal(); auto superarcsPortal = contourTree.Superarcs.ReadPortal(); auto forestAbovePortal = forest.Above.ReadPortal(); auto forestBelowPortal = forest.Below.ReadPortal(); auto forestIsNecessaryPortal = forest.IsNecessary.ReadPortal(); // print the header information outStream << "digraph InteriorForest\n\t{\n"; outStream << "\tlabel=\"" << std::setw(1) << label << "\"\n\tlabelloc=t\n\tfontsize=30\n"; outStream << "\t// Nodes" << std::endl; // call the boundary tree routine first, telling it to omit the header and footer // note that since we define our mask in the same bits as BRACT, we can pass through the mask outStream << BoundaryTreeDotGraphPrint( label, mesh, meshBoundaryExecutionObject, boundaryTree, localToGlobalIdRelabeler, field, vtkm::worklet::contourtree_distributed::SHOW_INTERIOR_FOREST_ALL, false); // now we need to show the forest and how it relates to the boundary tree // note - we will ignore the boundary tree Mesh Indices array for now auto meshSortOrderPortal = mesh.SortOrder.ReadPortal(); auto globalIds = mesh.GetGlobalIdsFromSortIndices(mesh.SortOrder, localToGlobalIdRelabeler); auto globalIdsPortal = globalIds.ReadPortal(); auto dataValuesPortal = field.ReadPortal(); // loop through all of the supernodes in the contour tree for (vtkm::Id supernode = 0; supernode < contourTree.Supernodes.GetNumberOfValues(); supernode++) { // per supernode // retrieve the various IDs for the supernode vtkm::Id sortID = supernodesPortal.Get(supernode); vtkm::Id regularID = meshSortOrderPortal.Get(sortID); // NOTE: globalIdsPortal already looked up by meshSortOrder so we need to // look up globalID now by supernode not sortID vtkm::Id globalID = globalIdsPortal.Get(supernode); auto dataValue = dataValuesPortal.Get(regularID); // vertices marked "necessary" are in the interior of the BRACT, but not all are in the BRACT // but the ones in the BRACT always have above/below pointing to themselves, so we test that if (forestIsNecessaryPortal.Get(supernode) && (forestAbovePortal.Get(supernode) == globalID) && (forestBelowPortal.Get(supernode) == globalID)) continue; // now print out the node // print the vertex outStream << "\tg" << std::setw(1) << globalID; outStream << "[style=filled,fixedsize=true,fontname=\"Courier\",margin=\"0.02,0.02\",height=" "\"1.7in\",width=\"1.7in\",penwidth=5,shape=circle"; outStream << ",fillcolor=white"; outStream << ",label=\""; if (showMask & vtkm::worklet::contourtree_distributed::SHOW_INTERIOR_FOREST_VERTEX_ID) outStream << "SN" << std::setw(vtkm::worklet::contourtree_distributed::INDEX_WIDTH) << supernode << "\\n"; if (showMask & vtkm::worklet::contourtree_distributed::SHOW_INTERIOR_FOREST_GLOBAL_ID) outStream << "g " << std::setw(vtkm::worklet::contourtree_distributed::INDEX_WIDTH) << globalID << "\\n"; if (showMask & vtkm::worklet::contourtree_distributed::SHOW_INTERIOR_FOREST_DATA_VALUE) outStream << "v " << std::setw(vtkm::worklet::contourtree_distributed::INDEX_WIDTH) << dataValue << "\\n"; if (showMask & vtkm::worklet::contourtree_distributed::SHOW_INTERIOR_FOREST_MESH_REGULAR_ID) outStream << "r " << std::setw(vtkm::worklet::contourtree_distributed::INDEX_WIDTH) << regularID << "\\n"; if (showMask & vtkm::worklet::contourtree_distributed::SHOW_INTERIOR_FOREST_MESH_SORT_ID) outStream << "s " << std::setw(vtkm::worklet::contourtree_distributed::INDEX_WIDTH) << sortID << "\\n"; outStream << "\"];\n"; } // per supernode // now loop through the superarcs in the contour tree for (vtkm::Id supernode = 0; supernode < contourTree.Supernodes.GetNumberOfValues(); supernode++) { // per supernode / superarc // retrieve the various IDs for the supernode vtkm::Id sortID = supernodesPortal.Get(supernode); vtkm::Id globalID = globalIdsPortal.Get(sortID); // for nodes not necessary, show their superarc if (!forestIsNecessaryPortal.Get(supernode)) { // not necessary // retrieve the target of its superarc vtkm::Id superarc = superarcsPortal.Get(supernode); // check to see if it exists if (vtkm::worklet::contourtree_augmented::NoSuchElement(superarc)) continue; // separate out the ID vtkm::Id superTo = vtkm::worklet::contourtree_augmented::MaskedIndex(superarc); vtkm::Id toSort = supernodesPortal.Get(superTo); vtkm::Id toGlobal = globalIdsPortal.Get(toSort); // then print out the edge if (contourtree_augmented::IsAscending(superTo)) outStream << "\tg" << std::setw(1) << toGlobal << " -> g" << globalID << "[dir=back,penwidth=3]" << std::endl; else outStream << "\tg" << std::setw(1) << globalID << " -> g" << toGlobal << "[penwidth=3]" << std::endl; } // not necessary else if ((forestAbovePortal.Get(supernode) != globalID) || (forestBelowPortal.Get(supernode) != globalID)) { // attachment point // all others are attachment points and have a valid above / below outStream << "\tg" << std::setw(1) << forestAbovePortal.Get(supernode) << " -> g" << std::setw(1) << globalID << "[penwidth=1,style=dotted,label=above,dir=back]" << std::endl; outStream << "\tg" << std::setw(1) << globalID << " -> g" << forestBelowPortal.Get(supernode) << "[penwidth=1,style=dotted,label=below]" << std::endl; } // attachment point } // per supernode / superarc // print the footer outStream << "\t}" << std::endl; // now return the string return outStream.str(); } // InteriorForestDotGraphPrint() // 5. Routine to print regular/super/hyper structure with similar options to contour tree VTKM_CONT template // template std::string HierarchicalContourTreeDotGraphPrint( const std::string& label, // the label to use as title for the graph const vtkm::worklet::contourtree_distributed::HierarchicalContourTree& hierarchicalTree, // the hierarchical contour tree itself const vtkm::Id showMask = vtkm::worklet::contourtree_distributed:: SHOW_HIERARCHICAL_STANDARD) // mask with flags for what elements to show // const unsigned long showMask = vtkm::worklet::contourtree_distributed::SHOW_HIERARCHICAL_STANDARD, // mask with flags for what elements to show // const VectorType &perNodeValues = VectorType(0)) // an arbitrary vector of values { // HierarchicalContourTreeDotGraphPrint() // initialise a string stream to capture the output std::stringstream outStream; // now grab portals to all the variables we will need auto regularNodeGlobalIdsPortal = hierarchicalTree.RegularNodeGlobalIds.ReadPortal(); auto dataValuesPortal = hierarchicalTree.DataValues.ReadPortal(); auto regularNodeSortOrderPortal = hierarchicalTree.RegularNodeSortOrder.ReadPortal(); auto regular2supernodePortal = hierarchicalTree.Regular2Supernode.ReadPortal(); auto superparentsPortal = hierarchicalTree.Superparents.ReadPortal(); auto supernodesPortal = hierarchicalTree.Supernodes.ReadPortal(); auto superarcsPortal = hierarchicalTree.Superarcs.ReadPortal(); auto hyperparentsPortal = hierarchicalTree.Hyperparents.ReadPortal(); auto super2hypernodePortal = hierarchicalTree.Super2Hypernode.ReadPortal(); auto whichRoundPortal = hierarchicalTree.WhichRound.ReadPortal(); auto whichIterationPortal = hierarchicalTree.WhichIteration.ReadPortal(); auto hypernodesPortal = hierarchicalTree.Hypernodes.ReadPortal(); auto hyperarcsPortal = hierarchicalTree.Hyperarcs.ReadPortal(); // TODO: Resolve passing conventions for per node values // auto perNodeValuesPortal = perNodeValues.ReadPortal(); // work out how long the computed value is // int nodeValueType = vtkm::worklet::contourtree_distributed::BAD_PER_NODE_VALUES; // vtkm::Id perNodeSize = perNodeValues.GetNumberOfValues(); // if (perNodeSize == 0) // nodeValueType = vtkm::worklet::contourtree_distributed::NO_PER_NODE_VALUES; // else if (perNodeSize == hierarchicalTree.Nodes.GetNumberOfValues()) // nodeValueType = vtkm::worklet::contourtree_distributed::PER_REGULAR_NODE_VALUES; // else if (perNodeSize == hierarchicalTree.Supernodes.GetNumberOfValues()) // nodeValueType = vtkm::worklet::contourtree_distributed::PER_SUPER_NODE_VALUES; // else if (perNodeSize == hierarchicalTree.Hypernodes.GetNumberOfValues()) // nodeValueType = vtkm::worklet::contourtree_distributed::PER_HYPER_NODE_VALUES; // else // { // error message // outStream << "ERROR in HierarchicalContourTreeDotGraphPrint().\n"; // outStream << "Per node values array must be empty, or\n"; // outStream << "Same length as regular nodes (" << std::setw(vtkm::worklet::contourtree_distributed::INDEX_WIDTH) << hierarchicalTree.Nodes.GetNumberOfValues() << "), or\n"; // outStream << "Same length as super nodes (" << std::setw(vtkm::worklet::contourtree_distributed::INDEX_WIDTH) << hierarchicalTree.Supernodes.GetNumberOfValues() << "), or\n"; // outStream << "Same length as hyper nodes (" << std::setw(vtkm::worklet::contourtree_distributed::INDEX_WIDTH) << hierarchicalTree.Hypernodes.GetNumberOfValues() << ")\n"; // outStream << "Actual length was (" << std::setw(vtkm::worklet::contourtree_distributed::INDEX_WIDTH) << perNodeValues.GetNumberOfValues() << ")\n"; // } // error message // print the header information outStream << "digraph HierarchicalContourTree\n\t{\n"; outStream << "\tlabel=\"" << std::setw(1) << label << "\"\n\tlabelloc=t\n\tfontsize=30\n"; outStream << "\t// Nodes" << std::endl; // loop through all of the nodes in the regular list for (vtkm::Id node = 0; node < hierarchicalTree.RegularNodeGlobalIds.GetNumberOfValues(); node++) { // per node // Since the superparent for an attachment point is set to another supernode, reset the calculation here //vtkm::Id whichRound = maskedIndex(hierarchicalTree.WhichRound[superID]); //vtkm::Id whichIteration = maskedIndex(hierarchicalTree.WhichIteration[superID]); // the regular ID in this case is the node itself vtkm::Id regularID = node; // for a sort ID, we will take the sort order vector vtkm::Id sortID = regularNodeSortOrderPortal.Get(node); // retrieve the global ID vtkm::Id globalID = regularNodeGlobalIdsPortal.Get(node); // retrieve the values auto dataValue = dataValuesPortal.Get(node); // retrieve the superparent vtkm::Id superparent = superparentsPortal.Get(node); // and retrieve the iteration # vtkm::Id whichRound = vtkm::worklet::contourtree_augmented::MaskedIndex(whichRoundPortal.Get(superparent)); vtkm::Id whichIteration = vtkm::worklet::contourtree_augmented::MaskedIndex(whichIterationPortal.Get(superparent)); // work out the super ID & hyper ID vtkm::Id superID = regular2supernodePortal.Get(node); vtkm::Id hyperparent = vtkm::worklet::contourtree_augmented::NO_SUCH_ELEMENT; vtkm::Id hyperID = vtkm::worklet::contourtree_augmented::NO_SUCH_ELEMENT; vtkm::Id nodeType = vtkm::worklet::contourtree_distributed::NODE_TYPE_REGULAR; // test for super if (!vtkm::worklet::contourtree_augmented::NoSuchElement(superID)) { // at least super // set hyperparent hyperparent = hyperparentsPortal.Get(superID); // set nodetype nodeType = vtkm::worklet::contourtree_distributed::NODE_TYPE_SUPER; // retrieve hyper ID hyperID = super2hypernodePortal.Get(superparent); // test it if (!vtkm::worklet::contourtree_augmented::NoSuchElement(hyperID)) { // hyper node nodeType = vtkm::worklet::contourtree_distributed::NODE_TYPE_HYPER; } // hyper node } // at least super // now, if we don't want the regular nodes, we want to skip them entirely, so bool showNode = false; // regular structure always shows all nodes if (showMask & vtkm::worklet::contourtree_distributed::SHOW_REGULAR_STRUCTURE) showNode = true; // super structure shows super & hyper nodes only else if (showMask & vtkm::worklet::contourtree_distributed::SHOW_SUPER_STRUCTURE) showNode = (nodeType != vtkm::worklet::contourtree_distributed::NODE_TYPE_REGULAR); else if (showMask & vtkm::worklet::contourtree_distributed::SHOW_HYPER_STRUCTURE) showNode = (nodeType == vtkm::worklet::contourtree_distributed::NODE_TYPE_HYPER); // if we didn't set the flag, skip the node if (!showNode) continue; // print the vertex ID, which should be the sort ID & needs to be left-justified to work outStream << "\ts" << std::setw(1) << sortID; // print the style characteristics - node is filled and fixed size outStream << " [style=filled,fixedsize=true,fontname=\"Courier\",margin=\"0.02,0.02\""; // specify the style based on the type of node if (nodeType == vtkm::worklet::contourtree_distributed::NODE_TYPE_REGULAR) outStream << ",height=\"1.7in\",width=\"1.7in\",penwidth=5"; else if (nodeType == vtkm::worklet::contourtree_distributed::NODE_TYPE_SUPER) outStream << ",height=\"2.5in\",width=\"2.5in\",penwidth=10"; else if (nodeType == vtkm::worklet::contourtree_distributed::NODE_TYPE_HYPER) outStream << ",height=\"2.5in\",width=\"2.5in\",penwidth=15"; // shape should always be circular. outStream << ",shape=circle"; // after setting the flag, its easy outStream << ",fillcolor=white"; // stroke colour depends on which round outStream << ",color=" << vtkm::worklet::contourtree_augmented::NODE_COLORS [whichRound % vtkm::worklet::contourtree_augmented::N_NODE_COLORS]; // start printing the label outStream << ",label=\""; // print the global ID if (showMask & vtkm::worklet::contourtree_distributed::SHOW_GLOBAL_ID) outStream << "g " << std::setw(vtkm::worklet::contourtree_distributed::INDEX_WIDTH) << globalID << "\\n"; // print the value if (showMask & vtkm::worklet::contourtree_distributed::SHOW_DATA_VALUE) outStream << "v " << std::setw(vtkm::worklet::contourtree_distributed::INDEX_WIDTH) << dataValue << "\\n"; // print the regular & sort IDs if (showMask & vtkm::worklet::contourtree_distributed::SHOW_MESH_REGULAR_ID) outStream << "r " << std::setw(vtkm::worklet::contourtree_distributed::INDEX_WIDTH) << regularID << "\\n"; if (showMask & vtkm::worklet::contourtree_distributed::SHOW_MESH_SORT_ID) outStream << "s " << std::setw(vtkm::worklet::contourtree_distributed::INDEX_WIDTH) << sortID << "\\n"; // print the superparent if (showMask & vtkm::worklet::contourtree_distributed::SHOW_SUPERPARENT) outStream << "sp" << std::setw(vtkm::worklet::contourtree_distributed::INDEX_WIDTH) << superparent << "\\n"; // add arbitrary per node value if it is regular in nature // if ((showMask & vtkm::worklet::contourtree_distributed::SHOW_EXTRA_DATA) && (nodeValueType == vtkm::worklet::contourtree_distributed::PER_REGULAR_NODE_VALUES)) // outStream << "x " << std::setw(vtkm::worklet::contourtree_distributed::INDEX_WIDTH) << perNodeValues[regularID] << "\\n"; // we now want to add labelling information specific to supernodes, but also present in hypernodes if (nodeType != vtkm::worklet::contourtree_distributed::NODE_TYPE_REGULAR) { // at least super // print the super node ID if (showMask & vtkm::worklet::contourtree_distributed::SHOW_SUPERNODE_ID) outStream << "SN" << std::setw(INDEX_WIDTH) << superID << "\\n"; // print the hyperparent as well if (showMask & vtkm::worklet::contourtree_distributed::SHOW_HYPERPARENT) outStream << "HP" << std::setw(vtkm::worklet::contourtree_distributed::INDEX_WIDTH) << hyperparent << "\\n"; if (showMask & vtkm::worklet::contourtree_distributed::SHOW_ITERATION) outStream << "IT" << std::setw(vtkm::worklet::contourtree_distributed::INDEX_WIDTH) << whichRound << "." << whichIteration << "\\n"; // add arbitrary per node value if it is super in nature // if ((showMask & vtkm::worklet::contourtree_distributed::SHOW_EXTRA_DATA) && (nodeValueType == PER_SUPER_NODE_VALUES)) // outStream << "X " << std::setw(vtkm::worklet::contourtree_distributed::INDEX_WIDTH) << perNodeValues[superID] << "\\n"; } // at least super // now add even more for hypernodes if (nodeType == vtkm::worklet::contourtree_distributed::NODE_TYPE_HYPER) { // hyper node if (showMask & vtkm::worklet::contourtree_distributed::SHOW_HYPERNODE_ID) outStream << "HN" << std::setw(vtkm::worklet::contourtree_distributed::INDEX_WIDTH) << hyperID << "\\n"; // add arbitrary per node value if it is hyper in nature // if ((showMask & vtkm::worklet::contourtree_distributed::SHOW_EXTRA_DATA) && (nodeValueType == vtkm::worklet::contourtree_distributed::PER_HYPER_NODE_VALUES)) // outStream << "X " << std::setw(vtkm::worklet::contourtree_distributed::INDEX_WIDTH) << perNodeValues[hyperID] << "\\n"; } // hyper node outStream << "\"]" << std::endl; } // per node // always show the null node outStream << "\t// Null Node" << std::endl; outStream << "\tNULL " "[style=filled,fixedsize=true,fontname=\"Courier\",margin=\"0.02,0.02\",height=\"0.5in\"," "width=\"0.5in\",penwidth=1,shape=circle,fillcolor=white,color=black,label=\"NULL\"]" << std::endl; // now show superarc nodes outStream << "\t// Superarc nodes\n"; // now repeat to create nodes for the middle of each superarc (to represent the superarcs themselves) for (vtkm::Id superarc = 0; superarc < hierarchicalTree.Superarcs.GetNumberOfValues(); superarc++) { // per superarc // retrieve ID of target superarc vtkm::Id superarcFrom = superarc; vtkm::Id superarcTo = superarcsPortal.Get(superarcFrom); // and retrieve the iteration # vtkm::Id whichRound = vtkm::worklet::contourtree_augmented::MaskedIndex(whichRoundPortal.Get(superarcFrom)); // if this is true, it is the last pruned vertex (attachment point or root) and has no superarc vertex if (vtkm::worklet::contourtree_augmented::NoSuchElement(superarcTo)) continue; // print the superarc vertex outStream << "\tSA" << std::setw(1) << superarc; outStream << "[shape=circle,color=" << vtkm::worklet::contourtree_augmented::NODE_COLORS [whichRound % vtkm::worklet::contourtree_augmented::N_NODE_COLORS]; outStream << ",fillcolor=white"; outStream << ",fixedsize=true"; outStream << ",height=0.8,width=0.8"; outStream << ",label=\""; if (showMask & vtkm::worklet::contourtree_distributed::SHOW_SUPERARC_ID) outStream << "SA" << std::setw(1) << superarc; outStream << "\"];" << std::endl; } // per superarc // now show regular arcs - since we do not maintain a sort, they will all attach to the parent superarc if (showMask & vtkm::worklet::contourtree_distributed::SHOW_REGULAR_STRUCTURE) { // showing regular nodes outStream << "\t// Superarc nodes\n"; for (vtkm::Id regularID = 0; regularID < hierarchicalTree.RegularNodeGlobalIds.GetNumberOfValues(); regularID++) { // per regular node // if it has a superID, then we don't want to attach it to a superarc if (!vtkm::worklet::contourtree_augmented::NoSuchElement( regular2supernodePortal.Get(regularID))) continue; // retrieve the sort ID vtkm::Id sortID = regularNodeSortOrderPortal.Get(regularID); // retrieve the superparent vtkm::Id superparent = superparentsPortal.Get(regularID); // and connect to the superarc outStream << "\ts" << sortID << " -> SA" << superparent << "[style=dotted]" << std::endl; } // per regular node } // showing regular nodes if (showMask & vtkm::worklet::contourtree_distributed::SHOW_SUPER_STRUCTURE) { // showing superstructure outStream << "\t// Superarc edges\n"; // loop through all superarcs to draw them for (vtkm::Id superarc = 0; superarc < hierarchicalTree.Superarcs.GetNumberOfValues(); superarc++) { // per superarc // retrieve ID of target supernode vtkm::Id superarcFrom = superarc; // retrieve the sort ID vtkm::Id fromRegular = supernodesPortal.Get(superarcFrom); vtkm::Id fromSort = regularNodeSortOrderPortal.Get(fromRegular); // and retrieve the destination vtkm::Id superarcTo = superarcsPortal.Get(superarcFrom); // and retrieve the iteration # vtkm::Id whichRound = vtkm::worklet::contourtree_augmented::MaskedIndex(whichRoundPortal.Get(superarcFrom)); // if this is true, it may be the last pruned vertex if (vtkm::worklet::contourtree_augmented::NoSuchElement(superarcTo)) { // no superarc // if it occurred on the final round, it's the global root and is shown as the NULL node if (whichRound == hierarchicalTree.NumRounds) outStream << "\ts" << fromSort << " -> NULL[label=\"SA" << superarc << "\",style=dotted]" << std::endl; else { // attachment point // otherwise, the target is actually a superarc vertex not a supernode vertex // so we use the regular ID to retrieve the superparent which tells us which superarc we insert into vtkm::Id regularFrom = supernodesPortal.Get(superarcFrom); superarcTo = superparentsPortal.Get(regularFrom); // output a suitable edge outStream << "\ts" << fromSort << " -> SA" << superarcTo << "[label=\"S" << superarc << "\",style=dotted,color=" << vtkm::worklet::contourtree_augmented::NODE_COLORS [whichRound % vtkm::worklet::contourtree_augmented::N_NODE_COLORS] << "]" << std::endl; } // attachment point } // no superarc else { // there is a superarc // retrieve the ascending flag bool ascendingSuperarc = vtkm::worklet::contourtree_augmented::IsAscending(superarcTo); // strip out the flags superarcTo = vtkm::worklet::contourtree_augmented::MaskedIndex(superarcTo); // retrieve the sort ID for the to end vtkm::Id toRegular = supernodesPortal.Get(superarcTo); vtkm::Id toSort = regularNodeSortOrderPortal.Get(toRegular); // how we print depends on whether the superarc ascends if (ascendingSuperarc) { // ascending arc outStream << "\ts" << toSort << " -> SA" << superarc << "[label=\"SA" << superarc << "\",dir=back"; outStream << ",penwidth=3,color=" << vtkm::worklet::contourtree_augmented::NODE_COLORS [whichRound % vtkm::worklet::contourtree_augmented::N_NODE_COLORS] << "]" << std::endl; outStream << "\tSA" << superarc << " -> s" << fromSort << "[label=\"SA" << superarc << "\",dir=back"; outStream << ",penwidth=3,color=" << vtkm::worklet::contourtree_augmented::NODE_COLORS [whichRound % vtkm::worklet::contourtree_augmented::N_NODE_COLORS] << "]" << std::endl; } // ascending arc else { // descending arc outStream << "\ts" << fromSort << " -> SA" << superarc << "[label=\"SA" << superarc << "\""; outStream << ",penwidth=3,color=" << vtkm::worklet::contourtree_augmented::NODE_COLORS [whichRound % vtkm::worklet::contourtree_augmented::N_NODE_COLORS] << "]" << std::endl; outStream << "\tSA" << superarc << " -> s" << toSort << "[label=\"SA" << superarc << "\""; outStream << ",penwidth=3,color=" << vtkm::worklet::contourtree_augmented::NODE_COLORS [whichRound % vtkm::worklet::contourtree_augmented::N_NODE_COLORS] << "]" << std::endl; } // descending arc } // there is a superarc } // per superarc } // showing superstructure if (showMask & vtkm::worklet::contourtree_distributed::SHOW_HYPER_STRUCTURE) { // show hyperstructure outStream << "\t// Hyperarcs\n"; // now loop through the hyperarcs to draw them for (vtkm::Id hyperarc = 0; hyperarc < hierarchicalTree.Hyperarcs.GetNumberOfValues(); hyperarc++) { // per hyperarc // down convert to a sort ID vtkm::Id fromSuper = hypernodesPortal.Get(hyperarc); vtkm::Id fromRegular = supernodesPortal.Get(fromSuper); vtkm::Id fromSort = regularNodeSortOrderPortal.Get(fromRegular); // and retrieve the iteration # vtkm::Id whichRound = vtkm::worklet::contourtree_augmented::MaskedIndex(whichRoundPortal.Get(fromSuper)); // and do the same with the to end vtkm::Id toSuper = hyperarcsPortal.Get(hyperarc); // if this is true, it is the last pruned vertex & connects to NULL if (vtkm::worklet::contourtree_augmented::NoSuchElement(toSuper)) outStream << "\ts" << fromSort << " -> NULL[label=\"HA" << hyperarc << "\",penwidth=3.0,style=dotted]" << std::endl; else { // not the last one // otherwise, retrieve the ascending flag bool ascendingHyperarc = vtkm::worklet::contourtree_augmented::IsAscending(toSuper); // strip out the flags toSuper = vtkm::worklet::contourtree_augmented::MaskedIndex(toSuper); // retrieve the sort index vtkm::Id toRegular = supernodesPortal.Get(toSuper); vtkm::Id toSort = regularNodeSortOrderPortal.Get(toRegular); // how we print depends on whether the hyperarc ascends if (ascendingHyperarc) { // ascending arc outStream << "\ts" << toSort << " -> s" << fromSort << "[label=\"HA" << hyperarc << "\",dir=back"; outStream << ",penwidth=5.0,color=" << vtkm::worklet::contourtree_augmented::NODE_COLORS [whichRound % vtkm::worklet::contourtree_augmented::N_NODE_COLORS] << "]" << std::endl; } // ascending arc else { // descending arc outStream << "\ts" << fromSort << " -> s" << toSort << "[label=\"HA" << hyperarc << "\""; outStream << ",penwidth=5.0,color=" << vtkm::worklet::contourtree_augmented::NODE_COLORS [whichRound % vtkm::worklet::contourtree_augmented::N_NODE_COLORS] << "]" << std::endl; } // descending arc } // not the last one } // per hyperarc } // show hyperstructure // print the footer information outStream << "\t}\n"; // now return the string return outStream.str(); } // HierarchicalContourTreeDotGraphPrint() } // namespace contourtree_distributed } // namespace worklet } // namespace vtkm #endif