// 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.

// ************************************************************************* //
//                            avtM3DC1FileFormat.C                           //
// ************************************************************************* //

#include <avtM3DC1FileFormat.h>
#include <avtM3DC1Field.h>

#include <string>

#include <vtkIntArray.h>
#include <vtkFloatArray.h>
#include <vtkDoubleArray.h>
#include <vtkUnstructuredGrid.h>
#include <vtkTriangle.h>
#include <vtkWedge.h>

#include <avtDatabaseMetaData.h>

#include <DBOptionsAttributes.h>
#include <avtLogicalSelection.h>
//#include <avtSpatialBoxSelection.h>
#include <Expression.h>

#include <avtCallback.h>
#include <NonCompliantException.h>
#include <InvalidFilesException.h>
#include <InvalidVariableException.h>
#include <DebugStream.h>

#include <vtk_hdf5.h>
#include <visit-hdf5.h>


#define ELEMENT_SIZE_2D 7
#define SCALAR_SIZE_2D 20

#define ELEMENT_SIZE_3D 9
#define SCALAR_SIZE_3D 80


// ****************************************************************************
//  Method: avtM3DC1FileFormat constructor
//
//  Programmer: allen -- generated by xml2avt
//  Creation:   Fri Dec 4 15:04:15 PST 2009
//
// ****************************************************************************

avtM3DC1FileFormat::avtM3DC1FileFormat(const char *filename,
                                       const DBOptionsAttributes *readOpts)
  : avtMTSDFileFormat(&filename, 1),
    processDataSelections(false), haveReadWholeData(true),
    m_filename(filename),
    m_refinement(2), m_dataLocation(AVT_NODECENT)
{
    if (readOpts != NULL) {
      for (int i=0; i<readOpts->GetNumberOfOptions(); ++i) {
        if (readOpts->GetName(i) == "Mesh refinement")
          m_refinement = readOpts->GetEnum("Mesh refinement");
        else if (readOpts->GetName(i) == "Linear mesh data location") 
        {
          int dataLocation = readOpts->GetEnum("Linear mesh data location");
          
          if( dataLocation == 0 )
            m_dataLocation = AVT_NODECENT;
          else if( dataLocation == 1 )
            m_dataLocation = AVT_ZONECENT;
        }
        else if (readOpts->GetName(i) == "Process Data Selections in the Reader")
          processDataSelections =
            readOpts->GetBool("Process Data Selections in the Reader");
      }
    }

    if( m_refinement < 0 )      m_refinement = 0;
    else if( m_refinement > 4 ) m_refinement = 4;

    if( processDataSelections && m_refinement )
    {
        EXCEPTION2( NonCompliantException, "M3DC1 Mesh",
                    "Can not subselect a refined mesh. " +
                    std::string(" Set the mesh refinement to 1 or disable") +
                    std::string(" processing data selections in the reader.") );
    }

    LoadFile();
}


// ****************************************************************************
//  Method: avtEMSTDFileFormat::GetNTimesteps
//
//  Purpose:
//      Tells the rest of the code how many timesteps there are in this file.
//
//  Programmer: allen -- generated by xml2avt
//  Creation:   Fri Dec 4 15:04:15 PST 2009
//
// ****************************************************************************

int
avtM3DC1FileFormat::GetNTimesteps(void)
{
    return (int)m_times.size();
}


// ****************************************************************************
//  Method: avtM3DC1FileFormat::FreeUpResources
//
//  Purpose:
//      When VisIt is done focusing on a particular timestep, it asks that
//      timestep to free up any resources (memory, file descriptors) that
//      it has associated with it.  This method is the mechanism for doing
//      that.
//
//  Programmer: allen -- generated by xml2avt
//  Creation:   Fri Dec 4 15:04:15 PST 2009
//
// ****************************************************************************

void
avtM3DC1FileFormat::FreeUpResources(void)
{
    H5Fclose( m_fileID );
}


// ****************************************************************************
//  Method: avtVsFileFormat::CanCacheVariable
//
//  Purpose:
//      To truly exercise the VS file format, we can't have VisIt caching
//      chunks of mesh and variables above the plugin.
//      
//  Programmer: Mark C. Miller 
//  Creation:   September 20, 2004 
//
// ****************************************************************************

bool
avtM3DC1FileFormat::CanCacheVariable(const char *var)
{
    // If processing the selections turn caching off.
    return !processDataSelections;

//    return haveReadWholeData;
}


// ****************************************************************************
//  Method: avtVsFileFormat::RegisterDataSelections
//
//  Purpose:
//      The Vs format can exploit some data selections so get them. 
//      
//  Programmer: Allen Sanderson
//  Creation:   March 4, 2011
//
// ****************************************************************************

void
avtM3DC1FileFormat::RegisterDataSelections(const std::vector<avtDataSelection_p> &sels,
                                        std::vector<bool> *selectionsApplied)
{
  selList     = sels;
  selsApplied = selectionsApplied;
}


// ****************************************************************************
//  Method: avtVsFileFormat::ProcessDataSelections
//
//  Purpose:
//      The Vs format can exploit some data selections so process them. 
//      
//  Programmer: Allen Sanderson
//  Creation:   March 4, 2011
//
// ****************************************************************************

bool
avtM3DC1FileFormat::ProcessDataSelections(int *mins, int *maxs, int *strides)
{
    bool retval = false;

    if( !processDataSelections )
      return retval;

    avtLogicalSelection composedSel;

    for (size_t i = 0; i < selList.size(); i++)
    {
        if (std::string(selList[i]->GetType()) == "Logical Data Selection")
        {
            avtLogicalSelection *sel = (avtLogicalSelection *) *(selList[i]);

            // overrwrite method-scope arrays with the new indexing
            composedSel.Compose(*sel);
            (*selsApplied)[i] = true;
            retval = true;
        }

        // Cannot handle avtSpatialBoxSelection without knowing the mesh.

//         else if (std::string(selList[i]->GetType()) == "Spatial Box Data Selection")
//         {
//             avtSpatialBoxSelection *sel =
//               (avtSpatialBoxSelection *) *(Sellist[i]);

//             double dmins[3], dmaxs[3];
//             sel->GetMins(dmins);
//             sel->GetMaxs(dmaxs);
//             avtSpatialBoxSelection::InclusionMode imode =
//                 sel->GetInclusionMode();

//             // we won't handle clipping of zones here
//             if ((imode != avtSpatialBoxSelection::Whole) &&
//                 (imode != avtSpatialBoxSelection::Partial))
//             {
//                 (*selsApplied)[i] = false;
//                 continue;
//             }

//             int imins[3], imaxs[3];
//             for (int j = 0; j < 3; j++)
//             {
//                 int imin = (int) dmins[j];
//                 if (((double) imin < dmins[j]) &&
//                     (imode == avtSpatialBoxSelection::Whole))
//                     imin++;
                
//                 int imax = (int) dmaxs[j];
//                 if (((double) imax < dmaxs[j]) &&
//                     (imode == avtSpatialBoxSelection::Partial))
//                     imax++;

//                 imins[j] = imin;
//                 imaxs[j] = imax;
//             }

//             avtLogicalSelection newSel;
//             newSel.SetStarts(imins);
//             newSel.SetStops(imaxs);

//             composedSel.Compose(newSel);
//             (*selsApplied)[i] = true;
//             retval = true;
//         }
        else
        {
            // indicate we won't handle this selection
            (*selsApplied)[i] = false;
        }
    }

    composedSel.GetStarts(mins);
    composedSel.GetStops(maxs);
    composedSel.GetStrides(strides);

    // If the user is a dumb ass and selects a dimension lower than
    // the actual dimension the min, max, and stride will be zero. So
    // fix it to be the full bounds and a stride of 1.
    for (int i = 0; i < 3; i++)
    {
      if( strides[i] == 0 )
      {
        mins[i] = 0;
        maxs[i] = -1;
        strides[i] = 1;
      }
    }

    return retval;
}


// ****************************************************************************
//  Method: avtM3DC1FileFormat::PopulateDatabaseMetaData
//
//  Purpose:
//      This database meta-data object is like a table of contents for the
//      file.  By populating it, you are telling the rest of VisIt what
//      information it can request from you.
//
//  Programmer: allen -- generated by xml2avt
//  Creation:   Fri Dec 4 15:04:15 PST 2009
//
// ****************************************************************************

void
avtM3DC1FileFormat::PopulateDatabaseMetaData(avtDatabaseMetaData *md,
                                             int timeState)
{
    avtMeshMetaData *mmd;
    avtMeshType meshType = AVT_UNSTRUCTURED_MESH;
    int nblocks = 1;
    int block_origin = 0;
    int cell_origin = 0;
    int group_origin = 0;
    int spatial_dimension = 3;
    int topological_dimension = 3;
    //double *extents = NULL;
    int bounds[3] = {nelms, 0, 0};

    char level[4];

    if( m_refinement != 0 )
    {
      sprintf( level, "_%d", m_refinement );
    }
    else
      level[0] = '\0';


    // Original meshes for the user to see.
    mmd = new avtMeshMetaData("equilibrium/mesh",
                              nblocks, block_origin,
                              cell_origin, group_origin,
                              spatial_dimension, topological_dimension,
                              meshType);
    
    mmd->SetBounds( bounds );
    mmd->SetNumberCells( nelms );
    md->Add(mmd);

    mmd = new avtMeshMetaData("mesh",
                              nblocks, block_origin,
                              cell_origin, group_origin,
                              spatial_dimension, topological_dimension,
                              meshType);

    mmd->SetBounds( bounds );
    mmd->SetNumberCells( nelms );
    md->Add(mmd);

    // Populate the scalar field vars that will be interpolate onto a
    // refined mesh.
    for ( size_t i = 0; i < m_fieldVarNames.size(); ++i )
    {
      std::string varname = "equilibrium/" + m_fieldVarNames[i];
      std::string meshname = std::string("equilibrium/mesh") + std::string(level);
      
      AddScalarVarToMetaData( md, varname, meshname, m_dataLocation );
      
      meshname = std::string("mesh") + std::string(level);
      AddScalarVarToMetaData( md, m_fieldVarNames[i], meshname, m_dataLocation );
    }

    // For now the mesh is the same mesh as the original mesh because
    // of needing it for the integration.
    AddVectorVarToMetaData( md, "B_C1_Elements",
                            std::string("mesh"),
                            AVT_ZONECENT, 3);
    
    // Interpolated on to a mesh for visualization only.
    AddVectorVarToMetaData( md, "B_Interpolated",
                            std::string("mesh") + std::string(level),
                            m_dataLocation, 3 );
    
    // Hidden refined meshes for working with the interpolated data
    if( m_refinement )
    {
      int nLevels = m_refinement + 1;

      nblocks = nelms * nLevels * nLevels;

      mmd =
        new avtMeshMetaData(std::string("equilibrium/mesh") + std::string(level),
                            nblocks, block_origin,
                            cell_origin, group_origin,
                            spatial_dimension, topological_dimension, meshType);
      mmd->hideFromGUI = true;
      md->Add(mmd);

      mmd =
        new avtMeshMetaData(std::string("mesh") + std::string(level),
                            nblocks, block_origin,
                            cell_origin, group_origin,
                            spatial_dimension, topological_dimension, meshType);
      mmd->hideFromGUI = true;
      md->Add(mmd);
    }

    nblocks = nelms;    

    // Hidden meshes for working with the elements directly
    mmd =
      new avtMeshMetaData("hidden/equilibrium/mesh",
                          nblocks, block_origin, cell_origin, group_origin,
                          spatial_dimension, topological_dimension, meshType);
    mmd->hideFromGUI = true;
    md->Add(mmd);

    mmd =
      new avtMeshMetaData("hidden/mesh",
                          nblocks, block_origin, cell_origin, group_origin,
                          spatial_dimension, topological_dimension, meshType);
    mmd->hideFromGUI = true;
    md->Add(mmd);


    // Hidden scalar header vars.
    for ( size_t i = 0; i < m_scalarVarNames.size(); ++i )
    {
      avtScalarMetaData *smd =
        new avtScalarMetaData("hidden/" + m_scalarVarNames[i],
                              "hidden/equilibrium/mesh", AVT_ZONECENT);
      
      smd->hideFromGUI = true;
      md->Add(smd);
    }
    
    // Add the elements so we have access to them for the interpolation
    avtVectorMetaData *amd =
      new avtVectorMetaData("hidden/equilibrium/elements",
                            "hidden/equilibrium/mesh",
                            AVT_ZONECENT, element_size);
    
    amd->hideFromGUI = true;
    md->Add(amd);
    
    amd =
      new avtVectorMetaData("hidden/elements", "hidden/mesh",
                            AVT_ZONECENT, element_size);

    amd->hideFromGUI = true;
    md->Add(amd);

    // Hidden array field vars so we have access to them for the interpolation
    std::string varname;

    for ( size_t i = 0; i < m_fieldVarNames.size(); ++i )
    {
      varname = "hidden/equilibrium/" + m_fieldVarNames[i];
      amd = new avtVectorMetaData(varname, "hidden/equilibrium/mesh",
                                  AVT_ZONECENT, scalar_size);
      
      amd->hideFromGUI = true;
      md->Add(amd);
    
      varname = "hidden/" + m_fieldVarNames[i];
      amd = new avtVectorMetaData(varname, "hidden/mesh",
                                 AVT_ZONECENT, scalar_size);

      amd->hideFromGUI = true;
      md->Add(amd);
    }

    md->SetCyclesAreAccurate(true);
    md->SetCycles( m_cycles );

    md->SetTimesAreAccurate(true);
    md->SetTimes( m_times );
}


// ****************************************************************************
//  Method: avtM3DC1FileFormat::GetElements
//
//  Purpose:
//      Gets the elements assoicated for the C1 mesh.
//
//  Arguments:
//      timestate   The index of the timestate.  If GetNTimesteps returned
//                  'N' time steps, this is guaranteed to be between 0 and N-1.
//      meshname    The name of the mesh of interest.  This can be ignored if
//                  there is only one mesh.
//
//  Programmer: allen -- generated by xml2avt
//  Creation:   Fri Dec 4 15:04:15 PST 2009
//
// ****************************************************************************

float *
avtM3DC1FileFormat::GetElements(int timestate, const char *meshname)
{
  char meshStr[64];

  // Parse the mesh naem into the hdf5 group name.
  if( strncmp(meshname, "equilibrium/mesh", 16 ) == 0 )
  {
    sprintf( meshStr, "/equilibrium/mesh" );
  } else if( strncmp(meshname, "mesh", 4 ) == 0 ) {
    sprintf( meshStr, "/time_%03d/mesh", timestate );
  } else
    EXCEPTION2( NonCompliantException, "M3DC1 Element Name Lookup",
                "Element '" + std::string(meshStr) + "' was not found." );

  // Open the group.
  hid_t meshId = H5Gopen( m_fileID, meshStr, H5P_DEFAULT);
  if ( meshId < 0 )
    EXCEPTION2( NonCompliantException, "M3DC1 Group Open",
                "Group '" + std::string(meshStr) + "' was not found." );
  
  // Read in the mesh information.
  int nElements;
  if ( ! ReadAttribute( meshId, "nelms", &nElements ) )
    EXCEPTION2( NonCompliantException, "M3DC1 Attribute Reader",
                "Attribute 'nelms' was not found or was the wrong type." );
  
  if( nElements != nelms )
    EXCEPTION2( NonCompliantException, "M3DC1 Element Check",
                "Time step 'nelms' does not match equilibrium 'nelms'" );
 
  // Open the dataset and space info for the elements.  
  hid_t datasetId = H5Dopen(meshId, "elements", H5P_DEFAULT);
  hid_t spaceId = H5Dget_space(datasetId);
  size_t rank = H5Sget_simple_extent_ndims(spaceId);
  std::vector<hsize_t> sdim(rank);
  H5Sget_simple_extent_dims(spaceId, &sdim[0], NULL);

  // Sanity check.  
  if( rank != 2 || (size_t)sdim[0] != nelms || sdim[1] != element_size )
  {
      EXCEPTION2( NonCompliantException, "M3DC1 Element Check",
                  "The number of elements or the element size does not match" );          
  }

  // Memory for the elements.  
  float *elements = new float[sdim[0]*sdim[1]];
  if( elements == 0 )
    EXCEPTION2( NonCompliantException, "M3DC1 Memory Allocation",
                "CAN NOT ALLOCATE MEMORY" );

  // Read in the elements.
  H5Dread( datasetId,
           H5T_NATIVE_FLOAT, H5S_ALL, spaceId, H5P_DEFAULT, elements );

  H5Sclose(spaceId);
  H5Dclose(datasetId);
  H5Gclose( meshId );

  return elements;
}

// ****************************************************************************
//  Method: avtM3DC1FileFormat::GetMeshPoints
//
//  Purpose: Gets the mesh points associated with this file.  The mesh
//      is returned as vtkPoints.
//
//  Arguments:
//      elements        The original C1 elements 
//      refinement      The amount of mesh refinement.
//
//  Programmer: allen -- generated by xml2avt
//  Creation:   Fri Dec 4 15:04:15 PST 2009
//
// ****************************************************************************

vtkPoints *
avtM3DC1FileFormat::GetMeshPoints( float *elements, int refinement )
{
  // Inital number of triangles before refinement.
  int npts = nelms * nvertices;
  int ncoords = nvertices * 3;

  // VTK structure for holding the mesh points. 
  vtkPoints *vtkPts = vtkPoints::New();
  vtkPts->SetNumberOfPoints( npts );
  float *pts = (float *) vtkPts->GetVoidPointer(0);

  float phi = 0;

  // Pointer for fast indexing through the elements.
  float *element = elements;

//   for( int i=0; i<nelms/2; ++i )
//     element += element_size;

  for( int i=0; i<nelms; ++i )
  {
    // The element values from Jardin, JCP 200:133 (2004)
    float a     = element[0];
    float b     = element[1];
    float c     = element[2];
    float theta = element[3];
    float x     = element[4];
    float z     = element[5];

    // The seventh value (call it "e") specifies which edges of the
    // element lie on the domain boundary.  The first edge is the
    // edge between the first and second nodes, the second edge
    // joins nodes 2 and 3, and the third edge joins nodes 3 and 1.
    // If edge n lies on the domain boundary, than the nth bit of e
    // is set.  For example, e=0 means no edges are on the domain
    // boundary, e=5 means the first and third edges are on the
    // domain boundary.
    //unsigned int e = element[6];

    if( element_dimension == 3 )
      phi = element[8];

    // The three points in the phi plane that define the triangle mesh element.
    pts[0] = x;
    pts[1] = phi;
    pts[2] = z;

    pts[3] = x + (a+b)*cos(theta);
    pts[4] = phi;
    pts[5] = z + (a+b)*sin(theta);
    
    pts[6] = x + b*cos(theta) - c*sin(theta);
    pts[7] = phi;
    pts[8] = z + b*sin(theta) + c*cos(theta);

    // The three points in the phi+d plane that define the second part of
    // the wedge element.
    if( element_dimension == 3 )
    {
      float d = element[7];

      pts[ 9] = x;
      pts[10] = phi + d;
      pts[11] = z;

      pts[12] = x + (a+b)*cos(theta);
      pts[13] = phi + d;
      pts[14] = z + (a+b)*sin(theta);
    
      pts[15] = x + b*cos(theta) - c*sin(theta);
      pts[16] = phi + d;
      pts[17] = z + b*sin(theta) + c*cos(theta);
    }

    // Increment for fast indexing.
    element += element_size;
    pts += ncoords;
  } 

  // Now do any needed refinement. Currently the flag is 0-4. The user
  // sees 1-5. With 1 being no refinement aka linear.
  if( refinement )
  {
    int nLevels = refinement + 1;

    // Pointer to the current mesh.
    float *pts = (float *) vtkPts->GetVoidPointer(0);

    // VTK structure for holding the refined mesh points. 
    vtkPoints *rf_vtkPts = vtkPoints::New();

    rf_vtkPts->SetNumberOfPoints( npts *
                                  (int) pow( (float) nLevels,
                                             (float) element_dimension) );

    float *rf_pts = (float *) rf_vtkPts->GetVoidPointer(0);

    if( element_dimension == 2 )
    {
      for( int i=0; i<npts; i+=nvertices )
      {
        // Refine the triangle based on barrycentric coordinates.
        float *A = &(pts[0]);
        float *B = &(pts[3]);
        float *C = &(pts[6]);

        for( int j=0; j<nLevels; ++j )
        {
          float a  = (float)  j      / (float) nLevels;
          float a1 = (float) (j + 1) / (float) nLevels;

          for( int k=0; k<nLevels-j; ++k )
          {
            float b   = (float) (nLevels-j-k)   / (float) nLevels;
            float b_1 = (float) (nLevels-j-k-1) / (float) nLevels;

            float c  = (float)  k    / (float) nLevels;
            float c1 = (float) (k+1) / (float) nLevels;

            rf_pts[0] = a * A[0] + b * B[0] + c * C[0];
            rf_pts[1] = a * A[1] + b * B[1] + c * C[1];
            rf_pts[2] = a * A[2] + b * B[2] + c * C[2];

            rf_pts[3] = a * A[0] + b_1 * B[0] + c1 * C[0];
            rf_pts[4] = a * A[1] + b_1 * B[1] + c1 * C[1];
            rf_pts[5] = a * A[2] + b_1 * B[2] + c1 * C[2];

            rf_pts[6] = a1 * A[0] + b_1 * B[0] + c * C[0];
            rf_pts[7] = a1 * A[1] + b_1 * B[1] + c * C[1];
            rf_pts[8] = a1 * A[2] + b_1 * B[2] + c * C[2];

            rf_pts += ncoords;

            if( k )
            {
              float c_1 = (float) (k-1) / (float) nLevels;

              rf_pts[0] = a * A[0] + b * B[0] + c * C[0];
              rf_pts[1] = a * A[1] + b * B[1] + c * C[1];
              rf_pts[2] = a * A[2] + b * B[2] + c * C[2];
            
              rf_pts[3] = a1 * A[0] + b_1 * B[0] + c * C[0];
              rf_pts[4] = a1 * A[1] + b_1 * B[1] + c * C[1];
              rf_pts[5] = a1 * A[2] + b_1 * B[2] + c * C[2];
            
              rf_pts[6] = a1 * A[0] + b * B[0] + c_1 * C[0];
              rf_pts[7] = a1 * A[1] + b * B[1] + c_1 * C[1];
              rf_pts[8] = a1 * A[2] + b * B[2] + c_1 * C[2];
            
              rf_pts += ncoords;
            }
          }
        }

        pts += ncoords;
      }
    }

    else //if( element_dimension == 3 )
    {
      for( int i=0; i<npts; i+=nvertices )
      {
        // Refine the triangle based on barrycentric coordinates.
        float *A = &(pts[0]);
        float *B = &(pts[3]);
        float *C = &(pts[6]);

        float *D = &(pts[9]);
        float *E = &(pts[12]);
        float *F = &(pts[15]);

        for( int j=0; j<nLevels; ++j )
        {
          float a  = (float)  j      / (float) nLevels;
          float a1 = (float) (j + 1) / (float) nLevels;
          
          for( int k=0; k<nLevels-j; ++k )
          {
            float b   = (float) (nLevels-j-k)   / (float) nLevels;
            float b_1 = (float) (nLevels-j-k-1) / (float) nLevels;
            
            float c  = (float)  k    / (float) nLevels;
            float c1 = (float) (k+1) / (float) nLevels;

            double left_pts[9], right_pts[9];

            left_pts[0] = a * A[0] + b * B[0] + c * C[0];
            left_pts[1] = a * A[1] + b * B[1] + c * C[1];
            left_pts[2] = a * A[2] + b * B[2] + c * C[2];

            left_pts[3] = a * A[0] + b_1 * B[0] + c1 * C[0];
            left_pts[4] = a * A[1] + b_1 * B[1] + c1 * C[1];
            left_pts[5] = a * A[2] + b_1 * B[2] + c1 * C[2];

            left_pts[6] = a1 * A[0] + b_1 * B[0] + c * C[0];
            left_pts[7] = a1 * A[1] + b_1 * B[1] + c * C[1];
            left_pts[8] = a1 * A[2] + b_1 * B[2] + c * C[2];


            right_pts[0] = a * D[0] + b * E[0] + c * F[0];
            right_pts[1] = a * D[1] + b * E[1] + c * F[1];
            right_pts[2] = a * D[2] + b * E[2] + c * F[2];

            right_pts[3] = a * D[0] + b_1 * E[0] + c1 * F[0];
            right_pts[4] = a * D[1] + b_1 * E[1] + c1 * F[1];
            right_pts[5] = a * D[2] + b_1 * E[2] + c1 * F[2];

            right_pts[6] = a1 * D[0] + b_1 * E[0] + c * F[0];
            right_pts[7] = a1 * D[1] + b_1 * E[1] + c * F[1];
            right_pts[8] = a1 * D[2] + b_1 * E[2] + c * F[2];

            for( int l=0; l<nLevels; ++l )
            {
              for( int ii=0; ii<9; ++ii )
                rf_pts[ii] = left_pts[ii] + (double) l *
                  (right_pts[ii] - left_pts[ii]) / (double) nLevels;

              rf_pts += ncoords/2;

              for( int ii=0; ii<9; ++ii )
                rf_pts[ii] = left_pts[ii] + (double) (l+1) *
                  (right_pts[ii] - left_pts[ii]) / (double) nLevels;

              rf_pts += ncoords/2;
            }

            if( k )
            {
              float c_1 = (float) (k-1) / (float) nLevels;

              left_pts[0] = a * A[0] + b * B[0] + c * C[0];
              left_pts[1] = a * A[1] + b * B[1] + c * C[1];
              left_pts[2] = a * A[2] + b * B[2] + c * C[2];
              
              left_pts[3] = a1 * A[0] + b_1 * B[0] + c * C[0];
              left_pts[4] = a1 * A[1] + b_1 * B[1] + c * C[1];
              left_pts[5] = a1 * A[2] + b_1 * B[2] + c * C[2];
              
              left_pts[6] = a1 * A[0] + b * B[0] + c_1 * C[0];
              left_pts[7] = a1 * A[1] + b * B[1] + c_1 * C[1];
              left_pts[8] = a1 * A[2] + b * B[2] + c_1 * C[2];
              
              right_pts[0] = a * D[0] + b * E[0] + c * F[0];
              right_pts[1] = a * D[1] + b * E[1] + c * F[1];
              right_pts[2] = a * D[2] + b * E[2] + c * F[2];
              
              right_pts[3] = a1 * D[0] + b_1 * E[0] + c * F[0];
              right_pts[4] = a1 * D[1] + b_1 * E[1] + c * F[1];
              right_pts[5] = a1 * D[2] + b_1 * E[2] + c * F[2];
              
              right_pts[6] = a1 * D[0] + b * E[0] + c_1 * F[0];
              right_pts[7] = a1 * D[1] + b * E[1] + c_1 * F[1];
              right_pts[8] = a1 * D[2] + b * E[2] + c_1 * F[2];

              for( int l=0; l<nLevels; ++l )
              {
                for( int ii=0; ii<9; ++ii )
                  rf_pts[ii] = left_pts[ii] + (double) l *
                    (right_pts[ii] - left_pts[ii]) / (double) nLevels;
                
                rf_pts += ncoords/2;
                
                for( int ii=0; ii<9; ++ii )
                  rf_pts[ii] = left_pts[ii] + (double) (l+1) *
                    (right_pts[ii] - left_pts[ii]) / (double) nLevels;
                
                rf_pts += ncoords/2;
              }
            }
          }
        }

        pts += ncoords;
      }
    }

    // Delete the old points.
    vtkPts->Delete();

    // Update the pointers to the new mesh
    vtkPts = rf_vtkPts;
  }

  return vtkPts;  
}



// ****************************************************************************
//  Method: avtM3DC1FileFormat::GetMesh
//
//  Purpose:
//      Gets the mesh associated with this file.  The mesh is returned as a
//      derived type of vtkDataSet (ie vtkRectilinearGrid, vtkStructuredGrid,
//      vtkUnstructuredGrid, etc).
//
//  Arguments:
//      timestate   The index of the timestate.  If GetNTimesteps returned
//                  'N' time steps, this is guaranteed to be between 0 and N-1.
//      meshname    The name of the mesh of interest.  This can be ignored if
//                  there is only one mesh.
//
//  Programmer: allen -- generated by xml2avt
//  Creation:   Fri Dec 4 15:04:15 PST 2009
//
// ****************************************************************************

vtkDataSet *
avtM3DC1FileFormat::GetMesh(int timestate, const char *meshname)
{
  const char *meshnamePtr = meshname;  
  char meshStr[64];

  bool haveDataSelections;
  int mins[3], maxs[3], strides[3];
  
  // Parse the mesh variable name to get the true mesh name and the
  // refiement level in the meshnamePtr.
  if( strncmp(meshnamePtr, "equilibrium/mesh", 16 ) == 0 )
  {
    meshnamePtr = &(meshnamePtr[16]);
    sprintf( meshStr, "equilibrium/mesh" );    
  }
  else if( strncmp(meshnamePtr, "mesh", 4 ) == 0 ) {
    meshnamePtr = &(meshnamePtr[4]);
    sprintf( meshStr, "mesh" );    
  }
  else
    EXCEPTION2( NonCompliantException, "M3DC1 Mesh Name",
                "Can not find '" + std::string(meshnamePtr) + "'" );

  bool refinedMesh = strlen(meshnamePtr);
  
  // Parse the mesh variable name to get the refinement level.
  int refinement;

  if( refinedMesh && atoi(&(meshnamePtr[1])) == m_refinement )
      refinement = m_refinement;
  else
    refinement = 0;

  // If hidden only one poloidal plane as the mesh will be used for
  // interpolation. There should also be no refinement.
  if( strncmp(meshname, "hidden/", 7 ) == 0 )
  {
    meshnamePtr = &(meshname[7]);

    mins[0] = 0;
    maxs[0] = nelms-1;
    strides[0] = 1;
      
    haveReadWholeData = true;
  }
  else
  {
    meshnamePtr = meshname;

    // Adjust for the data selections which are NODAL.
    if( (haveDataSelections = ProcessDataSelections(mins, maxs, strides) ))  /// TODO: check on assignment
    {
      if( refinement == 0 )
      {
        EXCEPTION2( NonCompliantException, "M3DC1 Mesh Name",
                    "Can not subselect a refined mesh '" +
                    std::string(meshnamePtr) +
                    std::string("'. Set the mesh refinement to 1.") );
      }

      debug5
        << "Have a logical cell selection for mesh  " << meshname << "  "
        << "(" << mins[0] << "," << maxs[0] << " stride " << strides[0] << ") "
        //      << "(" << mins[1] << "," << maxs[1] << " stride " << strides[1] << ") "
        //      << "(" << mins[2] << "," << maxs[2] << " stride " << strides[2] << ") "
        << std::endl;
      
      haveReadWholeData = false;
    }
    else
    {
      mins[0] = 0;

      if ( refinement == 0 )
        maxs[0] = nelms-1;
      else if( element_dimension == 2 )
        maxs[0] = nelms*pow((double) refinement+1.0, 2.0)-1;
      else if( element_dimension == 3 )
        maxs[0] = nelms*pow((double) refinement+1.0, 3.0)-1;

      strides[0] = 1;
      
      haveReadWholeData = true;
    }
  }
  
  // Get the C1 elements.
  float* elements = GetElements(timestate, meshStr);

  // Create a VTK grid for the mesh.
  vtkUnstructuredGrid *grid = vtkUnstructuredGrid::New();

  // Now get the points on the mesh based on the refinement level.
  vtkPoints *vtkPts = GetMeshPoints( elements, refinement );

  // Add the points to the VTK grid.
  //int npts = vtkPts->GetNumberOfPoints();
  grid->SetPoints( vtkPts );

  delete [] elements;

  // Add in the VTK cells. The connectivity is the same for all
  // triangles because each triangle is defined by three points that
  // are not unique. 

  grid->Allocate((maxs[0]-mins[0])/strides[0]+1);

  if( element_dimension == 2 )
  {
    vtkTriangle *tri = vtkTriangle::New();

    for( int i=mins[0]; i<=maxs[0]; i+=strides[0] )
    {
      unsigned int index = i * nvertices;
      tri->GetPointIds()->SetId( 0, index   );
      tri->GetPointIds()->SetId( 1, index+1 );
      tri->GetPointIds()->SetId( 2, index+2 );
      
      grid->InsertNextCell( tri->GetCellType(), tri->GetPointIds() );
    }
    
    tri->Delete();
  }
  else //if( element_dimension == 3 )
  {
    vtkWedge *wedge = vtkWedge::New();

//    mins[0] = maxs[0] = 187219;

    for( int i=mins[0]; i<=maxs[0]; i+=strides[0] )
    {
      unsigned int index = i * nvertices;
      wedge->GetPointIds()->SetId( 0, index   );
      wedge->GetPointIds()->SetId( 1, index+1 );
      wedge->GetPointIds()->SetId( 2, index+2 );
      wedge->GetPointIds()->SetId( 3, index+3 );
      wedge->GetPointIds()->SetId( 4, index+4 );
      wedge->GetPointIds()->SetId( 5, index+5 );

      grid->InsertNextCell( wedge->GetCellType(), wedge->GetPointIds() );
    }
    
    wedge->Delete();
  }


  // Retrieving the base mesh so add in the scalar global values
  // needed for the interpolation of the magnetic field.
  if( !refinedMesh )
  {
    // Create a new vtkFieldData which gets added to the vtkDataObject.
    vtkFieldData *fieldData = vtkFieldData::New();
    grid->SetFieldData(fieldData);

    // Save the number of elements to create a field with a single tuple.
    int tmp_nelms = nelms;
    nelms = 1;  // Used in GetHeaderVar

    vtkDataArray *eqsubtractVar = GetHeaderVar( timestate, "eqsubtract");
    fieldData->AddArray( eqsubtractVar );

    if( element_dimension == 2 )
    {
      vtkDataArray *linearVar = GetHeaderVar( timestate, "linear");
      fieldData->AddArray( linearVar );

      vtkDataArray *ntorVar = GetHeaderVar( timestate, "ntor");
      fieldData->AddArray( ntorVar );

      vtkDataArray *bzeroVar = GetHeaderVar( timestate, "bzero");
      fieldData->AddArray( bzeroVar );

      vtkDataArray *rzeroVar = GetHeaderVar( timestate, "rzero");
      fieldData->AddArray( rzeroVar );
    }
    else // if( element_dimension == 3 )
    {
      vtkDataArray *nplanesVar = GetHeaderVar( timestate, "nplanes");
      fieldData->AddArray( nplanesVar );
    }

    nelms = tmp_nelms;
  }

  return grid;
}


// ****************************************************************************
//  Method: avtM3DC1FileFormat::GetHeaderVar
//
//  Purpose:
//      Gets a scalar variable associated with this file.  Although VTK has
//      support for many different types, the best bet is vtkFloatArray, since
//      that is supported everywhere through VisIt.
//
//  Arguments:
//      timestate  The index of the timestate.  If GetNTimesteps returned
//                 'N' time steps, this is guaranteed to be between 0 and N-1.
//      varname    The name of the variable requested.
//
//  Programmer: allen -- generated by xml2avt
//  Creation:   Fri Dec 4 15:04:15 PST 2009
//
// ****************************************************************************

vtkDataArray *
avtM3DC1FileFormat::GetHeaderVar(int timestate, const char *varname)
{
  // Get the header variable
  vtkDataArray * dataArray = NULL; ///TODO: check on fix for uninitialized warning

  // Header variables are at the top level group.
  hid_t rootID = H5Gopen( m_fileID, "/", H5P_DEFAULT);
  if ( rootID < 0 )
    EXCEPTION2( NonCompliantException, "M3DC1 Group Open",
                "The root group '/' was not found" );

  // Everything is converted to floats by visit so might as well do it
  // here and save the copying time and memory.
  std::string variable(varname);

  // Read in 3D flag and nplanes an an int
  if( variable == "nplanes" )
  {
    int intVal;
    // 2D or 3D elements?
    if ( ReadAttribute( rootID, "3d", &intVal ) && intVal == 1 )
    {
      // Read in nplanes
      if ( ! ReadAttribute( rootID, "nplanes", &intVal ) )
      {
        EXCEPTION1( InvalidVariableException, "M3DC1 Attribute Reader - 'nplanes' was not found or was the wrong type." );

        intVal = 0;
      }
    }  
    else
    {
      intVal = 1;
    }

    vtkIntArray *var = vtkIntArray::New();

    dataArray = var;

    // Set the number of components before setting the number of tuples
    // for proper memory allocation.
    var->SetNumberOfComponents( 1 );
    var->SetNumberOfTuples( nelms );
    
    int* varPtr = (int *) var->GetVoidPointer(0);
    
    for( int i=0; i<nelms; ++i)
      *varPtr++ = intVal;
  }

  // Read in eqsubtract, linear flag and ntor as an int
  else if( variable == "eqsubtract" ||
           variable == "linear" ||
           variable == "ntor"   )
  {
      int intVal;
      if ( ! ReadAttribute( rootID, variable.c_str(), &intVal ) )
      {
        EXCEPTION2( NonCompliantException, "M3DC1 Attribute Reader",
                    "Attribute '" + variable + "' was not found or was the wrong type." );

        intVal = 0;
      }

      vtkIntArray *var = vtkIntArray::New();
      dataArray = var;

      // Set the number of components before setting the number of tuples
      // for proper memory allocation.
      var->SetNumberOfComponents( 1 );
      var->SetNumberOfTuples( nelms );
    
      int* varPtr = (int *) var->GetVoidPointer(0);
    
      for( int i=0; i<nelms; ++i)
        *varPtr++ = intVal;
  }
    
  // Read in bzero and rzero as a double
  else if( variable == "bzero" || variable == "rzero" )
  {
      double dblVal;
      if ( ! ReadAttribute( rootID, variable.c_str(), &dblVal ) )
      {
        EXCEPTION2( NonCompliantException, "M3DC1 Attribute Reader",
                    "Attribute '" + variable + "' was not found or was the wrong type." );

        dblVal = 0;
      }

      vtkDoubleArray *var = vtkDoubleArray::New();
      dataArray = var;

      // Set the number of components before setting the number of tuples
      // for proper memory allocation.
      var->SetNumberOfComponents( 1 );
      var->SetNumberOfTuples( nelms );
    
      double* varPtr = (double *) var->GetVoidPointer(0);
    
      for( int i=0; i<nelms; ++i)
        *varPtr++ = dblVal;
  }

  H5Gclose( rootID );

  dataArray->SetName(varname);

  return dataArray;
}


// ****************************************************************************
//  Method: avtM3DC1FileFormat::GetFieldVar
//
//  Purpose:
//      Gets a field variable associated with this file.  Although VTK has
//      support for many different types, the best bet is vtkFloatArray, since
//      that is supported everywhere through VisIt.
//
//  Arguments:
//      timestate  The index of the timestate.  If GetNTimesteps returned
//                 'N' time steps, this is guaranteed to be between 0 and N-1.
//      varname    The name of the variable requested.
//
//  Programmer: allen -- generated by xml2avt
//  Creation:   Fri Dec 4 15:04:15 PST 2009
//
// ****************************************************************************

vtkDataArray *
avtM3DC1FileFormat::GetFieldVar(int timestate, const char *varname)
{
  char groupStr[64];
  char varStr[64];

  int nComponents;

  // Parse the field variable name to get the HDF5 group and dataset
  // name.
  if( strncmp(varname, "equilibrium", 11 ) == 0 )
  {
    if( strcmp(&(varname[11]), "elements" ) == 0 ) {
      nComponents = element_size;;
      sprintf( groupStr, "/equilibrium/mesh" );
    } else {
      nComponents = scalar_size;;
      sprintf( groupStr, "/equilibrium/fields" );
    }

    strcpy( varStr, &(varname[12]) );

  } else {
    if( strcmp(varname, "elements" ) == 0 ) {
      nComponents = element_size;;
      sprintf( groupStr, "/time_%03d/mesh", timestate );
    } else {
      nComponents = scalar_size;;
      sprintf( groupStr, "/time_%03d/fields", timestate );
    }

    strcpy( varStr, varname );
  }

  // Open the group.
  hid_t groupId = H5Gopen( m_fileID, groupStr, H5P_DEFAULT);
  if ( groupId < 0 )
    EXCEPTION2( NonCompliantException, "M3DC1 Group Open",
                "Group '" + std::string(groupStr) + "' was not found" );

  // Open the field dataset
  hid_t datasetId = H5Dopen(groupId, varStr, H5P_DEFAULT);
  if ( datasetId < 0 )
    EXCEPTION2( NonCompliantException, "M3DC1 Dataset Open",
                "Dataset '" + std::string(varStr) + "' was not found" );
  
  // Read in the dataset information.
  hid_t spaceId = H5Dget_space(datasetId);
  size_t rank = H5Sget_simple_extent_ndims(spaceId);
  std::vector<hsize_t> sdim(rank);
  H5Sget_simple_extent_dims(spaceId, &sdim[0], NULL);
  
  if( rank != 2 || (size_t)sdim[0] != nelms || (size_t)sdim[1] != nComponents )
    EXCEPTION2( NonCompliantException, "M3DC1 Element Check",
                "Dataset '" +
                std::string(groupStr) + std::string("/") + std::string(varStr) +
                "' the number of elements or the component size does not match" );

  hid_t type = H5Dget_type(datasetId);

  vtkDataArray *vtkVar;
  hid_t nativeType;

  // Create the VTK structure to hold the field variable.
  if( H5Tget_class (type) == H5T_FLOAT )
  {
    //int size = H5Tget_size (type);

    // For now type cast everything to floats as that is the storage
    // and what is assumed down stream.
    vtkFloatArray *varFloat = vtkFloatArray::New();
    //varFloat->SetNumberOfValues( sdim[0]*sdim[1] );
    vtkVar = varFloat;

    nativeType = H5T_NATIVE_FLOAT;

    // if( size == 4 )
    // {
    //   vtkFloatArray *varFloat = vtkFloatArray::New();
    //   varFloat->SetNumberOfValues( sdim[0]*sdim[1] );
    //   vtkVar = varFloat;

    //   nativeType = H5T_NATIVE_FLOAT;

    // }
    // else if( size == 8 )
    // {
    //   vtkDoubleArray *varDouble = vtkDoubleArray::New();
    //   varDouble->SetNumberOfValues( sdim[0]*sdim[1] );
    //   vtkVar = varDouble;
      
    //   nativeType = H5T_NATIVE_DOUBLE;
    // }
  }
  else
  {
    EXCEPTION2( NonCompliantException, "M3DC1 Element Check",
                "Dataset '" +
                std::string(groupStr) + std::string("/") + std::string(varStr) +
                "' is not of native float or double type" );
  }

  // Normally an array would be created but instead use the VTK memory
  // directly - this usage works because the vtk and hdf5 memory
  // layout are the same.

  // void *vals;
  // if( nativeType == H5T_NATIVE_FLOAT )
  //   vals = (void *) new float[sdim[0]*sdim[1]];
  // else if( nativeType == H5T_NATIVE_DOUBLE )
  //   vals = (void *) new double[sdim[0]*sdim[1]];

  // if( H5Dread( datasetId,
  //           nativeType, H5S_ALL, spaceId, H5P_DEFAULT, vals ) < 0 )
  //   EXCEPTION2( NonCompliantException, "M3DC1 Dataset Read",
  //            "Dataset '" + std::string(groupStr) + std::string("/") + std::string(varStr) +
  //            "' can not be read" );
  //  ((vtkFloatArray*)vtkVar)->SetArray( ((float*)vals), sdim[0]*sdim[1], 0 ); //, operator delete[]( void* ) );
  //delete [] vals;
  

  // Set the number of components before setting the number of tuples
  // for proper memory allocation.
  vtkVar->SetNumberOfComponents( sdim[1] );
  vtkVar->SetNumberOfTuples( sdim[0] );

  // Pointer to the vtk memory.
  void* values = (void*) vtkVar->GetVoidPointer(0);
  
  // Read the data directly into the vtk memory - this call assume
  // that the hdfd5 and vtk memory layout are the same.
  if( H5Dread( datasetId,
               nativeType, H5S_ALL, spaceId, H5P_DEFAULT, values ) < 0 )
    EXCEPTION2( NonCompliantException, "M3DC1 Dataset Read",
                "Dataset '" + std::string(groupStr) + std::string("/") + std::string(varStr) +
                "' can not be read" );

  H5Sclose(spaceId);
  H5Dclose(datasetId);
  H5Gclose( groupId );
  
  return vtkVar;
}


// ****************************************************************************
//  Method: avtM3DC1FileFormat::GetVar
//
//  Purpose:
//      Gets a scalar variable associated with this file.  Although VTK has
//      support for many different types, the best bet is vtkFloatArray, since
//      that is supported everywhere through VisIt.
//
//  Arguments:
//      timestate  The index of the timestate.  If GetNTimesteps returned
//                 'N' time steps, this is guaranteed to be between 0 and N-1.
//      varname    The name of the variable requested.
//
//  Programmer: allen -- generated by xml2avt
//  Creation:   Fri Dec 4 15:04:15 PST 2009
//
// ****************************************************************************

vtkDataArray *
avtM3DC1FileFormat::GetVar(int timestate, const char *varname)
{
  // Hidden scalar variables are read from the header and put on the
  // C1 mesh.
  if( strncmp(varname, "hidden/", 7) == 0 )
  {
    char *varStr = (char *) malloc( strlen(varname) );
    strcpy( varStr, &(varname[7]) );

    if( strncmp(varStr, "header/", 7) == 0 )
    {
      strcpy( varStr, &(varname[14]) );

      return GetHeaderVar( timestate, varStr);
    }
    else
      return 0;
  }

  // First get the elements for this variable so that the variable can
  // be interpolated onto the linear mesh.
  float* elements;
  if( strncmp(varname, "equilibrium", 11 ) == 0 )
    elements = GetElements(timestate, "equilibrium/mesh");
  else
    elements = GetElements(timestate, "mesh");

  // Create a temporary mesh for getting the correct field variable
  // values on the linear mesh.
  vtkPoints *vtkPts = GetMeshPoints( elements, m_refinement );
  float* pts = (float *) vtkPts->GetVoidPointer(0);
  int npts = vtkPts->GetNumberOfPoints();

  unsigned int ptsPerElement = npts / nelms;

  // Get the M3D C1 field so the variables can be interpolated on the
  // linear mesh.
  avtM3DC1Field m3dField(elements, nelms, element_dimension, nplanes);

  // Get the field variable to be interpolated on the linear mesh.
  vtkDataArray* vtkVar = GetFieldVar( timestate, varname );
  float* values = (float*) vtkVar->GetVoidPointer(0);

  // Get the value at the node of each element on the linear mesh.
  int nvalues = 0; ///TODO: chec on fix for uninitialized values

  if( m_dataLocation == AVT_NODECENT)
    nvalues = npts;
  // Get the value at the center of each element on the linear mesh.
  else if( m_dataLocation == AVT_ZONECENT)
    nvalues = npts / nvertices;
    
  // VTK structure for the field variable on the linear mesh.
  vtkFloatArray *var = vtkFloatArray::New();

  // Set the number of components before setting the number of tuples
  // for proper memory allocation.
  var->SetNumberOfComponents( 1 );
  var->SetNumberOfTuples( nvalues );

  // Pointer to the field variable on the linear mesh.
  float* varPtr = (float *) var->GetVoidPointer(0);

  int element = 0; ///TODO: check on fix for uninitialized values
  double xieta[3];

  double pt[3], centroid[3];

  if( m_dataLocation == AVT_NODECENT)
  {
    for( int i=0; i<npts; ++i )
    {
      // Find the element containing the point; get local coords
      // xi,eta. We only want the index so that we know which element
      // the nodes are part of. Do this once so that it is
      // faster. Also it ensures that the correct element is found
      // because the points lie in the element.
      // 
      if( i % nvertices == 0 )
        element = i / ptsPerElement;

      pt[0] = pts[0];  pt[1] = pts[1];   pt[2] = pts[2];

      /* Find the element containing the point; get local coords xi,eta */
      if ((element = m3dField.get_tri_coords2D(pt, element, xieta)) >= 0)
      {
        *varPtr++ = m3dField.interp(values, element, xieta);

//      cout << i << "  " << element << "  "
//           << pt[0] << "  " << pt[1] << "  " << pt[2] << "  "
//           <<  m3dField.interp(values, element, xieta) << "  "
//           <<  m3dField.interpdR(values, element, xieta) << "  "
//           <<  m3dField.interpdz(values, element, xieta) << "  "
//           <<  m3dField.interpdR2(values, element, xieta) << "  "
//           <<  m3dField.interpdz2(values, element, xieta) << "  "
//           <<  m3dField.interpdRdz(values, element, xieta) << endl;
      }
      else
      {
        char buf[1024];

        sprintf( buf, "avtM3DC1FileFormat::GetVar - Get Triangle Coords 2d can not find element for point %10.6f %10.6f %10.6f", 
                 pt[0], pt[1], pt[2] );

        avtCallback::IssueWarning( buf );

        *varPtr++ = 0;
      }

      pts += 3;
    }
  }
  
  else if( m_dataLocation == AVT_ZONECENT)
  {
    for( int i=0; i<npts; i+=nvertices )
    {
      if( element_dimension == 2 )
      {
        centroid[0] = (pts[0] + pts[3] + pts[6] ) / nvertices;
        centroid[1] = (pts[1] + pts[4] + pts[7] ) / nvertices;
        centroid[2] = (pts[2] + pts[5] + pts[8] ) / nvertices;
      }
      else //if( element_dimension == 3 )
      {
        centroid[0] = (pts[0] + pts[3] + pts[6] +
                       pts[9] + pts[12] + pts[15] ) / nvertices;
        centroid[1] = (pts[1] + pts[4] + pts[7] +
                       pts[10] + pts[13] + pts[16] ) / nvertices;
        centroid[2] = (pts[2] + pts[5] + pts[8] +
                       pts[11] + pts[14] + pts[17] ) / nvertices;
      }

      element = i / ptsPerElement;

      /* Find the element containing the point; get local coords xi,eta */
      if ((element = m3dField.get_tri_coords2D(centroid, element, xieta)) >= 0)
      {
        *varPtr++ = m3dField.interp(values, element, xieta);
      }
      else
      {
        char buf[1024];

        sprintf( buf, "avtM3DC1FileFormat::GetVar - Get Triangle Coords 2d can not find element for centroid %10.6f %10.6f %10.6f", 
                 centroid[0], centroid[1], centroid[2] );
        
        avtCallback::IssueWarning( buf );

        *varPtr++ = 0;
      }
      
      pts += nvertices * 3;
    }
  }

  vtkPts->Delete();
  vtkVar->Delete();

  return var;
}


// ****************************************************************************
//  Method: avtM3DC1FileFormat::GetVectorVar
//
//  Purpose:
//      Gets a vector variable associated with this file.  Although VTK has
//      support for many different types, the best bet is vtkFloatArray, since
//      that is supported everywhere through VisIt.
//
//  Arguments:
//      timestate  The index of the timestate.  If GetNTimesteps returned
//                 'N' time steps, this is guaranteed to be between 0 and N-1.
//      varname    The name of the variable requested.
//
//  Programmer: allen -- generated by xml2avt
//  Creation:   Fri Dec 4 15:04:15 PST 2009
//
// ****************************************************************************

vtkDataArray *
avtM3DC1FileFormat::GetVectorVar(int timestate, const char *varname)
{
  // Hidden variables are read directly set down stream.
  if( strncmp(varname, "hidden/", 7) == 0 )
  {
    char varStr[64];

    strcpy( varStr, &(varname[7]) );
    
    return GetFieldVar( timestate, varStr );
  }
  else if( strcmp(varname, "B_C1_Elements") == 0 ||
           strcmp(varname, "B_Interpolated") == 0 )
  {
    avtCentering dataLocation = m_dataLocation;

    // When using by part the location is center based.
    if( strcmp(varname, "B_C1_Elements") == 0 )
      m_dataLocation = AVT_ZONECENT;
    
    // First get the elements for this variable so that the variable can
    // be interpolated onto the linear mesh.
    float* elements = GetElements(timestate, "mesh");

    // Create a temporary mesh for getting the correct field variable
    // values on the linear mesh.

    // For now the mesh is the same mesh as the original mesh because
    // of needing it for the integration.
    vtkPoints *vtkPts = NULL; //TODO: check on fix for uninitialized var
    if( strcmp(varname, "B_C1_Elements") == 0 )
      vtkPts = GetMeshPoints( elements, 0 );
    else if( strcmp(varname, "B_Interpolated") == 0 )
      vtkPts = GetMeshPoints( elements, m_refinement );

    float* pts = (float *) vtkPts->GetVoidPointer(0);
    int npts = vtkPts->GetNumberOfPoints();

    unsigned int ptsPerElement = npts / nelms;

    // Get the M3D C1 field so the variables can be interpolated on the
    // linear mesh.
    avtM3DC1Field m3dField(elements, nelms, element_dimension, nplanes);

    // Header variables are at the top level group.
    hid_t rootID = H5Gopen( m_fileID, "/", H5P_DEFAULT);
    if ( rootID < 0 )
      EXCEPTION2( NonCompliantException, "M3DC1 Group Open",
                  "The root group '/' was not found" );

    vtkDataArray* vtkVarF0 = NULL; ///TODO: check on uninitialized value warnings (also for all vtkVar variables below
    vtkDataArray* vtkVarPsi0 = NULL;
    vtkDataArray* vtkVarI0 = NULL;
    
    vtkDataArray* vtkVarF = NULL; 
    vtkDataArray* vtkVarF_i = NULL;
    vtkDataArray* vtkVarPsi = NULL;
    vtkDataArray* vtkVarPsi_i = NULL;
    vtkDataArray* vtkVarI = NULL;

    // Get the field variable to be interpolated on the linear mesh.
    if ( ! ReadAttribute( rootID, "bzero", &(m3dField.bzero) ) )
      EXCEPTION2( NonCompliantException, "M3DC1 Attribute Reader",
                  "Attribute 'bzero' was not found or was the wrong type." );
      
    if ( ! ReadAttribute( rootID, "rzero", &(m3dField.rzero) ) )
      EXCEPTION2( NonCompliantException, "M3DC1 Attribute Reader",
                  "Attribute 'rzero' was not found or was the wrong type." );

    m3dField.F0 = -m3dField.bzero * m3dField.rzero;

    if ( ! ReadAttribute( rootID, "eqsubtract", &(m3dField.eqsubtract) ) )
      EXCEPTION2( NonCompliantException, "M3DC1 Attribute Reader",
                  "Attribute 'eqsubtract' was not found or was the wrong type." );

    // Get the equalibrium if was subtracted out.
    if( m3dField.eqsubtract )
    {
      vtkVarF0 = GetFieldVar( timestate, "equilibrium/f");  
      m3dField.f0 = (float*) vtkVarF0->GetVoidPointer(0);
      
      vtkVarPsi0 = GetFieldVar( timestate, "equilibrium/psi");
      m3dField.psi0 = (float*) vtkVarPsi0->GetVoidPointer(0);

      if( element_dimension == 3 )
      {
        vtkVarI0 = GetFieldVar( timestate, "equilibrium/I");
        m3dField.I0 = (float*) vtkVarPsi0->GetVoidPointer(0);
      }
    }

    if( element_dimension == 2 )
    {
      if ( ! ReadAttribute( rootID, "linear", &(m3dField.linflag) ) )
        EXCEPTION2( NonCompliantException, "M3DC1 Attribute Reader",
                    "Attribute 'linear' was not found or was the wrong type." );
      
      if ( ! ReadAttribute( rootID, "ntor", &(m3dField.tmode) ) )
        EXCEPTION2( NonCompliantException, "M3DC1 Attribute Reader",
                    "Attribute 'ntor' was not found or was the wrong type." );
      
      // Variables on the mesh - N elements x 20
      if( m3dField.linflag )
      {
        vtkVarF = GetFieldVar( timestate, "f");
        m3dField.fnr = (float*) vtkVarF->GetVoidPointer(0);
        
        vtkVarPsi = GetFieldVar( timestate, "psi");
        m3dField.psinr = (float*) vtkVarPsi->GetVoidPointer(0);
        
        vtkVarF_i = GetFieldVar( timestate, "f_i");
        m3dField.fni = (float*) vtkVarF_i->GetVoidPointer(0);
        
        vtkVarPsi_i = GetFieldVar( timestate, "psi_i");
        m3dField.psini = (float*) vtkVarPsi_i->GetVoidPointer(0);
      }
    }
    else //if( element_dimension == 3 )
    {
      vtkVarF = GetFieldVar( timestate, "f");
      m3dField.f = (float*) vtkVarF->GetVoidPointer(0);
      
      vtkVarPsi = GetFieldVar( timestate, "psi");
      m3dField.psi = (float*) vtkVarPsi->GetVoidPointer(0);

      vtkVarI = GetFieldVar( timestate, "I");
      m3dField.I = (float*) vtkVarI->GetVoidPointer(0);
    }

    H5Gclose( rootID );
    
    // Get the value at the node of each element on the linear mesh.
    int nvalues = 0; ///TODO: check on fix for uninitialized variable

    if( m_dataLocation == AVT_NODECENT)
      nvalues = npts;
    // Get the value at the center of each element on the linear mesh.
    else if( m_dataLocation == AVT_ZONECENT)
      nvalues = npts / nvertices;
    
    // VTK structure for the field variable on the linear mesh.
    vtkFloatArray *var = vtkFloatArray::New();

    // Set the number of components before setting the number of tuples
    // for proper memory allocation.
    var->SetNumberOfComponents( 3 );
    var->SetNumberOfTuples( nvalues );

    // Pointer to the field variable on the linear mesh.
    float* varPtr = (float *) var->GetVoidPointer(0);

    int element = 0; ///TODO: check on uninitialized value
    float B[3];
    double pt[3], centroid[3], xieta[3];

    if( m_dataLocation == AVT_NODECENT)
    {
      for( int i=0; i<npts; ++i )
      {
        // Find the element containing the point; get local coords
        // xi,eta. We only want the index so that we know which element
        // the nodes are part of. Do this once so that it is
        // faster. Also it ensures that the correct element is found
        // because the points lie on the element.
        // 
        if( i % nvertices == 0 )
        {
          if( element_dimension == 2 )
          {
            centroid[0] = (pts[0] + pts[3] + pts[6] ) / nvertices;
            centroid[1] = (pts[1] + pts[4] + pts[7] ) / nvertices;
            centroid[2] = (pts[2] + pts[5] + pts[8] ) / nvertices;
          }
          else //if( element_dimension == 3 )
          {
            centroid[0] = (pts[0] + pts[3] + pts[6] +
                           pts[9] + pts[12] + pts[15] ) / nvertices;
            centroid[1] = (pts[1] + pts[4] + pts[7] +
                           pts[10] + pts[13] + pts[16] ) / nvertices;
            centroid[2] = (pts[2] + pts[5] + pts[8] +
                           pts[11] + pts[14] + pts[17] ) / nvertices;
          }

          element = i / ptsPerElement;
        }
        
        pt[0] = pts[0];  pt[1] = pts[1];   pt[2] = pts[2];

        /* Find the element containing the point; get local coords xi,eta */
        if ((element = m3dField.get_tri_coords2D(pt, element, xieta)) >= 0) 
        {
          m3dField.interpBcomps(B, pt, element, xieta);

          *varPtr++ = B[0]; *varPtr++ = B[1]; *varPtr++ = B[2];
        }
        else 
        {
          char buf[1024];

          sprintf( buf, "avtM3DC1FileFormat::GetVar - Get Triangle Coords 2d can not find element for point %10.6f %10.6f %10.6f", 
                   pt[0], pt[1], pt[2] );

          avtCallback::IssueWarning( buf );

          *varPtr++ = 0; *varPtr++ = 0; *varPtr++ = 0;
        }
        
        pts += 3;
      }
    }
  
    else if( m_dataLocation == AVT_ZONECENT)
    {
      for( int i=0; i<npts; i+=nvertices )
      {
        if( element_dimension == 2 )
        {
          centroid[0] = (pts[0] + pts[3] + pts[6] ) / nvertices;
          centroid[1] = (pts[1] + pts[4] + pts[7] ) / nvertices;
          centroid[2] = (pts[2] + pts[5] + pts[8] ) / nvertices;
        }
        else //if( element_dimension == 3 )
        {
          centroid[0] = (pts[0] + pts[3] + pts[6] +
                         pts[9] + pts[12] + pts[15] ) / nvertices;
          centroid[1] = (pts[1] + pts[4] + pts[7] +
                         pts[10] + pts[13] + pts[16] ) / nvertices;
          centroid[2] = (pts[2] + pts[5] + pts[8] +
                         pts[11] + pts[14] + pts[17] ) / nvertices;
        }

        element = i / ptsPerElement;

        /* Find the element containing the point; get local coords xi,eta */
        if ((element = m3dField.get_tri_coords2D(centroid, element, xieta)) >= 0) 
        {
          m3dField.interpBcomps(B, centroid, element, xieta);

          *varPtr++ = B[0];  *varPtr++ = B[1];  *varPtr++ = B[2];
        }
        else 
        {
          char buf[1024];

          sprintf( buf, "avtM3DC1FileFormat::GetVar - Get Triangle Coords 2d can not find element for centroid %10.6f %10.6f %10.6f",
                   centroid[0], centroid[1], centroid[2] );

          avtCallback::IssueWarning( buf );

          *varPtr++ = 0; *varPtr++ = 0; *varPtr++ = 0;
        }

        pts += nvertices * 3;
      }
    }

    // Set the pointers to null as the VTK delete operation will take
    // of deleting the data. Normally the M3DCIField thinks it needs
    // to delete the data.
    if( element_dimension == 2 )
    {
      m3dField.f0 = 0;
      m3dField.psi0 = 0;    

      m3dField.fnr = 0;
      m3dField.fni = 0;
      m3dField.psinr = 0;
      m3dField.psini = 0;

      if( m3dField.eqsubtract )
      {
        vtkVarF0->Delete();
        vtkVarPsi0->Delete();
      }

      if( m3dField.linflag )
      {
        vtkVarF->Delete();
        vtkVarF_i->Delete();
        vtkVarPsi->Delete();
        vtkVarPsi_i->Delete();
      }
    }
    else //if( element_dimension == 3 )
    {
      m3dField.f0   = 0;
      m3dField.psi0 = 0;    
      m3dField.I0   = 0;

      m3dField.f    = 0;
      m3dField.psi  = 0;    
      m3dField.I    = 0;

      if( m3dField.eqsubtract )
      {
        vtkVarF0->Delete();
        vtkVarPsi0->Delete();
        vtkVarI0->Delete();
      }

      vtkVarF->Delete();
      vtkVarPsi->Delete();
      vtkVarI->Delete();
    }

    vtkPts->Delete();

    // Make the original data location is used.
    m_dataLocation = dataLocation;

    return var;
  }

  else
    return 0;
}



// ****************************************************************************
//  Method: avtM3DFileFormat::NormalizeH5Type
//
//  Purpose:
//     Convert HDF5 types to visit types if necessary.
//
//  Arguments:
//      type       Input type from file.
//
//  Programmer: allen -- generated by xml2avt
//  Creation:   Tue Sep 25 08:49:28 PDT 2007
//
// ****************************************************************************
hid_t
avtM3DC1FileFormat::NormalizeH5Type( hid_t type )
{
    H5T_class_t tclass = H5Tget_class( type );
    size_t size = H5Tget_size( type );

    switch ( tclass )
    {
    case H5T_INTEGER:
        if ( size == 8 )
            return H5T_NATIVE_INT64;
        else if ( size == 4 )
            return H5T_NATIVE_INT32;
        else if ( size == 1 )
            return H5T_NATIVE_CHAR;
        break;
    case H5T_FLOAT:
        if ( size == 8 )
            return H5T_NATIVE_DOUBLE;
        else if ( size == 4 )
            return H5T_NATIVE_FLOAT;
    default:
        break;
    }
    return -1;
}

// ****************************************************************************
//  Method: avtM3DFileFormat::ReadStringAttribute
//
//  Purpose:
//      Read a string attribute from an HDF5 file.
//
//  Arguments:
//      parentID   The id of the parent.
//      attr       Name of the attribute to be read.
//      value      The attribute value that was read.
//
//  Programmer: allen
//  Creation:   Tue Sep 25 08:49:28 PDT 2007
//
// ****************************************************************************
bool
avtM3DC1FileFormat::ReadStringAttribute( hid_t parentID,
                                         const char *attr,
                                         std::string *value )
{
    hid_t attrID = H5Aopen_name( parentID, attr );
    if ( attrID <= 0 )
        return false;

    hid_t typeID = H5Aget_type( attrID );
    if ( typeID < 0 )
        return false;
    hsize_t nelem = H5Tget_size( typeID );
    if ( nelem <= 0 )
        return false;
    char *str = new char[nelem];
    H5Aread( attrID, typeID, str );
    *value = str;
    delete [] str;

    H5Tclose( typeID );
    H5Aclose( attrID );
    return true;
}

// ****************************************************************************
//  Method: avtM3DFileFormat::ReadAttribute
//
//  Purpose:
//      Read an attribute from an HDF5 file.
//
//  Arguments:
//      parentID   The id of the parent.
//      attr       Name of the attribute to be read.
//      value      The attribute value that was read.
//
//  Programmer: allen
//  Creation:   Tue Sep 25 08:49:28 PDT 2007
//
// ****************************************************************************
bool
avtM3DC1FileFormat::ReadAttribute( hid_t parentID,
                                   const char *attr,
                                   void *value )
{
    hid_t attrID = H5Aopen_name( parentID, attr );
    if ( attrID <= 0 )
        return false;

    hid_t attrType = H5Aget_type( attrID );
    if ( attrType < 0 )
        return false;

    hid_t spaceID = H5Aget_space( attrID );
    if ( spaceID < 0 )
        return false;

    hssize_t nelem = H5Sget_simple_extent_npoints( spaceID );
    if ( nelem < 0 )
        return false;

    hid_t typeID = NormalizeH5Type( attrType );
    if ( typeID < 0 )
        return false;

    if ( H5Aread ( attrID, typeID, value ) < 0 )
        return false;

    H5Sclose( spaceID );
    H5Tclose( attrType );
    H5Aclose( attrID );

    return true;
}

// ****************************************************************************
//  Method: avtM3DFileFormat::linkIterator
//
//  Purpose:
//      Iterate through all of the links in an HDF5 file.
//
//  Arguments:
//      locId   The id of the parent.
//      name    Name of the group.
//      linfo   link info
//      vopdata Optional data - in this case the avtM3DC1FileFormat.
//
//  Programmer: allen
//  Creation:   Tue Sep 25 08:49:28 PDT 2007
//
// ****************************************************************************

herr_t
avtM3DC1FileFormat::linkIterator(hid_t locId, const char* name,
                                 const H5L_info_t *linfo, void* opdata)
{
  switch (linfo->type) {
    case H5L_TYPE_HARD: {

      H5O_info_t objinfo;

      /* Stat the object */
      if(H5Oget_info_by_name(locId, name, &objinfo, H5P_DEFAULT) < 0) {
        debug5 << "visitLinks() - unable to open object with name " <<name <<std::endl;
        debug5 << "visitLinks() - this object and all children will be dropped." <<std::endl;
        return 0;
      }

      switch(objinfo.type)
      {
        case H5O_TYPE_GROUP:
        return groupIterator( locId, name, opdata );
        break;
        case H5O_TYPE_DATASET:
        return groupIterator( locId, name, opdata );
        break;

        default:
        debug5 << "visitLinks: node '" << name <<
        "' has an unknown type " << objinfo.type << std::endl;
        break;
      }
    }
    break;
    //end of case H5L_TYPE_HARD
    case H5L_TYPE_EXTERNAL: {

      char *targbuf = (char*) malloc( linfo->u.val_size );

      if (H5Lget_val(locId, name, targbuf, linfo->u.val_size, H5P_DEFAULT) < 0) {
        debug5 << "visitLinks() - unable to open external link with name " <<targbuf <<std::endl;
        debug5 << "visitLinks() - this object and all children will be dropped." <<std::endl;
        return 0;
      }
      
      const char *filename;
      const char *targname;

      if (H5Lunpack_elink_val(targbuf, linfo->u.val_size, 0, &filename, &targname) < 0) {
        debug5 << "visitLinks() - unable to open external file with name " <<filename <<std::endl;
        debug5 << "visitLinks() - this object and all children will be dropped." <<std::endl;
        return 0;
      }
      
      debug5 << "visitLinks(): node '" << name << "' is an external link." << std::endl;
      debug5 << "visitLinks(): node '" << targname << "' is an external target group." << std::endl;

      free(targbuf);
      targbuf = NULL;
      
      // Get info of the linked object.
      H5O_info_t objinfo;
      hid_t obj_id = H5Oopen(locId, name, H5P_DEFAULT);
      
      if (obj_id < 0) {
        debug5 << "visitLinks() - unable to get id for external object " <<name <<std::endl;
        debug5 << "visitLinks() - this object and all children will be dropped." <<std::endl;
        return 0;
      }

      //Test-open the linked object
      if (H5Oget_info (obj_id, &objinfo) < 0) {
        debug5 << "visitLinks() - unable to open external object " <<name <<std::endl;
        debug5 << "visitLinks() - this object and all children will be dropped." <<std::endl;
        return 0;
      }
      
      //Close the linked object to release hdf5 id
      H5Oclose( obj_id );

      //Finally, decide what to do depending on what type of object this is
      switch(objinfo.type)
      {
        case H5O_TYPE_GROUP:
        return groupIterator( locId, name, opdata );
        break;
        case H5O_TYPE_DATASET:
        return groupIterator( locId, name, opdata );
        break;

        default:
          debug5 << "visitLinks: node '" << name <<
        "' has an unknown type " << objinfo.type << std::endl;
        break;
      }
    }
    break;
      //END OF CASE H5L_TYPE_EXTERNAL
    
    default:
    debug5 << "visitLinks: node '" << name <<
    "' has an unknown object type " << linfo->type << std::endl;
    break;
  }

  return 0;
}

// ****************************************************************************
//  Method: avtM3DFileFormat::groupIterator
//
//  Purpose:
//      Iterate through all of the groups in an HDF5 file.
//
//  Arguments:
//      locId   The id of the parent.
//      name    Name of the group.
//      vopdata Optional data - in this case the avtM3DC1FileFormat.
//
//  Programmer: allen
//  Creation:   Tue Sep 25 08:49:28 PDT 2007
//
// ****************************************************************************
herr_t
avtM3DC1FileFormat::groupIterator(hid_t locId, const char* name, void* opdata) {

  avtM3DC1FileFormat* M3DC1FF = static_cast< avtM3DC1FileFormat* >(opdata);

  H5G_stat_t statbuf;
  H5Gget_objinfo (locId, name, true, &statbuf);

  switch (statbuf.type) {

    case H5G_DATASET: {
      hid_t datasetId = H5Dopen(locId, name, H5P_DEFAULT);
      hid_t spaceId = H5Dget_space(datasetId);
      size_t rank = H5Sget_simple_extent_ndims(spaceId);
      std::vector<hsize_t> sdim(rank);
      H5Sget_simple_extent_dims(spaceId, &sdim[0], NULL);

      H5Dclose(spaceId);
      H5Dclose(datasetId);

      if( rank != 2 ||
          (size_t)sdim[0] != M3DC1FF->nelms ||
          (size_t)sdim[1] != M3DC1FF->scalar_size )
      {
        EXCEPTION2( NonCompliantException, "M3DC1 Element Check",
                    "Dataset '" + std::string(name) +
                    "' is of the wrong rank or dimensions" );

        return -1;
      }
      else
      {
        M3DC1FF->m_fieldVarNames.push_back( std::string( name ) );

        return 0;
      }
    }

    default : break; ///TODO: should this handle several missing enumerations H5G_GROUP|H5G_LINK|H5G_UDLINK|H5G_RESERVED_X|etc...
  }
  return 0;
}


// ****************************************************************************
//  Method: avtM3DFileFormat::LoadFile
//
//  Purpose:
//      Open an m3d file and read in it's structure.
//
//  Arguments:
//
//  Programmer: allen -- generated by xml2avt
//  Creation:   Tue Sep 25 08:49:28 PDT 2007
//
//  Modifications:
//    Jeremy Meredith, Thu Jan  7 15:36:19 EST 2010
//    Close all open ids when returning an exception.
// ****************************************************************************
void
avtM3DC1FileFormat::LoadFile()
{
    debug1 << "Attempting to open M3D C1 file " << m_filename << std::endl;

    // Init HDF5 and turn off error message printing.
    H5open();
    H5Eset_auto( 0, NULL, NULL ); ///TODO: 1st arg should not be NULL 

    // Check for a valid M3D C1 file
    if( H5Fis_hdf5( m_filename.c_str() ) < 0 )
        EXCEPTION1( InvalidFilesException, m_filename.c_str() );

    if ( (m_fileID =
          H5Fopen( m_filename.c_str(), H5F_ACC_RDONLY, H5P_DEFAULT)) < 0)
      EXCEPTION1( InvalidFilesException, m_filename.c_str() );

    // Root group
    hid_t rootID = H5Gopen( m_fileID, "/", H5P_DEFAULT);
    if ( rootID < 0 )
    {
        H5Fclose(m_fileID);        
        EXCEPTION1( InvalidVariableException,
                    "M3DC1 Group Open - The root group '/' was not found" );
    }

    // HEADER

    // Read in step and time information.
    int ntime;
    if ( ! ReadAttribute( rootID, "ntime", &ntime ) )
    {
      if( nFiles != 0 )
      {
        ntime = nFiles;
      }
      else // if( nFiles == 0 )
      {
        H5Gclose(rootID);
        H5Fclose(m_fileID);
        EXCEPTION1( InvalidVariableException, "M3DC1 Attribute Reader - 'ntime' was not found or was the wrong type." );
      }
    }

    // 2D or 3D elements?
    if ( ReadAttribute( rootID, "3d", &element_dimension ) )
      element_dimension += 2;
    else
      element_dimension = 2;

    if( element_dimension == 2 )
    {
      nvertices = 3;

      element_size = ELEMENT_SIZE_2D;
      scalar_size = SCALAR_SIZE_2D;

      nplanes = 1;
    }  
    else //if( element_dimension == 3 )
    {
      nvertices = 6;

      element_size = ELEMENT_SIZE_3D;
      scalar_size = SCALAR_SIZE_3D;

      // Read in nplanes
      if ( ! ReadAttribute( rootID, "nplanes", &nplanes ) )
      {
        H5Gclose(rootID);
        H5Fclose(m_fileID);
        EXCEPTION1( InvalidVariableException, "M3DC1 Attribute Reader - 'nplanes' was not found or was the wrong type." );
      }
    }

    m_scalarVarNames.push_back("header/nplanes");
    // Read in eqsubtract, linear flag and ntor
    int eqsubtract;
    if ( ! ReadAttribute( rootID, "eqsubtract", &eqsubtract ) )
    {
        H5Gclose(rootID);
        H5Fclose(m_fileID);
        EXCEPTION1( InvalidVariableException, "M3DC1 Attribute Reader - 'eqsubtract' was not found or was the wrong type." );
    }
    
    m_scalarVarNames.push_back("header/eqsubtract");

    int linear;
    if ( ! ReadAttribute( rootID, "linear", &linear ) )
    {
        H5Gclose(rootID);
        H5Fclose(m_fileID);
        EXCEPTION1( InvalidVariableException, "M3DC1 Attribute Reader - 'linear' was not found or was the wrong type." );
    }
    
    m_scalarVarNames.push_back("header/linear");

    int ntor;
    if ( ! ReadAttribute( rootID, "ntor", &ntor ) )
    {
        H5Gclose(rootID);
        H5Fclose(m_fileID);
        EXCEPTION1( InvalidVariableException, "M3DC1 Attribute Reader - 'ntor' was not found or was the wrong type." );
    }
    
    m_scalarVarNames.push_back("header/ntor");

    // Read in bzero and rzero
    double bzero, rzero;
    if ( ! ReadAttribute( rootID, "bzero", &bzero ) )
    {
        H5Gclose(rootID);
        H5Fclose(m_fileID);
        EXCEPTION1( InvalidVariableException, "M3DC1 Attribute Reader - 'bzero' was not found or was the wrong type." );
    }

    m_scalarVarNames.push_back("header/bzero");

    if ( ! ReadAttribute( rootID, "rzero", &rzero ) )
    {
        H5Gclose(rootID);
        H5Fclose(m_fileID);
        EXCEPTION1( InvalidVariableException, "M3DC1 Attribute Reader - 'rzero' was not found or was the wrong type." );
    }

    m_scalarVarNames.push_back("header/rzero");

    H5Gclose( rootID );


    int nfields = -1;
    nelms = -1;
    
    // EQUILIBRIUM

    // Read in equilibrium mesh element information.
    hid_t groupId = H5Gopen( m_fileID, "/equilibrium/mesh", H5P_DEFAULT);
    if ( groupId < 0 )
    {
      H5Fclose(m_fileID);
      EXCEPTION1( InvalidVariableException, "M3DC1 Group Open - '/equilibrium/mesh' was not found" );
    }
    else
    {
      if ( ! ReadAttribute( groupId, "nelms", &nelms ) )
        EXCEPTION1( InvalidVariableException, "M3DC1 Attribute Reader - 'nelms' was not found or was the wrong type." );

      hid_t datasetId = H5Dopen(groupId, "elements", H5P_DEFAULT);
      hid_t spaceId = H5Dget_space(datasetId);
      size_t rank = H5Sget_simple_extent_ndims(spaceId);
      std::vector<hsize_t> sdim(rank);
      H5Sget_simple_extent_dims(spaceId, &sdim[0], NULL);
    
      H5Dclose(spaceId);
      H5Dclose(datasetId);
      
      if( rank != 2 ||
          (size_t)sdim[0] != nelms ||
          (size_t)sdim[1] != element_size )
      {
        EXCEPTION1( InvalidVariableException, "M3DC1 Element Check - number of elements or the element size does not match" );
      }

      H5Gclose( groupId );

      // Read in equilibrium field information.
      groupId = H5Gopen( m_fileID, "/equilibrium/fields", H5P_DEFAULT);
      if ( groupId < 0 )
      {
        EXCEPTION1( InvalidVariableException, "M3DC1 Group Open - '/equilibrium/fields' was not found" );
      }
      else
      {
        if ( ! ReadAttribute( groupId, "nfields", &nfields ) )
          EXCEPTION1( InvalidVariableException, "M3DC1 Attribute Reader - 'nfields' was not found or was the wrong type." );
        
        // Go through the field group and collect all of the field datasets
        //    H5Giterate(groupId, ".", NULL, groupIterator, this);
        H5Literate(groupId, H5_INDEX_NAME, H5_ITER_INC, 0, linkIterator, this);
        
        H5Gclose( groupId );
        
        if( (size_t)nfields != m_fieldVarNames.size() )
          EXCEPTION1( InvalidVariableException, "M3DC1 number of fields check - of fields does not match the number of datasets founds." );
      }
    }

    // TIME STEPS

    //Load basic info on variables for each time step.
    for ( int t=0; t<ntime; ++t )
    {
        char timeStep[64];
        sprintf( timeStep, "/time_%03d", t );

        hid_t groupID = H5Gopen( m_fileID, timeStep, H5P_DEFAULT);
        if ( groupID < 0 )
        {
          char buf[1024];

          sprintf( buf, "avtM3DC1FileFormat::LoadFile - M3DC1 Group Open - timeStep %s was not found", timeStep );

          avtCallback::IssueWarning( buf );

          // Really do not want either of these but there is no
          // warning at this point.
          debug1 << buf << std::endl;

          std::cerr << buf << std::endl;

          m_times.push_back( -1 );
          m_cycles.push_back( t );

          continue;

          EXCEPTION1( InvalidVariableException,
                   "M3DC1 Group Open - timeStep " + std::string(timeStep) +
                   " was not found" );
        }

        // Read the time value
        double time;
        if ( ! ReadAttribute( groupID, "time", &time ) )
          EXCEPTION1( InvalidVariableException,
                      "M3DC1 Group Open - 'time' was not found" );

        m_times.push_back( time );
        m_cycles.push_back( t );

        // Read in the mesh information.
        hid_t meshId = H5Gopen( groupID, "mesh", H5P_DEFAULT);

        int nElements;
        if ( ! ReadAttribute( meshId, "nelms", &nElements ) )
          EXCEPTION1( InvalidVariableException,
                      "M3DC1 Attribute Reader - 'nelms' was not found" );

        if( nElements != nelms )
          EXCEPTION1( InvalidVariableException,
                      "M3DC1 Element Check - Time step 'nelms' does not match equilibrium 'nelms'" );

        hid_t datasetId = H5Dopen(meshId, "elements", H5P_DEFAULT);
        hid_t spaceId = H5Dget_space(datasetId);
        size_t rank = H5Sget_simple_extent_ndims(spaceId);
        std::vector<hsize_t> sdim(rank);
        H5Sget_simple_extent_dims(spaceId, &sdim[0], NULL);
    
        H5Dclose(spaceId);
        H5Dclose(datasetId);

        if( rank != 2 ||
            (size_t)sdim[0] != nelms ||
            (size_t)sdim[1] != element_size )
        {
          EXCEPTION1( InvalidVariableException, "M3DC1 Element Check - The number of elements or the element size does not match" );
        }
        
        H5Gclose( meshId );


        // Read in the field information.
        hid_t fieldID = H5Gopen( groupID, "fields", H5P_DEFAULT);
        if ( fieldID < 0 )
          EXCEPTION1( InvalidVariableException, "M3DC1 Group Open - 'fields' was not found" );

        if ( ! ReadAttribute( fieldID, "nfields", &nfields ) )
          EXCEPTION1( InvalidVariableException, "M3DC1 Attribute Reader - 'nfields' was not found" );

        if( (size_t)nfields != m_fieldVarNames.size() )
          EXCEPTION1( InvalidVariableException, "M3DC1 Time Step Check - time step nfields does not match the equilibrium nfields" );
             
        for ( size_t i=0; i<m_fieldVarNames.size(); ++i )
        {
            hid_t datasetId =
              H5Dopen(fieldID, m_fieldVarNames[i].c_str(), H5P_DEFAULT);
            if ( datasetId < 0 )
              EXCEPTION1( InvalidVariableException, "M3DC1 Dataset Open - '" + std::string(timeStep) + std::string("/fields/") +
                          m_fieldVarNames[i] + "' was not found" );


            hid_t spaceId = H5Dget_space(datasetId);
            size_t rank = H5Sget_simple_extent_ndims(spaceId);
            std::vector<hsize_t> sdim(rank);
            H5Sget_simple_extent_dims(spaceId, &sdim[0], NULL);
            
            H5Dclose(spaceId);
            H5Dclose(datasetId);

            if( rank != 2 ||
                (size_t)sdim[0] != nelms ||
                (size_t)sdim[1] != scalar_size )
              {
                EXCEPTION1( InvalidVariableException, "M3DC1 Element Check - Dataset '" +
                            std::string(timeStep) + std::string("/fields/") + m_fieldVarNames[i] +
                            "' the number of elements or the element size does not match" );
              }
        }
            
        H5Gclose( fieldID );
    }

    debug1 << "SUCCESS in opening M3D C1 file " << m_filename << std::endl;
}


// ****************************************************************************
//  Method: avtM3DC1FileFormat::GetCycles
//
//  Purpose:
//      Returns the cycles
//
//  Arguments:
//      c          the cycles
//
//  Programmer: allen
//  Creation:   
//
// ****************************************************************************


void avtM3DC1FileFormat::GetCycles(std::vector<int> &cycles)
{
    cycles = m_cycles;
}



// ****************************************************************************
//  Method: avtM3DC1FileFormat::GetTimes
//
//  Purpose:
//      Returns the times
//
//  Arguments:
//      t          the times
//
//  Programmer: allen
//  Creation:   
//
// ****************************************************************************

void avtM3DC1FileFormat::GetTimes(std::vector<double> &times)
{
    times = m_times;
}
