// Copyright (c) Lawrence Livermore National Security, LLC and other VisIt
// Project developers.  See the top-level LICENSE file for dates and other
// details.  No copyright assignment is required to contribute to VisIt.

// ************************************************************************* //
//                                  avtFilter.C                              //
// ************************************************************************* //

#include <avtFilter.h>

#include <avtDebugDumpOptions.h>
#include <avtDynamicAttribute.h>
#include <avtExtents.h>
#include <avtMemory.h>
#include <avtMetaData.h>
#include <avtTerminatingSink.h>
#include <avtParallel.h>
#include <avtOriginatingSource.h>
#include <avtWebpage.h>

#include <vtkSystemIncludes.h> // for VTK_FLOAT

#include <DebugStream.h>
#include <ImproperUseException.h>
#include <NoInputException.h>
#include <TimingsManager.h>

#include <visitstream.h>

#include <string>
#include <vector>

#ifdef FECHECKS
#include <fenv.h>
#endif

using std::string;
using std::vector;
using std::ostringstream;


int  avtFilter::numInExecute = 0;


// ****************************************************************************
//  Method: avtFilter constructor
//
//  Programmer: Hank Childs
//  Creation:   May 30, 2001
//
//  Modifications:
//
//    Hank Childs, Fri Dec 22 07:55:21 PST 2006
//    Initialize webpage.
//
// ****************************************************************************

avtFilter::avtFilter()
{
    modified    = true;
    inExecute   = false;
    webpage     = NULL;
    updateDOI   = NULL;
    updateDOIData = NULL;
}


// ****************************************************************************
//  Method: avtFilter destructor
//
//  Purpose:
//      Defines the destructor.  Note: this should not be inlined in the header
//      because it causes problems for certain compilers.
//
//  Programmer: Hank Childs
//  Creation:   February 5, 2004
//
//  Modifications:
//
//    Hank Childs, Fri Dec 22 07:55:21 PST 2006
//    Delete the webpage.
//
// ****************************************************************************

avtFilter::~avtFilter()
{
    if (webpage != NULL)
        delete webpage;;
}


// ****************************************************************************
//  Method: avtFilter::UpdateProgress
//
//  Purpose:
//      Updates the progress of the filter through a callback.
//
//  Arguments:
//      current       The current node of a data tree that was just finished.
//      total         The total number of trees.
//
//  Notes:            This method is also used to indicate that a filter is
//                    totally done.  When that happens, the callback is called
//                    with total == 0.  Also, the name of description can be
//                    NULL.
//
//  Programmer:       Hank Childs
//  Creation:         June 20, 2001
//
// ****************************************************************************

void
avtFilter::UpdateProgress(int current, int total)
{
#ifndef VISIT_THREADS
    avtDataObjectSource::UpdateProgress(current, total, GetType(),
                                        GetDescription());
#endif // VISIT_THREADS
}


// ****************************************************************************
//  Method: avtFilter::Update
//
//  Purpose: 
//      Walks up a pipeline and determine what needs to re-execute and what
//      part of the data request can be reduced through the use of meta-data.
//
//  Arguments:
//      contract   The contract.
//
//  Returns:    A Boolean indicating if anything upstream was modified.
//
//  Programmer: Hank Childs
//  Creation:   May 30, 2001
//
//  Modifications:
//
//    Hank Childs, Wed Jun 20 16:29:15 PDT 2001
//    Added hooks for aborting, and progress.
// 
//    Jeremy Meredith, Fri Jun 29 15:15:39 PDT 2001
//    Made two hooks for progress -- one at start and one at finish.
//
//    Hank Childs, Wed Sep 12 10:16:47 PDT 2001
//    Use UpdateInput over Update, since there might be multiple inputs and
//    we want to abstract that into the data object sink classes.
//
//    Jeremy Meredith, Wed Sep 19 13:58:14 PDT 2001
//    Removed an unnecessary CheckAbort since UpdateProgress now checks for it.
//
//    Hank Childs, Wed Oct 24 14:21:18 PDT 2001
//    Added calls to PreExecute and PostExecute.
//
//    Hank Childs, Fri Mar 15 15:30:11 PST 2002
//    Accounted for dynamic attributes.
//
//    Hank Childs, Fri May 16 10:37:09 PDT 2003
//    Do a better job of catching exceptions.
//
//    Hank Childs, Sun Feb 27 13:04:32 PST 2005
//    If we are doing dynamic load balancing, clean up our input as we go.
//    Moved timings code from avtSIMODataTreeIterator to this routine so that
//    all filters are timed.
//
//    Hank Childs, Thu May 25 16:45:41 PDT 2006
//    Release data for all transient data when we are done executing, 
//    regardless of whether or not we're streaming.
//
//    Hank Childs, Thu Dec 21 09:15:00 PST 2006
//    Add support for the "-dump" option.
//
//    Hank Childs, Fri Jun 15 11:37:59 PDT 2007
//    Maintain "numInExecute" to keep track of how many nested executions
//    we have.  Also dump info for pipeline specifications.
//
//    Hank Childs, Mon Dec 10 14:17:47 PST 2007
//    Added debug statement for when the filter decides *not* to execute.
//
//    Cyrus Harrison, Wed Feb 13 13:37:29 PST 2008
//    Updated to use avtDebugDumpOptions.
//
//    Hank Childs, Tue Feb 19 19:45:43 PST 2008
//    Rename "dynamic" to "streaming", since we really care about whether we
//    are streaming, not about whether we are doing dynamic load balancing.
//    And the two are no longer synonymous.
//
//    Brad Whitlock, Wed Oct 29 14:48:33 PDT 2014
//    Take memory measurements on BGQ so we can get an idea of how much 
//    memory is used/left.
//
//    Mark C. Miller, Thu Apr 27 18:08:19 PDT 2017
//    Capture floating point exception (FE) logic for future use.
// ****************************************************************************

bool
avtFilter::Update(avtContract_p contract)
{
    debug1 << "Entered update for " << GetType() << endl;

    bool debug_dump = avtDebugDumpOptions::DumpEnabled();
    
    CheckAbort();

    //
    // If we don't have an input, there isn't much we can do.
    //
    avtDataObject_p input = GetInput();
    if (*input == NULL)
    {
        EXCEPTION0(NoInputException);
    }

    if (debug_dump)
    {
        InitializeWebpage();
        DumpContract(contract, "input");
    }

    //
    // By using meta-data, we can potentially reduce the amount of data that
    // we are actually interested in using.  Give the derived types an
    // opportunity to reduce the data we are interested in.
    //
    avtContract_p newSpec = ModifyContractAndDoBookkeeping(contract);

    if (debug_dump)
        DumpContract(newSpec, "output");

    bool modifiedUpstream = UpdateInput(newSpec);

    bool re_execute = modifiedUpstream || modified;
    if (re_execute)
    {
        inExecute = true;

        TRY
        {
            int timerHandle = visitTimer->StartTimer();

            //
            // If extents have changed, we need to copy that downstream.
            //
            PassOnDataObjectInfo();
    
#ifdef VISIT_BLUE_GENE_Q
            unsigned long mused = 0, rss = 0, mavail = 0;
            avtMemory::GetMemorySize(mused, rss);
            avtMemory::GetAvailableMemorySize(mavail);
            debug1 << "Executing " << GetType()
                   << ", memory(used=" << mused
                   << ", avail=" << mavail << ")" << endl;
#else
            debug1 << "Executing " << GetType() << endl;
#endif

            UpdateProgress(0, 0);
            ResolveDynamicAttributes();
            if (debug_dump)
                DumpDataObject(GetInput(), "input");
            numInExecute++;

#ifdef FECHECKS
            feclearexcept(FE_ALL_EXCEPT);
#endif

            PreExecute();
            Execute();
            PostExecute();

#ifdef FECHECKS
            int feerr = fetestexcept(FE_INVALID | FE_DIVBYZERO | FE_OVERFLOW | FE_UNDERFLOW);
            if (feerr)
            {
                feclearexcept(FE_ALL_EXCEPT);
                debug1 << "FPE(s) encountered in " << GetType() << " ";
                if (feerr & FE_INVALID) debug1 << "FE_INVALID ";
                if (feerr & FE_DIVBYZERO) debug1 << "FE_DIVBYZERO ";
                if (feerr & FE_OVERFLOW) debug1 << "FE_OVERFLOW ";
                if (feerr & FE_UNDERFLOW) debug1 << "FE_UNDERFLOW ";
                debug1 << endl;
            }
#endif

            if (debug_dump)
                DumpDataObject(GetOutput(), "output");
            UpdateProgress(1, 0);

#ifdef VISIT_BLUE_GENE_Q
            mused = 0; rss = 0; mavail = 0;
            avtMemory::GetMemorySize(mused, rss);
            avtMemory::GetAvailableMemorySize(mavail);
            debug1 << "Done executing " << GetType()
                   << ", memory(used=" << mused
                   << ", avail=" << mavail << ")" << endl;
#else
            debug1 << "Done executing " << GetType() << endl;
#endif
            modified = false;

            visitTimer->StopTimer(timerHandle, GetType());
            visitTimer->DumpTimings();
        }
        CATCH2(VisItException, e)
        {
            debug1 << "Exception occurred in " << GetType() << endl;
            avtDataValidity &v = GetOutput()->GetInfo().GetValidity();
            v.ErrorOccurred();
            string tmp = e.Message(); // Otherwise there is a const problem.
            v.SetErrorMessage(tmp);
        }
        ENDTRY

        if (debug_dump)
            FinalizeWebpage();
        inExecute = false;
        numInExecute--;
    }
    else
    {
        debug1 << "Decided to *not* re-execute " << GetType() << endl;
    }

    //
    // If we are doing dynamic load balancing, clean up as we go.
    //
    if (GetInput()->GetInfo().GetValidity().AreWeStreaming() ||
        GetInput()->IsTransient())
    {
        if (GetInput()->GetSource() != NULL)
        {
            int t0 = visitTimer->StartTimer();
            GetInput()->GetSource()->ReleaseData();
            visitTimer->StopTimer(t0, "Calling release data");
        }
    }

    debug1 << "Done Updating " << GetType() << endl;
    return re_execute;
}


// ****************************************************************************
//  Method: avtFilter::ModifyContractAndDoBookkeeping
//
//  Purpose:
//      Calls perform restriction and does some bookkeeping (like keeping
//      track of how many total filters there are in the pipeline).
//
//  Arguments:
//      contract   The current contract.
//
//  Returns:       The new contract.
//
//  Programmer:  Hank Childs
//  Creation:    June 20, 2001
//
//  Modifications:
//
//    Hank Childs, Mon Jul 30 15:41:06 PDT 2001
//    Allow for filters that have their own execute cycle with additional
//    filters.
//
//    Hank Childs, Wed Oct 24 14:21:18 PDT 2001
//    Added call to examine specification.
//
//    Hank Childs, Fri Mar 15 15:36:50 PST 2002
//    Added support for dynamic attributes.
//
//    Hank Childs, Fri Jun 15 17:21:24 PDT 2007
//    Have filters inspect the original specification, not the new one.
//
//    Hank Childs, Tue Aug 12 15:06:36 PDT 2008
//    Make sure that we always re-execute when doing on demand streaming.
//
// ****************************************************************************

avtContract_p
avtFilter::ModifyContractAndDoBookkeeping(avtContract_p contract)
{
    int   i;

    if (contract->DoingOnDemandStreaming())
    {
        // Make sure that it always re-executes if we are executing on demand.
        modified = true;
    }

    //
    // Some derived types need to examine a contract as it goes up.
    //
    ExamineContract(contract);

    avtContract_p newcontract = ModifyContract(contract);
    newcontract->AddFilter();
    int additionalFilters = AdditionalPipelineFilters();
    for (i = 0 ; i < additionalFilters ; i++)
    {
        newcontract->AddFilter();
    }

    //
    // Allow our dynamic attributes to add any variables they may need, etc.
    //
    int nAttributes = dynamicAttributes.size();
    for (i = 0 ; i < nAttributes ; i++)
    {
        newcontract = dynamicAttributes[i]->ModifyContract(newcontract);
    }

    return newcontract;
}


// ****************************************************************************
//  Method: avtFilter::ModifyContract
//
//  Purpose:
//      Meta-data can be used to reduce the amount of data that needs to be
//      considered.  This is the mechanism to reduce that data as an Update
//      goes up a pipeline.  This is defined in the base type so derived
//      types don't need to if the mechanism does not apply.
//
//  Arguments:
//      contract   The current contract.
//
//  Returns:       The new contract.
//
//  Programmer:  Hank Childs
//  Creation:    May 31, 2001
//
//  Modifications:
//
//    Mark C. Miller, Tue Apr  5 10:30:16 PDT 2005
//    Added code to set admissible types to float
//
//    Brad Whitlock, Fri Apr 20 14:47:48 PDT 2012
//    Let double through too. Gradually, we'll let them all through here and 
//    limit the individual filters that can't handle it.
//
// ****************************************************************************

avtContract_p
avtFilter::ModifyContract(avtContract_p contract)
{
    //
    // By default, all filters admit only VTK_FLOAT data
    //
    vector<int> dataTypes;
    dataTypes.push_back(VTK_FLOAT);
    dataTypes.push_back(VTK_DOUBLE);
    contract->GetDataRequest()->UpdateAdmissibleDataTypes(dataTypes);

    return contract;
}


// ****************************************************************************
//  Method: avtFilter::GetOriginatingSource
//
//  Purpose:
//      Gets the terminating source at the top of the pipeline.
//
//  Returns:   A terminating source.  Throws an exception over returning NULL.
//             (so return value is always valid).
//
//  Programmer: Hank Childs
//  Creation:   May 30, 2001
//
// ****************************************************************************

avtOriginatingSource *
avtFilter::GetOriginatingSource(void)
{
    avtDataObject_p input = GetInput();
    if (*input == NULL)
    {
        EXCEPTION0(NoInputException);
    }

    return input->GetOriginatingSource();
}


// ****************************************************************************
//  Method: avtFilter::GetQueryableSource
//
//  Purpose:
//      Gets the queryable source for the pipeline.
//
//  Returns:    A queryable source.  Throws an exception over returning NULL.
//              (so return value is always valid).
//
//  Programmer: Hank Childs
//  Creation:   July 28, 2003
//
// ****************************************************************************

avtQueryableSource *
avtFilter::GetQueryableSource(void)
{
    avtDataObject_p input = GetInput();
    if (*input == NULL)
    {
        EXCEPTION0(NoInputException);
    }

    return input->GetQueryableSource();
}


// ****************************************************************************
//  Method: avtFilter::ChangedInput
//
//  Purpose:
//      Catches the hook from the base class that the input has changed.
//
//  Programmer: Hank Childs
//  Creation:   May 30, 2001
//
// ****************************************************************************

void
avtFilter::ChangedInput(void)
{
    //
    // Give the derived types an opportunity to throw an exception if they
    // don't like the input.
    //
    VerifyInput();

    PassOnDataObjectInfo();

    //
    // Some filters may want to do some special things to initialize
    // themselves.  Give them that chance.
    //
    InitializeFilter();

    modified = true;
}


// ****************************************************************************
//  Method: avtFilter::InitializeFilter
//
//  Purpose:
//      This is a chance for the derived types to initialize themselves when
//      a new input is provided.
//
//  Programmer: Hank Childs
//  Creation:   May 31, 2001
//
// ****************************************************************************

void
avtFilter::InitializeFilter(void)
{
    ;
}


// ****************************************************************************
//  Method: avtFilter::VerifyInput
//
//  Purpose:
//      This is a chance for the derived types to verify a new input.  This
//      does not include type checking, which should be done already.  An
//      example of a filter that may want to use this mechanism would be an
//      index select, which would want to confirm that the input is curvilinear
//      or rectilinear.
//
//  Programmer: Hank Childs
//  Creation:   May 31, 2001
//
// ****************************************************************************

void
avtFilter::VerifyInput(void)
{
    ;
}


// ****************************************************************************
//  Method: avtFilter::PassOnDataObjectInfo
//
//  Purpose:
//      Passes on the data object information from the input to the output.
//      This only copies that information and then calls a virtual function
//      that will change information for the derived type in an appropriate
//      way (maybe none).
//
//  Programmer: Hank Childs
//  Creation:   May 31, 2001
//
//  Modifications:
//
//    Hank Childs, Tue Sep 10 09:08:51 PDT 2002
//    Allow for NULL inputs.
//
//    Brad Whitlock, Wed Mar 19 13:55:05 PDT 2014
//    Added a callback function.
//    Work partially supported by DOE Grant SC0007548.
//
// ****************************************************************************

void
avtFilter::PassOnDataObjectInfo(void)
{
    avtDataObject_p output = GetOutput();
    avtDataObject_p input  = GetInput();
    if (*input != NULL)
    {
        output->GetInfo().Copy(input->GetInfo());
    }

    UpdateDataObjectInfo();

    // Call a callback that can let other entities participate in the update.
    if(updateDOI != NULL)
    {
        (*updateDOI)(input, output, updateDOIData);
    }
}

// ****************************************************************************
// Method: avtFilter::SetUpdateDataObjectInfoCallback
//
// Purpose:
//   Sets a callback that will participate in UpdateDataObjectInfo.
//
// Arguments:
//   cb     : The callback function.
//   cbdata : Data that will be passed to the callback function.
//
// Returns:    
//
// Note:       Work partially supported by DOE Grant SC0007548.
//
// Programmer: Brad Whitlock
// Creation:   Wed Mar 19 13:54:03 PDT 2014
//
// Modifications:
//
// ****************************************************************************

void
avtFilter::SetUpdateDataObjectInfoCallback(
    void (*cb)(avtDataObject_p &input, avtDataObject_p &output, void *),
    void *cbdata)
{
    updateDOI = cb;
    updateDOIData = cbdata;
}

// ****************************************************************************
//  Method: avtFilter::UpdateDataObjectInfo
//
//  Purpose:
//      If the derived type of filter modifies the data object information,
//      then this is the method where that can be indicated.  This is defined
//      for the derived types that don't refashion the data object information.
//
//  Programmer: Hank Childs
//  Creation:   May 31, 2001
//
// ****************************************************************************

void
avtFilter::UpdateDataObjectInfo(void)
{
    ;
}


// ****************************************************************************
//  Method: avtFilter::GetMetaData
//
//  Purpose:
//      Gets the meta data object (and does some error checking as well).
//
//  Programmer: Hank Childs
//  Creation:   June 6, 2001
//
// ****************************************************************************

avtMetaData *
avtFilter::GetMetaData(void)
{
    avtOriginatingSource *src = GetOriginatingSource();
    if (src == NULL)
    {
        EXCEPTION0(NoInputException);
    }
    avtMetaData *md = src->GetMetaData();
    if (md == NULL)
    {
        EXCEPTION0(NoInputException);
    }

    return md;
}


// ****************************************************************************
//  Method: avtFilter::GetGeneralContract
//
//  Purpose:
//      Gets a pipeline that the load balancer knows not to mess with.
//
//  Programmer: Hank Childs
//  Creation:   June 6, 2001
//
// ****************************************************************************

avtContract_p
avtFilter::GetGeneralContract(void)
{
    return GetOriginatingSource()->GetGeneralContract();
}


// ****************************************************************************
//  Method: avtFilter::AdditionalPipelineFilters
//
//  Purpose:
//      Some filters create their own network with additional filters.  These
//      filters all make progress calls, so it needs to be reported that they
//      exist, so the progress is still meaningful.  Since most filters do
//      not create their own networks, this just returns 0.
//
//  Returns:     The number of additional filters used in a pipeline that is 
//               created in this filter's Execute (typically 0).
//
//  Programmer:  Hank Childs
//  Creation:    July 30, 2001
//
// ****************************************************************************

int
avtFilter::AdditionalPipelineFilters(void)
{
    return 0;
}


// ****************************************************************************
//  Method: avtFilter::TryDataExtents
//
//  Purpose:
//      Tries to get the data extents, but only if they are available (does
//      not resort to parallel unification).
//
//  Programmer: Hank Childs
//  Creation:   September 4, 2001
//
//  Modifications:
//
//    Hank Childs, Fri Dec 21 07:48:40 PST 2001
//    Do not use extents if they don't correspond to this variable.
//
//    Hank Childs, Wed Apr 17 09:33:15 PDT 2002
//    Made argument const.
//
//    Hank Childs, Thu Feb 26 09:14:49 PST 2004
//    Better account for multiple variables.
//
// ****************************************************************************

bool
avtFilter::TryDataExtents(double *outexts, const char *varname)
{
    avtDataAttributes &atts = GetInput()->GetInfo().GetAttributes();

    if (varname != NULL && !atts.ValidVariable(varname))
    {
        // We are looking for the extents of a different variable.
        return false;
    }
    if (varname == NULL && !atts.ValidActiveVariable())
    {
        return false;
    }

    //
    // Our first preference is for the desired extents.
    //
    avtExtents *eff = atts.GetDesiredDataExtents(varname);
    if (eff->HasExtents())
    {
        eff->CopyTo(outexts);
        return true;
    }
    else
    {
        //
        // If we had the extents in the meta-data, use that.
        //
        avtExtents *tr = atts.GetOriginalDataExtents(varname);
        if (tr->HasExtents())
        {
            tr->CopyTo(outexts);
            return true;
        }
    }

    return false;
}


// ****************************************************************************
//  Method: avtFilter::GetDataExtents
//
//  Purpose:
//      Gets the data extents -- parallel unification or not.
//
//  Programmer: Hank Childs
//  Creation:   September 4, 2001
//
//  Modifications:
//
//    Hank Childs, Fri Dec 21 07:48:40 PST 2001
//    Do not use extents if they don't correspond to this variable.
//
//    Hank Childs, Wed Apr 17 09:33:15 PDT 2002
//    Made argument const.
//
//    Hank Childs, Wed Jul  9 11:48:22 PDT 2003
//    Use the prescribed extents if the variable is named and that name is
//    the name of the default variable.
//
//    Mark C. Miller, Sat Jan 31 13:31:08 PST 2004
//    Added optional size of 18 for UnifyMinMax (max # comps = 9 for tensor)
//
//    Kathleen Bonnell, Thu Mar 11 10:14:20 PST 2004
//    DataExtents now restricted to only 2 components, regardless of variable
//    dimension. 
//
//    Hank Childs, Tue Jun 29 07:16:23 PDT 2004
//    Only use valid extents.
//
//    Hank Childs, Fri Dec  3 14:25:56 PST 2004
//    Add variable name to SearchDataForDataExtents.
//
//    Hank Childs, Sun Jan 30 14:25:00 PST 2005
//    Be leery of case where there is no input variable.
//
//    Hank Childs, Thu Aug 26 13:02:28 PDT 2010
//    Change named of extents object.
//
//    Hank Childs, Thu Sep 23 13:57:01 PDT 2010
//    Be more aggressive in setting back extents.  Also set it correctly
//    with the input ... not with the output (wrong).
//
//    Hank Childs, Sun Nov 21 12:14:19 PST 2010
//    Allow for input extents to be used in more cases for non-active 
//    variables.
//
// ****************************************************************************

void
avtFilter::GetDataExtents(double *outexts, const char *varname)
{
    if (TryDataExtents(outexts, varname))
    {
         //
         // We had them lying around -- no parallel communication necessary.
         // 
         return;
    }

    bool hadThemAlready = false;
    avtDataAttributes &atts = GetInput()->GetInfo().GetAttributes();
    bool checkExtents = false;
    if (varname != NULL)
    {
        if (atts.ValidVariable(varname))
            checkExtents = true;
    }
    else
    {
        if (atts.ValidActiveVariable())
            checkExtents = true;
    }
    if (checkExtents)
    {
        avtExtents *exts = atts.GetThisProcsOriginalDataExtents(varname);
        if (exts->HasExtents())
        {
            atts.GetThisProcsOriginalDataExtents(varname)->CopyTo(outexts);
            hadThemAlready = true;
        }
    }

    if (!hadThemAlready)
    {
        SearchDataForDataExtents(outexts, varname);
    }

    UnifyMinMax(outexts, 2, 2);

    //
    // We now have determined the true data extents, so we may as well 
    // set them back.
    //
    avtExtents *e = NULL;
    TRY
    {
        e = atts.GetOriginalDataExtents(varname);
    }
    CATCH(ImproperUseException)
    {
    }
    ENDTRY
    if (e != NULL)
        e->Set(outexts);
}


// ****************************************************************************
//  Method: avtFilter::TrySpatialExtents
//
//  Purpose:
//      Tries to get the spatial extents, but only if they are available (does
//      not resort to parallel unification).
//
//  Programmer: Hank Childs
//  Creation:   September 4, 2001
//
//  Modifications:
//
//    Tom Fogal, Tue Jun 23 20:21:36 MDT 2009
//    Marked as const.
//
// ****************************************************************************

bool
avtFilter::TrySpatialExtents(double *outexts) const
{
    const avtDataAttributes &atts = GetInput()->GetInfo().GetAttributes();

    //
    // Our first preference is for the desired extents.
    //
    avtExtents *eff = atts.GetDesiredSpatialExtents();
    if (eff->HasExtents())
    {
        eff->CopyTo(outexts);
        return true;
    }
    else
    {
        //
        // If we had the extents in the meta-spatial, use that.
        //
        avtExtents *tr = atts.GetOriginalSpatialExtents();
        if (tr->HasExtents())
        {
            tr->CopyTo(outexts);
            return true;
        }
    }

    return false;
}


// ****************************************************************************
//  Method: avtFilter::GetSpatialExtents
//
//  Purpose:
//      Gets the spatial extents -- parallel unification or not.
//
//  Programmer: Hank Childs
//  Creation:   September 4, 2001
//
//  Modifications:
//
//    Hank Childs, Mon Nov  3 09:49:11 PST 2003
//    Setting the extents with the output is a mistake -- the filter may even
//    modify those extents.  They should be set with the input.
//
//    Mark C. Miller, Sat Jan 31 13:31:08 PST 2004
//    Added altsize argument of 6 to UnifyMinMax call
//
//    Hank Childs, Thu Aug 26 13:02:28 PDT 2010
//    Change named of extents object.
//
//    Hank Childs, Tue Nov 30 20:38:36 PST 2010
//    Better support for getting extents when extents aren't known a priori.
//
// ****************************************************************************

void
avtFilter::GetSpatialExtents(double *newexts)
{
    if (TrySpatialExtents(newexts))
    {
         //
         // We had them lying around -- no parallel communication necessary.
         // 
         return;
    }

    const avtDataAttributes &atts = GetInput()->GetInfo().GetAttributes();
    if (atts.GetThisProcsOriginalSpatialExtents()->HasExtents())
        atts.GetThisProcsOriginalSpatialExtents()->CopyTo(newexts);
    else
        SearchDataForSpatialExtents(newexts);

    UnifyMinMax(newexts, atts.GetSpatialDimension()*2, 6);

    //
    // We now have determined the original spatial extents, so we may as well set
    // them back.
    //
    atts.GetOriginalSpatialExtents()->Set(newexts);
}


// ****************************************************************************
//  Method: avtFilter::TryActualDataExtents
//
//  Purpose:
//      Tries to get the actual data extents, but only if they are available
//      (does not resort to parallel unification).
//
//  Returns:    True if the extents were succesfully retrieved.
//
//  Arguments:
//    outexts   A place to hold the retrieved extents.
//
//
//  Programmer: Kathleen Bonnell
//  Creation:   October 2, 2001
//
//  Modifications:
//
//     Hank Childs, Thu Aug 26 13:02:28 PDT 2010
//     Change named of extents object.
//
// ****************************************************************************

bool
avtFilter::TryActualDataExtents(double *outexts)
{
    avtDataAttributes &atts = GetInput()->GetInfo().GetAttributes();

    avtExtents *actual = atts.GetActualDataExtents();
    if (actual->HasExtents())
    {
        actual->CopyTo(outexts);
        return true;
    }

    return false;
}


// ****************************************************************************
//  Method: avtFilter::GetActualDataExtents
//
//  Purpose:
//      Gets the data extents -- parallel unification or not.
//
//  Arguments:
//    outexts   A place to hold the retrieved extents.
//
//  Programmer: Kathleen Bonnell 
//  Creation:   October 2, 2001
//
//  Modifications:
//
//    Hank Childs, Mon Nov  3 09:49:11 PST 2003
//    Setting the extents with the output is a mistake -- the filter may even
//    modify those extents.  They should be set with the input.
//
//    Mark C. Miller, Sat Jan 31 13:31:08 PST 2004
//    Added optional size of 18 for UnifyMinMax (max # comps = 9 for tensor)
//
//    Kathleen Bonnell, Thu Mar 11 10:14:20 PST 2004
//    DataExtents now restricted to only 2 components, regardless of variable
//    dimension.
// 
//    Hank Childs, Thu Aug 26 13:02:28 PDT 2010
//    Change named of extents object.
//
// ****************************************************************************

void
avtFilter::GetActualDataExtents(double *newexts)
{
    if (TryActualDataExtents(newexts))
    {
         //
         // We had them lying around -- no parallel communication necessary.
         //
         return;
    }

    avtDataAttributes &atts = GetInput()->GetInfo().GetAttributes();
    atts.GetThisProcsActualDataExtents()->CopyTo(newexts);

    UnifyMinMax(newexts, 2, 2);

    //
    // We now have determined the actual data extents, so we may as well set
    // them back.
    //
    atts.GetActualDataExtents()->Set(newexts);
}


// ****************************************************************************
//  Method: avtFilter::TryActualSpatialExtents
//
//  Purpose:
//    Tries to get the actual spatial extents, but only if they are available
//    (does not resort to parallel unification).
//
//  Returns:    True if the extents were succesfully retrieved.
//
//  Arguments:
//    outexts   A place to hold the retrieved extents.
//
//  Programmer: Kathleen Bonnell
//  Creation:   October 2, 2001
//
//  Modifications:
//
//     Hank Childs, Thu Aug 26 13:02:28 PDT 2010
//     Change named of extents object.
//
// ****************************************************************************

bool
avtFilter::TryActualSpatialExtents(double *outexts)
{
    avtDataAttributes &atts = GetInput()->GetInfo().GetAttributes();

    //
    // Our first preference is for the desired extents.
    //
    avtExtents *actual = atts.GetActualSpatialExtents();
    if (actual->HasExtents())
    {
        actual->CopyTo(outexts);
        return true;
    }

    return false;
}


// ****************************************************************************
//  Method: avtFilter::GetActualSpatialExtents
//
//  Purpose:
//    Gets the actual spatial extents -- parallel unification or not.
//
//  Arguments:
//    outexts   A place to hold the retrieved extents.
//
//  Programmer: Kathleen Bonnell
//  Creation:   October 2, 2001
//
//  Modificaitons:
//
//    Mark C. Miller, Sat Jan 31 13:31:08 PST 2004
//    Added altsize argument of 6 to UnifyMinMax call
//
//    Hank Childs, Thu Aug 26 13:02:28 PDT 2010
//    Change named of extents object.
//
// ****************************************************************************

void
avtFilter::GetActualSpatialExtents(double *outexts)
{
    if (TryActualSpatialExtents(outexts))
    {
         //
         // We had them lying around -- no parallel communication necessary.
         //
         return;
    }

    avtDataAttributes &atts = GetInput()->GetInfo().GetAttributes();
    atts.GetThisProcsActualSpatialExtents()->CopyTo(outexts);

    UnifyMinMax(outexts, atts.GetSpatialDimension()*2, 6);

    //
    // We now have determined the actual spatial extents, so we may as well set
    // them back.
    //
    GetOutput()->GetInfo().GetAttributes().GetActualSpatialExtents()
                                                                ->Set(outexts);
}


// ****************************************************************************
//  Method: avtFilter::PreExecute
//
//  Purpose:
//      Called just before execution.  This is a hook for derived types.
//
//  Programmer: Hank Childs
//  Creation:   October 24, 2001
//
// ****************************************************************************

void
avtFilter::PreExecute(void)
{
    ;
}


// ****************************************************************************
//  Method: avtFilter::PostExecute
//
//  Purpose:
//      Called just after execution.  This is a hook for derived types.
//
//  Programmer: Hank Childs
//  Creation:   October 24, 2001
//
// ****************************************************************************

void
avtFilter::PostExecute(void)
{
    ;
}


// ****************************************************************************
//  Method: avtFilter::ExamineContract
//
//  Purpose:
//      Allows the base types an opportunity to examine the contract.
//      This is basically defined as a hook for derived types.
//
//  Programmer: Hank Childs
//  Creation:   October 24, 2001
//
// ****************************************************************************

void
avtFilter::ExamineContract(avtContract_p)
{
    ;
}


// ****************************************************************************
//  Method: avtFilter::ReleaseData
//
//  Purpose:
//      Makes the output release any data that it has as a memory savings.
//
//  Programmer: Hank Childs
//  Creation:   October 24, 2001
//
//  Modifications:
//
//    Hank Childs, Sat Feb 19 14:40:58 PST 2005
//    Release data is now defined in the base class.  Just call the base
//    class's method.  Left this one in place so that the debug statement
//    would stick around (very useful for debugging).
//
//    Hank Childs, Wed Jan 17 11:14:17 PST 2007
//    Tell the filter that it has been modified when it releases its data.
//
// ****************************************************************************

void
avtFilter::ReleaseData(void)
{
    debug3 << "Filter " << GetType() << " releasing its data" << endl;
    avtDataObjectSource::ReleaseData();
    modified = true;
}


// ****************************************************************************
//  Method: avtFilter::ResolveDynamicAttributes
//
//  Purpose:
//      Tells each of the dynamic attributes that it is time to resolve
//      themselves.
//
//  Programmer: Hank Childs
//  Creation:   March 15, 2002
//
// ****************************************************************************

void
avtFilter::ResolveDynamicAttributes(void)
{
    int nAttributes = dynamicAttributes.size();
    for (int i = 0 ; i < nAttributes ; i++)
    {
        dynamicAttributes[i]->ResolveAttributes(GetInput());
    }
}


// ****************************************************************************
//  Method: avtFilter::RegisterDynamicAttributes
//
//  Purpose:
//      Registers a dynamic attributes object with this base type.
//
//  Programmer: Hank Childs
//  Creation:   March 15, 2002
//
// ****************************************************************************

void
avtFilter::RegisterDynamicAttribute(avtDynamicAttribute *da)
{
    dynamicAttributes.push_back(da);
}


// ****************************************************************************
//  Method: avtFilter::SearchDataForDataExtents
//
//  Purpose:
//      This should search through the actual data to find the data extents.
//      Since this class does not know what type of data it has, it will
//      throw an exception.
//
//  Programmer: Hank Childs
//  Creation:   April 16, 2002
//
//  Modifications:
//
//    Hank Childs, Fri Dec  3 14:28:02 PST 2004
//    Added variable name.
//
// ****************************************************************************

void
avtFilter::SearchDataForDataExtents(double *, const char *)
{
    EXCEPTION0(ImproperUseException);
}


// ****************************************************************************
//  Method: avtFilter::SearchDataForSpatialExtents
//
//  Purpose:
//      This should search through the actual data to find the spatial extents.
//      Since this class does not know what type of data it has, it will
//      throw an exception.
//
//  Programmer: Hank Childs
//  Creation:   November 30, 2010
//
// ****************************************************************************

void
avtFilter::SearchDataForSpatialExtents(double *)
{
    EXCEPTION0(ImproperUseException);
}


// ****************************************************************************
//  Method: avtFilter::InitializeWebpage
//
//  Purpose:
//      Initializes the webpage.
//
//  Programmer: Hank Childs
//  Creation:   June 15, 2007
//
//  Modifications:
//    Cyrus Harrison, 
//    Added support for optional -dump output directory.
//
// ****************************************************************************

void
avtFilter::InitializeWebpage(void)
{
    if (webpage != NULL)
    {
        debug1 << "DUMP CODE: open file handle, exception previously?" 
               << endl;
        delete webpage;
    }
    
    static int filter_id = 0;
    
    ostringstream oss;
    
    const string &dump_dir = avtDebugDumpOptions::GetDumpDirectory();

    if (PAR_Size() > 1)
    {
        oss << dump_dir 
            << "filt" 
            << filter_id 
            << "." 
            << PAR_Rank()
            << ".html";    
        //sprintf(name, "filt%d.%d.html", filter_id, rank);
    }
    else
    {
        oss << dump_dir 
            << "filt" 
            << filter_id 
            << ".html";    
    }
    filter_id++;
    
    string file_name = oss.str();
    webpage = new avtWebpage(file_name.c_str());

    // reset ostringstr;
    oss.str("");
    oss <<  GetType() << " dump info";
    string pagename = oss.str();
    
    // Now set up our webpage.    
    webpage->InitializePage(pagename.c_str());
    webpage->WriteTitle(pagename.c_str());
    webpage->AddOnPageLink("input_contract", "Input contract");
    webpage->AddOnPageLink("output_contract", "Output contract");
    webpage->AddOnPageLink("input_data_object", "Input data object");
    webpage->AddOnPageLink("output_data_object", "Output data object");
}


// ****************************************************************************
//  Method: avtFilter::FinalizeWebpage
//
//  Purpose:
//      Finalizes the webpage.
//
//  Programmer: Hank Childs
//  Creation:   June 15, 2007
//
// ****************************************************************************

void
avtFilter::FinalizeWebpage(void)
{
    if (webpage == NULL)
    {
        debug1 << "DUMP CODE: webpage not open, error?" << endl;
        return;
    }
 
    webpage->FinalizePage();
    delete webpage;
    webpage = NULL;
}


// ****************************************************************************
//  Method: avtFilter::DumpDataObject
//
//  Purpose:
//      "Dumps" a data object.  This means put the data attributes into a web 
//      page.  The data object also can add to the web page, depending on 
//      whether or not it has reimplemented a virtual function on how to 
//      write itself out.
//
//  Programmer: Hank Childs
//  Creation:   December 21, 2006
//
//  Modifications:
//
//    Hank Childs, Fri Jun 15 12:41:41 PDT 2007
//    Moved some code to Initialize and FinalizeWebpage.
//
//    Cyrus Harrison, Wed Feb 13 13:51:32 PST 2008
//    Output dir should not be included dump reference. 
//
//    Brad Whitlock, Thu Mar 12 13:58:59 PST 2009
//    I changed before/after to input/output in the constructed prefix.
//
// ****************************************************************************

void
avtFilter::DumpDataObject(avtDataObject_p dob, const char *prefix)
{
    if (webpage == NULL)
    {
        debug1 << "Webpage not initialized ... shouldn't happen" << endl;
        return;
    }

    if (strcmp(prefix, "input") == 0)
    {
        // If we add the reference to the main web page right now, 
        // all of the filters will be in execution order.
        
        // we need the webpage name without the directory
        const string &dump_dir = avtDebugDumpOptions::GetDumpDirectory();
        string ref_name(webpage->GetName());
        
        // strip dump_dir from ref_name if necesary
        if( dump_dir != "" && ref_name.find(dump_dir) == 0)
            ref_name = ref_name.substr(dump_dir.length());
        
        avtTerminatingSink::AddDumpReference(ref_name.c_str(),
                                             GetType(), 
                                             numInExecute);

        std::string input_string;
        char prefix[128];
        sprintf(prefix, "input.%s", GetType());
        webpage->AddSectionForLinks("input_data_object");
        webpage->AddHeading("INPUT DATA OBJECT");
        dob->DebugDump(webpage, prefix);
    }
    else if (strcmp(prefix, "output") == 0)
    {
 
        std::string output_string;
        char prefix[128];
        sprintf(prefix, "output.%s", GetType());
        webpage->AddSectionForLinks("output_data_object");
        webpage->AddHeading("OUTPUT DATA OBJECT");
        dob->DebugDump(webpage, prefix);
    }
}


// ****************************************************************************
//  Method: avtFilter::DumpContract
//
//  Purpose:
//      "Dumps" a contract.  This means create a web page and put the
//      information about a contract into it.
//
//  Programmer: Hank Childs
//  Creation:   June 15, 2007
//
// ****************************************************************************

void
avtFilter::DumpContract(avtContract_p contract, const char *prefix)
{
    if (webpage == NULL)
    {
        debug1 << "Webpage not initialized ... shouldn't happen" << endl;
        return;
    }

    if (strcmp(prefix, "input") == 0)
    {
        webpage->AddSectionForLinks("input_contract");
        webpage->AddHeading("INPUT CONTRACT");
    }
    else if (strcmp(prefix, "output") == 0)
    {
        webpage->AddSectionForLinks("output_contract");
        webpage->AddHeading("OUTPUT CONTRACT");
    }
    contract->DebugDump(webpage);
}


// ****************************************************************************
//  Method:  avtFilter::FilterUnderstandsTransformedRectMesh
//
//  Purpose:
//    If this filter returns true, this means that it correctly deals
//    with rectilinear grids having an implied transform set in the
//    data attributes.  It can do this conditionally if desired.
//
//  Arguments:
//    none
//
//  Programmer:  Jeremy Meredith
//  Creation:    February 15, 2007
//
// ****************************************************************************
bool
avtFilter::FilterUnderstandsTransformedRectMesh(void)
{
    // This is the base class default, which reports "false".  This is
    // the safest behavior, allowing us to optimize those classes
    // that need it, and report "true" only for those known to exhibit
    // truly correct behavior.
    return false;
}


// ****************************************************************************
//  Method: avtFilter::CreateNamedSelection
//
//  Purpose:
//      Walk upstream until you find a filter that can create a named selection
//
//  Programmer: Hank Childs
//  Creation:   February 23, 2009
//
// ****************************************************************************

avtNamedSelection *
avtFilter::CreateNamedSelection(avtContract_p c, const std::string &selname)
{
    //
    // If this filter removes zones and it hasn't defined anything about 
    // named selections, then we are in trouble and have to give up.
    //
    if (GetInput()->GetInfo().GetValidity().GetZonesPreserved() != 
        GetOutput()->GetInfo().GetValidity().GetZonesPreserved())
        return NULL;
 
    return GetInput()->GetSource()->CreateNamedSelection(c, selname);
}


// ****************************************************************************
//  Method: avtFilter::FetchArbitraryRefPtr
//
//  Purpose:
//      Checks to see if a void_ref_ptr was cached in the pipeline and fetches
//      it if so.
//
//  Programmer: Hank Childs
//  Creation:   November 28, 2010
//
// ****************************************************************************

void_ref_ptr
avtFilter::FetchArbitraryRefPtr(int dependencies, const char *name, int dom, 
                                int ts, const char *type)
{
    // If we can't cache it, then there's something in the pipeline that
    // subset the cells, transforms the data, etc.
    // So, even if there is something in the cache, we can't use it ... it 
    // might be appropriate for a different pipeline, but not ours.
    bool canUse = CheckDependencies(dependencies);
    if (! canUse)
    {
        void_ref_ptr vrp;
        return vrp;
    }

    avtOriginatingSource *source = GetInput()->GetOriginatingSource();
    return source->FetchArbitraryRefPtr(name, dom, ts, type);
}


// ****************************************************************************
//  Method: avtFilter::StoreArbitraryRefPtr
//
//  Purpose:
//      Stores a ref_ptr in the database cache.
//
//  Programmer: Hank Childs
//  Creation:   November 28, 2010
//
// ****************************************************************************

void
avtFilter::StoreArbitraryRefPtr(int dependencies, const char *name, int dom, 
                                int ts, const char *type, void_ref_ptr vrp)
{
    bool canUse = CheckDependencies(dependencies);
    if (! canUse)
    {
        debug5 << "Cannot cache " << type << " for " << name << " because "
               << "dependencies were not satisfied.\n"
               << "This is an informational message, not an error." << endl;
        return;
    }

    avtOriginatingSource *source = GetInput()->GetOriginatingSource();
    source->StoreArbitraryRefPtr(name, dom, ts, type, vrp);
}


// ****************************************************************************
//  Method: avtFilter::CheckDependencies
//
//  Purpose:
//      Checks to see if the dependencies are satisfied for caching.
//
//  Arguments:
//     dependencies    A bit vector of CacheItemDependences
//
//  Returns:
//     true if you can use the cache, false if the dependencies are violated.
//
//  Programmer: Hank Childs
//  Creation:   November 28, 2010
//
// ****************************************************************************

bool
avtFilter::CheckDependencies(int dependencies)
{
    if ((dependencies & DATA_DEPENDENCE) && (! CanCacheDataItem()))
        return false;
    if ((dependencies & SPATIAL_DEPENDENCE) && (! CanCacheSpatialItem()))
        return false;
    if ((dependencies & CONNECTIVITY_DEPENDENCE) && (! CanCacheConnectivityItem()))
        return false;
    
    return true;
}


// ****************************************************************************
//  Method: avtFilter::CanCacheDataItem
//
//  Purpose:
//      Determines whether or not a data item can be cached.  If the scalar
//      field has been modified, then it should not be cached.  This typically
//      only happens when we commandeer variables (i.e. we take some existing
//      variable and overwrite it for our own purposes).  And we don't do that
//      very often.
//
//      So this method just checks to see if the zones haven't been modified, 
//      etc.
//
//  Returns:   true if items that have a data dependence can be cached.
//
//  Programmer: Hank Childs
//  Creation:   November 28, 2010
//
// ****************************************************************************

bool
avtFilter::CanCacheDataItem(void)
{
    avtDataValidity &validity = GetInput()->GetInfo().GetValidity();
    if (! validity.GetDataMetaDataPreserved())
       return false;
    if (! validity.GetZonesPreserved())
       return false;
    if (! validity.GetNodesPreserved())
       return false;
    if (! validity.GetOriginalZonesIntact())
       return false;

    return true;
}


// ****************************************************************************
//  Method: avtFilter::CanCacheSpatialItem
//
//  Purpose:
//      Determines whether or not a spatial item can be cached. 
//
//  Returns:   true if items that have a spatial dependence can be cached.
//
//  Programmer: Hank Childs
//  Creation:   November 28, 2010
//
//  Modifications:
//
//    Hank Childs, Sun Nov 28 12:34:06 PST 2010
//    Fix up goof in initial implementation.
//
// ****************************************************************************

bool
avtFilter::CanCacheSpatialItem(void)
{
    avtDataValidity &validity = GetInput()->GetInfo().GetValidity();
    if (! validity.GetSpatialMetaDataPreserved())
       return false;
    if (! validity.GetZonesPreserved())
       return false;
    if (! validity.GetNodesPreserved())
       return false;
    if (! validity.GetOriginalZonesIntact())
       return false;
    if (validity.GetPointsWereTransformed())
       return false;

    return true;
}


// ****************************************************************************
//  Method: avtFilter::CanCacheConnectivityItem
//
//  Purpose:
//      Determines if a mesh connectivity item can be cached.
//
//  Returns:   true if items that have a mesh dependence can be cached.
//
//  Programmer: Hank Childs
//  Creation:   November 28, 2010
//
// ****************************************************************************

bool
avtFilter::CanCacheConnectivityItem(void)
{
    avtDataValidity &validity = GetInput()->GetInfo().GetValidity();
    if (! validity.GetZonesPreserved())
       return false;
    if (! validity.GetNodesPreserved())
       return false;
    if (! validity.GetOriginalZonesIntact())
       return false;

    return true;
}


// ****************************************************************************
//  Method: avtFilter::ResetAllExtents
//
//  Purpose:
//      Walks up a pipeline resetting extents.
//
//  Programmer: Kathleen Biagas
//  Creation:   June 5, 2017
//
//  Modifications:
//
// ****************************************************************************

void
avtFilter::ResetAllExtents()
{
    avtDataObjectSink::ResetAllExtents();
}

