/*
 * Copyright (C) 2000 Giuseppe Aliberti <giuseppe.aliberti@aquila.infn.it>
 *
 * 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.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */


/*   Vel2Grid3D.c

        Modified version of Vel2Grid (by Antony Lomax) to generate a 3-D vel/slowness
        model grid from a 3-D velocity model generated by Simul2k or FDTomo.

 */


/*
        history:

        ver 01    31AUG2000  GA  Original version, supports only Simul2K models
        ver 02    30SEP2000  GA  Added support for FDTomo models
        ver 03    10APR2001  SH  Since I didn't understand the meaning of the
                                 control parameters orig_x, orig_y, and orig_z
                                 I changed the code, so that these parameters are
                                 no longer in use. They should be set to zero in
                                 the control file. Right-handed coord. system
                                 of SIMULPS is transformed in a left-handed
                                 coord. system by multiplying x-pos. of grid
                                 nodes [km] with -1. However, first velocity
                                 grid node of SIMULPS is still in lower left
                                 corner (SE corner). Control parameters
                                 orig_grid_x, orig_grid_y, and orig_grid_z
                                 specify x,y,z location of grid origin
                                 relative to geographic origin (see
                                 Vel2Grid3D control file statements).
        ver 04    27FEB2007  AJL  Minor modifications due to GridLib function call changes:
                                        1) int get_transform(int n_proj, char* in_line)
                                        2) int rect2latlon(int n_proj, double xrect, double yrect, double* pdlat, double* pdlong)
        ver 05    28FEB2007  AJL  Modified to match Zhang Left Hand Coordinates and formatting (see #define ZHANG_FORMAT_COORDS_POSX)
        ver 06    09MAR2007  AJL  Added ALBERTO ascii table format
        ver 07    01MAY2012  AJL  Misc bug fixes, and changes to prevent gcc warnings.
                                  Added output of ANU-FMM vgrids.in velocity model file
                                  and propgrid.in propagation grid file in current working direcgtory.



.........1.........2.........3.........4.........5.........6.........7.........8

 */


// ----------------------------------------------------------------------------
// IMPORTANT: check following flags
// Default format is original(?) SimulPS with %5f (fixed width) format for reading velocity values and right-handed, East-negative coordinate system
//
// Uncomment following for Zhang %7f (fixed width) format for reading velocity values and left-handed, East-positive coordinate system
//#define ZHANG_FORMAT_COORDS_POSX 1
// Uncomment following for ETH format (*f variable width, space delimited) for reading velocity values
//#define ETH_3D_FORMAT 1
// Uncomment following for left-handed, East-positive coordinate system
#define COORDS_POS_EAST 1
// ----------------------------------------------------------------------------

#ifdef ZHANG_FORMAT_COORDS_POSX
#define VEL_FORMAT "%7f"
#else
#ifdef ETH_3D_FORMAT
#define VEL_FORMAT "%f"
#else
#define VEL_FORMAT "%5f"
#endif
#endif



#define PNAME  "Vel2Grid3D"

#include "GridLib.h"
#include "velmod.h"

typedef struct {
    float *buffer; /* buffer (contiguous floats) */
    float ***array; /* array access to buffer */
    int numx; /* Number of nodes in x coord */
    int numy; /* Number of nodes in y coord */
    int numz; /* Number of nodes in z coord */
    double origx; /* X orig (km) */
    double origy; /* Y orig (km) */
    double origz; /* V orig (km) */
    float dx; /* distance of nodes in x coord (km) */
    float dy; /* distance of nodes in y coord (km) */
    float dz; /* distance of nodes in z coord (km) */
    float *deltax; /* array with x coords of nodes (km) */
    float *deltay; /* array with y coords of nodes (km) */
    float *deltaz; /* array with z coords of nodes (km) */
    int type; /* type of velocity model [TYPESIMUL/TYPEFDTOMO] */
} VelModel;

#define TYPESIMUL  0
#define TYPEFDTOMO 1
#define TYPEALBERTO 2


/* globals  */

char fn_vg_input[MAXLINE];
char fn_vg_output[MAXLINE];


/* wave type (P, S, ...) for vel grids */
#define MAX_NUM_WAVE_TYPES 10
char WaveType[MAX_NUM_WAVE_TYPES][12];
int NumWaveTypes;

// velocity clipping (VGCLIP)
double vel_clip_low = -1.0;
double vel_clip_high = LARGE_DOUBLE;
#define CLIP_METHOD_INVALID -1
#define CLIP_METHOD_LOW_HIGH 0
#define CLIP_METHOD_CHECK_BELOW_FOR_MOHO 1
int clip_method = CLIP_METHOD_INVALID;

/* function declarations */

int ReadVel2Grid3DInput(FILE*, VelModel*);
int ReadVelModel(VelModel*, GridDesc*);
int VelModToGrid3d(VelModel*, GridDesc*, char *);
int AllocateVelModel(VelModel*);
void FreeVelModel(VelModel*);
int VelModToFMM(VelModel* vel_model, GridDesc* grid, FILE* fp_fmmVgridFile, FILE* fp_fmmPropGridFile, FILE* fp_fmmInterfacesFile, char *waveType);

/*** program to generate  3-D vel/slowness grid */


#define NARGS 2
#define LENDELTA 6

int main(int argc, char *argv[]) {

    int istat;
    int nWaveType;
    char fileRoot[MAXLINE];
    char fn_fmmVgridFile[MAXLINE];
    FILE* fp_fmmVgridFile = NULL;
    char fn_fmmPropGridFile[MAXLINE];
    FILE* fp_fmmPropGridFile = NULL;
    char fn_fmmInterfacesFile[MAXLINE];
    FILE* fp_fmmInterfacesFile = NULL;

    GridDesc mod_grid; /* model grid */
    VelModel vel_model; /* Velocity model */



    vel_model.buffer = NULL;
    vel_model.array = NULL;
    vel_model.deltax = vel_model.deltay = vel_model.deltaz = NULL;


    /* set program name */
    strcpy(prog_name, PNAME);

    /* check command line for correct usage */

    if (argc != NARGS) {
        disp_usage(prog_name, "<control file>");
        exit(EXIT_ERROR_USAGE);
    }



    /* set constants */

    prog_mode_3d = 1;
    prog_mode_Mod2D3D = 0;
    NumWaveTypes = 0;
    SetConstants();


    /* read control file */

    strcpy(fn_control, argv[1]);
    if ((fp_control = fopen(fn_control, "r")) == NULL) {
        nll_puterr("ERROR: opening control file.");
        exit(EXIT_ERROR_FILEIO);
    }

    if ((istat = ReadVel2Grid3DInput(fp_control, &vel_model)) < 0) {
        exit(EXIT_ERROR_FILEIO);
    }
    mod_grid = grid_in;


    /* determine model coordinates mode - rect or latlon */
    SetModelCoordsMode(num_surfaces);


    /* read Velocity Model input file */

    if ((istat = ReadVelModel(&vel_model, &mod_grid)) < 0) {
        if (istat == -1) nll_puterr("ERROR: reading first line of Velocity Model input file.");
        if (istat == -2) nll_puterr("ERROR: allocating memory for Velocity Model input file.");
        if (istat == -3) nll_puterr("ERROR: reading pos. of x-nodes of Velocity Model input file.");
        if (istat == -4) nll_puterr("ERROR: reading pos. of y-nodes of Velocity Model input file.");
        if (istat == -5) nll_puterr("ERROR: reading pos. of z-nodes of Velocity Model input file.");
        if (istat == -6) nll_puterr("ERROR: reading velocities of Velocity Model input file.");
        /*	nll_puterr("ERROR: reading Velocity Model input file."); */
        exit(EXIT_ERROR_FILEIO);
    }

    /* initialize 3D grid */

    /* allocate model grid */
    mod_grid.buffer = AllocateGrid(&mod_grid);
    if (mod_grid.buffer == NULL) {
        nll_puterr("ERROR: allocating memory for 3D slowness grid buffer.");
        exit(EXIT_ERROR_MEMORY);
    }
    /* create array access pointers */
    mod_grid.array = CreateGridArray(&mod_grid);
    if (mod_grid.array == NULL) {
        nll_puterr("ERROR: creating array for accessing 3D vel/slowness grid buffer.");
        exit(EXIT_ERROR_MEMORY);
    }


    // open ANU-FMM propgird.in in current working directory
    sprintf(fn_fmmPropGridFile, "propgrid.in");
    if ((fp_fmmPropGridFile = fopen(fn_fmmPropGridFile, "w")) == NULL) {
        nll_puterr("ERROR: opening ANU-FMM propgrid.in file.");
        return (-1);
    }

    // open ANU-FMM interfaces.in in current working directory
    sprintf(fn_fmmInterfacesFile, "interfaces.in");
    if ((fp_fmmInterfacesFile = fopen(fn_fmmInterfacesFile, "w")) == NULL) {
        nll_puterr("ERROR: opening ANU-FMM interfaces.in file.");
        return (-1);
    }

    // open ANU-FMM vgrids.in in current working directory
    sprintf(fn_fmmVgridFile, "vgrids.in");
    if ((fp_fmmVgridFile = fopen(fn_fmmVgridFile, "w")) == NULL) {
        nll_puterr("ERROR: opening ANU-FMM vgrids.in file.");
        return (-1);
    }
    // line 1  : number of velocity grids , number of velocity types (1 or 2)
    //fprintf(fp_fmmVgridFile, "%d %d  : number of velocity grids , number of velocity types (1 or 2)\n", NumWaveTypes, NumWaveTypes);

    /* create model for each wave type */

    for (nWaveType = 0; nWaveType < NumWaveTypes; nWaveType++) {

        sprintf(fileRoot, "%s.mod", WaveType[nWaveType]);
        sprintf(MsgStr, "Creating model grid files: %s.%s.*",
                fn_vg_output, fileRoot);
        nll_putmsg(1, MsgStr);

        /* load vel model to grid */

        if ((istat = VelModToGrid3d(&vel_model, &mod_grid, WaveType[nWaveType])) < 0) {
            nll_puterr("ERROR: loading velocity model to grid.");
            exit(EXIT_ERROR_MODEL);
        }

        /* save grid to disk */

        if ((istat = WriteGrid3dBuf(&mod_grid, NULL, fn_vg_output, fileRoot)) < 0) {
            nll_puterr("ERROR: writing slowness grid to disk.");
            exit(EXIT_ERROR_IO);
        }

        /* write ANU-FMM velocity file vgrids.in */

        if ((istat = VelModToFMM(&vel_model, &mod_grid, fp_fmmVgridFile, fp_fmmPropGridFile, fp_fmmInterfacesFile, WaveType[nWaveType])) < 0) {
            nll_puterr("ERROR: loading velocity model to grid.");
            exit(EXIT_ERROR_MODEL);
        }

    }

    fclose(fp_fmmVgridFile);
    fclose(fp_fmmPropGridFile);
    fclose(fp_fmmInterfacesFile);

    FreeVelModel(&vel_model);
    exit(EXIT_NORMAL);

}


/*** function to read Velocity Model input file */

int ReadVelModel(VelModel* vel_model, GridDesc* mod_grid) {

    int istat;
    int i, j, k;
    int numwaves;
#ifdef ETH_3D_FORMAT
    double dunit;
#endif
    float unit, tempfloat;
    char line[2 * MAXLINE];
    FILE* inpfile;

    // ALBERTO
    int ix, iy, iz;
    double a_vel, a_x, a_y, a_z;

    if ((inpfile = fopen(fn_vg_input, "r")) == NULL) {
        nll_puterr("ERROR: opening Velocity Model input file.");
        return (-1);
    }

    if (vel_model->type == TYPEFDTOMO) {
        if (AllocateVelModel(vel_model) < 0) {
            fclose(inpfile);
            return (-2);
        }

        for (i = 0; i < vel_model->numx; i++)
            vel_model->deltax[i] = vel_model->origx + i * vel_model->dx;
        for (j = 0; j < vel_model->numy; j++)
            vel_model->deltay[j] = vel_model->origy + j * vel_model->dy;
        for (k = 0; k < vel_model->numz; k++)
            vel_model->deltaz[k] = vel_model->origz + k * vel_model->dz;

        for (k = 0; k < vel_model->numz; k++) {
            for (j = 0; j < vel_model->numy; j++) {
                for (i = 0; i < vel_model->numx; i++)
                    fread(&vel_model->array[i][j][k], sizeof (float), 1, inpfile);
            }
        }
    } else if (vel_model->type == TYPEALBERTO) {

        vel_model->origx = mod_grid->origx;
        vel_model->origy = mod_grid->origy;
        vel_model->origz = mod_grid->origz;
        vel_model->numx = mod_grid->numx;
        vel_model->numy = mod_grid->numy;
        vel_model->numz = mod_grid->numz;
        vel_model->dx = mod_grid->dx;
        vel_model->dy = mod_grid->dy;
        vel_model->dz = mod_grid->dz;

        if (AllocateVelModel(vel_model) < 0) {
            fclose(inpfile);
            return (-2);
        }

        for (k = 0; k < vel_model->numz; k++)
            for (j = 0; j < vel_model->numy; j++)
                for (i = 0; i < vel_model->numx; i++)
                    vel_model->array[i][j][k] = 0.0;

        while ((fgets(line, 2 * MAXLINE, inpfile))) {
            //   3.193     35.974678  -120.378952      -12.000   -10.000    -3.000
            if (sscanf(line, "%lf %*f %*f %lf %lf %lf", &a_vel, &a_x, &a_y, &a_z) == 4) {

                a_x *= -1.0;
                ix = (int) (0.1 + (a_x - vel_model->origx) / vel_model->dx);
                iy = (int) (0.1 + (a_y - vel_model->origy) / vel_model->dy);
                iz = (int) (0.1 + (a_z - vel_model->origz) / vel_model->dz);
                //printf("%f %f %f %f -> %d %d %d\n", a_vel, a_x, a_y, a_z, ix, iy, iz);
                vel_model->array[ix][iy][iz] = a_vel;

            }
        }
    } else {
        if (fgets(line, 2 * MAXLINE, inpfile)) {

#ifdef ETH_3D_FORMAT
            // "%4f%3d%3d%3d%3d"
            istat = ReadFortranReal(line, 1, 4, &dunit);
            unit = (float) dunit;
            istat += ReadFortranInt(line, 5, 3, &vel_model->numx);
            istat += ReadFortranInt(line, 8, 3, &vel_model->numy);
            istat += ReadFortranInt(line, 11, 3, &vel_model->numz);
            istat += ReadFortranInt(line, 14, 3, &numwaves);
#else
            istat = sscanf(line, "%f %d %d %d %d", &unit, &vel_model->numx, &vel_model->numy, &vel_model->numz, &numwaves);
#endif
            fprintf(stderr, "\nReadVelModel: no. grid nodes in x,y,z -> %d %d %d \n", vel_model->numx, vel_model->numy, vel_model->numz);
            if (istat < 5) {
                fclose(inpfile);
                return (-1);
            }
            if (AllocateVelModel(vel_model) < 0) {
                fclose(inpfile);
                return (-2);
            }
            //if ((fgets(line, 2 * MAXLINE, inpfile)) && (strlen(line) >= (LENDELTA * vel_model->numx))) {
            fprintf(stderr, "ReadVelModel: grid nodes in x-dir\n\t");
            int nread = 0;
            for (i = 0; i < vel_model->numx; i++) {
                fscanf(inpfile, "%f", &tempfloat);
                /*  vel_model->deltax[i]=vel_model->origx+unit*tempfloat; */
#ifdef ZHANG_FORMAT_COORDS_POSX
                vel_model->deltax[i] = unit*tempfloat;
#else
#ifdef COORDS_POS_EAST
                vel_model->deltax[i] = unit*tempfloat;
#else
                vel_model->deltax[i] = -unit*tempfloat;
#endif
#endif
                fprintf(stderr, " %5.1f ", vel_model->deltax[i]);
                nread++;
            }
            fprintf(stderr, "\n");
            //} else {
            if (nread < vel_model->numx) {
                fclose(inpfile);
                return (-3);
            }

            //if ((fgets(line, 2 * MAXLINE, inpfile)) && (strlen(line) >= (LENDELTA * vel_model->numy))) {
            fprintf(stderr, "ReadVelModel: grid nodes in y-dir\n\t");
            nread = 0;
            for (i = 0; i < vel_model->numy; i++) {
                fscanf(inpfile, "%f", &tempfloat);
                /* vel_model->deltay[i]=vel_model->origy+unit*tempfloat; */
                fprintf(stderr, " %5.1f ", tempfloat);
                vel_model->deltay[i] = unit*tempfloat;
                /* fprintf(stderr,"%f\n",vel_model->deltay[i]);  */
                nread++;
            }
            fprintf(stderr, "\n");
            //} else {
            if (nread < vel_model->numy) {
                fclose(inpfile);
                return (-4);
            }

            //if ((fgets(line, 2 * MAXLINE, inpfile)) && (strlen(line) >= (LENDELTA * vel_model->numz))) {
            fprintf(stderr, "ReadVelModel: grid nodes in z-dir\n\t");
            nread = 0;
            for (i = 0; i < vel_model->numz; i++) {
                fscanf(inpfile, "%f", &tempfloat);
                /* vel_model->deltaz[i]=vel_model->origz+unit*tempfloat; */
                fprintf(stderr, " %5.1f ", tempfloat);
                vel_model->deltaz[i] = unit*tempfloat;
                /* fprintf(stderr,"%f\n",vel_model->deltaz[i]); */
                nread++;
            }
            fprintf(stderr, "\n");
            //} else {
            if (nread < vel_model->numz) {
                fclose(inpfile);
                return (-5);
            }

#ifdef ZHANG_FORMAT_COORDS_POSX
#else
            fscanf(inpfile, "%*f %*f %*f");
#endif

            for (k = 0; k < vel_model->numz; k++) {
                /*  	for(j=vel_model->numy;j>0;j--) */
                for (j = 0; j < vel_model->numy; j++) {
                    //if ((fgets(line, 2 * MAXLINE, inpfile)) && (strlen(line) >= (LENVEL * vel_model->numx))) {
                    nread = 0;
                    for (i = 0; i < vel_model->numx; i++) {
                        fscanf(inpfile, VEL_FORMAT, &vel_model->array[i][j][k]);
                        nread++;
                    }
                    if (nread < vel_model->numx) {
                        fclose(inpfile);
                        return (-6);
                    }
                }
            }
        } else {
            fclose(inpfile);
            return (-1);
        }
    }

    fclose(inpfile);
    return (0);
}

/*** returns the velocity with interpolation in x,y at a fixed z
 * use specified x,y,z node inices i,j,k nad xy interpolation factors t,u
 */

float bilinearAtZ(VelModel* vel_model, int i, int j, int k, int t, int u) {

    float vel;

    vel = vel_model->array[i - 1][j - 1][k]*(1 - t)*(1 - u) +
            vel_model->array[i][j - 1][k]*(t)*(1 - u) +
            vel_model->array[i][j][k]*(t)*(u) +
            vel_model->array[i - 1][j][k]*(1 - t)*(u);

    return (vel);
}

/*** returns the velocity in a definite point of the space ***/

float get3Dvel(VelModel* vel_model, float posx, float posy, float posz) {
    float vel;
    int i, j, k;
    float t, u, v;

    //printf("get3Dvel(vel_model, xval %f, yval %f, zdepth %f\n", posx, posy, posz);

#ifdef ZHANG_FORMAT_COORDS_POSX
    for (i = 0; (i < vel_model->numx) && (vel_model->deltax[i] < posx); i++);
#else
#ifdef COORDS_POS_EAST
    for (i = 0; (i < vel_model->numx) && (vel_model->deltax[i] < posx); i++);
#else
    for (i = 0; (i < vel_model->numx) && (vel_model->deltax[i] > posx); i++);
#endif
#endif
    for (j = 0; (j < vel_model->numy) && (vel_model->deltay[j] < posy); j++);
    for (k = 0; (k < vel_model->numz) && (vel_model->deltaz[k] < posz); k++);

    if (i == 0) {
        t = 0;
        i++;
    } else if (i == vel_model->numx) {
        t = 1;
        i--;
    } else
        t = (posx - vel_model->deltax[i - 1]) / (vel_model->deltax[i] - vel_model->deltax[i - 1]);

    if (j == 0) {
        u = 0;
        j++;
    } else if (j == vel_model->numy) {
        u = 1;
        j--;
    } else
        u = (posy - vel_model->deltay[j - 1]) / (vel_model->deltay[j] - vel_model->deltay[j - 1]);

    if (k == 0) {
        v = 0;
        k++;
    } else if (k == vel_model->numz) {
        v = 1;
        k--;
    } else
        v = (posz - vel_model->deltaz[k - 1]) / (vel_model->deltaz[k] - vel_model->deltaz[k - 1]);

    //printf("get3Dvel(vel_model, i %d, j %d, k %d, t %f, u %f, v %f\n", i, j, k, t, u, v);

    // vel unset
    vel = -1.0;

    // clip velocity on CHECK BELOW FOR MOHO (vel>Vhigh)
    if (clip_method == CLIP_METHOD_CHECK_BELOW_FOR_MOHO) {
        double v_below_target = bilinearAtZ(vel_model, i, j, k, t, u); // k is always index at node below desired posz
        if (v_below_target > vel_clip_high) {
            // find vel at deepest node with vel < vel_clip_high
            double v_above_moho = -1.0; // vel unset
            int k_above_moho = k - 1;
            while (k_above_moho > 0) {
                double vtest = bilinearAtZ(vel_model, i, j, k_above_moho, t, u);
                if (vtest < vel_clip_high) {
                    v_above_moho = vtest;
                    break;
                }
                k_above_moho--;
            }
            // set vel
            vel = v_above_moho; // clip velocity to vel at deepest node with vel < vel_clip_high
        }
    }

    if (vel < 0.0) { // vel not yet set, use full trilinear interpolation
        vel = vel_model->array[i - 1][j - 1][k - 1]*(1 - t)*(1 - u)*(1 - v) +
                vel_model->array[i][j - 1][k - 1]*(t)*(1 - u)*(1 - v) +
                vel_model->array[i][j][k - 1]*(t)*(u)*(1 - v) +
                vel_model->array[i - 1][j][k - 1]*(1 - t)*(u)*(1 - v) +
                vel_model->array[i - 1][j - 1][k]*(1 - t)*(1 - u)*(v) +
                vel_model->array[i][j - 1][k]*(t)*(1 - u)*(v) +
                vel_model->array[i][j][k]*(t)*(u)*(v) +
                vel_model->array[i - 1][j][k]*(1 - t)*(u)*(v);
    }

    // clip velocity on low/high check
    if (clip_method == CLIP_METHOD_LOW_HIGH) {
        if (vel < vel_clip_low)
            vel = vel_clip_low;
        else if (vel > vel_clip_high)
            vel = vel_clip_high;
    }

    return (vel);
}

/*** function to read output file name ***/

int get_vg_outfile(char* line1) {

    sscanf(line1, "%s", fn_vg_output);

    sprintf(MsgStr, "Vel2Grid3D files:  Output: %s.*",
            fn_vg_output);
    nll_putmsg(3, MsgStr);

    return (0);
}

/*** function to read wave type ***/

int get_vg_type(char* line1) {

    if (NumWaveTypes >= MAX_NUM_WAVE_TYPES) {
        nll_puterr("WARNING: maximum number of wave types reached, ignoring wave type.");
        return (-1);
    }


    sscanf(line1, " %s", WaveType[NumWaveTypes]);

    sprintf(MsgStr, "Vel2Grid3D wave type:  %s", WaveType[NumWaveTypes]);
    nll_putmsg(3, MsgStr);

    NumWaveTypes++;


    return (0);
}

/*** function to read velocity model clipping ***/

int get_vg_clip(char* line1) {

    sscanf(line1, " %lf %lf", &vel_clip_low, &vel_clip_high);

    sprintf(MsgStr, "Vel2Grid3D velocity clipping:  Vlow: %g  Vhigh: %g", vel_clip_low, vel_clip_high);
    nll_putmsg(2, MsgStr);

    if (vel_clip_low > vel_clip_high && vel_clip_high > 0.0) {
        clip_method = CLIP_METHOD_CHECK_BELOW_FOR_MOHO;
        sprintf(MsgStr, "Vel2Grid3D velocity clipping: Method is clip with CHECK BELOW FOR MOHO (vel>Vhigh)");
    } else {
        clip_method = CLIP_METHOD_LOW_HIGH;
        sprintf(MsgStr, "Vel2Grid3D velocity clipping: Method is clip LOW/HIGH");
    }
    /*else {
        clip_method = CLIP_METHOD_INVALID;
        sprintf(MsgStr, "ERROR: Vel2Grid3D velocity clipping: Invalid combination of low and high clipping values:  Vlow: %g  Vhigh: %g", vel_clip_low, vel_clip_high);
    }*/
    nll_putmsg(2, MsgStr);

    return (0);
}

/*** function to read input file name ***/

int get_vg_inpfile(VelModel* vel_model, char* line1) {
    char type[MAXLINE];

    sscanf(line1, "%s %s", fn_vg_input, type);

    if (strcmp(type, "FDTOMO") == 0) {
        if (sscanf(line1, "%*s %*s %lf %lf %lf %d %d %d %f %f %f", &vel_model->origx, &vel_model->origy, &vel_model->origz,
                &vel_model->numx, &vel_model->numy, &vel_model->numz, &vel_model->dx, &vel_model->dy, &vel_model->dz) != 9) {
            nll_puterr("ERROR: reading FDTOMO 3D Velocity input parameters");
            return (-1);
        }
        vel_model->type = TYPEFDTOMO;
    } else if (strcmp(type, "ALBERTO") == 0) {
        if (sscanf(line1, "%*s %*s %lf %lf %lf", &vel_model->origx, &vel_model->origy, &vel_model->origz) != 3) {
            nll_puterr("ERROR: reading ALBERTO 3D Velocity input parameters");
            return (-1);
        }
        vel_model->type = TYPEALBERTO;
    } else {
        if (sscanf(line1, "%*s %*s %lf %lf %lf", &vel_model->origx, &vel_model->origy, &vel_model->origz) != 3) {
            nll_puterr("ERROR: reading SIMUL2K 3D Velocity input parameters");
            return (-1);
        }
        vel_model->type = TYPESIMUL;
    }

    sprintf(MsgStr, "Vel2Grid3D files:  Input: %s   Type: %s", fn_vg_input, type);
    nll_putmsg(3, MsgStr);

    return (0);
}

/*** function to read input file */

int ReadVel2Grid3DInput(FILE* fp_input, VelModel * vel_model) {
    int istat, iscan;
    char param[MAXLINE], *pchr;
    char line[2 * MAXLINE], *fgets_return;

    int flag_control = 0, flag_inpfile = 0, flag_outfile = 0, flag_grid = 0, flag_type = 0,
            flag_trans = 0, flag_clip = 0;
    int flag_include = 1;

    /*	vel_model->origx = vel_model->origy = vel_model->origz = 0; */

    /* read each input line */


    while ((fgets_return = fgets(line, 2 * MAXLINE, fp_input)) != NULL
            || fp_include != NULL) {


        /* check for end of include file */

        if (fgets_return == NULL && fp_include != NULL) {
            SwapBackIncludeFP(&fp_input);
            continue;
        }


        istat = -1;

        /*read parameter line */

        if ((iscan = sscanf(line, "%s", param)) < 0)
            continue;

        /* skip comment line or white space */

        if (strncmp(param, "#", 1) == 0 || isspace(param[0]))
            istat = 0;



        /* read include file params and set input to include file */

        if (strcmp(param, "INCLUDE") == 0) {
            if ((istat = GetIncludeFile(strchr(line, ' '),
                    &fp_input)) < 0) {
                nll_puterr("ERROR: processing include file.");
                flag_include = 0;
            }
        }


        /* read control params */

        if (strcmp(param, "CONTROL") == 0) {
            if ((istat = get_control(strchr(line, ' '))) < 0)
                nll_puterr("ERROR: reading control params.");
            else
                flag_control = 1;
        }


        /*read transform params */

        if (strcmp(param, "TRANS") == 0) {
            if ((istat = get_transform(0, strchr(line, ' '))) < 0)
                nll_puterr("ERROR: reading transformation parameters.");
            else
                flag_trans = 1;
        }


        /* read Input file name (INPfile) */

        if (strcmp(param, "VGINP") == 0) {
            if ((istat = get_vg_inpfile(vel_model, strchr(line, ' '))) < 0)
                nll_puterr("ERROR: reading 3D Velocity input parameters.");
            else
                flag_inpfile = 1;
        }


        /* read output file name (OUTfile) */

        if (strcmp(param, "VGOUT") == 0) {
            if ((istat = get_vg_outfile(strchr(line, ' '))) < 0)
                nll_puterr("ERROR: reading Vel2Grid3D output file name.");
            else
                flag_outfile = 1;
        }


        /* read grid params */

        if (strcmp(param, "VGGRID") == 0) {
            if ((istat = get_grid(strchr(line, ' '))) < 0)
                nll_puterr("ERROR: reading grid parameters.");
            else
                flag_grid = 1;
        }



        /* read grid type (VGTYPE) */

        if (strcmp(param, "VGTYPE") == 0) {
            if ((istat = get_vg_type(strchr(line, ' '))) < 0)
                nll_puterr("ERROR: reading Vel2Grid3D grid type.");
            else
                flag_type = 1;
        }


        /* read clipping (VGCLIP) */

        if (strcmp(param, "VGCLIP") == 0) {
            if ((istat = get_vg_clip(strchr(line, ' '))) < 0)
                nll_puterr("ERROR: reading Vel2Grid3D clipping.");
            else
                flag_clip = 1;
        }



        /* unrecognized input */

        if (istat < 0) {
            if ((pchr = strchr(line, '\n')) != NULL)
                *pchr = '\0';
            sprintf(MsgStr, "Skipping input: %s", line);
            nll_putmsg(4, MsgStr);
        }

    }



    /* check for missing input */

    if (!flag_control)
        nll_puterr("ERROR: no control (CONTROL) params read.");
    if (!flag_inpfile)
        nll_puterr("ERROR: no inputfile (VGINP) params read.");
    if (!flag_outfile)
        nll_puterr("ERROR: no outputfile (VGOUT) params read.");
    if (!flag_type)
        nll_puterr("ERROR: no type (VGTYPE) params read.");
    if (!flag_grid)
        nll_puterr("ERROR: no grid (VGGRID) params read.");

    if (!flag_trans) {
        sprintf(MsgStr, "INFO: no transformation (TRANS) params read.");
        nll_putmsg(1, MsgStr);
        Hypocenter.comment[0] = '\0';
    }
    if (!flag_clip) {
        sprintf(MsgStr, "INFO: no clipping (VGCLIP) params read.");
        nll_putmsg(1, MsgStr);
    }

    return (flag_include * flag_control * flag_inpfile * flag_outfile * flag_grid * flag_type - 1);
}

/*** function to load 3D velocity model to model grid ***/

/*  Notes:

        (1)	Implicit staggered grids used -
        model space and travel time grid is numx X numy X numz while
        slowness grid is numx-1 X numy-1 X numz-1.
        Slowness values are for mid-points of travel time grid cells,
        i.e. slowness grid is shifted (+dx/2,+dy/2,+dz/2) in space relative to
        travel time grid.

        (2)	Podvin Lecomte FFD uses cubic cells, i.e. dx=dy=dz.

 */

int VelModToGrid3d(VelModel* vel_model, GridDesc* grid, char *waveType) {

    int ix, iy, iz;
    //int imodel;
    char cWaveType;
    double xval, yval, xloc, yloc, zdepth;
    double vel;


    /* check wavetype */

    if (strcmp(waveType, "P") == 0)
        cWaveType = 'P';
    else if (strcmp(waveType, "S") == 0)
        cWaveType = 'S';
    else {
        nll_puterr2("ERROR: unrecognized wave type", waveType);
        return (-1);
    }


    /* generate grid values */
    /* Note:  staggered grid assumed, thus vel lookup is shifted +dx/2, etc. */

    xval = grid->origx + grid->dx / 2.0;
    for (ix = 0; ix < grid->numx; ix++) {
        yval = grid->origy + grid->dy / 2.0;
        for (iy = 0; iy < grid->numy; iy++) {
            if (ModelCoordsMode == COORDS_LATLON) {
                rect2latlon(0, xval, yval, &yloc, &xloc);
            } else {
                xloc = xval;
                yloc = yval;
            }
            zdepth = grid->origz + grid->dz / 2.0;
            for (iz = 0; iz < grid->numz; iz++) {
                if (vel_model->type == TYPEALBERTO) {
                    vel = vel_model->array[ix][iy][iz];
                } else {
                    vel = get3Dvel(vel_model, xval, yval, zdepth);
                    /*if (imodel >= LAYEROFFSET || imodel < 0) {
                        // check for surface
                        vel1 = get_surface_vel(
                                xloc, yloc, zdepth,
                                cWaveType, model_surface,
                                num_surfaces, &den, 0);
                        vel = vel1 > 0.0 ? vel1 : vel;
                    }*/
                }

                if (vel < 0.0) {
                    nll_puterr("ERROR: cannot get velocity.");
                    return (-1);
                }

                switch (grid->type) {

                    case GRID_VELOCITY:
                        ((GRID_FLOAT_TYPE ***) grid->array)[ix][iy][iz] = vel;
                        break;

                    case GRID_VELOCITY_METERS:
                        ((GRID_FLOAT_TYPE ***) grid->array)[ix][iy][iz] = 1000.0 * vel;
                        break;

                    case GRID_SLOW_LEN:
                        ((GRID_FLOAT_TYPE ***) grid->array)[ix][iy][iz] = grid->dx / vel;
                        break;

                    case GRID_SLOW2_METERS:
                        ((GRID_FLOAT_TYPE ***) grid->array)[ix][iy][iz] =
                                (1.0e-3 / vel) * (1.0e-3 / vel);
                        break;

                    default:
                        nll_puterr("ERROR: unrecognized grid type.");
                        return (-1);

                }

                zdepth += grid->dz;
            }
            yval += grid->dy;
        }
        xval += grid->dx;
    }


    return (0);

}

int AllocateVelModel(VelModel * vel_model) {
    int ix, iy, numyz;
    float ***garray;


    /* Allocates memory for 3D Velocity Model node distance arrays */

    vel_model->deltax = (float *) malloc((size_t) (vel_model->numx * sizeof (float)));
    vel_model->deltay = (float *) malloc((size_t) (vel_model->numy * sizeof (float)));
    vel_model->deltaz = (float *) malloc((size_t) (vel_model->numz * sizeof (float)));

    if ((vel_model->deltax == NULL) || (vel_model->deltay == NULL) || (vel_model->deltaz == NULL)) {
        nll_puterr("ERROR: allocating memory for 3D Velocity Model node distance arrays.");
        return (-1);
    }
    NumAllocations += 3;



    /* allocate Velocity model buffer */

    vel_model->buffer = (float *) malloc((size_t)
            (vel_model->numx * vel_model->numy * vel_model->numz * sizeof (float)));
    if (vel_model->buffer == NULL) {
        nll_puterr("ERROR: allocating memory for 3D Velocity Model buffer.");
        return (-1);
    }
    NumAllocations++;



    /* creates array for accessing 3D velocity model */

    garray = (float ***) malloc((size_t) vel_model->numx * sizeof (float **));
    if (garray == NULL) {
        nll_puterr("ERROR: creating array for accessing 3D Velocity Model buffer.");
        return (-1);
    }
    NumAllocations++;
    numyz = vel_model->numy * vel_model->numz;
    for (ix = 0; ix < vel_model->numx; ix++) {
        garray[ix] = (float **) malloc((size_t) vel_model->numy * sizeof (float *));
        if (garray[ix] == NULL) {
            nll_puterr("ERROR: creating array for accessing 3D Velocity Model buffer.");
            return (-1);
        }
        NumAllocations++;
        for (iy = 0; iy < vel_model->numy; iy++)
            garray[ix][iy] = vel_model->buffer + ix * numyz + iy * vel_model->numz;
    }
    vel_model->array = garray;
    return (0);
}

/*** function to free the allocations in the velocity model structure ***/
void FreeVelModel(VelModel * vel_model) {
    int ix;

    /*** frees the array for accessing 3D velocity model ***/
    if (vel_model->array) {
        for (ix = 0; ix < vel_model->numx; ix++) {
            free(vel_model->array[ix]);
            NumAllocations--;
        }

        free(vel_model->array);
        vel_model->array = NULL;
        NumAllocations--;

    }


    /*** frees the buffer for 3D velocity model ***/
    if (vel_model->buffer) {
        free(vel_model->buffer);
        vel_model->buffer = NULL;
        NumAllocations--;
    }


    /* frees the arrays for 3D Velocity Model node distance */
    if (vel_model->deltax)
        free(vel_model->deltax);
    vel_model->deltax = NULL;
    if (vel_model->deltay)
        free(vel_model->deltay);
    vel_model->deltay = NULL;
    if (vel_model->deltaz)
        free(vel_model->deltaz);
    vel_model->deltaz = NULL;

}

/*** function to write ANU-FMM velocity file vgrids.in */

int VelModToFMM(VelModel* vel_model, GridDesc* grid, FILE* fp_fmmVgridFile, FILE* fp_fmmPropGridFile, FILE* fp_fmmInterfacesFile, char *waveType) {

    if (vel_model->type == TYPEALBERTO) {
        nll_puterr("WARNING: no FMM output for ALBERTO 3D Velocity input.");
    }
    //int imodel;
    char cWaveType;

    // check wavetype

    if (strcmp(waveType, "P") == 0)
        cWaveType = 'P';
    else if (strcmp(waveType, "S") == 0)
        cWaveType = 'S';
    else {
        nll_puterr2("ERROR: unrecognized wave type", waveType);
        return (-1);
    }

    int pad_for_propagation_grid = 2; // propagation grid is extended at each boudary:
    int pad_for_cublic_spline = 2 + pad_for_propagation_grid; // velocity grid is extended 2 cell beyond pad_for_propagation_grid at each boudary:
    // cubic spline interpolation requires that all the nodes of the FM propagation grid must be
    // INSIDE the SECOND layer of velocity grid nodes as counted from the grid boundaries

    // ANU-FMM header lines
    // next    : number of velocity grid nodes in radius,latitude, and longitude for grid
    fprintf(fp_fmmVgridFile, "%d %d %d  : number of velocity grid nodes in radius,latitude, and longitude for grid\n",
            grid->numz + 2 * pad_for_cublic_spline, grid->numy + 2 * pad_for_cublic_spline, grid->numx + 2 * pad_for_cublic_spline);

    // estimate lat and lon step from lat/lon of upper right and lower left corners of grid
    double xoff = 0.0;
    double yoff = 0.0;
    double lat_ll, lon_ll;
    rect2latlon(0, grid->origx + grid->dx * xoff, grid->origy + grid->dy * yoff, &lat_ll, &lon_ll);
    xoff = (double) (grid->numx - 1);
    yoff = (double) (grid->numy - 1);
    double lat_ur, lon_ur;
    rect2latlon(0, grid->origx + grid->dx * xoff, grid->origy + grid->dy * yoff, &lat_ur, &lon_ur);
    double lat_step = (lat_ur - lat_ll) / (double) (grid->numy - 2);
    double lat_ul, lon_ul;
    rect2latlon(0, grid->origx, grid->origy + grid->dy * yoff, &lat_ul, &lon_ul);
    double lat_lr, lon_lr;
    rect2latlon(0, grid->origx + grid->dx * xoff, grid->origy, &lat_lr, &lon_lr);

    // get lat/lon of grid center
    xoff = (double) (grid->numx / 2);
    yoff = (double) (grid->numy / 2);
    double lat_cent, lon_cent;
    rect2latlon(0, grid->origx + grid->dx * xoff, grid->origy + grid->dy * yoff, &lat_cent, &lon_cent);

    // to guarantee that FMM grid conaints velocity model grid,
    // estimate long step from uppoer (lower) boundary for Northern (Southern) hemisphere
    double lon_step = 0.0;
    if (lat_cent > 0.0) { // Northern hemisphere
        lon_step = (lon_ur - lon_ul) / (double) (grid->numx - 2);
    } else {
        lon_step = (lon_lr - lon_ll) / (double) (grid->numx - 2);
    }
    // next    : grid node spacing in radius (km) and in lat and in long (in radians) for grid 1
    fprintf(fp_fmmVgridFile, "%lf %lf %lf  # dz/dlat/dlon=%f/%f/%f\n", grid->dz, lat_step * cRPD, lon_step * cRPD, grid->dz, lat_step, lon_step);

    // get origin for FMM
    double lat_orig = lat_cent - yoff * lat_step; // orig FMM is calculated from grid center, not NLL grid origin.
    double lat_orig_vel = lat_orig - lat_step * pad_for_cublic_spline;
    double lon_orig = lon_cent - xoff * lon_step; // orig FMM is calculated from grid center, not NLL grid origin.
    double lon_orig_vel = lon_orig - lon_step * pad_for_cublic_spline;
    // next    : origin of velocity grid in radius (km) and in lat and long (in radians) for grid 1
    double z_orig = grid->origz + grid->dz * (double) (grid->numz - 1);
    double z_orig_vel = z_orig + grid->dz * pad_for_cublic_spline;
    fprintf(fp_fmmVgridFile, "%lf %lf %lf  # z/lat/lon=%f/%f/%f\n",
            AVG_ERAD - z_orig_vel, lat_orig_vel * cRPD, lon_orig_vel * cRPD,
            z_orig_vel, lat_orig_vel, lon_orig_vel
            );

    // write interfaces.in header
    int n_interfaces_to_write = 2;
    fprintf(fp_fmmInterfacesFile, "%d    # number of interfaces\n", n_interfaces_to_write);
    fprintf(fp_fmmInterfacesFile, "%d %d      # number of interface grid nodes in lat and in long\n",
            grid->numy + 2 * pad_for_cublic_spline, grid->numx + 2 * pad_for_cublic_spline);
    fprintf(fp_fmmInterfacesFile, "%lf %lf      # grid node spacing in lat and in long (in radians)  = %fdeg %fdeg\n",
            lat_step * cRPD, lon_step * cRPD, lat_step, lon_step);
    fprintf(fp_fmmInterfacesFile, "%lf %lf      # origin of interface grid in lat and long (in radians)  = %fdeg %fdeg\n",
            lat_orig_vel * cRPD, lon_orig_vel *cRPD, lat_orig_vel, lon_orig_vel);


    // next    : values of the velocity at each velocity grid node for grid 1, in km/sec,
    //           one per line with longitude index varying the fastest, latitude
    //           index the second fastest and radius index the slowest


    // generate grid values
    double xval, yval;
    int ix, iy, iz;
    double vel;
    int n_interfaces_written = 0;
    double zdepth = z_orig_vel;
    int iz_start = grid->numz - 1 + pad_for_cublic_spline;
    int iz_end = -pad_for_cublic_spline;
    for (iz = iz_start; iz >= iz_end; iz--) {
        double ylat = lat_orig_vel;
        for (iy = -pad_for_cublic_spline; iy < grid->numy + pad_for_cublic_spline; iy++) {
            double xlon = lon_orig_vel;
            for (ix = -pad_for_cublic_spline; ix < grid->numx + pad_for_cublic_spline; ix++) {
                // vgrids.in
                if (ModelCoordsMode == COORDS_LATLON) {
                    xval = xlon;
                    yval = ylat;
                } else {
                    latlon2rect(0, ylat, xlon, &xval, &yval);
                }
                vel = get3Dvel(vel_model, xval, yval, zdepth);
                /*if (imodel >= LAYEROFFSET || imodel < 0) {
                    // check for surface
                    vel1 = get_surface_vel(
                            xlon, ylat, zdepth,
                            cWaveType, model_surface,
                            num_surfaces, &den, 0);
                    vel = vel1 > 0.0 ? vel1 : vel;
                }*/
                if (vel < 0.0) {
                    nll_puterr("ERROR: cannot get velocity.");
                    return (-1);
                }
                fprintf(fp_fmmVgridFile, "%lf\n", vel);
                // interface.in
                if (iz == iz_start)
                    fprintf(fp_fmmInterfacesFile, "%lf\n", AVG_ERAD - (grid->origz - grid->dz * pad_for_propagation_grid)); // top of prop grid
                else if (iz == iz_end)
                    fprintf(fp_fmmInterfacesFile, "%lf\n", AVG_ERAD - (grid->origz + grid->dz * (grid->numz - 1 + pad_for_propagation_grid))); // bottom of prop grid

                xlon += lon_step;
            }
            ylat += lat_step;
        }
        n_interfaces_written++;
        zdepth -= grid->dz;
    }


    // write propgrid.in
    // line 1  : number of propagation grid points in r, lat and long
    fprintf(fp_fmmPropGridFile, "%d %d %d    \t number of propagation grid points in r, lat and long\n",
            grid->numz + 2 * pad_for_propagation_grid, grid->numy + 2 * pad_for_propagation_grid, grid->numx + 2 * pad_for_propagation_grid);
    // line 2  : grid intervals in r (km) lat,long (deg)
    fprintf(fp_fmmPropGridFile, "%lf %lf %lf    \t grid intervals in r (km) lat,long (deg)\n", grid->dz, lat_step, lon_step);
    // line 3  : origin of the grid height (km),lat,long (deg)
    fprintf(fp_fmmPropGridFile, "%lf %lf %lf    \t origin of the grid height (km),lat,long (deg)\n",
            -grid->origz + grid->dz * pad_for_propagation_grid, lat_orig - lat_step * pad_for_propagation_grid, lon_orig - lon_step * pad_for_propagation_grid);
    // line 4  : refinement factor and # of propgrid cells in refined source grid
    fprintf(fp_fmmPropGridFile, "%d %d    \t refinement factor and number of propgrid cells in refined source grid\n", 5, 10);
    fprintf(fp_fmmPropGridFile, "\n\n");



    return (0);

}

