surfDepValues *determineSurfaceDepthsBasin(globalBasinData *basinData ,gridStruct *location, char *fileName, int basinNum, int surfNum)
/*
 Purpose:   obtain the depths for all lat lon points within the  for a given surface file
 
 Input variables:
 location  - structure containing lat lon grid
 fileName  - filename of the surface file for reading
 
 Output variables:
 surfDep   - (malloc'd) pointer to structure containing surface depths for all lat lon points
 */
{
    // read in the filename
    surfRead *currentSurface;
    currentSurface = loadSurface(fileName);
    
    surfDepValues *surfDep;
    surfDep = malloc(sizeof(surfDepValues));
    adjacentPointsStruct *points;
    
    // loop over values and find the depth of the surface at all points (2D interpolation)
    for(int i = 0; i < location->nX; i++)
    {
        for(int j = 0; j < location->nY; j++)
        {
            if(basinData->inBasinLatLon[basinNum][basinData->boundaryType[basinNum][surfNum]][i][j] == 1)
            {
                // find adjacent points
                points = findAdjacentPoints(currentSurface, location->Lat[i][j], location->Lon[i][j]);
                //                printf("%lf %lf\n",location->Lat[i][j], location->Lon[i][j]);
                //                printf("%i\n",basinData->inBasinLatLon[basinNum][basinData->boundaryType[basinNum][surfNum]][i][j]);
                
                if (points->inSurfaceBounds == 1)
                {
                    // interpolate
                    surfDep->dep[i][j] = biLinearInterpolation( currentSurface->loni[points->lonInd[0]], currentSurface->loni[points->lonInd[1]], currentSurface->lati[points->latInd[0]], currentSurface->lati[points->latInd[1]], currentSurface->raster[points->lonInd[0]][points->latInd[0]], currentSurface->raster[points->lonInd[0]][points->latInd[1]], currentSurface->raster[points->lonInd[1]][points->latInd[0]], currentSurface->raster[points->lonInd[1]][points->latInd[1]], location->Lon[i][j], location->Lat[i][j]);
                    free(points);
                }
                else
                {
                    printf("%lf %lf\n",location->Lat[i][j], location->Lon[i][j]);

                    printf("Error, point lies outside basin surface domain.\n");
                }
            }
            else
            {
                surfDep->dep[i][j] = NAN; // define as NAN if surface is outside of the boundary
            }
        }
    }
    free(currentSurface);
    
    return surfDep;
}
TEST(stencil, biLinearInterpolationTest) {
  ASSERT_EQ(1.0, biLinearInterpolation(1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0));

  // CORNERS
  ASSERT_EQ(1.0, biLinearInterpolation(0.0, 1.0, 0.0, 1.0, 1.0, 2.0, 3.0, 4.0));

  ASSERT_EQ(2.0, biLinearInterpolation(1.0, 0.0, 0.0, 1.0, 1.0, 2.0, 3.0, 4.0));

  ASSERT_EQ(3.0, biLinearInterpolation(1.0, 0.0, 1.0, 0.0, 1.0, 2.0, 3.0, 4.0));

  ASSERT_EQ(4.0, biLinearInterpolation(0.0, 1.0, 1.0, 0.0, 1.0, 2.0, 3.0, 4.0));

  // CENTER OF EDGES
  ASSERT_EQ(1.5, biLinearInterpolation(1.0, 1.0, 0.0, 1.0, 1.0, 2.0, 3.0, 4.0));

  ASSERT_EQ(2.5, biLinearInterpolation(1.0, 0.0, 1.0, 1.0, 1.0, 2.0, 3.0, 4.0));

  ASSERT_EQ(3.5, biLinearInterpolation(1.0, 1.0, 1.0, 0.0, 1.0, 2.0, 3.0, 4.0));

  ASSERT_EQ(2.5, biLinearInterpolation(0.0, 1.0, 1.0, 1.0, 1.0, 2.0, 3.0, 4.0));

  // CENTRE
  ASSERT_EQ(2.5, biLinearInterpolation(1.0, 1.0, 1.0, 1.0, 1.0, 2.0, 3.0, 4.0));
}
void extractSlice(gridStruct *location, modOrigin modelOrigin, sliceExtent sliceBounds, globalDataValues *globDataVals, char *outputDirectory, int sliceNumber)
{
    
    sliceExtractData *sliceData;
    sliceData = malloc(sizeof(sliceExtractData));
    generateSliceXYpoints(sliceData, modelOrigin, sliceBounds);

    
    // loop over points
    int xInd = 0;
    int yInd = 0;
    double X1, X2, Y1, Y2;
    double Q11Vp, Q12Vp, Q21Vp, Q22Vp;
    double Q11Vs, Q12Vs, Q21Vs, Q22Vs;
    double Q11Rho, Q12Rho, Q21Rho, Q22Rho;
    
    for(int i = 0; i < sliceData->nPts; i++)
    {
        // if x or y value is outside of the bound of the dataset return NaNs.
//        printf("%lf %lf %lf\n", sliceData->xPts[i], location->X[0],location->X[location->nX-1]);
//        printf("%lf %lf %lf\n", sliceData->yPts[i], location->Y[0],location->Y[location->nY-1]);

        if(sliceData->xPts[i]<location->X[0] || sliceData->xPts[i]>location->X[location->nX-1] || sliceData->yPts[i]>location->Y[location->nY-1] || sliceData->yPts[i]<location->Y[0] )
        {
            for(int k = 0; k < location->nZ; k++)
            {
                sliceData->Vp[i][k] = NAN;
                sliceData->Vs[i][k] = NAN;
                sliceData->Rho[i][k] = NAN;
            }
        }
        else  // get indice of nearby points
        {
            for(int k = 0; k < location->nX; k++)
            {
                if(location->X[k]>=sliceData->xPts[i])
                {
                    xInd = k-1;
                    break;
                }
            }
            for(int j = 0; j < location->nY; j++)
            {
                if(location->Y[j]>=sliceData->yPts[i])
                {
                    yInd = j-1;
                    break;
                }
            }
            // interpolate points
            X1 = location->X[xInd];
            X2 = location->X[xInd+1];
            Y1 = location->Y[yInd];
            Y2 = location->Y[yInd+1];
            
//            double lat1, lat2, lon1, lon2;
//            lat1 = location->Lat[xInd][yInd];
//            lat2 = location->Lat[xInd+1][yInd+1];
//            lon1 = location->Lon[xInd][yInd];
//            lon2 = location->Lon[xInd+1][yInd+1];
//            
//            printf("%lf %lf %lf %lf\n", X1, X2, Y1,Y2);
//            printf("%lf %lf\n",sliceData->xPts[i], sliceData->yPts[i]);
//            printf("%lf %lf %lf %lf\n", lat1, lat2, lon1, lon2);
//            printf("%lf %lf\n",sliceData->latPts[i], sliceData->lonPts[i]);
            
            for(int k = 0; k < location->nZ; k++)
            {
                // Vs
                Q11Vs = globDataVals->Vs[xInd][yInd][k];
                Q12Vs = globDataVals->Vs[xInd][yInd+1][k];
                Q21Vs = globDataVals->Vs[xInd+1][yInd][k];
                Q22Vs = globDataVals->Vs[xInd+1][yInd+1][k];
                
//                printf("%lf %lf %lf %lf\n", Q11Vs, Q21Vs, Q22Vs, Q12Vs);
                
                sliceData->Vs[i][k] = biLinearInterpolation(X1, X2, Y1, Y2, Q11Vs, Q12Vs, Q21Vs, Q22Vs, sliceData->xPts[i], sliceData->yPts[i]);
                
                // Vp
                Q11Vp = globDataVals->Vp[xInd][yInd][k];
                Q12Vp = globDataVals->Vp[xInd][yInd+1][k];
                Q21Vp = globDataVals->Vp[xInd+1][yInd][k];
                Q22Vp = globDataVals->Vp[xInd+1][yInd+1][k];
//                printf("%lf %lf %lf %lf\n", Q11Vp, Q21Vp, Q22Vp, Q12Vp);

                
                sliceData->Vp[i][k] = biLinearInterpolation(X1, X2, Y1, Y2, Q11Vp, Q12Vp, Q21Vp, Q22Vp, sliceData->xPts[i], sliceData->yPts[i]);
                
                // Rho
                Q11Rho = globDataVals->Rho[xInd][yInd][k];
                Q12Rho = globDataVals->Rho[xInd][yInd+1][k];
                Q21Rho = globDataVals->Rho[xInd+1][yInd][k];
                Q22Rho = globDataVals->Rho[xInd+1][yInd+1][k];
                
                sliceData->Rho[i][k] = biLinearInterpolation(X1, X2, Y1, Y2, Q11Rho, Q12Rho, Q21Rho, Q22Rho, sliceData->xPts[i], sliceData->yPts[i]);
            }
        }
        
    }
    char sliceDir[128];
    sprintf(sliceDir,"%s/Slices",outputDirectory);

    struct stat st = {0};
    if (stat(sliceDir, &st) == -1)
    {
        // create output directory and the velocity model
        mkdir(sliceDir, 0700);
    }
    
    // generate file for writing
    FILE *fp2;
    double currRho, currVp, currVs;
    char fName[128];
    sprintf(fName,"%s/ExtractedSlice%i.txt",sliceDir,sliceNumber);
    fp2 = fopen(fName,"w");
    fprintf(fp2,"Extracted slice #%i.\n",sliceNumber);
    fprintf(fp2,"Slice_Horizontal_Resolution\t%i\n",sliceBounds.resXY);
    fprintf(fp2,"LatA:\t%lf\n",sliceData->latPts[0]);
    fprintf(fp2,"LatB:\t%lf\n",sliceData->latPts[sliceData->nPts-1]);
    fprintf(fp2,"LonA:\t%lf\n",sliceData->lonPts[0]);
    fprintf(fp2,"LonB:\t%lf\n",sliceData->lonPts[sliceData->nPts-1]);
    fprintf(fp2,"Lat \t Lon \t Depth \t Vp \t Vs \t Rho\n");
    for(int i = 0; i < sliceData->nPts; i++)
    {
        for(int m = 0; m < location->nZ; m++)
        {
            currVp = sliceData->Vp[i][m];
            currRho = sliceData->Rho[i][m];
            currVs = sliceData->Vs[i][m];
            fprintf(fp2, "%lf\t%lf\t%lf\t%lf\t%lf\t%lf\n",sliceData->latPts[i],sliceData->lonPts[i], location->Z[m], currVp, currVs, currRho);
            
        }
        
    }
    
    fclose(fp2);
    free(sliceData);
    
}
surfDepValues *determineSurfaceDepths(gridStruct *location, char *fileName)
/*
 Purpose:   obtain the depths for all lat lon points for a given surface file
 
 Input variables:
 location  - structure containing lat lon grid
 fileName  - filename of the surface file for reading
 
 Output variables:
 surfDep   - (malloc'd) pointer to structure containing surface depths for all lat lon points
 */
{
    // read in the filename
    surfRead *currentSurface;
    currentSurface = loadSurface(fileName);
    
    surfDepValues *surfDep;
    surfDep = malloc(sizeof(surfDepValues));
    adjacentPointsStruct *points;
    
    double p1, p2, p3, v1, v2;

    
    // loop over values and find the depth of the surface at all points (2D interpolation)
    for(int i = 0; i < location->nX; i++)
    {
        for(int j = 0; j < location->nY; j++)
        {
            // find adjacent points
            points = findAdjacentPoints(currentSurface, location->Lat[i][j], location->Lon[i][j]);
            
            if (points->inSurfaceBounds == 1)
            {
            // interpolate
            surfDep->dep[i][j] = biLinearInterpolation( currentSurface->loni[points->lonInd[0]], currentSurface->loni[points->lonInd[1]], currentSurface->lati[points->latInd[0]], currentSurface->lati[points->latInd[1]], currentSurface->raster[points->lonInd[0]][points->latInd[0]], currentSurface->raster[points->lonInd[0]][points->latInd[1]], currentSurface->raster[points->lonInd[1]][points->latInd[0]], currentSurface->raster[points->lonInd[1]][points->latInd[1]], location->Lon[i][j], location->Lat[i][j]);
            }
            else if (points->inLatExtensionZone == 1)
            {
                p1 = currentSurface->loni[points->lonInd[0]];
                p2 = currentSurface->loni[points->lonInd[1]];
                v1 = currentSurface->raster[points->lonInd[0]][points->latEdgeInd];
                v2 = currentSurface->raster[points->lonInd[1]][points->latEdgeInd];
                p3 = location->Lon[i][j];
                surfDep->dep[i][j] = linearInterpolation(p1, p2, v1, v2, p3);
            }
            else if (points->inLonExtensionZone == 1)
            {
                p1 = currentSurface->lati[points->latInd[0]];
                p2 = currentSurface->lati[points->latInd[1]];
                v1 = currentSurface->raster[points->lonEdgeInd][points->latInd[0]];
                v2 = currentSurface->raster[points->lonEdgeInd][points->latInd[1]];
                p3 = location->Lat[i][j];
                surfDep->dep[i][j] = linearInterpolation(p1, p2, v1, v2, p3);
            }
            else if (points->inCornerZone == 1)
            {
                surfDep->dep[i][j] = currentSurface->raster[points->cornerLonInd][points->cornerLatInd];
            }
            free(points);

        }
    }
    free(currentSurface);
        
    return surfDep;
}
depInterpVals *loadEPtomo2010subMod(gridStruct *location)
/*
 Purpose:   read in the Eberhart-Phillips 2010 tomography dataset
 
 Input variables:
 location       - struct containing the lat lon and dep points
 
 Output variables:
 surfDepVals    - (malloc'd) pointer to a struct containing the values of the "surfaces" at each lat lon value 
 
 */
{
    const char *varNames[3];
    varNames[0] = "vp", varNames[1] = "vs", varNames[2] = "rho";
    int nElev = 14; // only read first 14 for efficiency
    int elev[17] = {15, 1, -3, -8, -15, -23, -30, -38, -48, -65, -85, -105, -130, -155, -185, -225, -275 };
//    int elev[21] = {15, 1, -3, -5, -8, -11, -15, -23, -30, -38, -48, -65, -85, -105, -130, -155, -185, -225, -275, -370, -630};
    //int elev[20] = {10, 1, -5, -8, -11, -15, -23, -30, -38, -48, -65, -85, -105, -130, -155, -185, -225, -275, -370, -620};
    char baseFilename[256];
    
    depInterpVals *surfDepVals = NULL;
    surfDepVals = malloc(sizeof(depInterpVals));
    surfDepVals->numSurf = nElev;
    surfRead *tempSurf;
    adjacentPointsStruct *points;
    
    double X1, X2, Y1, Y2, Q11, Q12, Q22, Q21, X, Y, interpVal;


    for(int i = 0; i < nElev; i++)
    {
        surfDepVals->deps[i] = elev[i];
        for(int j = 0; j < 3; j++)
        {
            sprintf(baseFilename,"Data/Tomography/surf_tomography_%s_elev%i.in",varNames[j],elev[i]);
            // read the surface
            tempSurf = loadSurface(baseFilename);
            // write a surface vector (for IDW)
            for(int k = 0; k < location->nX; k++)
            {
                for(int n = 0; n < location->nY; n++)
                {
                    points = findAdjacentPoints(tempSurf, location->Lat[k][n], location->Lon[k][n]); // a little inefficient as process is repeated for each of Vp Vs and Rho!
                    X1 = tempSurf->loni[points->lonInd[0]];
                    X2 = tempSurf->loni[points->lonInd[1]];
                    Y1 = tempSurf->lati[points->latInd[0]];
                    Y2 = tempSurf->lati[points->latInd[1]];
                    X = location->Lon[k][n];
                    Y = location->Lat[k][n];
                    Q11 = tempSurf->raster[points->lonInd[0]][points->latInd[0]];
                    Q12 = tempSurf->raster[points->lonInd[0]][points->latInd[1]];
                    Q21 = tempSurf->raster[points->lonInd[1]][points->latInd[0]];
                    Q22 = tempSurf->raster[points->lonInd[1]][points->latInd[1]];
                    
                    interpVal = biLinearInterpolation(X1, X2, Y1, Y2, Q11, Q12, Q21, Q22, X, Y);
                    if(j == 0)
                    {
                        surfDepVals->Vp[i][k][n] = interpVal;
                    }
                    else if(j == 1)
                    {
                        surfDepVals->Vs[i][k][n] = interpVal;
                    }
                    else if(j ==2)
                    {
                        surfDepVals->Rho[i][k][n] = interpVal;
                    }
                    free(points);
                }
            }
            free(tempSurf);
        }
        printf("\rReading tomography data %d%% complete.", i*100/nElev);
        fflush(stdout);
    }
    printf("\rReading tomography data 100%% complete.");
    fflush(stdout);
    printf("\n");

return surfDepVals;

}