/* Cast-a-Box software, Copyright 2003 Adam Powell
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation; either version 2 of the License, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 *
 * The license can be obtained from the web at http://www.gnu.org/licenses/ .
 *
 * If obtained via MIT OpenCourseWare (http://ocw.mit.edu/), you may use it
 * under the terms of the MIT Open Courseware license instead; this can be
 * obtained from the web at http://ocw.mit.edu/global/terms-of-use.html .
 *
 * $Header$
 */

#include <stdlib.h> /* For atoi() */
#include <string.h>
/* This also #includes petscda.h, petscvec.h and several other dependencies. */
#include <illuminator.h>

/* Compile with -DDEBUG to print debugging information */
#undef DPRINTF
#ifdef DEBUG
#define DPRINTF(fmt, args...) ierr = PetscPrintf (PETSC_COMM_WORLD, "%s: " fmt, __FUNCT__, args); CHKERRQ (ierr)
#else
#define DPRINTF(fmt, args...)
#endif

/* This is printed (along with a TON of other stuff) when you run:
   castabox -help */
static char help[] =
"Cast-a-box version 0.5 by Adam Powell September 25, 2005\n\
\n\
This very simple program simulates 3-D heat conduction and solidification of\n\
a substance in one quarter of a box mold.  It uses the enthalpy method for\n\
solidification front tracking (which also permits modification to model a\n\
freezing range) and explicit finite differencing with a uniform grid to\n\
discretize the equations.  There is no fluid flow in this model.\n\
\n\
Internally, it uses PETSc distributed arrays to store the temperature and\n\
enthalpy fields, so it runs efficiently in parallel (where available).\n\
For visualization, it uses the Illuminator library.  For info, see URLs:\n\
  http://www.mcs.anl.gov/petsc/\n\
  http://lyre.mit.edu/~powell/illuminator.html\n\
\n\
You can set parameters at the command line, such as grid and 1/4 box sizes:\n\
  -grid_x ## [default 5]            -width_x ## [default 0.5]\n\
  -grid_y ## [default 5]            -width_y ## [default 0.5]\n\
  -grid_z ## [default 10]           -width_z ## [default 1.0]\n\
\n\
Material properties can be set using:\n\
  -liquid_k ##   [default 1.0]      -solid_k ##     [default 1.0]\n\
  -liquid_rho ## [default 1.0]      -solid_rho ##   [default 1.0]\n\
  -liquid_cp ##  [default 1.0]      -solid_cp ##    [default 1.0]\n\
  -melt_temp ##  [default 1000.0]   -latent_heat ## [default 100.0]\n\
\n\
Finally, initial and boundary conditions are set by:\n\
  -init_temp ##   [default 1200.0]  -init_enthalpy ## [default 300.0]\n\
  -xmax_tenv ##   [default 0.0]     -xmax_h ##        [default 1.0]\n\
  -ymax_tenv ##   [default 0.0]     -ymax_h ##        [default 1.0]\n\
  -bottom_tenv ## [default 0.0]     -bottom_h ##      [default 1.0]\n\
  -top_tenv ##    [default 0.0]     -top_h ##         [default 0.5]\n\
  -top_epssig ##  [default 5.67e-11]\n\
[The top surface has a mixed convection-radiation boundary condition:\n\
q = h (T-Tenv) + epssig (T^4 - Tenv^4)]\n\
\n\
The maximum possible explicit timestep size is calculated automatically from\n\
the larger of the liquid and solid thermal diffusivities and grid spacing.\n\
\n";

static char help_commands[] = 
"Cast-a-box commands:\n\
  h or ?       Print this helpful information\n\
  g nnn        Go nnn timesteps\n\
  p [T1 T2 T3] Plot specified temperature contours (none to print settings)\n\
  o            Turn plotting off\n\
  q or x       Exit\n\
";

/* This just makes it easier to pass all of the parameters between functions */
typedef struct {
  int grid_x, grid_y, grid_z;                  /* Grid dimensions */
  PetscScalar width_x, width_y, width_z;       /* (1/4) box dimensions */
  PetscScalar deltax_m1, deltay_m1, deltaz_m1; /* Inverse square spacings */
  PetscScalar liquid_k, liquid_rho, liquid_cp; /* Liquid properties */
  PetscScalar solid_k, solid_rho, solid_cp;    /* Solid properties */
  PetscScalar melt_temp, latent_heat;          /* Melting behavior */
  PetscScalar xmax_tenv, ymax_tenv, bottom_tenv, top_tenv; /* Boundary temps */
  PetscScalar xmax_h, ymax_h, bottom_h, top_h, top_epssig; /* Boundary hs */
} parameter_values;

/* Tell which field variable in the distributed array is which */
typedef struct {
  PetscScalar T, H;
} Field;
#define TEMPERATURE 0
#define ENTHALPY 1


/* This is the T(H) function, the inverse of the H(T) function */
static inline PetscScalar temperature(PetscScalar enthalpy, parameter_values p)
{
  /* This uses reference enthalpy=0 for solid at the melting point */
  if (enthalpy < 0)
    return p.melt_temp + enthalpy/p.solid_rho/p.solid_cp;

  /* This will have to be modified for a freezing range */
  if (enthalpy > p.latent_heat)
    return p.melt_temp + (enthalpy-p.latent_heat)/p.liquid_rho/p.liquid_cp;

  /* This too */
  return p.melt_temp;
}


/* This gives the liquid conductivity in the liquid, solid in the
   solid, and a linear interpolation in between */
static inline PetscScalar conductivity
(PetscScalar enthalpy, parameter_values p)
{
  if (enthalpy < 0)
    return p.solid_k;

  if (enthalpy > p.latent_heat)
    return p.liquid_k;

  return p.liquid_k * (enthalpy)/p.latent_heat +
    p.solid_k * (1.-(enthalpy)/p.latent_heat);
}


/* The do_timestep() function does a single timestep */
#undef __FUNCT__
#define __FUNCT__ "do_timestep"
int do_timestep (DA box_da, parameter_values p, PetscScalar deltat)
{
  Vec global, localold;
  static Vec localnew=(Vec)NULL;
  Field ***localold_array, ***localnew_array;
  int xs,ys,zs, xm,ym,zm, gxs,gys,gzs, gxm,gym,gzm, ix,iy,iz, ierr;

  DPRINTF ("Getting the vectors, sending global vector to local.\n",0);
  ierr = DAGetGlobalVector (box_da, &global); CHKERRQ (ierr);
  ierr = DAGetLocalVector (box_da, &localold); CHKERRQ (ierr);
  ierr = DAGlobalToLocalBegin (box_da, global, INSERT_VALUES, localold);
  CHKERRQ (ierr);
  ierr = DAGlobalToLocalEnd (box_da, global, INSERT_VALUES, localold);
  CHKERRQ (ierr);
  if (localnew == NULL)
    {
      DPRINTF ("Creating the static vector to hold the new timestep\n",0);
      ierr = VecDuplicate (localold, &localnew); CHKERRQ (ierr);
    }

  DPRINTF ("Getting corner configuration and creating local arrays.\n",0);
  ierr = DAGetCorners (box_da, &xs,&ys,&zs, &xm,&ym,&zm); CHKERRQ (ierr);
  ierr = DAGetGhostCorners (box_da, &gxs,&gys,&gzs, &gxm,&gym,&gzm);
  CHKERRQ (ierr);
  ierr = DAVecGetArray (box_da, localold, (void **)&localold_array);
  CHKERRQ (ierr);
  ierr = DAVecGetArray (box_da, localnew, (void **)&localnew_array);
  CHKERRQ (ierr);

  DPRINTF ("Doing a timestep of size %g.\n", deltat);
  for (iz=zs; iz<zs+zm; iz++)
    for (iy=ys; iy<ys+ym; iy++)
      for (ix=xs; ix<xs+xm; ix++)
	{
	  PetscScalar flux_zp1,flux_zm1, flux_yp1,flux_ym1, flux_xp1,flux_xm1;

	  /* Calculate flux on x-1 and x+1 faces using average conductivity */
	  if (ix == 0) /* Symmetry boundary condition at x=0 */
	    flux_xm1 = 0.;
	  else
	    flux_xm1 = 0.5 * (conductivity (localold_array[iz][iy][ix-1].H, p)+
			      conductivity (localold_array[iz][iy][ix].H, p)) *
	      (localold_array[iz][iy][ix-1].T - localold_array[iz][iy][ix].T) *
	      p.deltax_m1;

	  if (ix == p.grid_x-1) /* Outer x boundary condition */
	    flux_xp1 = p.xmax_h * (localold_array[iz][iy][ix].T - p.xmax_tenv);
	  else
	    flux_xp1 = 0.5 * (conductivity (localold_array[iz][iy][ix].H, p) +
			      conductivity (localold_array[iz][iy][ix+1].H,p))*
	      (localold_array[iz][iy][ix].T - localold_array[iz][iy][ix+1].T) *
	      p.deltax_m1;

	  /* Calculate flux on y-1 and y+1 faces using average conductivity */
	  if (iy == 0) /* Symmetry boundary condition at y=0 */
	    flux_ym1 = 0.;
	  else
	    flux_ym1 = 0.5 * (conductivity (localold_array[iz][iy-1][ix].H, p)+
			      conductivity (localold_array[iz][iy][ix].H, p)) *
	      (localold_array[iz][iy-1][ix].T - localold_array[iz][iy][ix].T) *
	      p.deltay_m1;

	  if (iy == p.grid_y-1) /* Outer y boundary condition */
	    flux_yp1 = p.ymax_h * (localold_array[iz][iy][ix].T - p.ymax_tenv);
	  else
	    flux_yp1 = 0.5 * (conductivity (localold_array[iz][iy][ix].H, p) +
			      conductivity (localold_array[iz][iy+1][ix].H,p))*
	      (localold_array[iz][iy][ix].T - localold_array[iz][iy+1][ix].T) *
	      p.deltay_m1;

	  /* Calculate flux on z-1 and z+1 faces using average conductivity */
	  if (iz == 0) /* Bottom boundary condition */
	    flux_zm1 = p.bottom_h*(p.bottom_tenv-localold_array[iz][iy][ix].T);
	  else
	    flux_zm1 = 0.5 * (conductivity (localold_array[iz-1][iy][ix].H, p)+
			      conductivity (localold_array[iz][iy][ix].H, p)) *
	      (localold_array[iz-1][iy][ix].T - localold_array[iz][iy][ix].T) *
	      p.deltaz_m1;

	  if (iz == p.grid_z-1) /* Top boundary condition: h, radiation */
	    {
	      flux_zp1 = p.top_h * (localold_array[iz][iy][ix].T - p.top_tenv)+
		p.top_epssig *
		(localold_array[iz][iy][ix].T * localold_array[iz][iy][ix].T *
		 localold_array[iz][iy][ix].T * localold_array[iz][iy][ix].T -
		 p.top_tenv * p.top_tenv * p.top_tenv * p.top_tenv);
	      /* DPRINTF ("Top: ix=%d, iy=%d, T=%g, flux=%g\n", ix, iy,
		 localold_array[iz][iy][ix].T, flux_zp1); */
	    }
	  else
	    flux_zp1 = 0.5 * (conductivity (localold_array[iz][iy][ix].H, p) +
			      conductivity (localold_array[iz+1][iy][ix].H,p))*
	      (localold_array[iz][iy][ix].T - localold_array[iz+1][iy][ix].T) *
	      p.deltaz_m1;

	  /* Calculate new enthalpy */
	  localnew_array[iz][iy][ix].H = localold_array[iz][iy][ix].H + deltat*
	    ((flux_xm1-flux_xp1)*p.deltax_m1 +
	     (flux_ym1-flux_yp1)*p.deltay_m1 +
	     (flux_zm1-flux_zp1)*p.deltaz_m1);

	  /* Calculate new temperature */
	  localnew_array[iz][iy][ix].T =
	    temperature (localnew_array[iz][iy][ix].H, p);
	}

  DPRINTF ("Restoring arrays, copying new values into the old vector.\n",0);
  ierr = DAVecRestoreArray (box_da, localnew, (void **)&localnew_array);
  CHKERRQ (ierr);
  ierr = DAVecRestoreArray (box_da, localold, (void **)&localold_array);
  CHKERRQ (ierr);
  ierr = VecCopy (localnew, localold);

  DPRINTF ("Sending local back to global, restoring vectors.\n",0);
  ierr = DALocalToGlobal (box_da, localold, INSERT_VALUES, global);
  CHKERRQ(ierr);
  ierr = DARestoreLocalVector (box_da, &localold); CHKERRQ (ierr);
  ierr = DARestoreGlobalVector (box_da, &global); CHKERRQ (ierr);
  return 0;
}


/* The timestep_loop() function handles user input and does the timstepping. */
#undef __FUNCT__
#define __FUNCT__ "timestep_loop"
int timestep_loop (DA box_da, parameter_values p)
{
  Vec global;
  PetscScalar deltat, current_time=0., Tmax, Tmin,
    plot_temps[6] = { 1195., 1150., 1100., 1050., 999.99, 950. },
    plot_colors[24] = { 1.,0.,0.,.5, 1.,1.,0.,.5, 0.,1.,0.,.5, 0.,1.,1.,.5,
		    0.,0.,1.,.5, 1.,0.,1.,.5 };
  int ts=0, plots=6, keepon=1, ierr;
  char outstring [101];
  ISurface Surf;
  IDisplay Disp;

  DPRINTF ("Starting the Geomview display using Illuminator.\n",0);
  ierr = SurfCreate (&Surf); CHKERRQ (ierr);
  ierr = GeomviewBegin (PETSC_COMM_WORLD, &Disp); CHKERRQ (ierr);

  DPRINTF ("Calculating maximum stable explicit timestep size.\n",0);
  {
    double liquid_alpha, solid_alpha;
    liquid_alpha = p.liquid_k/p.liquid_rho/p.liquid_cp;
    solid_alpha = p.solid_k/p.solid_rho/p.solid_cp;

    deltat = 1./2. / PetscMax (liquid_alpha, solid_alpha) /
      (p.deltax_m1*p.deltax_m1 + p.deltay_m1*p.deltay_m1 +
       p.deltaz_m1*p.deltaz_m1);
  }

  ierr = DAGetGlobalVector (box_da, &global); CHKERRQ (ierr);
  ierr = VecStrideMax (global, TEMPERATURE, PETSC_NULL, &Tmax);
  CHKERRQ (ierr);
  ierr = VecStrideMin (global, TEMPERATURE, PETSC_NULL, &Tmin);
  CHKERRQ (ierr);
  ierr = DARestoreGlobalVector (box_da, &global); CHKERRQ (ierr);

  snprintf (outstring,100,"Timestep %d (time %g): deltat %g, Tmax %g, Tmin %g",
	    ts, current_time, deltat, Tmax, Tmin);
#ifdef DEBUG
  ierr = PetscPrintf (PETSC_COMM_WORLD, "%s\n", outstring); CHKERRQ (ierr);
#else
  strncat (outstring, "            ", 79-strlen(outstring));
  ierr = PetscPrintf (PETSC_COMM_WORLD, "\r%s\n", outstring); CHKERRQ (ierr);
#endif

  while (keepon)
    {
      char instring[100];

      ierr = PetscPrintf (PETSC_COMM_WORLD,
			  "Cast-a-box command (h for help): ");
      CHKERRQ (ierr);
      ierr = PetscSynchronizedFGets (PETSC_COMM_WORLD, stdin, 99, instring);
      CHKERRQ (ierr);

      switch (*instring)
	{
	case 'h':
	case 'H':
	case '?':
	  ierr = PetscPrintf (PETSC_COMM_WORLD, help_commands); CHKERRQ (ierr);
	  break;

	case 'q':
	case 'Q':
	case 'x':
	case 'X':
	  keepon=0;
	  break;

	case 'p':
	case 'P':
	  {
	    int count=0, newplots=0;

	    while (newplots<6 && instring[count] != 0)
	      {
		while ((instring[count] < '0' || instring[count] > '9') &&
		       instring[count] != '-' && instring[count] != '.' &&
		       instring[count] != '\0')
		  count++;
		if (instring[count])
		  {
		    sscanf (instring+count, "%lf\n", plot_temps+newplots);
		    newplots++;
		    while ((instring[count] >= '0' && instring[count] <= '9')||
			   instring[count] == '-' || instring[count] == '.')
		      count++;
		  }
	      }

	    if (newplots)
	      {
		PetscScalar minmax[6] =
		  { 0., p.width_x, 0., p.width_y, 0., p.width_z };

		ierr = DATriangulate (Surf, box_da,global, TEMPERATURE, minmax,
				      plots=newplots, plot_temps, plot_colors,
				      PETSC_FALSE, PETSC_FALSE, PETSC_FALSE);
		CHKERRQ (ierr);
		/* Work-around for illuminator non-periodic bug */
		minmax[1] = p.width_x * (p.grid_x-1) / p.grid_x;
		minmax[3] = p.width_y * (p.grid_y-1) / p.grid_y;
		minmax[5] = p.width_z * (p.grid_z-1) / p.grid_z;
		ierr = GeomviewDisplayTriangulation
		  (PETSC_COMM_WORLD, Surf, Disp, minmax, "Temperature",
		   PETSC_FALSE); CHKERRQ(ierr);
	      }
	    else
	      {
		ierr = PetscPrintf (PETSC_COMM_WORLD,
				    "Current plot temperatures:");
		CHKERRQ (ierr);
		for (count=0; count<plots; count++)
		  {
		    ierr = PetscPrintf (PETSC_COMM_WORLD, " %g",
					plot_temps[count]); CHKERRQ (ierr);
		  }
		ierr = PetscPrintf (PETSC_COMM_WORLD, "\n"); CHKERRQ (ierr);
	      }

	    break;
	  }

	case 'g':
	case 'G':
	case '\0':
	  {
	    int count=0, i;
	    while ((instring[count] < '0' || instring[count] > '9') &&
		   instring[count] != '\0')
	      count++;

	    DPRINTF ("count = %d\n", count);

	    if (instring[count] == '\0')
	      count = 1;
	    else
	      count = atoi (instring+count);

	    DPRINTF ("Doing %d timesteps\n", count);

	    for (i=0; i<count; i++)
	      {
		ierr = do_timestep (box_da, p, deltat); CHKERRQ (ierr);

		ierr = DAGetGlobalVector (box_da, &global); CHKERRQ (ierr);
		ierr = VecStrideMax (global, TEMPERATURE, PETSC_NULL, &Tmax);
		CHKERRQ (ierr);
		ierr = VecStrideMin (global, TEMPERATURE, PETSC_NULL, &Tmin);
		CHKERRQ (ierr);
		ierr = DARestoreGlobalVector (box_da, &global); CHKERRQ (ierr);

		current_time += deltat;
		ts++;

		snprintf (outstring,100,"Timestep %d (time %g): deltat %g, Tmax %g, Tmin %g",
			  ts, current_time, deltat, Tmax, Tmin);
#ifdef DEBUG
		ierr = PetscPrintf (PETSC_COMM_WORLD, "%s\n", outstring); CHKERRQ (ierr);
#else
		strncat (outstring, "            ", 79-strlen(outstring));
		ierr = PetscPrintf (PETSC_COMM_WORLD, "\r%s", outstring); CHKERRQ (ierr);
#endif

		if (plots)
		  {
		    PetscScalar minmax[6] =
		      { 0., p.width_x, 0., p.width_y, 0., p.width_z };

		    ierr = DATriangulate (Surf, box_da, global, TEMPERATURE,
					  minmax, plots,plot_temps,plot_colors,
					  PETSC_FALSE,PETSC_FALSE,PETSC_FALSE);
		    CHKERRQ (ierr);
		    /* Work-around for illuminator non-periodic bug */
		    minmax[1] = p.width_x * (p.grid_x-1) / p.grid_x;
		    minmax[3] = p.width_y * (p.grid_y-1) / p.grid_y;
		    minmax[5] = p.width_z * (p.grid_z-1) / p.grid_z;
		    ierr = GeomviewDisplayTriangulation
		      (PETSC_COMM_WORLD, Surf, Disp, minmax, "Temperature",
		       PETSC_FALSE); CHKERRQ (ierr);
		    ierr = SurfClear (Surf); CHKERRQ (ierr);
		  }
	      }
#ifndef DEBUG
	    if (count)
	      {
		ierr = PetscPrintf (PETSC_COMM_WORLD, "\n"); CHKERRQ (ierr);
	      }
#endif
	    break;
	  }

	default:
	  ierr = PetscPrintf (PETSC_COMM_WORLD, "Unrecognized: %s\n",instring);
	  CHKERRQ (ierr);
	}
    }

  ierr = GeomviewEnd (PETSC_COMM_WORLD, Disp); CHKERRQ (ierr);
  return 0;
}


/* The main() function reads in the parameters, calculates the timestep size,
 * creates the distributed array, and starts Geomview for visualization, then
 * calls the timestep loop to do the simulation. */
#undef __FUNCT__
#define __FUNCT__ "main"
int main (int argc, char *argv[])
{
  DA box_da;
  parameter_values p;
  int ierr;

  /* Start with PETSc initialization */
  ierr = PetscInitialize (&argc, &argv, NULL, help); CHKERRQ (ierr);

  DPRINTF ("Getting the parameters from the command line into p.\n",0);
  p.grid_x = p.grid_y = 5;
  p.grid_z = 10;
  ierr = PetscOptionsGetInt (PETSC_NULL, "-grid_x", &p.grid_x, PETSC_NULL);
  CHKERRQ (ierr);
  ierr = PetscOptionsGetInt (PETSC_NULL, "-grid_y", &p.grid_y, PETSC_NULL);
  CHKERRQ (ierr);
  ierr = PetscOptionsGetInt (PETSC_NULL, "-grid_z", &p.grid_z, PETSC_NULL);
  CHKERRQ (ierr);

  p.width_x = p.width_y = 0.5;
  p.width_z = 1.;
  ierr = PetscOptionsGetScalar (PETSC_NULL, "-width_x", &p.width_x,
				PETSC_NULL); CHKERRQ (ierr);
  ierr = PetscOptionsGetScalar (PETSC_NULL, "-width_y", &p.width_y,
				PETSC_NULL); CHKERRQ (ierr);
  ierr = PetscOptionsGetScalar (PETSC_NULL, "-width_z", &p.width_z,
				PETSC_NULL); CHKERRQ (ierr);
  p.deltax_m1 = (PetscScalar) p.grid_x / p.width_x;
  p.deltay_m1 = (PetscScalar) p.grid_y / p.width_y;
  p.deltaz_m1 = (PetscScalar) p.grid_z / p.width_z;

  p.liquid_k = p.liquid_rho = p.liquid_cp = 1.;
  ierr = PetscOptionsGetScalar (PETSC_NULL, "-liquid_k", &p.liquid_k,
				PETSC_NULL); CHKERRQ (ierr);
  ierr = PetscOptionsGetScalar (PETSC_NULL, "-liquid_rho", &p.liquid_rho,
				PETSC_NULL); CHKERRQ (ierr);
  ierr = PetscOptionsGetScalar (PETSC_NULL, "-liquid_cp", &p.liquid_cp,
				PETSC_NULL); CHKERRQ (ierr);

  p.solid_k = p.solid_rho = p.solid_cp = 1.;
  ierr = PetscOptionsGetScalar (PETSC_NULL, "-solid_k", &p.solid_k,
				PETSC_NULL); CHKERRQ (ierr);
  ierr = PetscOptionsGetScalar (PETSC_NULL, "-solid_rho", &p.solid_rho,
				PETSC_NULL); CHKERRQ (ierr);
  ierr = PetscOptionsGetScalar (PETSC_NULL, "-solid_cp", &p.solid_cp,
				PETSC_NULL); CHKERRQ (ierr);

  p.melt_temp = 1000.;
  p.latent_heat = 100.;
  ierr = PetscOptionsGetScalar (PETSC_NULL, "-melt_temp", &p.melt_temp,
				PETSC_NULL); CHKERRQ (ierr);
  ierr = PetscOptionsGetScalar (PETSC_NULL, "-latent_heat", &p.latent_heat,
				PETSC_NULL); CHKERRQ (ierr);

  p.xmax_tenv = p.ymax_tenv = p.bottom_tenv = p.top_tenv = 0.;
  ierr = PetscOptionsGetScalar (PETSC_NULL, "-xmax_tenv", &p.xmax_tenv,
				PETSC_NULL); CHKERRQ (ierr);
  ierr = PetscOptionsGetScalar (PETSC_NULL, "-ymax_tenv", &p.ymax_tenv,
				PETSC_NULL); CHKERRQ (ierr);
  ierr = PetscOptionsGetScalar (PETSC_NULL, "-bottom_tenv", &p.bottom_tenv,
				PETSC_NULL); CHKERRQ (ierr);
  ierr = PetscOptionsGetScalar (PETSC_NULL, "-top_tenv", &p.top_tenv,
				PETSC_NULL); CHKERRQ (ierr);

  p.xmax_h = p.ymax_h = p.bottom_h = 1.;
  p.top_h = 0.5;
  p.top_epssig = 5.67e-11;
  ierr = PetscOptionsGetScalar (PETSC_NULL, "-xmax_h", &p.xmax_h, PETSC_NULL);
  CHKERRQ (ierr);
  ierr = PetscOptionsGetScalar (PETSC_NULL, "-ymax_h", &p.ymax_h, PETSC_NULL);
  CHKERRQ (ierr);
  ierr = PetscOptionsGetScalar (PETSC_NULL, "-bottom_h", &p.bottom_h,
				PETSC_NULL); CHKERRQ (ierr);
  ierr = PetscOptionsGetScalar (PETSC_NULL, "-top_h", &p.top_h,
				PETSC_NULL); CHKERRQ (ierr);
  ierr = PetscOptionsGetScalar (PETSC_NULL, "-top_epssig", &p.top_epssig,
				PETSC_NULL); CHKERRQ (ierr);

  DPRINTF ("Simulation parameters:\n",0);
  DPRINTF ("Grid dimensions: %dx%dx%d, Physical dimensions: %gx%gx%g\n",
	   p.grid_x, p.grid_y, p.grid_z, p.width_x, p.width_y, p.width_z);
  DPRINTF ("Liquid properties: k=%g, rho=%g, cp=%g, melt temp=%g\n",
	   p.liquid_k, p.liquid_rho, p.liquid_cp, p.melt_temp);
  DPRINTF ("Solid properties:  k=%g, rho=%g, cp=%g, latent heat=%g\n",
	   p.solid_k, p.solid_rho, p.solid_cp, p.latent_heat);
  DPRINTF ("Env. temperatures: xmax=%g, ymax=%g, top=%g, bottom=%g\n",
	   p.xmax_tenv, p.ymax_tenv, p.top_tenv, p.bottom_tenv);
  DPRINTF ("h values: xmax=%g, ymax=%g, top=%g, bottom=%g, epssig=%g\n",
	   p.xmax_h, p.ymax_h, p.bottom_h, p.top_h, p.top_epssig);

  DPRINTF ("Creating the distributed array with box stencil of width 1,\n"
	   "  2 field variables (temperature and enthalpy).\n",0);
  ierr = DACreate3d (PETSC_COMM_WORLD, DA_NONPERIODIC, DA_STENCIL_BOX,
		     p.grid_x, p.grid_y, p.grid_z,
		     PETSC_DECIDE, PETSC_DECIDE, PETSC_DECIDE,
		     2, 1, PETSC_NULL, PETSC_NULL, PETSC_NULL, &box_da);
  CHKERRQ (ierr);
  ierr = DASetFieldName (box_da, TEMPERATURE, "Temperature"); CHKERRQ (ierr);
  ierr = DASetFieldName (box_da, ENTHALPY, "Enthalpy"); CHKERRQ (ierr);

  DPRINTF ("Setting the initial conditions.\n",0);
  {
    Vec global;
    Field ***global_array, initial;
    int xs,ys,zs, xm,ym,zm, ix,iy,iz;

    /* Default: 200 degree initial superheat */
    initial.H = p.latent_heat + 200.*p.liquid_rho*p.liquid_cp;
    ierr = PetscOptionsGetScalar (PETSC_NULL, "-init_enthalpy", &initial.H,
				  PETSC_NULL); CHKERRQ (ierr);
    /* Provided initial temperature overrides enthalpy */
    initial.T = temperature (initial.H, p);
    ierr = PetscOptionsGetScalar (PETSC_NULL, "-init_temp", &initial.T,
				  PETSC_NULL); CHKERRQ (ierr);
    if (initial.T != temperature (initial.H, p))
      initial.H = ((initial.T<p.melt_temp) ?
		   p.solid_rho*p.solid_cp*(initial.T-p.melt_temp) :
		   p.liquid_rho*p.liquid_cp*(initial.T-p.melt_temp) +
		   p.latent_heat);

    ierr = DAGetGlobalVector (box_da, &global); CHKERRQ (ierr);
    ierr = DAVecGetArray (box_da, global, (void **)&global_array); CHKERRQ (ierr);
    ierr = DAGetCorners(box_da, &xs,&ys,&zs, &xm,&ym,&zm); CHKERRQ (ierr);

    for (iz=zs; iz<zs+zm; iz++)
      for (iy=ys; iy<ys+ym; iy++)
	for (ix=xs; ix<xs+xm; ix++)
	  global_array[iz][iy][ix] = initial;

    ierr = DAVecRestoreArray (box_da, global, (void **)&global_array); CHKERRQ (ierr);
    ierr = DARestoreGlobalVector (box_da, &global); CHKERRQ (ierr);
  }

  DPRINTF ("Starting the timestep loop...\n",0);
  ierr = timestep_loop (box_da, p);

  DPRINTF ("Shutting down.\n",0);
  ierr = DADestroy (box_da); CHKERRQ (ierr);
  ierr = PetscFinalize (); CHKERRQ (ierr);
  return 0;
}

