/*************************************************************************\
* Copyright (c) 2002 The University of Chicago, as Operator of Argonne
* National Laboratory.
* Copyright (c) 2002 The Regents of the University of California, as
* Operator of Los Alamos National Laboratory.
* This file is distributed subject to a Software License Agreement found
* in the file LICENSE that is included with this distribution. 
\*************************************************************************/

/*
 * $Log: not supported by cvs2svn $
 * Revision 1.12  2004/12/11 03:14:08  emery
 * Removed the CVS log comment which generated duplication of CVS comments.
 *
 * Revision 1.11  2004/12/04 20:24:32  emery
 * Changed short to long for the region value of the 3D or 2D grid elements.
 * This allows for a number of regions larger than 2^15 = 32k.
 * Added axialDirection variable for the cylindrical geometry.
 * Now the cconcentric ylinders can be defined along one of
 * the three axes instead of just z. Mixing of directions in one
 * run is not allowed. Increased INCREMENT_ARRAY_SIZE to 100.
 *
 * Revision 1.10  2002/08/14 21:58:38  soliday
 * Fixed compiler warning.
 *
 * Revision 1.9  2002/08/14 20:34:34  soliday
 * Added Open License
 *
 * Revision 1.8  2001/05/30 23:05:15  emery
 * Changed tests for boundary of grid.
 *
 * Revision 1.7  2000/02/21 16:48:11  soliday
 * Removed some warning messages on Linux and WIN32 by adding some casts
 *
 * Revision 1.6  1996/11/20 22:09:16  emery
 * Use an include statement to define structures.
 *
 * Revision 1.5  1996/06/28  13:28:16  emery
 * Changed the "table" part of some variables to "page".
 * Changed SDDS_TABLE to SDDS_DATASET.
 *
 * Revision 1.4  1996/06/27  19:23:10  emery
 * Changed spelling of some variables.
 *
 * Revision 1.3  1996/05/30  22:23:33  emery
 * Added the log comment.
 *
 * file find_region.c
 * contains functions find_region, find_distance_to_boundary, setup_2D_blocks_grid
 * sort_and_remove_duplicates, find_position, (plot_geometry is removed from the cylindrical version)
 * 11/03/95 13:18:21: change to writing lines segments for plotting geometry.
 *
 */
#include "mdb.h"
#include "SDDS.h"
#include "showerTypes.h"

#define ARRAY_SIZE_INCREMENT 6

#define OUT_OF_BOUNDS -1     /* return value of region index if x,y,z is out-of-bounds */

#define X_DIRECTION 0
#define Y_DIRECTION 1
#define Z_DIRECTION 2

/* variables axial<...> are used in the cylindrical geometry */
double *xPlane, *yPlane, *rPlane, *zPlane, *axialPlane;
int32_t xPlanes, yPlanes, rPlanes, zPlanes, axialPlanes, axialDirection;
int32_t **cylRegionIndexGrid;
int32_t ***blockRegionIndexGrid;

void setup3DblocksGrid( REGION *region,  int32_t regions);
void setup2DCylindersGrid( REGION *region,  int32_t regions);
int32_t cylFindRegion(double x,  double y,  double z);
int32_t blockFindRegion(double x,  double y,  double z);
double cylFindDistanceToBoundary(double x,  double y,  double z,  double u,  double v,  double w);
double blockFindDistanceToBoundary(double x,  double y,  double z,  double u,  double v,  double w);
void sortAndRemoveDuplicates(double *array,  int32_t *size);
int32_t findPosition( double *array,  int32_t arraySize,  double boundary);
void plot3Dgeometry( REGION *region,  char *file);
void plot2Dgeometry( REGION *region,  char *file);


void setup2DcylindersGrid( REGION *region,  int32_t regions)
{
  int32_t iRegion;
  /* for historical reasons, */
  /* index i for radial direction, index k for axial direction */
  int32_t iPlane, kPlane;
  int32_t i, k;
  int32_t irlo,  irup,  izlo=0,  izup=0;
  
  rPlane = (double *) tmalloc(2*regions*sizeof(double));
  axialPlane = (double *) tmalloc(2*regions*sizeof(double));
  /* do r, y, z planes */
  for (iRegion=0;iRegion<regions;iRegion++){
    rPlane[iRegion*2] = region[iRegion].r_lo;
    rPlane[iRegion*2+1] = region[iRegion].r_up;
    switch( axialDirection = region[iRegion].axialDirection ) {
    case X_DIRECTION:
      axialPlane[iRegion*2] = region[iRegion].x_lo;
      axialPlane[iRegion*2+1] = region[iRegion].x_up;
      break;
    case Y_DIRECTION:
      axialPlane[iRegion*2] = region[iRegion].y_lo;
      axialPlane[iRegion*2+1] = region[iRegion].y_up;
      break;
    case Z_DIRECTION:
      axialPlane[iRegion*2] = region[iRegion].z_lo;
      axialPlane[iRegion*2+1] = region[iRegion].z_up;
      break;
    }
  }
  /* sort values */
  rPlanes = axialPlanes = 2 * regions;
  sortAndRemoveDuplicates( rPlane,  &rPlanes);
  sortAndRemoveDuplicates( axialPlane,  &axialPlanes);
  /* grid dimensions are equal to the number of planes. The last cell is
     supposed to be out-of bounds */
  cylRegionIndexGrid = (int32_t **) malloc( rPlanes * sizeof(int32_t *) );
  for ( iPlane=0; iPlane < rPlanes; iPlane++){
    cylRegionIndexGrid[iPlane] = (int32_t *) malloc( axialPlanes * sizeof(int32_t) );
    for (kPlane=0; kPlane < axialPlanes; kPlane++){
      /* initialize 2d grid to undefined region value (value regions) */
      if ( (iPlane == rPlanes - 1) || (kPlane == axialPlanes - 1) ) {
        cylRegionIndexGrid[iPlane][kPlane] = regions + 1; /*out-of-bounds*/
      }
      else {
        cylRegionIndexGrid[iPlane][kPlane] = (int32_t)regions;
      }
    }
  }
  /* assign regions in the order of definitions in the geometry file.
     First region in the geometry file is processed last. */
  for ( iRegion=regions-1; iRegion>=0; iRegion--) {
    region[iRegion].irlo=irlo=findPosition(rPlane, rPlanes, region[iRegion].r_lo);
    region[iRegion].irup=irup=findPosition(rPlane, rPlanes, region[iRegion].r_up);
    switch( axialDirection = region[iRegion].axialDirection ) {
    case X_DIRECTION:
      region[iRegion].ixlo=izlo=findPosition(axialPlane, axialPlanes, region[iRegion].x_lo);
      region[iRegion].ixup=izup=findPosition(axialPlane, axialPlanes, region[iRegion].x_up);
      break;
    case Y_DIRECTION:
      region[iRegion].iylo=izlo=findPosition(axialPlane, axialPlanes, region[iRegion].y_lo);
      region[iRegion].iyup=izup=findPosition(axialPlane, axialPlanes, region[iRegion].y_up);
      break;
    case Z_DIRECTION:
      region[iRegion].izlo=izlo=findPosition(axialPlane, axialPlanes, region[iRegion].z_lo);
      region[iRegion].izup=izup=findPosition(axialPlane, axialPlanes, region[iRegion].z_up);
      break;
    }
    
    for ( i=irlo; i<irup; i++) {
      for ( k=izlo; k<izup; k++) {
        cylRegionIndexGrid[i][k] = (int32_t)iRegion;
      }
    }
  }
  /* calculate the volume by adding the volumes of the washer grid elements */
  for (iRegion=0; iRegion <= regions; iRegion++) 
    region[iRegion].Volume = 0;
  /* Do it for cells that are not out-of-bounds cells */
  for (i=0; i < (rPlanes-1); i++) {
    for (k=0; k < (axialPlanes-1); k++) {
      region[cylRegionIndexGrid[i][k]].Volume +=
        PI*(sqr(rPlane[i+1])-sqr(rPlane[i]))*(axialPlane[k+1]-axialPlane[k])/1e6;
    }
  }
  
  printf("%"PRId32"x%"PRId32" 2D grid defined from &cylinder definitions.\n", rPlanes, axialPlanes);
}

void setup3DblocksGrid( REGION *region,  int32_t regions)
{
  int32_t iRegion;
  int32_t iPlane, jPlane;
  int32_t i, j, k;
  int32_t ixlo,  ixup,  iylo,  iyup,  izlo,  izup;

  xPlane = (double *) tmalloc(2*regions*sizeof(double));
  yPlane = (double *) tmalloc(2*regions*sizeof(double));
  zPlane = (double *) tmalloc(2*regions*sizeof(double));
  /* do x, y, z planes */
  for (iRegion=0;iRegion<regions;iRegion++){
    xPlane[iRegion*2] = region[iRegion].x_lo;
    xPlane[iRegion*2+1] = region[iRegion].x_up;
    yPlane[iRegion*2] = region[iRegion].y_lo;
    yPlane[iRegion*2+1] = region[iRegion].y_up;
    zPlane[iRegion*2] = region[iRegion].z_lo;
    zPlane[iRegion*2+1] = region[iRegion].z_up;
  }
  /* sort values */
  xPlanes=yPlanes=zPlanes=2*regions;
  sortAndRemoveDuplicates(xPlane,  &xPlanes);
  sortAndRemoveDuplicates(yPlane,  &yPlanes);
  sortAndRemoveDuplicates(zPlane,  &zPlanes);
  blockRegionIndexGrid = (int32_t ***) malloc( xPlanes * sizeof(int32_t **));
  /* grid dimensions are equal to the number of planes. The last cell is
     supposed to be out-of bounds */
  for ( iPlane=0; iPlane < xPlanes; iPlane++){
    blockRegionIndexGrid[iPlane] = (int32_t **) malloc( yPlanes  * sizeof(int32_t *));
    for ( jPlane=0; jPlane < yPlanes; jPlane++){
      blockRegionIndexGrid[iPlane][jPlane] = (int32_t *) malloc( zPlanes * sizeof(int32_t));
      for ( k=0; k < zPlanes; k++) {
        /* initialize 3d grid to undefined region value (regions) */
        if ( (iPlane == xPlanes - 1) || (jPlane == yPlanes - 1) || (k == zPlanes - 1) ) {
          blockRegionIndexGrid[iPlane][jPlane][k] = regions + 1; /*out-of-bounds*/
        }
        else {
          blockRegionIndexGrid[iPlane][jPlane][k] = (int32_t)regions;
        }
      }
    }
  }
  /* assign regions in the order of definitions in the geometry file.
     First region in the geometry file is processed last. */
  for ( iRegion=regions-1; iRegion>=0; iRegion--) {
    region[iRegion].ixlo=ixlo=findPosition(xPlane, xPlanes, region[iRegion].x_lo);
    region[iRegion].ixup=ixup=findPosition(xPlane, xPlanes, region[iRegion].x_up);
    region[iRegion].iylo=iylo=findPosition(yPlane, yPlanes, region[iRegion].y_lo);
    region[iRegion].iyup=iyup=findPosition(yPlane, yPlanes, region[iRegion].y_up);
    region[iRegion].izlo=izlo=findPosition(zPlane, zPlanes, region[iRegion].z_lo);
    region[iRegion].izup=izup=findPosition(zPlane, zPlanes, region[iRegion].z_up);
    for ( i=ixlo; i < ixup; i++) {
      for ( j=iylo;j < iyup; j++) {
        for ( k=izlo;k < izup; k++) {
          blockRegionIndexGrid[i][j][k] = (int32_t)iRegion;
        }
      }
    }
  }
  /* calculate the volume by adding up all the grid cells belonging to a region */
  for ( iRegion=0; iRegion <= regions; iRegion++) 
    region[iRegion].Volume = 0;
  
  /* Do it for cells that are not out-of-bounds cells */
  for ( i=0; i < (xPlanes - 1); i++) {
    for ( j=0; j < (yPlanes - 1); j++) {
      for ( k=0; k < (zPlanes - 1); k++) {
        region[blockRegionIndexGrid[i][j][k]].Volume +=
          (xPlane[i+1]-xPlane[i])*(yPlane[j+1]-yPlane[j])*(zPlane[k+1]-zPlane[k])/1e6;
      }
    }
  }
  printf("%"PRId32"x%"PRId32"x%"PRId32" 3D grid defined from &block definitions.\n", xPlanes, yPlanes, zPlanes);
}

int32_t cylFindRegion(double x,  double y,  double z) 
{
  int32_t i, k;
  double r=0, axialCoordinate=0;
  /* Points x, y, z that are out of bounds have indices equal to -1, (n_rPlanes-1), or (n_axialPlanes-1). */
  /* If the point is out of bounds,  -1 is returned as the region number */
  /* axialDirection is a global variable set in function setup2DcylindersGrid */
  switch( axialDirection ) {
  case X_DIRECTION:
    r=sqrt(y*y+z*z);
    axialCoordinate = x;
    break;
  case Y_DIRECTION:
    r=sqrt(z*z+x*x);
    axialCoordinate = y;
   break;
  case Z_DIRECTION:
    r=sqrt(x*x+y*y);
    axialCoordinate = z;
   break;
  }
  i=findPosition(rPlane, rPlanes, r);
  if(i==-1 || i==rPlanes-1)
    return(OUT_OF_BOUNDS);
  k=findPosition(axialPlane, axialPlanes, axialCoordinate);
  if(k==-1 || k==axialPlanes-1)
    return(OUT_OF_BOUNDS);
  /* The values in region_index_grid are the REGION *region array element positions. */
  return(cylRegionIndexGrid[i][k]);
}

double cylFindDistanceToBoundary(double x,  double y,  double z,  double u,  double v,  double w) {
  int32_t i, k;
  double distToBoundary;
  double r, r2=0, d1, d2, d3, d4, a=0, b=0, c, discriminant;
  double axialCoordinate=0, axialSlope=0;
  distToBoundary = 1e30;
  /* point x, y, z is always inside a defined region when find_distance_to_boundary is called from howfar_. */
  switch( axialDirection ) {
  case X_DIRECTION:
    r2 = y*y+z*z;
    a = v*v + w*w;
    b = y*v + z*w;
    axialCoordinate = x;
    axialSlope = u;
    break;
  case Y_DIRECTION:
    r2 = z*z+x*x;
    a = w*w + u*u;
    b = z*w + x*u;
    axialCoordinate = y;
    axialSlope = v;
    break;
  case Z_DIRECTION:
    r2 = x*x+y*y;
    a = u*u + v*v;
    b = x*u + y*v;
    axialCoordinate = z;
    axialSlope = w;
    break;
  }
  r=sqrt(r2);
  i=findPosition(rPlane, rPlanes, r); /* find inner and outer radii of region */
  k=findPosition(axialPlane, axialPlanes, axialCoordinate);
  /* distance to radii i and i+1  */
  /* solve quadratic equation with 2's simplified out of expressions */
  /* See expressions for a, b in switch statement above */
  /* If the inner radius is 0 then the point is inside a full cylinder instead of a hollow cylinder. */
  /* There are a maximum of only two solutions instead of four. */
  /* Select the solution for the smallest of the positive d's. */
  if(rPlane[i]) { 
    c = r2 - sqr(rPlane[i]);
    discriminant = b*b-a*c;
    if( discriminant >=0 ) {
      d1 = (-b - sqrt(discriminant))/a;
      if( d1 > 0 )
        distToBoundary=MIN(distToBoundary, d1);
      d2 = (-b + sqrt(discriminant))/a;
      if( d2 > 0 )
        distToBoundary=MIN(distToBoundary, d2);
    }
  }
  c = r2 - sqr(rPlane[i+1]);
  discriminant = b*b-a*c;
  if( discriminant >=0 ) {
    d3 = (-b - sqrt(discriminant))/a;
    if( d3 > 0 )
      distToBoundary=MIN(distToBoundary, d3);
    d4 = (-b + sqrt(discriminant))/a;
    if( d4 > 0 )
      distToBoundary=MIN(distToBoundary, d4);
  }
  if (axialSlope>0) {
    /* distance to plane k+1  */
    distToBoundary=MIN(distToBoundary, (axialPlane[k+1] - axialCoordinate)/axialSlope);
  }
  else if (axialSlope<0) {
    /* distance to plane k  */
    distToBoundary=MIN(distToBoundary, (axialCoordinate - axialPlane[k])/(-axialSlope));
  }
  return(distToBoundary);
}

int32_t blockFindRegion(double x,  double y,  double z) 
{
  int32_t i, j, k;
  /* Points x, y, z that are out of bounds have indices equal to -1 or ([xyz]Planes-1). */
  /* If the point is out of bounds,  -1 is returned as the region number */
  i=findPosition(xPlane, xPlanes, x);
  if(i==-1 || i==xPlanes-1)
    return(OUT_OF_BOUNDS);
  j=findPosition(yPlane, yPlanes, y);
  if(j==-1 || j==yPlanes-1)
    return(OUT_OF_BOUNDS);
  k=findPosition(zPlane, zPlanes, z);
  if(k==-1 || k==zPlanes-1)
    return(OUT_OF_BOUNDS);
  /* The values in region_index_grid are the REGION *region array element positions. */
  return(blockRegionIndexGrid[i][j][k]);
}

double blockFindDistanceToBoundary(double x,  double y,  double z,  double u,  double v,  double w) {
  int32_t i, j, k;
  double distToBoundary;
  distToBoundary = 1e30;
  /* point x, y, z is always within defined region when findDistanceToBoundary is called. */
  i=findPosition(xPlane, xPlanes, x);
  j=findPosition(yPlane, yPlanes, y);
  k=findPosition(zPlane, zPlanes, z);
  if (u>0) {
    /* distance to plane i+1  */
    distToBoundary=MIN(distToBoundary, (xPlane[i+1] - x)/u);
  }
  else if (u<0) {
    /* distance to plane i  */
    distToBoundary=MIN(distToBoundary, (x - xPlane[i])/(-u));
  }
  if (v>0) {
    /* distance to plane j+1  */
    distToBoundary=MIN(distToBoundary, (yPlane[j+1] - y)/v);
  }
  else if (v<0) {
    /* distance to plane j  */
    distToBoundary=MIN(distToBoundary, (y - yPlane[j])/(-v));
  }
  if (w>0) {
    /* distance to plane k+1  */
    distToBoundary=MIN(distToBoundary, (zPlane[k+1] - z)/w);
  }
  else if (w<0) {
    /* distance to plane k  */
    distToBoundary=MIN(distToBoundary, (z - zPlane[k])/(-w));
  }
  return(distToBoundary);
}


void sortAndRemoveDuplicates(double *array,  int32_t *size)
{
  int32_t i, j, k;
  for ( i=1; i < *size; i++) {
    for ( j=0; j < i; j++) {
      if (array[i] == array[j]) {
        /*reduce array size and shift elements */
        (*size)--;
        for ( k=i; k < (*size); k++) 
          array[k] = array[k+1];
        i--; /*restart j loop with same i value */
        break;
      }
      else if ( array[j] > array[i] )
        SWAP_DOUBLE(array[j], array[i]);
    }
  }
}

int32_t findPosition(double *array,  int32_t arraySize,  double value)
{
  /* returns interval number in which value lies */
  /* negative unbounded interval is -1 */
  /* positive unbounded interval is array_size-1 */
  /* first bounded interval is 0 */ 
  /* bounded intervals are closed at negative end and open at positive end */
  int32_t i;
  for (i=arraySize-1;i>=0;i--) {
    if(array[i]<=value) {
      return(i);
    }
  }
  /*  if(value<array[0])*/
    return(-1);
}

void plot3Dgeometry(REGION *region,  char *file) {
  SDDS_DATASET outputPage;
  int32_t i, j, k;
  int32_t octant1material, octant2material, octant3material, octant4material;
  int32_t octant5material, octant6material, octant7material;

  if ( !SDDS_InitializeOutput(&outputPage, SDDS_BINARY, 1, "Line segment coordinates",
                              "Line segment coordinates", file) ||
      0>SDDS_DefineSimpleColumn(&outputPage, "x", "m", SDDS_DOUBLE) ||
      0>SDDS_DefineSimpleColumn(&outputPage, "y", "m", SDDS_DOUBLE) ||
      0>SDDS_DefineSimpleColumn(&outputPage, "z", "m", SDDS_DOUBLE) ||
      !SDDS_WriteLayout(&outputPage) )
    SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors|SDDS_EXIT_PrintErrors);

  /* triple loop to scan each grid point for drawing a single line segment 
     along +x,  +y,  or +z direction,  or all three segments
     */
  for ( i=0; i < xPlanes; i++) {
    for ( j=0; j < yPlanes; j++) {
      for ( k=0; k < zPlanes; k++) {
        /* it is easier to check when NOT to define a segment 
           rather then testing when to define one.
           * Here are the only cases when not to define a segment going perpendicularly through the center.
           *    A | A    A | B    This applies with A=B also, of course.
           *    --+--    --+--    
           *    B | B    A | B
           * At each grid point, 8 octants are numbered 1 through 8 and are depicted below.
           * Note octant 8 not used in tests.
           *      y                          y              
           *    2 | 1    for z>0           6 | 5    for z<0 
           *    --+-- x                    --+-- x          
           *    4 | 3                      8 | 7            
           *
           *      y                          y              
           *    5 | 1    for x>0           6 | 2    for x<0    
           *    --+-- z                    --+-- z          
           *    7 | 3                      8 | 4            
           *
           *      x                          x              
           *    2 | 1    for y>0           6 | 3    for y<0    
           *    --+-- z                    --+-- z          
           *    6 | 5                      8 | 7            
           * depending on the material configuration either one segment or three segments are created.
           */
        octant1material=region[blockRegionIndexGrid[i][j][k]].materialCode;
        if (i)
          octant2material=region[blockRegionIndexGrid[i-1][j][k]].materialCode;
        else
          octant2material=0;
        if (j)
          octant3material=region[blockRegionIndexGrid[i][j-1][k]].materialCode;
        else
          octant3material=0;
        if (i && j)
          octant4material=region[blockRegionIndexGrid[i-1][j-1][k]].materialCode;
        else
          octant4material=0;
        if (k)
          octant5material=region[blockRegionIndexGrid[i][j][k-1]].materialCode;
        else
          octant5material=0;
        if (i && k)
          octant6material=region[blockRegionIndexGrid[i-1][j][k-1]].materialCode;
        else
          octant6material=0;
        if (j && k)
          octant7material=region[blockRegionIndexGrid[i][j-1][k-1]].materialCode;
        else
          octant7material=0;
        /* for x segment,  test octant 1, 3, 5, 7    */
        if ( !((octant1material==octant3material && octant5material==octant7material)  || 
               (octant1material==octant5material && octant3material==octant7material)) ) {
          if (!SDDS_StartPage(&outputPage,2) ||
              !SDDS_SetRowValues( &outputPage, SDDS_SET_BY_NAME|SDDS_PASS_BY_VALUE, 0,
                                 "x", xPlane[i]/100,
                                 "y", yPlane[j]/100,
                                 "z", zPlane[k]/100, NULL) ||
              !SDDS_SetRowValues( &outputPage, SDDS_SET_BY_NAME|SDDS_PASS_BY_VALUE, 1,
                                 "x", xPlane[i+1]/100,
                                 "y", yPlane[j]/100,
                                 "z", zPlane[k]/100, NULL) ||
              !SDDS_WritePage( &outputPage ) )
            SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors|SDDS_EXIT_PrintErrors);
        }
        /* for y segment,  test octant 1, 2, 5, 6    */
        if ( !((octant1material==octant2material && octant5material==octant6material)  || 
               (octant1material==octant5material && octant2material==octant6material)) ) {
          if (!SDDS_StartPage(&outputPage,2) ||
              !SDDS_SetRowValues( &outputPage, SDDS_SET_BY_NAME|SDDS_PASS_BY_VALUE, 0,
                                 "x", xPlane[i]/100,
                                 "y", yPlane[j]/100,
                                 "z", zPlane[k]/100, NULL) ||
              !SDDS_SetRowValues( &outputPage, SDDS_SET_BY_NAME|SDDS_PASS_BY_VALUE, 1,
                                 "x", xPlane[i]/100,
                                 "y", yPlane[j+1]/100,
                                 "z", zPlane[k]/100, NULL) ||
              !SDDS_WritePage( &outputPage ) )
            SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors|SDDS_EXIT_PrintErrors);
        }
        /* for z segment,  test octant 1, 2, 3, 4    */
        if ( !((octant1material==octant2material && octant3material==octant4material)  || 
               (octant1material==octant3material && octant2material==octant4material)) ) {
          if (!SDDS_StartPage(&outputPage,2) ||
              !SDDS_SetRowValues( &outputPage, SDDS_SET_BY_NAME|SDDS_PASS_BY_VALUE, 0,
                                 "x", xPlane[i]/100,
                                 "y", yPlane[j]/100,
                                 "z", zPlane[k]/100, NULL) ||
              !SDDS_SetRowValues( &outputPage, SDDS_SET_BY_NAME|SDDS_PASS_BY_VALUE, 1,
                                 "x", xPlane[i]/100,
                                 "y", yPlane[j]/100,
                                 "z", zPlane[k+1]/100, NULL) ||
              !SDDS_WritePage( &outputPage ) )
            SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors|SDDS_EXIT_PrintErrors);
        }
      }
    }    
  }
  SDDS_Terminate(&outputPage);
}

void plot2Dgeometry(REGION *region,  char *file) {
  SDDS_DATASET outputPage;
  int32_t i, k;
  int32_t quadrant1material, quadrant2material, quadrant3material, quadrant4material;
  double x, y, z;
  
#define ARC_SEGMENTS_DEFAULT 50
  int32_t point, points, segments;
  
  segments = ARC_SEGMENTS_DEFAULT;
  points = segments + 1;
  
  if ( !SDDS_InitializeOutput(&outputPage, SDDS_BINARY, 1, "Line segment coordinates",
                              "Line segment coordinates", file) ||
      0>SDDS_DefineSimpleColumn(&outputPage, "x", "m", SDDS_DOUBLE) ||
      0>SDDS_DefineSimpleColumn(&outputPage, "y", "m", SDDS_DOUBLE) ||
      0>SDDS_DefineSimpleColumn(&outputPage, "z", "m", SDDS_DOUBLE) ||
      !SDDS_WriteLayout(&outputPage) )
    SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors|SDDS_EXIT_PrintErrors);
  /* Double loop to scan each grid point for drawing a single line segment along +r, or +z direction.
   */
  for ( i=1; i < rPlanes; i++) { /* skip r=0 nodes. There are no line segments to plot for r=0 */
    for ( k=0; k < axialPlanes; k++) {
      /* Quadrant numbering:
       *      y      
       *    2 | 1    
       *    --+-- z  
       *    4 | 3    
       */
      quadrant1material = region[cylRegionIndexGrid[i][k]].materialCode;
      if (k) {
        quadrant2material = region[cylRegionIndexGrid[i][k-1]].materialCode;
      }
      else {
        quadrant2material = 0;
      }
      if (i) {
        quadrant3material=region[cylRegionIndexGrid[i-1][k]].materialCode;
      }
      else {
        quadrant3material=0;
      }
      if (i && k) {
        quadrant4material=region[cylRegionIndexGrid[i-1][k-1]].materialCode;
      }
      else {
        quadrant4material=0;
      }
      if ( !((quadrant1material==quadrant2material && quadrant3material==quadrant4material)  || 
             (quadrant1material==quadrant3material && quadrant2material==quadrant4material)) ) {
        if ( !SDDS_StartPage( &outputPage, points))
          SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors|SDDS_EXIT_PrintErrors);
        switch( axialDirection ) {
        case X_DIRECTION:
          for ( point=0; point < points; point++ ){
            y = rPlane[i]/100 * cos(2*PI*point/segments);
            z = rPlane[i]/100 * sin(2*PI*point/segments);
            if ( !SDDS_SetRowValues( &outputPage, SDDS_SET_BY_NAME|SDDS_PASS_BY_VALUE, point,
                                    "x", axialPlane[k]/100,
                                    "y", y,
                                    "z", z, NULL))
              SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors|SDDS_EXIT_PrintErrors);
          }
          break;
        case Y_DIRECTION:
          for ( point=0; point < points; point++ ){
            z = rPlane[i]/100 * cos(2*PI*point/segments);
            x = rPlane[i]/100 * sin(2*PI*point/segments);
            if ( !SDDS_SetRowValues( &outputPage, SDDS_SET_BY_NAME|SDDS_PASS_BY_VALUE, point,
                                    "x", x,
                                    "y", axialPlane[k]/100,
                                    "z", z, NULL))
              SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors|SDDS_EXIT_PrintErrors);
          }
          break;
        case Z_DIRECTION:
          for ( point=0; point < points; point++ ){
            x = rPlane[i]/100 * cos(2*PI*point/segments);
            y = rPlane[i]/100 * sin(2*PI*point/segments);
            if ( !SDDS_SetRowValues( &outputPage, SDDS_SET_BY_NAME|SDDS_PASS_BY_VALUE, point,
                                    "x", x,
                                    "y", y,
                                    "z", axialPlane[k]/100, NULL))
              SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors|SDDS_EXIT_PrintErrors);
          }
          break;
          
        }
        
        if ( !SDDS_WritePage( &outputPage ) )
          SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors|SDDS_EXIT_PrintErrors);
      }
    }
  }
  SDDS_Terminate(&outputPage);
}
