static bool DetectAWENETCDF(NETCDFFileObject *f)
{
    bool retval = false;

    // If format has not been recognised yet, see if it looks like an AWEnetCDF file
    // Look for a variable called "codevers" (but not concerned what's in it)
    // (InqVariable() would also work if we really don't want to see codevers contents)
    TypeEnum vartyp = NO_TYPE;
    int ndims = 0, *dims = 0;
    void *values = 0;

    if(f->ReadVariable("codevers", &vartyp, &ndims, &dims, &values))
    {
        debug4 << "Database seems to be AWEnetCDF format - found codevers";
        if(vartyp == CHARARRAY_TYPE && ndims == 1 && *dims > 0)
        {
          char *codevers_val = (char *)values;
          debug4 << " = \"" << codevers_val  << "\"" ;
        }
        debug4 << "; Abandoning NETCDF reader." << endl;
        delete [] dims;
        free_void_mem(values, vartyp);
        retval = true;
    }

    return retval;
}
bool 
NETCDFFileObject::ReadStringAttribute(const char *attname, std::string &attval)
{
    const char *mName = "NETCDFFileObject::ReadStringAttribute: ";
    bool retval = false;
    TypeEnum t = NO_TYPE;
    int ndims = 0;
    int *dims = 0;
    void *value = 0;

    debug4 << mName << "(" << attname << ")" << endl;
    if(ReadAttribute(attname, &t, &ndims, &dims, &value))
    {
        if(t == CHARARRAY_TYPE && ndims == 1)
        {
            char *c = (char *)value;
            attval = std::string(c);
            retval = true;
        }

        delete [] dims;
        free_void_mem(value, t);
    }

    return retval;
}
bool
NETCDFFileObject::ReadAttributeAsDouble(const char *varname, const char *attname,
     TypeEnum *originalType, double **attvalues, int *nvalues)
{
    bool retval = false;

    TypeEnum t = NO_TYPE;
    int ndims = 0, *dims = 0;
    void *values = 0;
    if(ReadAttribute(varname, attname, &t, &ndims, &dims, &values))
    {
        int nvals = 1;
        for(int j = 0; j < ndims; ++j)
            nvals *= dims[j];

        double *data = new double[nvals];
        for(int j = 0; j < nvals; ++j)
        {
            data[j] = 0.;
            if(t == CHARARRAY_TYPE)
                data[j] = double(((char *)values)[j]);
            else if(t == UCHARARRAY_TYPE)
                data[j] = double(((unsigned char *)values)[j]);
            else if(t == SHORTARRAY_TYPE)
                data[j] = double(((short *)values)[j]);
            else if(t == INTEGERARRAY_TYPE)
                data[j] = double(((int *)values)[j]);
            else if(t == LONGARRAY_TYPE)
                data[j] = double(((long *)values)[j]);
            else if(t == FLOATARRAY_TYPE)
                data[j] = double(((float *)values)[j]);
            else if(t == DOUBLEARRAY_TYPE)
                data[j] = double(((double *)values)[j]);
        }

        delete [] dims;
        free_void_mem(values, t);

        retval = true;
        *attvalues = data;
        *nvalues = nvals;
        *originalType = t;
    }
    else
    {
        *attvalues = NULL;
        *nvalues = 0;
        *originalType = NO_TYPE;
    }

    return retval;
}
bool
avtLODIParticleFileFormat::ReadTimes()
{
    const char *mName = "avtLODIParticleFileFormat::ReadTimes: ";
    debug4 << mName << endl;
    if(!timesRead)
    {
        // Set the times
        TypeEnum t = NO_TYPE;
        int ndims = 0, *dims = 0;
        void *values = 0;
        if(fileObject->ReadVariable("elapsed_time", &t, &ndims, &dims, &values))
        {
            if(ndims == 1 && t == DOUBLEARRAY_TYPE)
            {
                debug4 << mName << "times={";
                double *dptr = (double *)values;
                intVector cycles;
                for(int i = 0; i < dims[0]; ++i)
                {
                    debug4 << ", " << *dptr;
                    cycles.push_back(i);
                    times.push_back(*dptr++);
                }
                debug4 << "}" << endl;
                timesRead = true;
            }
            else
            {
                debug4 << mName << "elapsed_time was read but it was the "
                       << "wrong type." << endl;
            }

            delete [] dims;
            free_void_mem(values, t);
        }
        else
        {
            debug4 << mName << "Could not read elapsed_time array!" << endl;
        }
    }

    return timesRead;
}
void
avtLODIParticleFileFormat::PopulateDatabaseMetaData(avtDatabaseMetaData *md)
{
    const char *mName = "avtLODIParticleFileFormat::PopulateDatabaseMetaData: ";
    debug4 << mName << endl;
    if(DebugStream::Level4())
        fileObject->PrintFileContents(DebugStream::Stream4());

    // Assemble a database title.
    std::string comment(GetType()), titleString, create_version, 
                create_date_time;
    if(fileObject->ReadStringAttribute("title", titleString))
    {
        comment += (std::string(" database: title=") + titleString);

        if(fileObject->ReadStringAttribute("create_version", create_version))
            comment += (std::string(", create_version=") + create_version);

        if(fileObject->ReadStringAttribute("create_date_time", create_date_time))
            comment += (std::string(", create_date_time=") + create_date_time);

        md->SetDatabaseComment(comment);
    }

    //
    // Add a point mesh for the particles
    //
    avtMeshMetaData *mmd = new avtMeshMetaData("particles", 1, 0, 1, 0, 3, 0,
        AVT_POINT_MESH);
    // Read the mesh units
    std::string meshUnits;
    if(fileObject->ReadStringAttribute("part_posn", "units", meshUnits))
    {
        mmd->xUnits = meshUnits;
        mmd->yUnits = meshUnits;
        mmd->zUnits = meshUnits;
    }
    // Read the mesh labels
    TypeEnum t = NO_TYPE;
    int ndims = 0, *dims = 0;
    void *values = 0;
    debug4 << mName << "Trying to read dimnum_labels" << endl;
    if(fileObject->ReadVariable("dimnum_labels", &t, &ndims, &dims, &values))
    {
        if(ndims == 2 && t == CHARARRAY_TYPE)
        {
            int len = dims[1];
            char *cptr = (char *)values;

            char *labels = new char[3 * (len+1)];
            char *xL = labels;
            char *yL = labels + len + 1;
            char *zL = yL + len + 1;
            memset(labels, 0, 3 * (len+1));
            memcpy(xL, cptr,       len);
            memcpy(yL, cptr+len,   len);
            memcpy(zL, cptr+len*2, len);

            mmd->xLabel = xL;
            debug4 << mName << "xLabel = " << xL << endl;

            mmd->yLabel = yL;
            debug4 << mName << "yLabel = " << yL << endl;

            mmd->zLabel = zL;
            debug4 << mName << "zLabel = " << zL << endl;

            delete [] labels;
        }

        delete [] dims;
        free_void_mem(values, t);
    }
    md->Add(mmd);

    //
    // Read the sourceid variable and create material names from it.
    //
    sourceids.clear();
    if(fileObject->ReadVariable("sourceid", &t, &ndims, &dims, &values))
    {
        if(t == CHARARRAY_TYPE && ndims == 2)
        {
            int nsrcs = dims[0];
            int len = dims[1];
            char *name = new char[len+1];
            char *start = (char *)values;

            debug4 << mName << "sourceid={";
            for(int i = 0; i < nsrcs; ++i)
            {
                char *namestart = start + len * i;
                memcpy((void*)name, (void*)namestart, len);
                name[len] = '\0';

                char *end = name + len - 1;
                while(end >= name && *end == ' ')
                    *end-- = '\0';

                sourceids.push_back(name);

                if(i > 0)
                    debug4 << ", ";
                debug4 << name;
            }
            debug4 << "}" << endl;
            delete [] name;

            // Add the material to the metadata.
            avtMaterialMetaData *matmd = new avtMaterialMetaData("sourceid",
                "particles", (int)sourceids.size(), sourceids);
            md->Add(matmd);
        }

        delete [] dims;
        free_void_mem(values, t);
    }

    //
    // Look for variables defined on the particles mesh.
    //
    if(fileObject->InqVariable("part_posn", &t, &ndims, &dims))
    {
        // Iterate over all of the variables and pick those that have
        // the same number of elements as nPts.
        int status, nDims, nVars, nGlobalAtts, unlimitedDimension;
        status = nc_inq(fileObject->GetFileHandle(), &nDims, &nVars, &nGlobalAtts,
                        &unlimitedDimension);
        if(status != NC_NOERR)
        {
            fileObject->HandleError(status);
            return;
        }

        // Get the sizes of all dimensions.
        int i;
        size_t *dimSizes = new size_t[nDims];
        for(i = 0; i < nDims; ++i)
        {
            int status = nc_inq_dimlen(fileObject->GetFileHandle(), i, &dimSizes[i]);
            if(status != NC_NOERR)
                fileObject->HandleError(status);
        }

        // Determine the maximum number of points in the particle mesh.
        int nElems = 1;
        for(i = 0; i < ndims; ++i)
            nElems *= dims[i];
        int nPts = nElems / 3;
        delete [] dims;

        // Look for variables with the same number of values as nPts.
        for(i = 0; i < nVars; ++i)
        {
            char varname[NC_MAX_NAME+1];
            nc_type vartype;
            int  varndims;
            int  vardims[NC_MAX_VAR_DIMS];
            int  varnatts;
            if((status = nc_inq_var(fileObject->GetFileHandle(), i, varname,
                                    &vartype, &varndims, 
                                    vardims, &varnatts)) == NC_NOERR)
            {
                nElems = 1;
                for(int j = 0; j < varndims; ++j)
                    nElems *= dimSizes[vardims[j]];
                if(nElems == nPts)
                {
                    avtScalarMetaData *smd = new avtScalarMetaData(varname,
                        "particles", AVT_NODECENT);
                    smd->hasUnits = fileObject->ReadStringAttribute(
                        varname, "units", smd->units);
                    md->Add(smd);
                }
            }
        }

        delete [] dimSizes;
    }
}