/*--------------------------------------------------------------------------*/
/* alberta_meshtv: An interface from ALBERTA to the visualization package   */
/*                 MeshTV                                                   */
/*                                                                          */
/* file: alberta_meshtv.c                                                   */
/*                                                                          */
/*                                                                          */
/* description: This program converts ALBERTA meshes and dof_real_[d_]vecs  */
/*              to SILO format understood by MeshTV.                        */
/*              Program calling syntax was adopted from albertagrape*.      */
/*--------------------------------------------------------------------------*/
/*                                                                          */
/*  authors:   Daniel Koester                                               */
/*             Institut fuer Mathematik                                     */
/*             Universitaet Augsburg                                        */
/*             Universitaetsstr. 14                                         */
/*             D-86159 Augsburg, Germany                                    */
/*                                                                          */
/*  (c) by D. Koester (2004)                                                */
/*                                                                          */
/*--------------------------------------------------------------------------*/

#include <stdio.h>
#include <getopt.h>
#include <ctype.h>
#include <silo.h>
#include "alberta.h"


/***************************************************************************/
/* convert_string(in): Replace all non-alphanumeric characters in "string" */
/*                     with underscores.                                   */
/***************************************************************************/
static void convert_string(char *pos)
{
  for(; *pos; pos++)
    if(!isalnum((int) *pos)) *pos = '_';
}

/***************************************************************************/
/* add_mesh(silo, mesh): Convert given ALBERTA mesh to SILO UCD format and */
/* add the result to the output file. Return value is a pointer to the     */
/* SILO name of the mesh (needed for the output of subsequent              */
/* DOF_REAL_[D_]VECS).                                                     */
/***************************************************************************/

#if DIM_OF_WORLD > 1
static char *add_mesh(DBfile *silo, MESH *mesh)
{
  FUNCNAME("add_mesh");
  MACRO_DATA *data;
  int i,j;
  REAL *new_coords[DIM_OF_WORLD];
  int shapesize[1];
  int shapecnt[1];
#if DIM_OF_WORLD == 3
  DBfacelist *facelist = NULL;
#endif
  static int meshcount;
  static char mesh_name[1024];
  char zonelist_name[1024] = { '\0', };
  char facelist_name[1024] = { '\0', };


/* Step 0: Unfortunately, this stupidity turned out to be necessary. SILO  */
/* demands a unique name for each object!!                                 */

  ++meshcount;

  if(strlen(mesh->name) > 900)
    ERROR_EXIT("mesh->name is too long!\n");

  convert_string((char *)mesh->name);

  sprintf(mesh_name, "%s%d", mesh->name, meshcount);
  sprintf(zonelist_name, "%s_ZONELIST", mesh_name); 
  sprintf(facelist_name, "%s_FACELIST", mesh_name); 
  
/* Step 1: convert the current mesh to a macro triangulation, which        */
/* has index-based information about the current triangulation.            */
/* Some checks on mesh sanity are also done by this routine.               */

  data = mesh2macro_data(mesh);

/* Step 2: The coordinate array needs to be resorted:                      */
/* Instead of (c[0][0] ... c[0][DOW-1] c[1][0] ... c[1][DOW-1]...) we need */
/* (c[0][0] ... c[N][0]), (c[0][1] ... c[N][1]), ...(c[0][DOW-1]...        */

  for(i = 0; i < DIM_OF_WORLD; i++) {
    new_coords[i] = MEM_ALLOC(data->n_total_vertices, REAL);

    for(j = 0; j < data->n_total_vertices; j++)
      new_coords[i][j] = data->coords[j][i];
  }

/* Step 3: Write a SILO zonelist object to file "silo".                    */

  shapesize[0] = N_VERTICES(DIM_OF_WORLD);
  shapecnt[0] = data->n_macro_elements;

  TEST_EXIT(!DBPutZonelist(silo,                          /* The SILO file */
			   zonelist_name,   /* name for the zonelist object */
			   data->n_macro_elements,       /* number of zones */
			   DIM_OF_WORLD,    /* number of spacial dimensions */
			   data->mel_vertices,              /* node indices */
			   data->n_macro_elements              /* length of */
			   * N_VERTICES(DIM_OF_WORLD),/* data->mel_vertices */
			   0,             /* origin of indices in the array */
			   shapesize,  /* array containing size of elements */ 
			   shapecnt,       /* no. of elements of each shape */
			   1),				  /* no. of shapes */
	    "Could not write zone list!\n");


#if DIM_OF_WORLD == 3
/* Step 4: Have SILO calculate an external face list for us.               */
  
  facelist = DBCalcExternalFacelist(
                  data->mel_vertices,             /* array of node indices */
		  data->n_total_vertices,          /* no. of nodes in mesh */
		  0,                                  /* origin of indices */
		  shapesize,	      /* array containing size of elements */
		  shapecnt,		  /* no. of elements of each shape */
		  1,					  /* no. of shapes */
		  NULL,				  /* material number array */
		  0);			   /* method for calculating faces */

  TEST_EXIT(facelist, "Could not calculate external face list!\n");

/* Step 5: Write the face list to the SILO file.                           */

  TEST_EXIT(!DBPutFacelist(silo,			  /* The SILO file */
			   facelist_name,    /* name of the facelist object */
			   facelist->nfaces,   /* number of external faces */
			   DIM_OF_WORLD,         /* number of dims in mesh */
			   facelist->nodelist,     /* indices of all faces */
			   facelist->lnodelist,      /* length of nodelist */
			   facelist->origin,          /* origin of indices */
			   NULL,               /* zone number of each face */
			   facelist->shapesize,     /* array of face sizes */
			   facelist->shapecnt,/* no. of faces of each size */
			   facelist->nshapes,         /* no. of face sizes */
			   NULL,              /* face types, not necessary */
			   NULL,       /* identifier for each type, unused */
			   0),                /* no. of face types, unused */
	    "Could not write face list!\n");
#endif

/* Step 5: Write the mesh to the SILO file.                                */

  TEST_EXIT(!DBPutUcdmesh(silo,
			  mesh_name,
			  DIM_OF_WORLD,
			  NULL,        /* ndims-length array of axis names */
			  (float **)(void *)new_coords,    /* stupid conversion... */
			  data->n_total_vertices,
			  data->n_macro_elements,
			  zonelist_name,
			  DIM_OF_WORLD < 3 ? NULL : facelist_name,
/* A primitive hack to get the correct data format                         */
			  (sizeof(REAL)==sizeof(double))?DB_DOUBLE:DB_FLOAT,
			  NULL),
	    "Could not write mesh!\n");

/* Step 6: Clean up.                                                       */

  for(i = 0; i < DIM_OF_WORLD; i++)
    MEM_FREE(new_coords[i], data->n_total_vertices, REAL);
  free_macro_data(data);

  return(mesh_name);
}
#endif

/***************************************************************************/
/* add_drv(silo, mesh, mesh_name, drv): Convert given ALBERTA DOF_REAL_VEC */
/* to SILO format and add the result to the output file.                   */
/***************************************************************************/

void add_drv(DBfile *silo, MESH *mesh, char *mesh_name, DOF_REAL_VEC *drv)
{
  FUNCNAME("add_drv");
  REAL            *new_vec;
  static int       drv_count = 0;
  char             drv_name[1024];
  int              i, n0, index = 0;
  int              is_node_centered = true;
  int             *vert_ind = NULL;
  const EL_INFO   *el_info = NULL;
  DOF            **local_dof_ptr;
  DOF_INT_VEC     *dof_vert_ind = NULL;
  const DOF_ADMIN *admin = drv->fe_space->admin;
  TRAVERSE_STACK  *stack = get_traverse_stack();

/* Step 0: The same naming hysteria as above...                            */

  ++drv_count;
  if(strlen(drv->name) > 900)
    ERROR_EXIT("drv.name is too long!\n");

  convert_string((char *)drv->name);

  sprintf(drv_name, "%s%d", drv->name, drv_count);
 
/* Step 1: We decide whether the values are node-centered or zone-centered */
/* based on the value of drv->fe_space->bas_fcts->ndof.                    */

  if(drv->fe_space->bas_fcts->n_dof[VERTEX])
    is_node_centered = true;
  else if(drv->fe_space->bas_fcts->n_dof[CENTER])
    is_node_centered = false;
  else
    ERROR_EXIT("Could not determine centering type of data (n_dof[VERTEX] and n_dof[CENTER] both NULL).\n");

  if(is_node_centered) {
    new_vec = MEM_ALLOC(mesh->n_vertices, REAL);

    n0 = admin->n0_dof[VERTEX];
    
    dof_vert_ind = get_dof_int_vec("vertex indices", drv->fe_space);
    GET_DOF_VEC(vert_ind, dof_vert_ind);
    FOR_ALL_DOFS(admin, vert_ind[dof] = 0);
  }
  else {
    new_vec = MEM_ALLOC(mesh->n_elements, REAL);

    n0 = admin->n0_dof[CENTER];
  }

/* Step 2: We need to copy the correct values of drv->vec into new_vec.     */

  for (el_info = traverse_first(stack, mesh, -1, CALL_LEAF_EL);
       el_info;
       el_info=traverse_next(stack,el_info)) {

    local_dof_ptr = el_info->el->dof;

    if(is_node_centered) {
      for (i = 0; i < N_VERTICES(DIM_OF_WORLD); i++)
	if (!vert_ind[local_dof_ptr[mesh->node[VERTEX] + i][n0]]) {
/* assign a global index to each vertex in the same way as above.           */
	  vert_ind[local_dof_ptr[i][n0]] = -1;    
	  
	  new_vec[index] = drv->vec[local_dof_ptr[i][n0]];

	  index++;
	}
    }
    else {
/* just use the first CENTER dof of each element.                           */
      new_vec[index] = drv->vec[local_dof_ptr[mesh->node[CENTER]][n0]];

      index++;
    }
  }

/* Just another cheap check to see if everything is alright.               */
  if(is_node_centered)
    TEST_EXIT(index == mesh->n_vertices, "Wrong no. of vertices counted!\n");
  else
    TEST_EXIT(index == mesh->n_elements, "Wrong no. of elements counted!\n");

/* Step 3: Write the node values to the SILO file.                          */

  TEST_EXIT(!DBPutUcdvar1(silo,
			  drv_name,
			  mesh_name,
			  (float *)new_vec,
			  index, 		       /* length of new_vec */
			  NULL,		       /* mixed-data values, unused */
			  0,	      /* length of mixed-data array, unused */
			  (sizeof(REAL)==sizeof(double))?DB_DOUBLE:DB_FLOAT,
			  (is_node_centered ? DB_NODECENT : DB_ZONECENT),
			  NULL),			 /* optlist, unused */
	    "Could not write DOF_REAL_VEC %s to file!\n", drv->name);

/* Step 4: Clean up.                                                        */

  MEM_FREE(new_vec, index, REAL);
  free_traverse_stack(stack);
  free_dof_int_vec(dof_vert_ind);
}


/***************************************************************************/
/* add_drdv(silo, mesh, mesh_name, drv): Convert given DOF_REAL_D_VEC      */
/* to SILO format and add the result to the output file.                   */
/***************************************************************************/

void add_drdv(DBfile *silo, MESH *mesh, char *mesh_name, DOF_REAL_D_VEC *drdv)
{
  FUNCNAME("add_drdv");
  REAL            *new_vec[DIM_OF_WORLD];
  static int       drdv_count = 0;
  char             drdv_name[1024];
  int              i, j, n0, index = 0;
  int              is_node_centered = true;
  int             *vert_ind = NULL;
  const char      *varnames[3] = {"x", "y", "z"};
  const EL_INFO   *el_info = NULL;
  DOF            **local_dof_ptr;
  DOF_INT_VEC     *dof_vert_ind = NULL;
  const DOF_ADMIN *admin = drdv->fe_space->admin;
  TRAVERSE_STACK  *stack = get_traverse_stack();

/* Step 0: The same naming hysteria as above...                            */

  ++drdv_count;
  if(strlen(drdv->name) > 900)
    ERROR_EXIT("drdv.name is too long!\n");

  convert_string((char *)drdv->name);

  sprintf(drdv_name, "%s%d", drdv->name, drdv_count);
 
/* Step 1: We decide whether the values are node-centered or zone-centered */
/* based on the value of drdv->fe_space->bas_fcts->ndof.                   */

  if(drdv->fe_space->bas_fcts->n_dof[VERTEX])
    is_node_centered = true;
  else if(drdv->fe_space->bas_fcts->n_dof[CENTER])
    is_node_centered = false;
  else
    ERROR_EXIT("Could not determine centering type of data (n_dof[VERTEX] and n_dof[CENTER] both NULL).\n");

  if(is_node_centered) {
    for(i = 0; i < DIM_OF_WORLD; i++)
      new_vec[i] = MEM_ALLOC(mesh->n_vertices, REAL);

    n0 = admin->n0_dof[VERTEX];
    
    dof_vert_ind = get_dof_int_vec("vertex indices", drdv->fe_space);
    GET_DOF_VEC(vert_ind, dof_vert_ind);
    FOR_ALL_DOFS(admin, vert_ind[dof] = 0);
  }
  else {
    for(i = 0; i < DIM_OF_WORLD; i++)
      new_vec[i] = MEM_ALLOC(mesh->n_elements, REAL);

    n0 = admin->n0_dof[CENTER];
  }

/* Step 2: We need to copy the correct values of drdv->vec into new_vec.    */

  for (el_info = traverse_first(stack, mesh, -1, CALL_LEAF_EL);
       el_info;
       el_info=traverse_next(stack,el_info)) {

    local_dof_ptr = el_info->el->dof;

    if(is_node_centered) {
      for (i = 0; i < N_VERTICES(DIM_OF_WORLD); i++)
	if (!vert_ind[local_dof_ptr[mesh->node[VERTEX] + i][n0]]) {
/* assign a global index to each vertex in the same way as above.           */
	  vert_ind[local_dof_ptr[i][n0]] = -1;    
	  
	  for (j = 0; j < DIM_OF_WORLD; j++) 
	    new_vec[j][index] = drdv->vec[local_dof_ptr[i][n0]][j];

	  index++;
	}
    }
    else {
/* just use the first CENTER dof of each element.                           */
      for (j = 0; j < DIM_OF_WORLD; j++) 
	new_vec[j][index] =
	  drdv->vec[local_dof_ptr[mesh->node[CENTER]][n0]][j];

      index++;
    }
  }

/* Just another cheap check to see if everything is alright.               */
  if(is_node_centered)
    TEST_EXIT(index == mesh->n_vertices, "Wrong no. of vertices counted!\n");
  else
    TEST_EXIT(index == mesh->n_elements, "Wrong no. of elements counted!\n");

/* Step 3: Write the node values to the SILO file.                          */

  TEST_EXIT(!DBPutUcdvar(silo,
			 drdv_name,
			 mesh_name,
			 DIM_OF_WORLD,
			 (char **)varnames,
			 (float **)(void *)new_vec, /*??? new_vec is double!!!! */
			 index, 		       /* length of new_vec */
			 NULL,		       /* mixed-data values, unused */
			 0,	      /* length of mixed-data array, unused */
			 (sizeof(REAL)==sizeof(double))?DB_DOUBLE:DB_FLOAT,
			 (is_node_centered ? DB_NODECENT : DB_ZONECENT),
			 NULL),			 /* optlist, unused */
    "Could not write DOF_REAL_VEC %s to file!\n", drdv->name);

/* Step 4: Clean up.                                                        */

  for (j = 0; j < DIM_OF_WORLD; j++) 
    MEM_FREE(new_vec[j], index, REAL);
  free_traverse_stack(stack);
  free_dof_int_vec(dof_vert_ind);
}

#if DIM_OF_WORLD == 1
/***************************************************************************/
/* add_curve(silo, mesh, drv): Convert given "drv" to a SILO curve object  */
/* and write the result to the output file. This method of plotting does   */
/* not seem to be fully supported my MeshTV at the moment. :-(             */
/***************************************************************************/

static void add_curve(DBfile *silo, MESH *mesh, DOF_REAL_VEC *drv)
{
  FUNCNAME("add_curve");
  REAL            *coord_vec;
  REAL            *data_vec;
  static int       drv_count = 0;
  char             drv_name[1024];
  int              i, n0, index = 0;
  int              is_node_centered = true;
  int             *vert_ind = NULL;
  const EL_INFO   *el_info = NULL;
  DOF            **local_dof_ptr;
  DOF_INT_VEC     *dof_vert_ind = NULL;
  const DOF_ADMIN *admin = drv->fe_space->admin;
  TRAVERSE_STACK  *stack = get_traverse_stack();

/* Step 0: The same naming hysteria as above...                            */

  ++drv_count;
  if(strlen(drv->name) > 900)
    ERROR_EXIT("drv.name is too long!\n");

  convert_string((char *)drv->name);

  sprintf(drv_name, "%s%d", drv->name, drv_count);
 
/* Step 1: We decide whether the values are node-centered or zone-centered */
/* based on the value of drv->fe_space->bas_fcts->ndof.                    */

  if(drv->fe_space->bas_fcts->n_dof[VERTEX])
    is_node_centered = true;
  else if(drv->fe_space->bas_fcts->n_dof[CENTER])
    is_node_centered = false;
  else
    ERROR_EXIT("Could not determine centering type of data (n_dof[VERTEX] and n_dof[CENTER] both NULL).\n");

  if(is_node_centered) {
    coord_vec = MEM_ALLOC(mesh->n_vertices, REAL);
    data_vec = MEM_ALLOC(mesh->n_vertices, REAL);

    n0 = admin->n0_dof[VERTEX];
    
    dof_vert_ind = get_dof_int_vec("vertex indices", drv->fe_space);
    GET_DOF_VEC(vert_ind, dof_vert_ind);
    FOR_ALL_DOFS(admin, vert_ind[dof] = 0);
  }
  else {
    coord_vec = MEM_ALLOC(mesh->n_elements, REAL);
    data_vec = MEM_ALLOC(mesh->n_elements, REAL);

    n0 = admin->n0_dof[CENTER];
  }

/* Step 2: We need to copy the correct values of drv->vec into new_vec.     */

  for (el_info = traverse_first(stack, mesh, -1, CALL_LEAF_EL|FILL_COORDS);
       el_info;
       el_info=traverse_next(stack,el_info)) {

    local_dof_ptr = el_info->el->dof;

    if(is_node_centered) {
      for (i = 0; i < 2; i++)
	if (!vert_ind[local_dof_ptr[mesh->node[VERTEX] + i][n0]]) {
/* assign a global index to each vertex in the same way as above.           */
	  vert_ind[local_dof_ptr[i][n0]] = -1;    

	  coord_vec[index] = el_info->coord[i][0];
	  data_vec[index] = drv->vec[local_dof_ptr[i][n0]];

	  index++;
	}
    }
    else {
/* just use the first CENTER dof of each element.                           */
      coord_vec[index] = 0.5 *(el_info->coord[0][0] + el_info->coord[1][0]);
      data_vec[index] = drv->vec[local_dof_ptr[mesh->node[CENTER]][n0]];

      index++;
    }
  }

/* Just another cheap check to see if everything is alright.               */
  if(is_node_centered)
    TEST_EXIT(index == mesh->n_vertices, "Wrong no. of vertices counted!\n");
  else
    TEST_EXIT(index == mesh->n_elements, "Wrong no. of elements counted!\n");

/* Step 3: Write the node values to the SILO file.                          */

  TEST_EXIT(!DBPutCurve(silo,
			drv_name,
			coord_vec,
			data_vec,
			(sizeof(REAL)==sizeof(double))?DB_DOUBLE:DB_FLOAT,
			index, 		               /* length of vectors */
			NULL),
	    "Could not write DOF_REAL_VEC %s to file!\n", drv->name);

/* Step 4: Clean up.                                                        */

  MEM_FREE(data_vec, index, REAL);
  MEM_FREE(coord_vec, index, REAL);
  free_traverse_stack(stack);
  free_dof_int_vec(dof_vert_ind);
}
#endif

/***************************************************************************/
/* print_help(): Print the error/help message to file "f" when calling the */
/* program "myname" from the command line. Exit with "status".             */
/***************************************************************************/

static void print_help(DBfile *silo, const char *myname, FILE *f, int status)
{
  fprintf(f,
"Usage: %s [-p PATH] [OPTIONS]\n"					      \
"   -o SILOFILE\n"							      \
"   -m MESH [-s DRV] [-s DRV2] ... [-v DRDV] [-v DRDV2]..."		      \
"  [-m MESH2 ...]\n"							      \
"\n"									      \
"Example:\n"								      \
"  %s --mesh=mymesh -s temperature --vector velocity -o tvmesh\n"	      \
"    where \"mymesh\", \"temperature\" and \"velocity\" are file-names.\n"    \
"\n"									      \
"If a long option shows an argument as mandatory, then it is mandatory\n"     \
"for the equivalent short option also.  Similarly for optional arguments.\n"  \
"\n"									      \
"The order of the options _is_ significant in the following cases:\n"	      \
"`-p PATH' alters the search path for all following data-files.\n"	      \
"`-m MESH' specifies a mesh for all following DRVs and DRDVs (see below)\n"   \
"\n"								              \
"Options:\n"								      \
"  -m, --mesh=MESH\n"							      \
"            The file-name of an ALBERTA-mesh gnereated by the ALBERTA\n"     \
"            library routines `write_mesh()' or `write_mesh_xdr()'\n"	      \
"            `-x' and `-b' options below.\n"				      \
"            This option is mandatory and may not be omitted. This option\n"  \
"            may be specified multiple times. All following dof-vectors\n"    \
"            given by the `-s' and `-v' options must belong to the most\n"    \
"            recently specified mesh.\n"				      \
"            NOTE: Only dim==DIM_OF_WORLD=2,3 is possible!\n"                 \
"  -b, --binary\n"							      \
"            Expect MESH, DRV and DRDV to contain data in host dependent\n"   \
"            byte-order, generated by `write_SOMETHING()' routines of the\n"  \
"            ALBERTA library (SOMETHING is `mesh', `dof_real_vec' etc.\n"     \
"  -x, --xdr\n"								      \
"            This is the default and just mentioned here for completeness.\n" \
"            Expect MESH, DRV and DRDV to contain data in network\n"	      \
"            byte-order, generated by `write_SOMETHING_xdr()' routines\n"     \
"            of the ALBERTA library. Per convention this means big-endian\n"  \
"            byte-order.\n"						      \
"  -s, --scalar=DRV\n"							      \
"            Load the data-file DRV which must contain a DOF_REAL_VEC\n"      \
"            dumped to disk by `write_dof_real_vec[_xdr]()'.\n"		      \
"            This option may be specified multiple times. The DOF_REAL_VECs\n"\
"            must belong to the most recently specified mesh.\n"	      \
"            See `-m' and `-b' above.\n"			       	      \
"  -v, --vector=DRDV\n"							      \
"            Load the data-file DRDV which must contain a DOF_REAL_D_VEC\n"   \
"            dumped to disk by `write_dof_real_d_vec[_xdr]()'.\n"	      \
"            This option may be specified multiple times. The vector\n"	      \
"            must belong to the most recently specified mesh.\n"	      \
"            See `-m' and `-b' above.\n"				      \
"  -o, --output=SILOFILE\n"						      \
"            Specify an output file name in SILO format. Must come before\n"  \
"            the first mesh file, otherwise 'output.silo' is taken as\n"      \
"            the default value.\n"					      \
"  -p, --path=PATH\n"							      \
"            Specify a path prefix for all following files. This option\n"    \
"            may be specified multiple times. PATH is supposed to be the\n"   \
"            directory containing all data-files specified by the following\n"\
"            `-m', `-s' and `-v' options.\n"				      \
"  -h, --help\n"							      \
"            Print this help.\n",
	  myname, myname);
  
/* Be nice and close the silo file before exiting.                           */

  if(silo)
    DBClose(silo);    

  exit(status);
}

static char *filename(const char *path, const char *fn)
{
  static char  name[1024];
 
  if (!fn) return(NULL);
 
  if (path == NULL || path[0] == '\0')
  {
    sprintf(name, "./%s", fn);
  }
  else
  {
    const char *cp = path;
    while (*cp)
      cp++;
    cp--;
    if (*cp == '/')
      sprintf(name, "%s%s", path, fn);
    else
      sprintf(name, "%s/%s", path, fn);
  }
  return(name);
}

static struct option long_options[] = {
  {"help",   0, 0, 'h' },
  {"binary", 0, 0, 'b' },
  {"xdr",    0, 0, 'x' },
  {"output", 1, 0, 'o' },
  {"path",   1, 0, 'p' },
  {"mesh",   1, 0, 'm' },
  {"scalar", 1, 0, 's' },
  {"vector", 1, 0, 'v' },
  { NULL,    0, 0, '\0' }
};

int main(int argc, char **argv)
{
  FUNCNAME("main");
  DBfile         *silo = NULL;
  char           *silo_filename = "output.silo";
  MESH           *mesh = NULL;
  char           *mesh_name = NULL;
  char           *mesh_filename = NULL;
  DOF_REAL_VEC   *drv = NULL;
  DOF_REAL_D_VEC *drdv = NULL;
  char           *fn, *path = NULL;
  int             c, option_index;

  MESH *(*rm)(const char *, REAL *,
	      NODE_PROJECTION *(*init_node_proj)(MESH *, MACRO_EL *, int),
	      MESH *master);
  DOF_REAL_VEC   *(*rdrv)(const char *, MESH *, FE_SPACE *);
  DOF_REAL_D_VEC *(*rdrdv)(const char *, MESH *, FE_SPACE *);

  rm = read_mesh_xdr;
  rdrv = read_dof_real_vec_xdr;
  rdrdv = read_dof_real_d_vec_xdr;

  while (1) {
    c = getopt_long(argc, argv, "bhm:o:s:v:x", long_options, &option_index);
    if (c == -1)
      break;
    switch (c) {
	case 'b':
	  rm = read_mesh;
	  rdrv = read_dof_real_vec;
	  rdrdv = read_dof_real_d_vec;
	  break;
	case 'h':
	  print_help(silo, argv[0], stdout, 0);
	  break;
	case 'o':
	  if(silo) {
	    ERROR("an output file was already specified!\n");
	    print_help(silo, argv[0], stderr, 1);
	  }	    
	  silo_filename = optarg;
	  break;
	case 'm':
	  mesh_filename = optarg;
	  fn = filename(path, mesh_filename);
	  MSG("reading mesh `%s'\n", fn);
	  mesh = rm(fn, NULL, NULL, NULL);
	  TEST_EXIT(mesh, "could not read mesh file `%s'!\n", fn);
	  TEST_EXIT(mesh->dim == DIM_OF_WORLD,
		    "mesh.dim must equal DIM_OF_WORLD == %d!\n", DIM_OF_WORLD);

	  if(!silo) {
	    fn = filename(path, silo_filename);
	    MSG("Creating SILO file '%s'\n", fn);
	    silo = DBCreate(fn, DB_CLOBBER, DB_LOCAL, NULL, DB_PDB);
	    TEST_EXIT(silo, "could not create SILO file '%s'!\n", fn);
	  }
/* If DIM_OF_WORLD == 1, then we do not write a mesh, since this is not      */
/* supported by SILO. Instead we simply use DBPutCurve.                      */
#if DIM_OF_WORLD > 1
	  MSG("adding mesh '%s' to SILO file '%s'\n",
	      mesh_filename, silo_filename);
	  mesh_name = add_mesh(silo, mesh);
#endif
	  break;
	case 'p':
	  path = optarg;
	  break;
	case 's':
	  if (!mesh) {
	    ERROR("a mesh has to be given before any functions!\n");
	    print_help(silo, argv[0], stderr, 1);
	  }
	  fn = filename(path, optarg);
	  MSG("reading dof_real_vec `%s'\n", fn);
	  drv = (*rdrv)(fn, mesh, NULL);
	  TEST_EXIT(drv, "could not read dof_real_vec `%s'!\n", fn);
	  MSG("adding DOF_REAL_VEC '%s' to SILO file '%s'\n",
	      drv->name, silo_filename);
#if DIM_OF_WORLD > 1
	  add_drv(silo, mesh, mesh_name, drv);
#else
	  add_curve(silo, mesh, drv);
#endif
	  break;
	case 'v':
#if DIM_OF_WORLD ==1
	  ERROR_EXIT("A DOF_REAL_D_VEC in 1D? Please use DOF_REAL_VECs instead!\n");
#endif
	  if (!mesh) {
	    ERROR("a mesh has to be given before any functions!\n");
	    print_help(silo, argv[0], stderr, 1);
	  }
	  fn = filename(path, optarg);
	  MSG("reading dof_real_d_vec `%s'\n", fn);
	  drdv = (*rdrdv)(fn, mesh, NULL);
	  TEST_EXIT(drdv, "could not read dof_real_d_vec `%s'!\n", fn);
	  MSG("adding DOF_REAL_D_VEC '%s' to SILO file '%s'\n",
	      drdv->name, silo_filename);
	  add_drdv(silo, mesh, mesh_name, drdv);
	  break;
	case 'x':
	  rm = read_mesh_xdr;
	  rdrv = read_dof_real_vec_xdr;
	  rdrdv = read_dof_real_d_vec_xdr;
	  break;
	default:
	  print_help(silo, argv[0], stderr, 1);
    }
  }
  
  if (mesh_filename == NULL) {
    ERROR("a mesh has to be given!\n\n");
    print_help(silo, argv[0], stderr, 1);
  }

  DBClose(silo);

  return(0);
}
