/**
 *  @file    vgrid.c
 *  @author  Nathan Baker
 *  @brief   Class Vgrid methods
 *  @ingroup Vgrid
 *  @version $Id$
 *  @attention
 *  @verbatim
 *
 * APBS -- Adaptive Poisson-Boltzmann Solver
 *
 *  Nathan A. Baker (nathan.baker@pnnl.gov)
 *  Pacific Northwest National Laboratory
 *
 *  Additional contributing authors listed in the code documentation.
 *
 * Copyright (c) 2010-2020 Battelle Memorial Institute. Developed at the
 * Pacific Northwest National Laboratory, operated by Battelle Memorial
 * Institute, Pacific Northwest Division for the U.S. Department of Energy.
 *
 * Portions Copyright (c) 2002-2010, Washington University in St. Louis.
 * Portions Copyright (c) 2002-2020, Nathan A. Baker.
 * Portions Copyright (c) 1999-2002, The Regents of the University of
 * California.
 * Portions Copyright (c) 1995, Michael Holst.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 *
 * Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * Neither the name of the developer nor the names of its contributors may be
 * used to endorse or promote products derived from this software without
 * specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 * THE POSSIBILITY OF SUCH DAMAGE.
 *
 * @endverbatim
 */

#include "vgrid.h"
#include <stdio.h>

VEMBED(rcsid="$Id$")

#if !defined(VINLINE_VGRID)
    VPUBLIC unsigned long int Vgrid_memChk(Vgrid *thee) {
        return Vmem_bytes(thee->mem);
    }
#endif
#define IJK(i,j,k)  (((k)*(nx)*(ny))+((j)*(nx))+(i))

#if defined(_WIN32) && (_MSC_VER < 1800)
#include <float.h>
int isnan(double d)
{
    return _isnan(d);
}
#endif

VPRIVATE char *MCwhiteChars = " =,;\t\n";
VPRIVATE char *MCcommChars  = "#%";
VPRIVATE double Vcompare;
VPRIVATE char Vprecision[26];

/* ///////////////////////////////////////////////////////////////////////////
// Routine:  Vgrid_ctor
// Author:   Nathan Baker
/////////////////////////////////////////////////////////////////////////// */
VPUBLIC Vgrid* Vgrid_ctor(int nx,
                          int ny,
                          int nz,
                          double hx,
                          double hy,
                          double hzed,
                          double xmin,
                          double ymin,
                          double zmin,
                          double *data
                         ) {

    Vgrid *thee = VNULL;

    thee = (Vgrid*)Vmem_malloc(VNULL, 1, sizeof(Vgrid));
    VASSERT(thee != VNULL);
    VASSERT(Vgrid_ctor2(thee, nx, ny, nz, hx, hy, hzed,
                  xmin, ymin, zmin, data));

    return thee;
}

/* ///////////////////////////////////////////////////////////////////////////
// Routine:  Vgrid_ctor2
// Author:   Nathan Baker
/////////////////////////////////////////////////////////////////////////// */
VPUBLIC int Vgrid_ctor2(Vgrid *thee, int nx, int ny, int nz,
                  double hx, double hy, double hzed,
                  double xmin, double ymin, double zmin,
                  double *data) {

    if (thee == VNULL) return 0;
    thee->nx = nx;
    thee->ny = ny;
    thee->nz = nz;
    thee->hx = hx;
    thee->hy = hy;
    thee->hzed = hzed;
    thee->xmin = xmin;
    thee->xmax = xmin + (nx-1)*hx;
    thee->ymin = ymin;
    thee->ymax = ymin + (ny-1)*hy;
    thee->zmin = zmin;
    thee->zmax = zmin + (nz-1)*hzed;
    if (data == VNULL) {
        thee->ctordata = 0;
        thee->readdata = 0;
    } else {
        thee->ctordata = 1;
        thee->readdata = 0;
        thee->data = data;
    }

    thee->mem = Vmem_ctor("APBS:VGRID");

    Vcompare = pow(10,-1*(VGRID_DIGITS - 2));
    sprintf(Vprecision,"%%12.%de %%12.%de %%12.%de", VGRID_DIGITS,
            VGRID_DIGITS, VGRID_DIGITS);

    return 1;
}

/* ///////////////////////////////////////////////////////////////////////////
// Routine:  Vgrid_dtor
// Author:   Nathan Baker
/////////////////////////////////////////////////////////////////////////// */
VPUBLIC void Vgrid_dtor(Vgrid **thee) {

    if ((*thee) != VNULL) {
        Vgrid_dtor2(*thee);
        Vmem_free(VNULL, 1, sizeof(Vgrid), (void **)thee);
        (*thee) = VNULL;
    }
}

/* ///////////////////////////////////////////////////////////////////////////
// Routine:  Vgrid_dtor2
// Author:   Nathan Baker
/////////////////////////////////////////////////////////////////////////// */
VPUBLIC void Vgrid_dtor2(Vgrid *thee) {

    if (thee->readdata) {
        Vmem_free(thee->mem, (thee->nx*thee->ny*thee->nz), sizeof(double),
          (void **)&(thee->data));
    }
    Vmem_dtor(&(thee->mem));

}

/* ///////////////////////////////////////////////////////////////////////////
// Routine:  Vgrid_value
// Author:   Nathan Baker
/////////////////////////////////////////////////////////////////////////// */
VPUBLIC int Vgrid_value(Vgrid *thee, double pt[3], double *value) {

    int nx, ny, nz;
    size_t ihi, jhi, khi, ilo, jlo, klo;
    double hx, hy, hzed, xmin, ymin, zmin, ifloat, jfloat, kfloat;
    double xmax, ymax, zmax;
    double u, dx, dy, dz;

    if (thee == VNULL) {
        Vnm_print(2, "Vgrid_value:  Error -- got VNULL thee!\n");
        VASSERT(0);
    }
    if (!(thee->ctordata || thee->readdata)) {
        Vnm_print(2, "Vgrid_value:  Error -- no data available!\n");
        VASSERT(0);
    }

    nx = thee->nx;
    ny = thee->ny;
    nz = thee->nz;
    hx = thee->hx;
    hy = thee->hy;
    hzed = thee->hzed;
    xmin = thee->xmin;
    ymin = thee->ymin;
    zmin = thee->zmin;
    xmax = thee->xmax;
    ymax = thee->ymax;
    zmax = thee->zmax;

    u = 0;

    ifloat = (pt[0] - xmin)/hx;
    jfloat = (pt[1] - ymin)/hy;
    kfloat = (pt[2] - zmin)/hzed;

    ihi = (int)ceil(ifloat);
    jhi = (int)ceil(jfloat);
    khi = (int)ceil(kfloat);
    ilo = (int)floor(ifloat);
    jlo = (int)floor(jfloat);
    klo = (int)floor(kfloat);
    if (VABS(pt[0] - xmin) < Vcompare) ilo = 0;
    if (VABS(pt[1] - ymin) < Vcompare) jlo = 0;
    if (VABS(pt[2] - zmin) < Vcompare) klo = 0;
    if (VABS(pt[0] - xmax) < Vcompare) ihi = nx-1;
    if (VABS(pt[1] - ymax) < Vcompare) jhi = ny-1;
    if (VABS(pt[2] - zmax) < Vcompare) khi = nz-1;

    /* See if we're on the mesh */
    /*the condions starting with ilo>=0 seem unnecessary since they are of type size_t*/
    if ((ihi<nx) && (jhi<ny) && (khi<nz) /*&&
                                           (ilo>=0) && (jlo>=0) && (klo>=0)*/) {

      dx = ifloat - (double)(ilo);
      dy = jfloat - (double)(jlo);
      dz = kfloat - (double)(klo);
      u = dx      *dy      *dz      *(thee->data[IJK(ihi,jhi,khi)])
        + dx      *(1.0-dy)*dz      *(thee->data[IJK(ihi,jlo,khi)])
        + dx      *dy      *(1.0-dz)*(thee->data[IJK(ihi,jhi,klo)])
        + dx      *(1.0-dy)*(1.0-dz)*(thee->data[IJK(ihi,jlo,klo)])
        + (1.0-dx)*dy      *dz      *(thee->data[IJK(ilo,jhi,khi)])
        + (1.0-dx)*(1.0-dy)*dz      *(thee->data[IJK(ilo,jlo,khi)])
        + (1.0-dx)*dy      *(1.0-dz)*(thee->data[IJK(ilo,jhi,klo)])
        + (1.0-dx)*(1.0-dy)*(1.0-dz)*(thee->data[IJK(ilo,jlo,klo)]);

      *value = u;

      if (isnan(u)) {
        Vnm_print(2, "Vgrid_value:  Got NaN!\n");
        Vnm_print(2, "Vgrid_value:  (x, y, z) = (%4.3f, %4.3f, %4.3f)\n",
            pt[0], pt[1], pt[2]);
        Vnm_print(2, "Vgrid_value:  (ihi, jhi, khi) = (%d, %d, %d)\n",
            ihi, jhi, khi);
        Vnm_print(2, "Vgrid_value:  (ilo, jlo, klo) = (%d, %d, %d)\n",
            ilo, jlo, klo);
        Vnm_print(2, "Vgrid_value:  (nx, ny, nz) = (%d, %d, %d)\n",
            nx, ny, nz);
        Vnm_print(2, "Vgrid_value:  (dx, dy, dz) = (%4.3f, %4.3f, %4.3f)\n",
            dx, dy, dz);
        Vnm_print(2, "Vgrid_value:  data[IJK(ihi,jhi,khi)] = %g\n",
            thee->data[IJK(ihi,jhi,khi)]);
        Vnm_print(2, "Vgrid_value:  data[IJK(ihi,jlo,khi)] = %g\n",
            thee->data[IJK(ihi,jlo,khi)]);
        Vnm_print(2, "Vgrid_value:  data[IJK(ihi,jhi,klo)] = %g\n",
            thee->data[IJK(ihi,jhi,klo)]);
        Vnm_print(2, "Vgrid_value:  data[IJK(ihi,jlo,klo)] = %g\n",
            thee->data[IJK(ihi,jlo,klo)]);
        Vnm_print(2, "Vgrid_value:  data[IJK(ilo,jhi,khi)] = %g\n",
            thee->data[IJK(ilo,jhi,khi)]);
        Vnm_print(2, "Vgrid_value:  data[IJK(ilo,jlo,khi)] = %g\n",
            thee->data[IJK(ilo,jlo,khi)]);
        Vnm_print(2, "Vgrid_value:  data[IJK(ilo,jhi,klo)] = %g\n",
            thee->data[IJK(ilo,jhi,klo)]);
        Vnm_print(2, "Vgrid_value:  data[IJK(ilo,jlo,klo)] = %g\n",
            thee->data[IJK(ilo,jlo,klo)]);
      }
      return 1;

    } else {

      *value = 0;
      return 0;

    }

    return 0;

}

/* ///////////////////////////////////////////////////////////////////////////
// Routine:  Vgrid_curvature
//
//   Notes:  cflag=0 ==> Reduced Maximal Curvature
//           cflag=1 ==> Mean Curvature (Laplace)
//           cflag=2 ==> Gauss Curvature
//           cflag=3 ==> True Maximal Curvature
//
// Authors:  Stephen Bond and Nathan Baker
/////////////////////////////////////////////////////////////////////////// */
VPUBLIC int Vgrid_curvature(Vgrid *thee, double pt[3], int cflag,
    double *value) {

  double hx, hy, hzed, curv;
  double dxx, dyy, dzz;
  double uleft, umid, uright, testpt[3];

  if (thee == VNULL) {
    Vnm_print(2, "Vgrid_curvature:  Error -- got VNULL thee!\n");
    VASSERT(0);
  }
  if (!(thee->ctordata || thee->readdata)) {
    Vnm_print(2, "Vgrid_curvature:  Error -- no data available!\n");
    VASSERT(0);
  }

  hx = thee->hx;
  hy = thee->hy;
  hzed = thee->hzed;

  curv = 0.0;

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

  /* Compute 2nd derivative in the x-direction */
  VJMPERR1(Vgrid_value( thee, testpt, &umid));
  testpt[0] = pt[0] - hx;
  VJMPERR1(Vgrid_value( thee, testpt, &uleft));
  testpt[0] = pt[0] + hx;
  VJMPERR1(Vgrid_value( thee, testpt, &uright));
  testpt[0] = pt[0];

  dxx = (uright - 2*umid + uleft)/(hx*hx);

  /* Compute 2nd derivative in the y-direction */
  VJMPERR1(Vgrid_value( thee, testpt, &umid));
  testpt[1] = pt[1] - hy;
  VJMPERR1(Vgrid_value( thee, testpt, &uleft));
  testpt[1] = pt[1] + hy;
  VJMPERR1(Vgrid_value( thee, testpt, &uright));
  testpt[1] = pt[1];

  dyy = (uright - 2*umid + uleft)/(hy*hy);

  /* Compute 2nd derivative in the z-direction */
  VJMPERR1(Vgrid_value( thee, testpt, &umid));
  testpt[2] = pt[2] - hzed;
  VJMPERR1(Vgrid_value( thee, testpt, &uleft));
  testpt[2] = pt[2] + hzed;
  VJMPERR1(Vgrid_value( thee, testpt, &uright));

  dzz = (uright - 2*umid + uleft)/(hzed*hzed);


  if ( cflag == 0 ) {
    curv = fabs(dxx);
    curv = ( curv > fabs(dyy) ) ? curv : fabs(dyy);
    curv = ( curv > fabs(dzz) ) ? curv : fabs(dzz);
  } else if ( cflag == 1 ) {
    curv = (dxx + dyy + dzz)/3.0;
  } else {
    Vnm_print(2, "Vgrid_curvature:  support for cflag = %d not available!\n", cflag);
    VASSERT( 0 ); /* Feature Not Coded Yet! */
  }

  *value = curv;
  return 1;

VERROR1:
  return 0;

}

/* ///////////////////////////////////////////////////////////////////////////
// Routine:  Vgrid_gradient
//
// Authors:  Nathan Baker and Stephen Bond
/////////////////////////////////////////////////////////////////////////// */
VPUBLIC int Vgrid_gradient(Vgrid *thee, double pt[3], double grad[3]) {

  double hx, hy, hzed;
  double uleft, umid, uright, testpt[3];
  int haveleft, haveright;

  if (thee == VNULL) {
    Vnm_print(2, "Vgrid_gradient:  Error -- got VNULL thee!\n");
    VASSERT(0);
  }
  if (!(thee->ctordata || thee->readdata)) {
    Vnm_print(2, "Vgrid_gradient:  Error -- no data available!\n");
    VASSERT(0);
  }

  hx = thee->hx;
  hy = thee->hy;
  hzed = thee->hzed;

  /* Compute derivative in the x-direction */
  testpt[0] = pt[0];
  testpt[1] = pt[1];
  testpt[2] = pt[2];
  VJMPERR1( Vgrid_value( thee, testpt, &umid));
  testpt[0] = pt[0] - hx;
  if (Vgrid_value( thee, testpt, &uleft)) haveleft = 1;
  else haveleft = 0;
  testpt[0] = pt[0] + hx;
  if (Vgrid_value( thee, testpt, &uright)) haveright = 1;
  else haveright = 0;
  if (haveright && haveleft) grad[0] = (uright - uleft)/(2*hx);
  else if (haveright) grad[0] = (uright - umid)/hx;
  else if (haveleft) grad[0] = (umid - uleft)/hx;
  else VJMPERR1(0);

  /* Compute derivative in the y-direction */
  testpt[0] = pt[0];
  testpt[1] = pt[1];
  testpt[2] = pt[2];
  VJMPERR1(Vgrid_value(thee, testpt, &umid));
  testpt[1] = pt[1] - hy;
  if (Vgrid_value( thee, testpt, &uleft)) haveleft = 1;
  else haveleft = 0;
  testpt[1] = pt[1] + hy;
  if (Vgrid_value( thee, testpt, &uright)) haveright = 1;
  else haveright = 0;
  if (haveright && haveleft) grad[1] = (uright - uleft)/(2*hy);
  else if (haveright) grad[1] = (uright - umid)/hy;
  else if (haveleft) grad[1] = (umid - uleft)/hy;
  else VJMPERR1(0);

  /* Compute derivative in the z-direction */
  testpt[0] = pt[0];
  testpt[1] = pt[1];
  testpt[2] = pt[2];
  VJMPERR1(Vgrid_value(thee, testpt, &umid));
  testpt[2] = pt[2] - hzed;
  if (Vgrid_value( thee, testpt, &uleft)) haveleft = 1;
  else haveleft = 0;
  testpt[2] = pt[2] + hzed;
  if (Vgrid_value( thee, testpt, &uright)) haveright = 1;
  else haveright = 0;
  if (haveright && haveleft) grad[2] = (uright - uleft)/(2*hzed);
  else if (haveright) grad[2] = (uright - umid)/hzed;
  else if (haveleft) grad[2] = (umid - uleft)/hzed;
  else VJMPERR1(0);

  return 1;

VERROR1:
  return 0;

}

/* ///////////////////////////////////////////////////////////////////////////
// Routine:  Vgrid_readGZ
//
// Author:   David Gohara
/////////////////////////////////////////////////////////////////////////// */
#ifdef HAVE_ZLIB
#define off_t long
#include "zlib.h"
#endif
VPUBLIC int Vgrid_readGZ(Vgrid *thee, const char *fname) {

#ifdef HAVE_ZLIB
  size_t i, j, k, u;
  size_t len; // Temporary counter variable for loop conditionals
  size_t header, incr;
  double *temp;
  double dtmp1, dtmp2, dtmp3;
  gzFile infile;
  char line[VMAX_ARGLEN];

  header = 0;

  /* Check to see if the existing data is null and, if not, clear it out */
  if (thee->data != VNULL) {
    Vnm_print(1, "%s:  destroying existing data!\n", __func__);
    Vmem_free(thee->mem, thee->nx * thee->ny * thee->nz, sizeof(double),
        (void **)&(thee->data));
  }

  thee->readdata = 1;
  thee->ctordata = 0;

  infile = gzopen(fname, "rb");
  if (infile == Z_NULL) {
    Vnm_print(2, "%s:  Problem opening compressed file %s\n", __func__, fname);
    return VRC_FAILURE;
  }

  thee->hx = 0.0;
  thee->hy = 0.0;
  thee->hzed = 0.0;

  //read data here
  while (header < 7) {
    if(gzgets(infile, line, VMAX_ARGLEN) == Z_NULL){
      return VRC_FAILURE;
    }

    // Skip comments and newlines
    if(strncmp(line, "#", 1) == 0) continue;
    if(line[0] == '\n') continue;

    switch (header) {
      case 0:
        sscanf(line, "object 1 class gridpositions counts %d %d %d",
            &(thee->nx),&(thee->ny),&(thee->nz));
        break;
      case 1:
        sscanf(line, "origin %lf %lf %lf",
            &(thee->xmin),&(thee->ymin),&(thee->zmin));
        break;
      case 2:
      case 3:
      case 4:
        sscanf(line, "delta %lf %lf %lf",&dtmp1,&dtmp2,&dtmp3);
        thee->hx += dtmp1;
        thee->hy += dtmp2;
        thee->hzed += dtmp3;
        break;
      default:
        break;
    }

    header++;
  }

  /* Allocate space for the data */
  Vnm_print(0, "%s:  allocating %d x %d x %d doubles for storage\n",
      __func__, thee->nx, thee->ny, thee->nz);
  len = thee->nx * thee->ny * thee->nz;

  thee->data = VNULL;
  thee->data = Vmem_malloc(thee->mem, len, sizeof(double));
  if (thee->data == VNULL) {
    Vnm_print(2, "%s:  Unable to allocate space for data!\n", __func__);
    return 0;
  }

  /* Allocate a temporary buffer to store the compressed
   * data into (column major order). Add 2 to ensure the buffer is
   * big enough to take extra data on the final read loop.
   */
  temp = (double *)malloc(len * (2 * sizeof(double)));

  for (i = 0; i < len; i += 3){
    memset(&line, 0, sizeof(line));
    gzgets(infile, line, VMAX_ARGLEN);
    sscanf(line, "%lf %lf %lf", &temp[i], &temp[i+1], &temp[i+2]);
  }

  /* Now move the data to row major order */
  incr = 0;
  for (i=0; i<thee->nx; i++) {
    for (j=0; j<thee->ny; j++) {
      for (k=0; k<thee->nz; k++) {
        u = k*(thee->nx)*(thee->ny)+j*(thee->nx)+i;
        (thee->data)[u] = temp[incr++];
      }
    }
  }

  /* calculate grid maxima */
  thee->xmax = thee->xmin + (thee->nx-1)*thee->hx;
  thee->ymax = thee->ymin + (thee->ny-1)*thee->hy;
  thee->zmax = thee->zmin + (thee->nz-1)*thee->hzed;

  /* Close off the socket */
  gzclose(infile);
  free(temp);
#else

  Vnm_print(0, "WARNING\n");
  Vnm_print(0, "Vgrid_readGZ:  gzip read/write support is disabled in this build\n");
  Vnm_print(0, "Vgrid_readGZ:  configure and compile without the --disable-zlib flag.\n");
  Vnm_print(0, "WARNING\n");
#endif
  return VRC_SUCCESS;
}

/**
 * Load grid from an input file using sockets.
 * @author Nathan Baker
 */
VPUBLIC int Vgrid_readDX(Vgrid *thee,
    const char *iodev,
    const char *iofmt,
    const char *thost,
    const char *fname
    ) {

  size_t i, j, k, itmp, u;
  double dtmp;
  char tok[VMAX_BUFSIZE];
  Vio *sock;

  /* Check to see if the existing data is null and, if not, clear it out */
  if (thee->data != VNULL) {
    Vnm_print(1, "Vgrid_readDX:  destroying existing data!\n");
    Vmem_free(thee->mem, (thee->nx*thee->ny*thee->nz), sizeof(double),
        (void **)&(thee->data)); }
    thee->readdata = 1;
    thee->ctordata = 0;

    /* Set up the virtual socket */
    sock = Vio_ctor(iodev,iofmt,thost,fname,"r");
    if (sock == VNULL) {
      Vnm_print(2, "Vgrid_readDX: Problem opening virtual socket %s\n",
          fname);
      return 0;
    }
    if (Vio_accept(sock, 0) < 0) {
      Vnm_print(2, "Vgrid_readDX: Problem accepting virtual socket %s\n",
          fname);
      return 0;
    }

    Vio_setWhiteChars(sock, MCwhiteChars);
    Vio_setCommChars(sock, MCcommChars);

    /* Read in the DX regular positions */
    /* Get "object" */
    VJMPERR2(1 == Vio_scanf(sock, "%s", tok));
    VJMPERR1(!strcmp(tok, "object"));
    /* Get "1" */
    VJMPERR2(1 == Vio_scanf(sock, "%d", &itmp));
    /* Get "class" */
    VJMPERR2(1 == Vio_scanf(sock, "%s", tok));
    VJMPERR1(!strcmp(tok, "class"));
    /* Get "gridpositions" */
    VJMPERR2(1 == Vio_scanf(sock, "%s", tok));
    VJMPERR1(!strcmp(tok, "gridpositions"));
    /* Get "counts" */
    VJMPERR2(1 == Vio_scanf(sock, "%s", tok));
    VJMPERR1(!strcmp(tok, "counts"));
    /* Get nx */
    VJMPERR2(1 == Vio_scanf(sock, "%s", tok));
    VJMPERR1(1 == sscanf(tok, "%d", &(thee->nx)));
    /* Get ny */
    VJMPERR2(1 == Vio_scanf(sock, "%s", tok));
    VJMPERR1(1 == sscanf(tok, "%d", &(thee->ny)));
    /* Get nz */
    VJMPERR2(1 == Vio_scanf(sock, "%s", tok));
    VJMPERR1(1 == sscanf(tok, "%d", &(thee->nz)));
    Vnm_print(0, "Vgrid_readDX:  Grid dimensions %d x %d x %d grid\n",
        thee->nx, thee->ny, thee->nz);
    /* Get "origin" */
    VJMPERR2(1 == Vio_scanf(sock, "%s", tok));
    VJMPERR1(!strcmp(tok, "origin"));
    /* Get xmin */
    VJMPERR2(1 == Vio_scanf(sock, "%s", tok));
    VJMPERR1(1 == sscanf(tok, "%lf", &(thee->xmin)));
    /* Get ymin */
    VJMPERR2(1 == Vio_scanf(sock, "%s", tok));
    VJMPERR1(1 == sscanf(tok, "%lf", &(thee->ymin)));
    /* Get zmin */
    VJMPERR2(1 == Vio_scanf(sock, "%s", tok));
    VJMPERR1(1 == sscanf(tok, "%lf", &(thee->zmin)));
    Vnm_print(0, "Vgrid_readDX:  Grid origin = (%g, %g, %g)\n",
        thee->xmin, thee->ymin, thee->zmin);
    /* Get "delta" */
    VJMPERR2(1 == Vio_scanf(sock, "%s", tok));
    VJMPERR1(!strcmp(tok, "delta"));
    /* Get hx */
    VJMPERR2(1 == Vio_scanf(sock, "%s", tok));
    VJMPERR1(1 == sscanf(tok, "%lf", &(thee->hx)));
    /* Get 0.0 */
    VJMPERR2(1 == Vio_scanf(sock, "%s", tok));
    VJMPERR1(1 == sscanf(tok, "%lf", &dtmp));
    VJMPERR1(dtmp == 0.0);
    /* Get 0.0 */
    VJMPERR2(1 == Vio_scanf(sock, "%s", tok));
    VJMPERR1(1 == sscanf(tok, "%lf", &dtmp));
    VJMPERR1(dtmp == 0.0);
    /* Get "delta" */
    VJMPERR2(1 == Vio_scanf(sock, "%s", tok));
    VJMPERR1(!strcmp(tok, "delta"));
    /* Get 0.0 */
    VJMPERR2(1 == Vio_scanf(sock, "%s", tok));
    VJMPERR1(1 == sscanf(tok, "%lf", &dtmp));
    VJMPERR1(dtmp == 0.0);
    /* Get hy */
    VJMPERR2(1 == Vio_scanf(sock, "%s", tok));
    VJMPERR1(1 == sscanf(tok, "%lf", &(thee->hy)));
    /* Get 0.0 */
    VJMPERR2(1 == Vio_scanf(sock, "%s", tok));
    VJMPERR1(1 == sscanf(tok, "%lf", &dtmp));
    VJMPERR1(dtmp == 0.0);
    /* Get "delta" */
    VJMPERR2(1 == Vio_scanf(sock, "%s", tok));
    VJMPERR1(!strcmp(tok, "delta"));
    /* Get 0.0 */
    VJMPERR2(1 == Vio_scanf(sock, "%s", tok));
    VJMPERR1(1 == sscanf(tok, "%lf", &dtmp));
    VJMPERR1(dtmp == 0.0);
    /* Get 0.0 */
    VJMPERR2(1 == Vio_scanf(sock, "%s", tok));
    VJMPERR1(1 == sscanf(tok, "%lf", &dtmp));
    VJMPERR1(dtmp == 0.0);
    /* Get hz */
    VJMPERR2(1 == Vio_scanf(sock, "%s", tok));
    VJMPERR1(1 == sscanf(tok, "%lf", &(thee->hzed)));
    Vnm_print(0, "Vgrid_readDX:  Grid spacings = (%g, %g, %g)\n",
        thee->hx, thee->hy, thee->hzed);
    /* Get "object" */
    VJMPERR2(1 == Vio_scanf(sock, "%s", tok));
    VJMPERR1(!strcmp(tok, "object"));
    /* Get "2" */
    VJMPERR2(1 == Vio_scanf(sock, "%s", tok));
    /* Get "class" */
    VJMPERR2(1 == Vio_scanf(sock, "%s", tok));
    VJMPERR1(!strcmp(tok, "class"));
    /* Get "gridconnections" */
    VJMPERR2(1 == Vio_scanf(sock, "%s", tok));
    VJMPERR1(!strcmp(tok, "gridconnections"));
    /* Get "counts" */
    VJMPERR2(1 == Vio_scanf(sock, "%s", tok));
    VJMPERR1(!strcmp(tok, "counts"));
    /* Get the dimensions again */
    VJMPERR2(1 == Vio_scanf(sock, "%s", tok));
    VJMPERR2(1 == Vio_scanf(sock, "%s", tok));
    VJMPERR2(1 == Vio_scanf(sock, "%s", tok));
    /* Get "object" */
    VJMPERR2(1 == Vio_scanf(sock, "%s", tok));
    VJMPERR1(!strcmp(tok, "object"));
    /* Get # */
    VJMPERR2(1 == Vio_scanf(sock, "%s", tok));
    /* Get "class" */
    VJMPERR2(1 == Vio_scanf(sock, "%s", tok));
    VJMPERR1(!strcmp(tok, "class"));
    /* Get "array" */
    VJMPERR2(1 == Vio_scanf(sock, "%s", tok));
    VJMPERR1(!strcmp(tok, "array"));
    /* Get "type" */
    VJMPERR2(1 == Vio_scanf(sock, "%s", tok));
    VJMPERR1(!strcmp(tok, "type"));
    /* Get "double" */
    VJMPERR2(1 == Vio_scanf(sock, "%s", tok));
    VJMPERR1(!strcmp(tok, "double"));
    /* Get "rank" */
    VJMPERR2(1 == Vio_scanf(sock, "%s", tok));
    VJMPERR1(!strcmp(tok, "rank"));
    /* Get # */
    VJMPERR2(1 == Vio_scanf(sock, "%s", tok));
    /* Get "items" */
    VJMPERR2(1 == Vio_scanf(sock, "%s", tok));
    VJMPERR1(!strcmp(tok, "items"));
    /* Get # */
    VJMPERR2(1 == Vio_scanf(sock, "%s", tok));
    VJMPERR1(1 == sscanf(tok, "%zu", &itmp));
    u = (size_t)thee->nx * thee->ny * thee->nz;
    VJMPERR1(u == itmp);
    /* Get "data" */
    VJMPERR2(1 == Vio_scanf(sock, "%s", tok));
    VJMPERR1(!strcmp(tok, "data"));
    /* Get "follows" */
    VJMPERR2(1 == Vio_scanf(sock, "%s", tok));
    VJMPERR1(!strcmp(tok, "follows"));

    /* Allocate space for the data */
    Vnm_print(0, "Vgrid_readDX:  allocating %d x %d x %d doubles for storage\n",
        thee->nx, thee->ny, thee->nz);
    thee->data = VNULL;
    thee->data = (double*)Vmem_malloc(thee->mem, u, sizeof(double));
    if (thee->data == VNULL) {
      Vnm_print(2, "Vgrid_readDX:  Unable to allocate space for data!\n");
      return 0;
    }

    for (i=0; i<thee->nx; i++) {
      for (j=0; j<thee->ny; j++) {
        for (k=0; k<thee->nz; k++) {
          u = k*(thee->nx)*(thee->ny)+j*(thee->nx)+i;
          VJMPERR2(1 == Vio_scanf(sock, "%s", tok));
          VJMPERR1(1 == sscanf(tok, "%lf", &dtmp));
          (thee->data)[u] = dtmp;
        }
      }
    }

    /* calculate grid maxima */
    thee->xmax = thee->xmin + (thee->nx-1)*thee->hx;
    thee->ymax = thee->ymin + (thee->ny-1)*thee->hy;
    thee->zmax = thee->zmin + (thee->nz-1)*thee->hzed;

    /* Close off the socket */
    Vio_acceptFree(sock);
    Vio_dtor(&sock);

    return 1;

VERROR1:
    Vio_dtor(&sock);
    Vnm_print(2, "Vgrid_readDX:  Format problem with input file <%s>\n",
        fname);
    return 0;

VERROR2:
    Vio_dtor(&sock);
    Vnm_print(2, "Vgrid_readDX:  I/O problem with input file <%s>\n",
        fname);
    return 0;
}

/**
 * Load grid from an input dx binary file.
 * @author Juan Brandi
 */
VPUBLIC int Vgrid_readDXBIN(Vgrid *thee, const char *iodev, const char *iofmt,
    const char *thost, const char *fname) {

  size_t i, j, k, itmp, u;
  double dtmp, dtmp2;
  char tok[VMAX_BUFSIZE];
  int isBinary = 0;
  //Vio *sock;

  /* Check to see if the existing data is null and, if not, clear it out */
  if (thee->data != VNULL) {
    Vnm_print(1, "Vgrid_readDXBIN: destroying existing data!\n");
    Vmem_free(thee->mem, (thee->nx*thee->ny*thee->nz), sizeof(double),
        (void **)&(thee->data)); }
    thee->readdata = 1;
    thee->ctordata = 0;

    /*Opend file fd for binary reading*/
    FILE *fd = fopen(fname,"rb");
    if(fd == NULL){
      printf("Vgrid_readDXBIN: Problem opening file %s\n",fname);
      fclose(fd);
      return 0;
    }

    /* Set up the virtual socket */
    //	    sock = Vio_ctor(iodev,iofmt,thost,fname,"r");
    //	    if (sock == VNULL) {
    //	        Vnm_print(2, "Vgrid_readDX: Problem opening virtual socket %s\n",
    //	          fname);
    //	        return 0;
    //	    }
    //	    if (Vio_accept(sock, 0) < 0) {
    //	        Vnm_print(2, "Vgrid_readDX: Problem accepting virtual socket %s\n",
    //	          fname);
    //	        return 0;
    //	    }
    //
    //	    Vio_setWhiteChars(sock, MCwhiteChars);
    //	    Vio_setCommChars(sock, MCcommChars);

    //skip comments
    do{
      fgets(tok, VMAX_BUFSIZE, fd);
    }
    while(tok[0]=='#');

    //get counts
    if(sscanf(tok,"object 1 class gridpositions counts %i %i %i\n",&(thee->nx),&(thee->ny),&(thee->nz))!= 3){
      printf("Vgrid_readDXBIN: Failed to read dimensions.\n");
      fclose(fd);
      return 0;
    }
    printf("Vgrid_readDXBIN: Grid dimensions %d x %d x %d grid\n",thee->nx, thee->ny, thee->nz);

    /* Read in the DX regular positions */

    /* Get "object" */
    //	    VJMPERR2(1 == Vio_scanf(sock, "%s", tok));
    //	    VJMPERR1(!strcmp(tok, "object"));
    //	    /* Get "1" */
    //	    VJMPERR2(1 == Vio_scanf(sock, "%d", &itmp));
    //	    /* Get "class" */
    //	    VJMPERR2(1 == Vio_scanf(sock, "%s", tok));
    //	    VJMPERR1(!strcmp(tok, "class"));
    //	    /* Get "gridpositions" */
    //	    VJMPERR2(1 == Vio_scanf(sock, "%s", tok));
    //	    VJMPERR1(!strcmp(tok, "gridpositions"));
    //	    /* Get "counts" */
    //	    VJMPERR2(1 == Vio_scanf(sock, "%s", tok));
    //	    VJMPERR1(!strcmp(tok, "counts"));
    //	    /* Get nx */
    //	    VJMPERR2(1 == Vio_scanf(sock, "%s", tok));
    //	    VJMPERR1(1 == sscanf(tok, "%d", &(thee->nx)));
    //	    /* Get ny */
    //	    VJMPERR2(1 == Vio_scanf(sock, "%s", tok));
    //	    VJMPERR1(1 == sscanf(tok, "%d", &(thee->ny)));
    //	    /* Get nz */
    //	    VJMPERR2(1 == Vio_scanf(sock, "%s", tok));
    //	    VJMPERR1(1 == sscanf(tok, "%d", &(thee->nz)));
    //	    Vnm_print(0, "Vgrid_readDX:  Grid dimensions %d x %d x %d grid\n",
    //	     thee->nx, thee->ny, thee->nz);

    if(fgets(tok,VMAX_BUFSIZE, fd) == NULL){
      printf("Vgrid_readDXBIN: unexpected end of file.\n");
      fclose(fd);
      return 0;
    }
    if(sscanf(tok,"origin %lf %lf %lf",&(thee->xmin),&(thee->ymin),&(thee->zmin))!=3){
      printf("Vgrid_readDXBIN: Failed to read origin cell data.\n");
      fclose(fd);
      return 0;
    }
    printf("Vgrid_readDXBIN: Grid origin = (%g %g %g)\n",thee->xmin, thee->ymin, thee->zmin);

    //get Delta x
    if(fgets(tok,VMAX_BUFSIZE, fd) == NULL){
      printf("Vgrid_readDXBIN: unexpected end of file.\n");
      fclose(fd);
      return 0;
    }
    if(sscanf(tok,"delta %lf %lf %lf",&(thee->hx),&dtmp,&dtmp2)!=3){
      printf("Vgrid_readDXBIN: Failed to read delta x data.\n");
      fclose(fd);
      return 0;
    }
    //get Delta y
    if(fgets(tok,VMAX_BUFSIZE, fd) == NULL){
      printf("Vgrid_readDXBIN: Unexpected end of file\n");
      fclose(fd);
      return 0;
    }
    if(sscanf(tok,"delta %lf %lf %lf",&dtmp,&(thee->hy),&dtmp2)!=3){
      printf("Vgrid_readDXBIN: Failed to read delta y data.\n");
      fclose(fd);
      return 0;
    }
    //get Delta z
    if(fgets(tok,VMAX_BUFSIZE, fd) == NULL){
      printf("Vgrid_readDXBIN: Unexpected end of file.\n");
      fclose(fd);
      return 0;
    }
    if(sscanf(tok,"delta %lf %lf %lf",&dtmp,&dtmp2,&(thee->hzed))!=3){
      printf("Vgrid_readDXBIN: Failed to read delta z data.\n");
      fclose(fd);
      return 0;
    }
    printf("Vgrid_readDXBIN: Grid spacings = (%g, %g, %g)\n",thee->hx, thee->hy, thee->hzed);

    //skip a line
    if(fgets(tok,VMAX_BUFSIZE, fd) == NULL){
      printf("Vgrid_readDXBIN: Unexpected end of file.\n");
      fclose(fd);
      return 0;
    }

    //scan the buffer for the word binary
    if(fgets(tok,VMAX_BUFSIZE, fd) == NULL){
      printf("Vgrid_readDXBIN: Unexpected end of file.\n");
      fclose(fd);
      return 0;
    }
    if(strstr(tok,"binary")){
      isBinary = 1;
    }
    else{
      printf("Vgrid_readDXBIN: Binary tag not found. Will continue to try to read binary data.");
    }

    u = (size_t)thee->nx * thee->ny * thee->nz;
    int tot = thee->nx * thee->ny * thee->nz;

    /*Allocate space for the data*/
    printf("Vgrid_readDXBIN: allocating %d x %d x %d doubled for storage\n", thee->nx, thee->ny, thee->nz);
    thee->data = NULL;
    thee->data = (double *)malloc(tot*sizeof(double));

    if(thee->data == NULL){
      printf("Vgrid_readDXBIN: Unable to allocate space for data!\n");
      fclose(fd);
      return 0;
    }

    int counter = 0, r;

    for (i=0; i<thee->nx; i++) {
      for (j=0; j<thee->ny; j++) {
        for (k=0; k<thee->nz; k++) {
          u = k*(thee->nx)*(thee->ny)+j*(thee->nx)+i;
          r = fread(&dtmp,sizeof(double),1,fd);
          (thee->data)[u] = dtmp;
          if(r!= 1){
            printf("Vgrid_readDXBIN: Failed to read doubles.\n");
            return 0;
          }
          counter++;
        }
      }
    }

    if(counter!=tot){
      printf("Vgrid_readDXBIN: Read double = %d not equal to items = %d\n",counter, tot);
    }

    /* calculate grid maxima */
    thee->xmax = thee->xmin + (thee->nx-1)*thee->hx;
    thee->ymax = thee->ymin + (thee->ny-1)*thee->hy;
    thee->zmax = thee->zmin + (thee->nz-1)*thee->hzed;

    fclose(fd);

    return 1;
}


/* ///////////////////////////////////////////////////////////////////////////
// Routine:  Vgrid_writeGZ
//
// Author:   Nathan Baker
/////////////////////////////////////////////////////////////////////////// */
VPUBLIC void Vgrid_writeGZ(Vgrid *thee, const char *iodev, const char *iofmt,
    const char *thost, const char *fname, char *title, double *pvec) {

#ifdef HAVE_ZLIB
  double xmin, ymin, zmin, hx, hy, hzed;

  int nx, ny, nz, nxPART, nyPART, nzPART;
  int usepart, gotit;
  size_t icol, i, j, k, u;
  double x, y, z, xminPART, yminPART, zminPART;

  size_t txyz;
  double txmin, tymin, tzmin;

  char header[8196];
  char footer[8196];
  char line[80];
  char newline[] = "\n";
  gzFile outfile;
  char precFormat[VMAX_BUFSIZE];

  if (thee == VNULL) {
    Vnm_print(2, "Vgrid_writeGZ:  Error -- got VNULL thee!\n");
    VASSERT(0);
  }
  if (!(thee->ctordata || thee->readdata)) {
    Vnm_print(2, "Vgrid_writeGZ:  Error -- no data available!\n");
    VASSERT(0);
  }

  hx = thee->hx;
  hy = thee->hy;
  hzed = thee->hzed;
  nx = thee->nx;
  ny = thee->ny;
  nz = thee->nz;
  xmin = thee->xmin;
  ymin = thee->ymin;
  zmin = thee->zmin;

  if (pvec == VNULL) usepart = 0;
  else usepart = 1;

  /* Set up the virtual socket */
  Vnm_print(0, "Vgrid_writeGZ:  Opening file...\n");
  outfile = gzopen(fname, "wb");

  if (usepart) {
    /* Get the lower corner and number of grid points for the local
     * partition */
    xminPART = VLARGE;
    yminPART = VLARGE;
    zminPART = VLARGE;
    nxPART = 0;
    nyPART = 0;
    nzPART = 0;
    /* First, search for the lower corner */
    for (k=0; k<nz; k++) {
      z = k*hzed + zmin;
      for (j=0; j<ny; j++) {
        y = j*hy + ymin;
        for (i=0; i<nx; i++) {
          x = i*hx + xmin;
          if (pvec[IJK(i,j,k)] > 0.0) {
            if (x < xminPART) xminPART = x;
            if (y < yminPART) yminPART = y;
            if (z < zminPART) zminPART = z;
          }
        }
      }
    }
    /* Now search for the number of grid points in the z direction */
    for (k=0; k<nz; k++) {
      gotit = 0;
      for (j=0; j<ny; j++) {
        for (i=0; i<nx; i++) {
          if (pvec[IJK(i,j,k)] > 0.0) {
            gotit = 1;
            break;
          }
        }
        if (gotit) break;
      }
      if (gotit) nzPART++;
    }
    /* Now search for the number of grid points in the y direction */
    for (j=0; j<ny; j++) {
      gotit = 0;
      for (k=0; k<nz; k++) {
        for (i=0; i<nx; i++) {
          if (pvec[IJK(i,j,k)] > 0.0) {
            gotit = 1;
            break;
          }
        }
        if (gotit) break;
      }
      if (gotit) nyPART++;
    }
    /* Now search for the number of grid points in the x direction */
    for (i=0; i<nx; i++) {
      gotit = 0;
      for (k=0; k<nz; k++) {
        for (j=0; j<ny; j++) {
          if (pvec[IJK(i,j,k)] > 0.0) {
            gotit = 1;
            break;
          }
        }
        if (gotit) break;
      }
      if (gotit) nxPART++;
    }

    if ((nxPART != nx) || (nyPART != ny) || (nzPART != nz)) {
      Vnm_print(0, "Vgrid_writeGZ:  printing only subset of domain\n");
    }

    txyz = (nxPART*nyPART*nzPART);
    txmin = xminPART;
    tymin = yminPART;
    tzmin = zminPART;

  }else {

    txyz = (nx*ny*nz);
    txmin = xmin;
    tymin = ymin;
    tzmin = zmin;

  }

  /* Write off the title (if we're not XDR) */
  sprintf(header,
      "# Data from %s\n"	\
      "# \n"							\
      "# %s\n"			\
      "# \n"							\
      "object 1 class gridpositions counts %i %i %i\n"	\
      "origin %12.6e %12.6e %12.6e\n"	\
      "delta %12.6e 0.000000e+00 0.000000e+00\n"		\
      "delta 0.000000e+00 %12.6e 0.000000e+00\n"		\
      "delta 0.000000e+00 0.000000e+00 %12.6e\n"		\
      "object 2 class gridconnections counts %i %i %i\n"\
      "object 3 class array type double rank 0 items %lu data follows\n",
      PACKAGE_STRING,title,nx,ny,nz,txmin,tymin,tzmin,
      hx,hy,hzed,nx,ny,nz,txyz);
  gzwrite(outfile, header, strlen(header)*sizeof(char));

  /* Now write the data */
  icol = 0;
  for (i=0; i<nx; i++) {
    for (j=0; j<ny; j++) {
      for (k=0; k<nz; k++) {
        u = k*(nx)*(ny)+j*(nx)+i;
        if (pvec[u] > 0.0) {
          sprintf(line, "%12.6e ", thee->data[u]);
          gzwrite(outfile, line, strlen(line)*sizeof(char));
          icol++;
          if (icol == 3) {
            icol = 0;
            gzwrite(outfile, newline, strlen(newline)*sizeof(char));
          }
        }
      }
    }
  }
  if(icol < 3){
    char newline[] = "\n";
    gzwrite(outfile, newline, strlen(newline)*sizeof(char));
  }

  /* Create the field */
  sprintf(footer, "attribute \"dep\" string \"positions\"\n" \
      "object \"regular positions regular connections\" class field\n" \
      "component \"positions\" value 1\n" \
      "component \"connections\" value 2\n" \
      "component \"data\" value 3\n");
  gzwrite(outfile, footer, strlen(footer)*sizeof(char));

  gzclose(outfile);
#else

  Vnm_print(0, "WARNING\n");
  Vnm_print(0, "Vgrid_readGZ:  gzip read/write support is disabled in this build\n");
  Vnm_print(0, "Vgrid_readGZ:  configure and compile without the --disable-zlib flag.\n");
  Vnm_print(0, "WARNING\n");
#endif
}

/* ///////////////////////////////////////////////////////////////////////////
// Routine:  Vgrid_writeDX
//
// Author:   Nathan Baker
/////////////////////////////////////////////////////////////////////////// */
VPUBLIC void Vgrid_writeDX(Vgrid *thee, const char *iodev, const char *iofmt,
    const char *thost, const char *fname, char *title, double *pvec) {

  double xmin, ymin, zmin, hx, hy, hzed;
  int nx, ny, nz, nxPART, nyPART, nzPART;
  int usepart, gotit;
  size_t icol, i, j, k, u;
  double x, y, z, xminPART, yminPART, zminPART;
  Vio *sock;
  char precFormat[VMAX_BUFSIZE];

  if (thee == VNULL) {
    Vnm_print(2, "Vgrid_writeDX:  Error -- got VNULL thee!\n");
    VASSERT(0);
  }
  if (!(thee->ctordata || thee->readdata)) {
    Vnm_print(2, "Vgrid_writeDX:  Error -- no data available!\n");
    VASSERT(0);
  }

  hx = thee->hx;
  hy = thee->hy;
  hzed = thee->hzed;
  nx = thee->nx;
  ny = thee->ny;
  nz = thee->nz;
  xmin = thee->xmin;
  ymin = thee->ymin;
  zmin = thee->zmin;

  if (pvec == VNULL) usepart = 0;
  else usepart = 1;

  /* Set up the virtual socket */
  Vnm_print(0, "Vgrid_writeDX:  Opening virtual socket...\n");
  sock = Vio_ctor(iodev,iofmt,thost,fname,"w");
  if (sock == VNULL) {
    Vnm_print(2, "Vgrid_writeDX:  Problem opening virtual socket %s\n",
        fname);
    return;
  }
  if (Vio_connect(sock, 0) < 0) {
    Vnm_print(2, "Vgrid_writeDX: Problem connecting virtual socket %s\n",
        fname);
    return;
  }

  Vio_setWhiteChars(sock, MCwhiteChars);
  Vio_setCommChars(sock, MCcommChars);

  Vnm_print(0, "Vgrid_writeDX:  Writing to virtual socket...\n");

  if (usepart) {
    /* Get the lower corner and number of grid points for the local
     * partition */
    xminPART = VLARGE;
    yminPART = VLARGE;
    zminPART = VLARGE;
    nxPART = 0;
    nyPART = 0;
    nzPART = 0;
    /* First, search for the lower corner */
    for (k=0; k<nz; k++) {
      z = k*hzed + zmin;
      for (j=0; j<ny; j++) {
        y = j*hy + ymin;
        for (i=0; i<nx; i++) {
          x = i*hx + xmin;
          if (pvec[IJK(i,j,k)] > 0.0) {
            if (x < xminPART) xminPART = x;
            if (y < yminPART) yminPART = y;
            if (z < zminPART) zminPART = z;
          }
        }
      }
    }
    /* Now search for the number of grid points in the z direction */
    for (k=0; k<nz; k++) {
      gotit = 0;
      for (j=0; j<ny; j++) {
        for (i=0; i<nx; i++) {
          if (pvec[IJK(i,j,k)] > 0.0) {
            gotit = 1;
            break;
          }
        }
        if (gotit) break;
      }
      if (gotit) nzPART++;
    }
    /* Now search for the number of grid points in the y direction */
    for (j=0; j<ny; j++) {
      gotit = 0;
      for (k=0; k<nz; k++) {
        for (i=0; i<nx; i++) {
          if (pvec[IJK(i,j,k)] > 0.0) {
            gotit = 1;
            break;
          }
        }
        if (gotit) break;
      }
      if (gotit) nyPART++;
    }
    /* Now search for the number of grid points in the x direction */
    for (i=0; i<nx; i++) {
      gotit = 0;
      for (k=0; k<nz; k++) {
        for (j=0; j<ny; j++) {
          if (pvec[IJK(i,j,k)] > 0.0) {
            gotit = 1;
            break;
          }
        }
        if (gotit) break;
      }
      if (gotit) nxPART++;
    }

    if ((nxPART != nx) || (nyPART != ny) || (nzPART != nz)) {
      Vnm_print(0, "Vgrid_writeDX:  printing only subset of domain\n");
    }


    /* Write off the title (if we're not XDR) */
    if (Vstring_strcasecmp(iofmt, "XDR") == 0) {
      Vnm_print(0, "Vgrid_writeDX:  Skipping comments for XDR format.\n");
    } else {
      Vnm_print(0, "Vgrid_writeDX:  Writing comments for %s format.\n",
          iofmt);
      Vio_printf(sock, "# Data from %s\n", PACKAGE_STRING);
      Vio_printf(sock, "# \n");
      Vio_printf(sock, "# %s\n", title);
      Vio_printf(sock, "# \n");
    }

    /* Write off the DX regular positions */
    Vio_printf(sock, "object 1 class gridpositions counts %d %d %d\n",
        nxPART, nyPART, nzPART);

    sprintf(precFormat, Vprecision, xminPART, yminPART, zminPART);
    Vio_printf(sock, "origin %s\n", precFormat);
    sprintf(precFormat, Vprecision, hx, 0.0, 0.0);
    Vio_printf(sock, "delta %s\n", precFormat);
    sprintf(precFormat, Vprecision, 0.0, hy, 0.0);
    Vio_printf(sock, "delta %s\n", precFormat);
    sprintf(precFormat, Vprecision, 0.0, 0.0, hzed);
    Vio_printf(sock, "delta %s\n", precFormat);

    /* Write off the DX regular connections */
    Vio_printf(sock, "object 2 class gridconnections counts %d %d %d\n",
        nxPART, nyPART, nzPART);

    /* Write off the DX data */
    Vio_printf(sock, "object 3 class array type double rank 0 items %lu \
        data follows\n", (nxPART*nyPART*nzPART));
    icol = 0;
    for (i=0; i<nx; i++) {
      for (j=0; j<ny; j++) {
        for (k=0; k<nz; k++) {
          u = k*(nx)*(ny)+j*(nx)+i;
          if (pvec[u] > 0.0) {
            Vio_printf(sock, "%12.6e ", thee->data[u]);
            icol++;
            if (icol == 3) {
              icol = 0;
              Vio_printf(sock, "\n");
            }
          }
        }
      }
    }

    if (icol != 0) Vio_printf(sock, "\n");

    /* Create the field */
    Vio_printf(sock, "attribute \"dep\" string \"positions\"\n");
    Vio_printf(sock, "object \"regular positions regular connections\" \
        class field\n");
    Vio_printf(sock, "component \"positions\" value 1\n");
    Vio_printf(sock, "component \"connections\" value 2\n");
    Vio_printf(sock, "component \"data\" value 3\n");

  } else {
    /* Write off the title (if we're not XDR) */
    if (Vstring_strcasecmp(iofmt, "XDR") == 0) {
      Vnm_print(0, "Vgrid_writeDX:  Skipping comments for XDR format.\n");
    } else {
      Vnm_print(0, "Vgrid_writeDX:  Writing comments for %s format.\n",
          iofmt);
      Vio_printf(sock, "# Data from %s\n", PACKAGE_STRING);
      Vio_printf(sock, "# \n");
      Vio_printf(sock, "# %s\n", title);
      Vio_printf(sock, "# \n");
    }


    /* Write off the DX regular positions */
    Vio_printf(sock, "object 1 class gridpositions counts %d %d %d\n",
        nx, ny, nz);

    sprintf(precFormat, Vprecision, xmin, ymin, zmin);
    Vio_printf(sock, "origin %s\n", precFormat);
    sprintf(precFormat, Vprecision, hx, 0.0, 0.0);
    Vio_printf(sock, "delta %s\n", precFormat);
    sprintf(precFormat, Vprecision, 0.0, hy, 0.0);
    Vio_printf(sock, "delta %s\n", precFormat);
    sprintf(precFormat, Vprecision, 0.0, 0.0, hzed);
    Vio_printf(sock, "delta %s\n", precFormat);

    /* Write off the DX regular connections */
    Vio_printf(sock, "object 2 class gridconnections counts %d %d %d\n",
        nx, ny, nz);

    /* Write off the DX data */
    Vio_printf(sock, "object 3 class array type double rank 0 items %lu \
        data follows\n", (nx*ny*nz));
    icol = 0;
    for (i=0; i<nx; i++) {
      for (j=0; j<ny; j++) {
        for (k=0; k<nz; k++) {
          u = k*(nx)*(ny)+j*(nx)+i;
          Vio_printf(sock, "%12.6e ", thee->data[u]);
          icol++;
          if (icol == 3) {
            icol = 0;
            Vio_printf(sock, "\n");
          }
        }
      }
    }
    if (icol != 0) Vio_printf(sock, "\n");

    /* Create the field */
    Vio_printf(sock, "attribute \"dep\" string \"positions\"\n");
    Vio_printf(sock, "object \"regular positions regular connections\" \
        class field\n");
    Vio_printf(sock, "component \"positions\" value 1\n");
    Vio_printf(sock, "component \"connections\" value 2\n");
    Vio_printf(sock, "component \"data\" value 3\n");
  }

  /* Close off the socket */
  Vio_connectFree(sock);
  Vio_dtor(&sock);
}

/* ///////////////////////////////////////////////////////////////////////////
// Routine:  Vgrid_writeDXBIN
//
// Author:   Juan Brandi
/////////////////////////////////////////////////////////////////////////// */
VPUBLIC void Vgrid_writeDXBIN(Vgrid *thee, const char *iodev, const char *iofmt,
    const char *thost, const char *fname, char *title, double *pvec){

  double xmin, ymin, zmin, hx, hy, hzed;
  int nx, ny, nz, nxPART, nyPART, nzPART;
  int usepart, gotit;
  size_t icol, i, j, k, u;
  double x, y, z, xminPART, yminPART, zminPART;
  //Vio *sock;
  char precFormat[VMAX_BUFSIZE];

  if (thee == VNULL) {
    Vnm_print(2, "Vgrid_writeDXBIN:  Error -- got VNULL thee!\n");
    VASSERT(0);
  }
  if (!(thee->ctordata || thee->readdata)) {
    Vnm_print(2, "Vgrid_writeDXBIN:  Error -- no data available!\n");
    VASSERT(0);
  }


  hx = thee->hx;
  hy = thee->hy;
  hzed = thee->hzed;
  nx = thee->nx;
  ny = thee->ny;
  nz = thee->nz;
  xmin = thee->xmin;
  ymin = thee->ymin;
  zmin = thee->zmin;

  if (pvec == VNULL) usepart = 0;
  else usepart = 1;

  /*will not use vio methods to try to avoid using malloc.*/
  FILE *fd = fopen(fname,"wb");

  //check to se if the file was created/open successfully.
  if(fd == NULL){
    printf("Vgrid_writeDXBIN: Problem opening file %s for writing.\n", fname);
    return;
  }

  printf("Vgrid_writeDXBIN: Writing to file...\n");

  if (usepart) {
    /* Get the lower corner and number of grid points for the local
     * partition */
    xminPART = VLARGE;
    yminPART = VLARGE;
    zminPART = VLARGE;
    nxPART = 0;
    nyPART = 0;
    nzPART = 0;
    /* First, search for the lower corner */
    for (k=0; k<nz; k++) {
      z = k*hzed + zmin;
      for (j=0; j<ny; j++) {
        y = j*hy + ymin;
        for (i=0; i<nx; i++) {
          x = i*hx + xmin;
          if (pvec[IJK(i,j,k)] > 0.0) {
            if (x < xminPART) xminPART = x;
            if (y < yminPART) yminPART = y;
            if (z < zminPART) zminPART = z;
          }
        }
      }
    }
    /* Now search for the number of grid points in the z direction */
    for (k=0; k<nz; k++) {
      gotit = 0;
      for (j=0; j<ny; j++) {
        for (i=0; i<nx; i++) {
          if (pvec[IJK(i,j,k)] > 0.0) {
            gotit = 1;
            break;
          }
        }
        if (gotit) break;
      }
      if (gotit) nzPART++;
    }
    /* Now search for the number of grid points in the y direction */
    for (j=0; j<ny; j++) {
      gotit = 0;
      for (k=0; k<nz; k++) {
        for (i=0; i<nx; i++) {
          if (pvec[IJK(i,j,k)] > 0.0) {
            gotit = 1;
            break;
          }
        }
        if (gotit) break;
      }
      if (gotit) nyPART++;
    }
    /* Now search for the number of grid points in the x direction */
    for (i=0; i<nx; i++) {
      gotit = 0;
      for (k=0; k<nz; k++) {
        for (j=0; j<ny; j++) {
          if (pvec[IJK(i,j,k)] > 0.0) {
            gotit = 1;
            break;
          }
        }
        if (gotit) break;
      }
      if (gotit) nxPART++;
    }

    if ((nxPART != nx) || (nyPART != ny) || (nzPART != nz)) {
      Vnm_print(0, "Vgrid_writeDXBIN:  printing only subset of domain\n");
    }

    /* Write title (we're in XDR and "wb") */
    //Vnm_print(0, "Vgrid_writeDXBIN:  Writing comments for dxbin format.\n");
    printf("Vgrid_writeDXBIN: Writing comments for dxbin format\n");
    fprintf(fd, "# Data from %s\n",PACKAGE_STRING);
    fprintf(fd, "# \n");
    fprintf(fd, "# %s\n",title);
    fprintf(fd, "# \n");

    /* Write off the DX regular positions */
    fprintf(fd, "object 1 class gridpositions counts %d %d %d\n",nxPART, nyPART, nzPART);

    sprintf(precFormat, Vprecision, xminPART, yminPART, zminPART);
    fprintf(fd, "origin %s\n",precFormat);

    sprintf(precFormat, Vprecision, hx, 0.0, 0.0);
    fprintf(fd, "delta %s\n",precFormat);

    sprintf(precFormat, Vprecision, 0.0, hy, 0.0);
    fprintf(fd, "delta %s\n", precFormat);

    sprintf(precFormat, Vprecision, 0.0, 0.0, hzed);
    fprintf(fd, "delta %s\n", precFormat);

    /* Write off the DX regular connections */
    fprintf(fd, "object 2 class gridconnections counts %d %d %d\n", nxPART, nyPART, nzPART);

    /* Write off the DX data */
    fprintf(fd, "object 3 class array type double rank 0 items %d binary data follows\n",(nxPART*nyPART*nzPART));

    icol = 0;
    for (i=0; i<nx; i++) {
      for (j=0; j<ny; j++) {
        for (k=0; k<nz; k++) {
          u = k*(nx)*(ny)+j*(nx)+i;
          if (pvec[u] > 0.0) {
            fwrite(&(thee->data)[u],sizeof(double),1,fd);
            icol++;
            /*don't need the column formating to write binary doubles.*/
            if (icol == 3) {
              icol = 0;
            }
          }
        }
      }
    }

    fprintf(fd,"\n");

    /* Create the field */
    fprintf(fd, "attribute \"dep\" string \"positions\"\n");
    fprintf(fd, "object \"regular positions regular connections\" class field\n");
    fprintf(fd, "component \"positions\" value 1\n");
    fprintf(fd, "component \"connections\" value 2\n");
    fprintf(fd, "component \"data\" value 3\n");

    fclose(fd);

  } else {
    /*write dx format title*/
    printf("Vgrid_writeDXBIN: Writing comments for %s format.\n",iofmt);
    fprintf(fd,"# Data from %s\n", PACKAGE_STRING);
    fprintf(fd,"# \n");
    fprintf(fd, "# %s\n", title);
    fprintf(fd, "# \n");

    /* Write off the DX regular positions */
    fprintf(fd, "object 1 class gridpositions counts %d %d %d\n", nx, ny, nz);

    sprintf(precFormat, Vprecision, xmin, ymin, zmin);
    fprintf(fd, "origin %s\n", precFormat);

    sprintf(precFormat, Vprecision, hx, 0.0, 0.0);
    fprintf(fd, "delta %s\n", precFormat);

    sprintf(precFormat, Vprecision, 0.0, hy, 0.0);
    fprintf(fd, "delta %s\n", precFormat);

    sprintf(precFormat, Vprecision, 0.0, 0.0, hzed);
    fprintf(fd, "delta %s\n", precFormat);

    /* Write off the DX regular connections */
    fprintf(fd, "object 2 class gridconnections counts %d %d %d\n", nx, ny, nz);

    /* Write off the DX data */
    fprintf(fd, "object 3 class array type double rank 0 items %d binary data follows\n", (nx*ny*nz));

    icol = 0;
    for (i=0; i<nx; i++) {
      for (j=0; j<ny; j++) {
        for (k=0; k<nz; k++) {
          u = k*(nx)*(ny)+j*(nx)+i;
          fwrite(&(thee->data)[u],sizeof(double),1,fd);
          icol++;
          if (icol == 3) {
            icol = 0;
          }
        }
      }
    }

    fprintf(fd, "\n");

    /* Create the field */
    fprintf(fd, "attribute \"dep\" string \"positions\"\n");
    fprintf(fd, "object \"regular positions regular connections\" class field\n");
    fprintf(fd, "component \"positions\" value 1\n");
    fprintf(fd, "component \"connections\" value 2\n");
    fprintf(fd, "component \"data\" value 3\n");

    fclose(fd);
  }
}


/* ///////////////////////////////////////////////////////////////////////////
// Routine:  Vgrid_writeUHBD
// Author:   Nathan Baker
/////////////////////////////////////////////////////////////////////////// */
VPUBLIC void Vgrid_writeUHBD(Vgrid *thee, const char *iodev, const char *iofmt,
    const char *thost, const char *fname, char *title, double *pvec) {

  size_t u, icol, i, j, k;
  size_t gotit, nx, ny, nz;
  double xmin, ymin, zmin, hzed, hy, hx;
  Vio *sock;

  if (thee == VNULL) {
    Vnm_print(2, "Vgrid_writeUHBD:  Error -- got VNULL thee!\n");
    VASSERT(0);
  }
  if (!(thee->ctordata || thee->readdata)) {
    Vnm_print(2, "Vgrid_writeUHBD:  Error -- no data available!\n");
    VASSERT(0);
  }

  if ((thee->hx!=thee->hy) || (thee->hy!=thee->hzed)
      || (thee->hx!=thee->hzed)) {
    Vnm_print(2, "Vgrid_writeUHBD: can't write UHBD mesh with non-uniform \
        spacing\n");
    return;
  }

  /* Set up the virtual socket */
  sock = Vio_ctor(iodev,iofmt,thost,fname,"w");
  if (sock == VNULL) {
    Vnm_print(2, "Vgrid_writeUHBD: Problem opening virtual socket %s\n",
        fname);
    return;
  }
  if (Vio_connect(sock, 0) < 0) {
    Vnm_print(2, "Vgrid_writeUHBD: Problem connecting virtual socket %s\n",
        fname);
    return;
  }

  /* Get the lower corner and number of grid points for the local
   * partition */
  hx = thee->hx;
  hy = thee->hy;
  hzed = thee->hzed;
  nx = thee->nx;
  ny = thee->ny;
  nz = thee->nz;
  xmin = thee->xmin;
  ymin = thee->ymin;
  zmin = thee->zmin;

  /* Let interested folks know that partition information is ignored */
  if (pvec != VNULL) {
    gotit = 0;
    for (i=0; i<(nx*ny*nz); i++) {
      if (pvec[i] == 0) {
        gotit = 1;
        break;
      }
    }
    if (gotit) {
      Vnm_print(2, "Vgrid_writeUHBD:  IGNORING PARTITION INFORMATION!\n");
      Vnm_print(2, "Vgrid_writeUHBD:  This means I/O from parallel runs \
          will have significant overlap.\n");
    }
  }

  /* Write out the header */
  Vio_printf(sock, "%72s\n", title);
  Vio_printf(sock, "%12.5e%12.5e%7d%7d%7d%7d%7d\n", 1.0, 0.0, -1, 0,
      nz, 1, nz);
  Vio_printf(sock, "%7d%7d%7d%12.5e%12.5e%12.5e%12.5e\n", nx, ny, nz,
      hx, (xmin-hx), (ymin-hx), (zmin-hx));
  Vio_printf(sock, "%12.5e%12.5e%12.5e%12.5e\n", 0.0, 0.0, 0.0, 0.0);
  Vio_printf(sock, "%12.5e%12.5e%7d%7d", 0.0, 0.0, 0, 0);

  /* Write out the entries */
  icol = 0;
  for (k=0; k<nz; k++) {
    Vio_printf(sock, "\n%7d%7d%7d\n", k+1, thee->nx, thee->ny);
    icol = 0;
    for (j=0; j<ny; j++) {
      for (i=0; i<nx; i++) {
        u = k*(nx)*(ny)+j*(nx)+i;
        icol++;
        Vio_printf(sock, " %12.5e", thee->data[u]);
        if (icol == 6) {
          icol = 0;
          Vio_printf(sock, "\n");
        }
      }
    }
  }
  if (icol != 0) Vio_printf(sock, "\n");

  /* Close off the socket */
  Vio_connectFree(sock);
  Vio_dtor(&sock);
}

VPUBLIC double Vgrid_integrate(Vgrid *thee) {

  size_t i, j, k;
  int nx, ny, nz;
  double sum, w;

  if (thee == VNULL) {
    Vnm_print(2, "Vgrid_integrate:  Got VNULL thee!\n");
    VASSERT(0);
  }

  nx = thee->nx;
  ny = thee->ny;
  nz = thee->nz;

  sum = 0.0;

  for (k=0; k<nz; k++) {
    w = 1.0;
    if ((k==0) || (k==(nz-1))) w = w * 0.5;
    for (j=0; j<ny; j++) {
      w = 1.0;
      if ((j==0) || (j==(ny-1))) w = w * 0.5;
      for (i=0; i<nx; i++) {
        w = 1.0;
        if ((i==0) || (i==(nx-1))) w = w * 0.5;
        sum = sum + w*(thee->data[IJK(i,j,k)]);
      }
    }
  }

  sum = sum*(thee->hx)*(thee->hy)*(thee->hzed);

  return sum;

}


VPUBLIC double Vgrid_normL1(Vgrid *thee) {

  size_t i, j, k;
  int nx, ny, nz;
  double sum;

  if (thee == VNULL) {
    Vnm_print(2, "Vgrid_normL1:  Got VNULL thee!\n");
    VASSERT(0);
  }

  nx = thee->nx;
  ny = thee->ny;
  nz = thee->nz;

  sum = 0.0;
  for (k=0; k<nz; k++) {
    for (j=0; j<ny; j++) {
      for (i=0; i<nx; i++) {
        sum = sum + VABS(thee->data[IJK(i,j,k)]);
      }
    }
  }

  sum = sum*(thee->hx)*(thee->hy)*(thee->hzed);

  return sum;

}

VPUBLIC double Vgrid_normL2(Vgrid *thee) {

  size_t i, j, k;
  int nx, ny, nz;
  double sum;

  if (thee == VNULL) {
    Vnm_print(2, "Vgrid_normL2:  Got VNULL thee!\n");
    VASSERT(0);
  }

  nx = thee->nx;
  ny = thee->ny;
  nz = thee->nz;

  sum = 0.0;
  for (k=0; k<nz; k++) {
    for (j=0; j<ny; j++) {
      for (i=0; i<nx; i++) {
        sum = sum + VSQR(thee->data[IJK(i,j,k)]);
      }
    }
  }

  sum = sum*(thee->hx)*(thee->hy)*(thee->hzed);

  return VSQRT(sum);

}

VPUBLIC double Vgrid_seminormH1(Vgrid *thee) {

  size_t i, j, k;
  int nx, ny, nz, d;
  double pt[3], grad[3], sum, hx, hy, hzed, xmin, ymin, zmin;

  if (thee == VNULL) {
    Vnm_print(2, "Vgrid_seminormH1:  Got VNULL thee!\n");
    VASSERT(0);
  }

  nx = thee->nx;
  ny = thee->ny;
  nz = thee->nz;
  hx = thee->hx;
  hy = thee->hy;
  hzed = thee->hzed;
  xmin = thee->xmin;
  ymin = thee->ymin;
  zmin = thee->zmin;

  sum = 0.0;
  for (k=0; k<nz; k++) {
    pt[2] = k*hzed + zmin;
    for (j=0; j<ny; j++) {
      pt[1] = j*hy + ymin;
      for (i=0; i<nx; i++) {
        pt[0] = i*hx + xmin;
        VASSERT(Vgrid_gradient(thee, pt, grad));
        for (d=0; d<3; d++) sum = sum + VSQR(grad[d]);
      }
    }
  }

  sum = sum*(hx)*(hy)*(hzed);

  if (VABS(sum) < VSMALL) sum = 0.0;
  else sum = VSQRT(sum);

  return sum;

}

VPUBLIC double Vgrid_normH1(Vgrid *thee) {

  double sum = 0.0;

  if (thee == VNULL) {
    Vnm_print(2, "Vgrid_normH1:  Got VNULL thee!\n");
    VASSERT(0);
  }

  sum = VSQR(Vgrid_seminormH1(thee)) + VSQR(Vgrid_normL2(thee));

  return VSQRT(sum);

}

VPUBLIC double Vgrid_normLinf(Vgrid *thee) {

  size_t i, j, k;
  int nx, ny, nz, gotval;
  double sum, val;

  if (thee == VNULL) {
    Vnm_print(2, "Vgrid_normLinf:  Got VNULL thee!\n");
    VASSERT(0);
  }

  nx = thee->nx;
  ny = thee->ny;
  nz = thee->nz;

  sum = 0.0;
  gotval = 0;
  for (k=0; k<nz; k++) {
    for (j=0; j<ny; j++) {
      for (i=0; i<nx; i++) {
        val = VABS(thee->data[IJK(i,j,k)]);
        if (!gotval) {
          gotval = 1;
          sum = val;
        }
        if (val > sum) sum = val;
      }
    }
  }

  return sum;

}
