Working with multi-block/multi-piece data sets

I’m fiddling with a plugin and trying to determine a quick test for the existence/non-existence of a particular cell-data array, or point-data array and its overall range. I was hoping for a min/max vtkAlgorithm or equivalent but seem to be overseeing it. The problem is that my data are most likely to be multi-block with sub-block, the final leaves are either unstructured-grid or multi-piece containing unstructured-grid.

At the moment, I’ve coded up a GetRange() traversing method that works but is just so ugly that it cannot be the right way to do things. Attached is an example of my ad hoc min/max for a particular CellData or PointData field. Does anyone know of a simpler means of doing this?

// Works, but really cannot be correct (too ugly)

typedef std::pair<double,double> doublePair;

const doublePair invalidMinMax(GREAT, -GREAT);
template<class DataType>
doublePair getMinMax(DataType* input, const std::string& name)
{
    return invalidMinMax;
}


const doublePair doMinMax(const doublePair& a, const doublePair& b)
{
    return doublePair
    (
        std::min(a.first,  b.first),
        std::max(a.second, b.second)
    );
}

doublePair getMinMax(vtkFieldData* input, const std::string& name)
{
    // Info<< "minmax(" << name << ") of vtkFieldData" << nl;
    doublePair result(invalidMinMax);

    if (input && name.size())
    {
        // Get min/max
        vtkDataArray* field = vtkDataArray::SafeDownCast
        (
            input->GetAbstractArray(name.c_str())
        );

        // May wish to handle vectors too?
        if (field && field->GetNumberOfComponents() == 1)
        {
            double range[2]{ GREAT, -GREAT };
            field->GetRange(range);
            result.first  = range[0];
            result.second = range[1];
        }
    }

    return result;
}


doublePair getMinMax(vtkCellData* input, const std::string& name)
{
    //  Info<< "minmax(" << name << ") of vtkCellArray" << nl;
    return getMinMax(vtkFieldData::SafeDownCast(input), name);
}


doublePair getMinMax(vtkPointData* input, const std::string& name)
{
    // Info<< "minmax(" << name << ") of vtkCellArray" << nl;
    return getMinMax(vtkFieldData::SafeDownCast(input), name);
}


template<class FieldType>
doublePair getMinMax(vtkDataSet* input, const std::string& name)
{
    // Info<< "minmax(" << name << ") FieldType" << nl;
    return invalidMinMax;
}

template<>
doublePair getMinMax<vtkCellData>(vtkDataSet* input, const std::string& name)
{
    // Info<< "minmax(" << name << ") vtkDataSet -> CellData" << nl;
    if (input && name.size())
    {
        return getMinMax(input->GetCellData(), name);
    }

    return invalidMinMax;
}


template<>
doublePair getMinMax<vtkPointData>(vtkDataSet* input, const std::string& name)
{
    // Info<< "minmax(" << name << ") vtkDataSet -> PointData" << nl;
    if (input && name.size())
    {
        return getMinMax(input->GetPointData(), name);
    }

    return invalidMinMax;
}


template<class FieldType>
doublePair getMinMax(vtkMultiPieceDataSet* input, const std::string& name)
{
    doublePair result(invalidMinMax);

    const unsigned n =
        ((input && name.size()) ? input->GetNumberOfPieces() : 0);

    // Info<< "minmax(" << name << ") vtkMultiPieceDataSet ("
    //    << n << ") pieces" << nl;

    for (unsigned i = 0; i < n; ++i)
    {
        doublePair local = getMinMax<FieldType>
        (
            vtkDataSet::SafeDownCast(input->GetPiece(i)),
            name
        );

        result = doMinMax(result, local);
    }

    return result;
}

emplate<class FieldType>
doublePair getMinMax(vtkMultiBlockDataSet* input, const std::string& name)
{
    doublePair result(invalidMinMax);

    const unsigned n =
        ((input && name.size()) ? input->GetNumberOfBlocks() : 0);

    // Info<< "minmax(" << name << ") vtkMultiBlockDataSet ("
    //    << n << ") blocks" << nl;

    for (unsigned i = 0; i < n; ++i)
    {
        vtkDataObject* obj = input->GetBlock(i);

        vtkMultiBlockDataSet* mb = vtkMultiBlockDataSet::SafeDownCast(obj);
        if (mb)
        {
            doublePair local = getMinMax<FieldType>(mb, name);
            result = doMinMax(result, local);
            continue;
        }

        vtkMultiPieceDataSet* mp = vtkMultiPieceDataSet::SafeDownCast(obj);
        if (mp)
        {
            doublePair local = getMinMax<FieldType>(mp, name);
            result = doMinMax(result, local);
            continue;
        }

        vtkDataSet* dobj = vtkDataSet::SafeDownCast(obj);
        if (dobj)
        {
            doublePair local = getMinMax<FieldType>(dobj, name);
            result = doMinMax(result, local);
            continue;
        }
    }

    return result;
}

This whole thing would finally be called like this:

    doublePair minmax = getMinMax<vtkCellData>(mesh,  myFieldName);
    // parallel reduce

We can also check if the lower/upper values are in the correct order. If they are not, then the field was not found at all.

There must surely be something simpler and more obvious, but I didn’t see it.

/mark

You should a vtkCompositeDataSetIterator with OnlyLeavesOn and use GetRange on each leaf.

Oh yes, that looks much better.
Thank you!

/mark

For others, this is what the revised version looks like:

doublePair getMinMax
(
    vtkCompositeDataSet* data,
    const std::string& name,
    const vtkDataObject::FieldAssociations attr =
        vtkDataObject::FIELD_ASSOCIATION_POINTS_THEN_CELLS
)
{
    doublePair result(invalidMinMax);

    auto iter = vtkSmartPointer<vtkDataObjectTreeIterator>::New();

    iter->SetDataSet(data);
    iter->VisitOnlyLeavesOn();
    iter->SkipEmptyNodesOn();

    for (iter->InitTraversal(); !iter->IsDoneWithTraversal(); iter->GoToNextItem())
    {
        vtkDataSet* dataset = vtkDataSet::SafeDownCast
        (
            iter->GetCurrentDataObject()
        );

        if (dataset)
        {
            doublePair local = getMinMax
            (
                dataset->GetAttributesAsFieldData(attr),
                name
            );

            result = doMinMax(result, local);
         }
    }

    return result;
}