int
avtDisplacementExpression::GetVariableDimension(void)
{
    if (*(GetInput()) == NULL)
        return avtMultipleInputExpressionFilter::GetVariableDimension();
    if (varnames.size() != 2)
        return avtMultipleInputExpressionFilter::GetVariableDimension();
    
    avtDataAttributes &atts = GetInput()->GetInfo().GetAttributes();
    if (! atts.ValidVariable(varnames[0]))
        return avtMultipleInputExpressionFilter::GetVariableDimension();
    int ncomp1 = atts.GetVariableDimension(varnames[0]);
    
    if (! atts.ValidVariable(varnames[1]))
        return avtMultipleInputExpressionFilter::GetVariableDimension();
    int ncomp2 = atts.GetVariableDimension(varnames[1]);
    
    return GetNumberOfComponentsInOutput(ncomp1, ncomp2);
}
int
avtUnaryMathExpression::GetVariableDimension(void)
{
    if (*(GetInput()) == NULL)
        return avtSingleInputExpressionFilter::GetVariableDimension();

    //
    // The base class will set the variable of interest to be the 
    // 'activeVariable'.  This is a by-product of how the base class sets its
    // input.  If that method should change (SetActiveVariable), this
    // technique for inferring the variable name may stop working.
    //
    const char *varname = activeVariable;
    if (varname == NULL)
        return avtSingleInputExpressionFilter::GetVariableDimension();

    avtDataAttributes &atts = GetInput()->GetInfo().GetAttributes();
    if (! atts.ValidVariable(varname))
        return avtSingleInputExpressionFilter::GetVariableDimension();
    int ncomp = atts.GetVariableDimension(varname);

    return GetNumberOfComponentsInOutput(ncomp);
}
vtkDataArray *
avtUnaryMathExpression::DeriveVariable(vtkDataSet *in_ds, int currentDomainsIndex)
{
    int  i;

    vtkDataArray *cell_data = NULL;
    vtkDataArray *point_data = NULL;
    vtkDataArray *data = NULL;

    if (activeVariable == NULL)
    {
        //
        // This hack is getting more and more refined.  This situation comes up
        // when we don't know what the active variable is (mostly for the
        // constant creation filter).  We probably need more infrastructure
        // to handle this.
        // Iteration 1 of this hack said take any array.
        // Iteration 2 said take any array that isn't vtkGhostLevels, etc.
        // Iteration 3 says take the first scalar array if one is available,
        //             provided that array is not vtkGhostLevels, etc.
        //             This is because most constants we create are scalar.
        //
        // Note: this hack used to be quite important because we would use
        // the resulting array to determine the centering of the variable.
        // Now we use the IsPointVariable() method.  So this data array is
        // only used to get the type.
        //
        int ncellArray = in_ds->GetCellData()->GetNumberOfArrays();
        for (i = 0 ; i < ncellArray ; i++)
        {
            vtkDataArray *candidate = in_ds->GetCellData()->GetArray(i);
            if (strstr(candidate->GetName(), "vtk") != NULL)
                continue;
            if (strstr(candidate->GetName(), "avt") != NULL)
                continue;
            if (candidate->GetNumberOfComponents() == 1)
            {
                // Definite winner
                cell_data = candidate;
                break;
            }
            else
                // Potential winner -- keep looking
                cell_data = candidate;
        }
        int npointArray = in_ds->GetPointData()->GetNumberOfArrays();
        for (i = 0 ; i < npointArray ; i++)
        {
            vtkDataArray *candidate = in_ds->GetPointData()->GetArray(i);
            if (strstr(candidate->GetName(), "vtk") != NULL)
                continue;
            if (strstr(candidate->GetName(), "avt") != NULL)
                continue;
            if (candidate->GetNumberOfComponents() == 1)
            {
                // Definite winner
                point_data = candidate;
                break;
            }
            else
                // Potential winner -- keep looking
                point_data = candidate;
        }

        if (cell_data != NULL && cell_data->GetNumberOfComponents() == 1)
        {
            data = cell_data;
            centering = AVT_ZONECENT;
        }
        else if (point_data != NULL && point_data->GetNumberOfComponents()== 1)
        {
            data = point_data;
            centering = AVT_NODECENT;
        }
        else if (cell_data != NULL)
        {
            data = cell_data;
            centering = AVT_ZONECENT;
        }
        else
        {
            data = point_data;
            centering = AVT_NODECENT;
        }
    } 
    else
    {
        cell_data = in_ds->GetCellData()->GetArray(activeVariable);
        point_data = in_ds->GetPointData()->GetArray(activeVariable);

        if (cell_data != NULL)
        {
            data = cell_data;
            centering = AVT_ZONECENT;
        }
        else
        {
            data = point_data;
            centering = AVT_NODECENT;
        }
    }

    //
    // Set up a VTK variable reflecting the calculated variable
    //
    int ncomps = 0;
    int nvals = 0;
    if (FilterCreatesSingleton())
        nvals = 1;
    else if (activeVariable == NULL || data == NULL)
        nvals = (IsPointVariable() ? in_ds->GetNumberOfPoints() 
                                   : in_ds->GetNumberOfCells());
    else
        nvals = data->GetNumberOfTuples();

    vtkDataArray *dv = NULL;
    if (data == NULL)
    {
        //
        // We could not find a single array.  We must be doing something with
        // the mesh.
        //
        ncomps = 1;
        dv = CreateArrayFromMesh(in_ds);
    }
    else
    {
        ncomps = data->GetNumberOfComponents();
        dv = CreateArray(data);
    }

    if (data == NULL)
    {
        if (! NullInputIsExpected())
        {
            // One way to get here is to have vtkPolyData Curve plots.
            EXCEPTION2(ExpressionException, outputVariableName,
                 "An internal error occurred when "
                 "trying to calculate your expression.  Please contact a "
                 "VisIt developer.");
        }
    }

    int noutcomps = GetNumberOfComponentsInOutput(ncomps);
    dv->SetNumberOfComponents(noutcomps);
    dv->SetNumberOfTuples(nvals);

    //
    // Should we send in ncomps or noutcomps?  They are the same number 
    // unless the derived type re-defined GetNumberOfComponentsInOutput.
    // If it did, it probably doesn't matter.  If not, then it is the same
    // number.  So send in the input.  Really doesn't matter.
    //
    cur_mesh = in_ds;
    DoOperation(data, dv, ncomps, nvals);
    cur_mesh = NULL;

    return dv;
}