Skip to content
Snippets Groups Projects
JespersRayTracer.c 25.89 KiB
//
//  JespersRayTracer.c
//  
//
//  Written by Jesper Spetzler
//
//  changed to C by Jan Thorbecke on 21/09/2017.
//
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include "raytime.h"

#define MAX(x,y) ((x) > (y) ? (x) : (y))
#define MIN(x,y) ((x) < (y) ? (x) : (y))
#define NINT(x) ((int)((x)>0.0?(x)+0.5:(x)-0.5))

static float H, L, W, iH, iL, iW;

int getnRay(icoord size, fcoord s, fcoord r, float dx, int nRayStep);
int traceTwoPoint(fcoord s, fcoord r, int nRay, fcoord *rayReference3D);
float takeOffAngle(fcoord s, fcoord r);
float referenceSlowness(float *slowness, icoord size, int nRay, fcoord r, fcoord s);
int xPointIndex(const float _x, int nx, float L);
int zPointIndex(const float _z, int nz, float H);
int yPointIndex(const float _y, int ny, float W);
fcoord getSlownessGradient(const float _x, const float _z, float *slowness, icoord size);
float qMulGradU1(const float _x, const float _z, const float _angle, float *slowness, icoord size);
float greenTwoP(const float _so, const float _slow, const float _sL, int nRay, fcoord s, fcoord r, float *slowness, icoord size);
float qatso(const float _so, const float _angle, int nRay, fcoord s, fcoord r, fcoord *rayReference3D, float *slowness, icoord size, float uo);
float slownessA(float *slowness, icoord size, float _x, float _y, float _z);
float getdT2(const float _x, const float _z, const float so, const float _angle, const float _ds, int nRay, fcoord s, fcoord r, fcoord *rayReference3D, float *slowness, icoord size, float uo);
float greenIntP(const float _so, const float _s, const float _sL, float *slowness, icoord size, int nRay, fcoord r, fcoord s);
float secondDerivativeU1(float *slowness, icoord size, const float _x, const float _z, const float _angle, fcoord s, fcoord r);
int calculatePerturbedRay(fcoord *rayPerturbed3D, fcoord s, fcoord r, int nRay, fcoord *rayReference3D, float *slowness, icoord size, float uo);
float angle2qx(const float _angle);
float angle2qz(const float _angle);
float ModelInterpolation_slowness2D(float *slowness, icoord size, const float _x, const float _z);
float ModelInterpolation_slowness3D(float *slowness, icoord size, const float _x, const float _z, const float _y);
void applyMovingAverageFilter(float *slowness, icoord size, int window, int dim, float *averageModel);



#define lGradient 1
#define EPSMIN 0.1
#define minValueGradient 1e-10
#define PI 3.1514926535
#define minValueSecondDerivativeU1 1e-6
#define DPHI_ANGLE 1.0   // 0.5

int getWaveParameter(float *slowness, icoord size, float dgrid, fcoord s, fcoord r, rayPar ray, fcoord *T, float *Jr)
{
	static int first=1;
	float *smooth;
    float T0, T1, T2;
    float uo, u1, lengthRefRay;
    float x, y, z;
    float dx, dy, dz, dl, so, ds;
    float angle;
    float dQdPhi, J, greentmp;
    int nRayTmp, error, i;
    fcoord *rayReference3D;
    
    T0 = T1 = T2 = 0;
    J = 1;
    error = 0;
    
    nRayTmp = getnRay(size, s, r, dgrid, ray.nray);
    
    //fprintf(stderr,"Calling getnRay gives nRayTmp=%d nRayStep=%d\n", nRayTmp, nRayStep);

    rayReference3D = (fcoord *)calloc(nRayTmp,sizeof(fcoord));
    traceTwoPoint(s, r, nRayTmp, rayReference3D);
    
    dx = rayReference3D[nRayTmp-1].x - rayReference3D[0].x;
    dy = rayReference3D[nRayTmp-1].y - rayReference3D[0].y;
    dz = rayReference3D[nRayTmp-1].z - rayReference3D[0].z;
    lengthRefRay = sqrt(pow(dx, 2) + pow(dy, 2) + pow(dz, 2));
    
    angle = takeOffAngle(s, r);
    
    if ((lengthRefRay <= 0) || (nRayTmp <= 1))
        return(-1);
    
    uo = referenceSlowness(slowness, size, nRayTmp, r, s);

    T0 = lengthRefRay*uo;
    ds = lengthRefRay/(nRayTmp-1);
    J = lengthRefRay;
    dQdPhi = 0;

    for (i = 0; i < nRayTmp-1; i++)
    {
        x = 0.5*(rayReference3D[i+1].x + rayReference3D[i].x);
        y = 0.5*(rayReference3D[i+1].y + rayReference3D[i].y);
        z = 0.5*(rayReference3D[i+1].z + rayReference3D[i].z);
        
        u1 = slownessA(slowness, size, x, z, y) -  uo;
        
        dx = rayReference3D[i+1].x - rayReference3D[i].x;
        dy = rayReference3D[i+1].y - rayReference3D[i].y;
        dz = rayReference3D[i+1].z - rayReference3D[i].z;
        
        dl = sqrt(pow(dx, 2) + pow(dy, 2) + pow(dz, 2));
        
        T1 += dl*u1;
        
        so = i*ds;
        
        if (ray.useT2 != 0)
            T2 += getdT2(x, z, so, angle, ds, nRayTmp, s, r, rayReference3D, slowness, size, uo);

        /*if (ray.geomspread != 0) {
            if (so <= 0) {
                dQdPhi = 0;
            }
            else {
                greentmp = 0;
                if (so <= lengthRefRay) greentmp = (lengthRefRay - so)/uo;
                dQdPhi += greentmp*secondDerivativeU1(slowness, size, x, z, angle, r, s)*ds/so;
            }
        }*/
    }

    if (ray.useT2)
        T2 *= 0.5;
    
    T->x = T0;
    T->y = T1;
    T->z = T2;
    
    // The geometrical spreading factor
    
    if (ray.geomspread)
    {
        J += dQdPhi;
        
        if (J == 0)
            J = 1;
        
        if (J < 0)
        {
            error = -1; //snegativeGeometricalSpreadingFactor;
            J = fabs(J);
        }
    }
    
    if (size.y == 1) {
        J = sqrt(J);
    }
    
    *Jr = J;
    free(rayReference3D);
    
    return(error);
}

int getnRay(icoord size, fcoord s, fcoord r, float dx, int nRayStep)
{
    int dn, nRayTmp;
    float dl, dr;
    
    H = (size.z-1)*dx;
    L = (size.x-1)*dx;
    W = (size.y-1)*dx;
    
    if (H!=0.0) iH = 1.0/H;
    if (L!=0.0) iL = 1.0/L;
    if (W!=0.0) iW = 1.0/W;

    if (size.y == 1) { // 2D model
        dn = (size.x + size.z)/2;
        dl = sqrt(pow(L, 2) + pow(H, 2))/dn;
        dr = sqrt(pow(r.x-s.x, 2) + pow(r.z-s.z, 2));
    }
    else { // 3D model
        dn = (size.x + size.z + size.y)/3;
        dl = sqrt(pow(L, 2) + pow(H, 2) + pow(W, 2))/dn;
        dr = sqrt(pow(r.x-s.x, 2) + pow(r.z-s.z, 2) + pow(r.y-s.y, 2));
        
    }
    nRayTmp = MIN(300,dr*nRayStep/dl);
    //fprintf(stderr,"getnRay: gives nRayTmp=%d dr=%f dl=%f\n", nRayTmp, dr, dl);

    if (nRayTmp <= nRayStep)
        nRayTmp = nRayStep;
    
    return nRayTmp;

}

int traceTwoPoint(fcoord s, fcoord r, int nRay, fcoord *rayReference3D)
{
    float x, y, z;
    int i;
    
    for (i = 0; i < nRay; i++)
    {
        x = s.x + (r.x - s.x)*i/(nRay-1);
        y = s.y + (r.y - s.y)*i/(nRay-1);
        z = s.z + (r.z - s.z)*i/(nRay-1);
        rayReference3D[i].z=z;
        rayReference3D[i].x=x;
        rayReference3D[i].y=y;
    }
    
    return 0;
}

int calculatePerturbedRay(fcoord *rayPerturbed3D, fcoord s, fcoord r, int nRay, fcoord *rayReference3D, float *slowness, icoord size, float uo)
{
    float si, sl, deltaS, gso, angle, qx, qz;
    int i;
    
    sl = sqrt(pow((r.x-s.x), 2) + pow((r.y-s.y), 2) + pow((r.z-s.z), 2));
    
    if ((sl <= 0) || (nRay <= 1))
        return 0;
    
    deltaS = sl/(nRay-1);
    angle = takeOffAngle(s, r);
    
    qx = angle2qx(angle);
    qz = angle2qz(angle);
    
    for (i = 0; i < nRay; i++)
    {
        si = i*deltaS;
        
        gso = qatso(si, angle, nRay, s, r, rayReference3D, slowness, size, uo);
        
        rayPerturbed3D[i].x = rayReference3D[i].x + qx*gso;
        rayPerturbed3D[i].z = rayReference3D[i].z + qz*gso;
        rayPerturbed3D[i].y = rayReference3D[i].y;

    }
    
    return 0;
}

float takeOffAngle(fcoord s, fcoord r)
{
    float angle = 0;
    
    if ((s.x == r.x) && (s.z == r.z))
        angle = PI/2;
    else if ((s.x <= r.x) && (s.z < r.z))
        angle = atan(fabs(r.x-s.x)/fabs(r.z-s.z));
    else if ((s.x < r.x) && (s.z >= r.z))
        angle = PI/2 + atan(fabs(r.z-s.z)/fabs(r.x-s.x));
    else if ((s.x >= r.x) && (s.z > r.z))
        angle = PI + atan(fabs(r.x-s.x)/fabs(r.z-s.z));
    else if ((s.x > r.x) && (s.z <= r.z))
        angle = 3*PI/2 + atan(fabs(r.z-s.z)/fabs(r.x-s.x));
    
    return (angle);
}

float angle2qx(const float _angle)
{
    float qx = 0;
    
    if ((_angle >= 0) && (_angle < PI/2))
        qx = -cos(_angle);
    else if ((_angle >= PI/2) && (_angle < PI))
        qx = sin(_angle - PI/2);
    else if ((_angle >= PI) && (_angle < 3*PI/2))
        qx = cos(_angle - PI);
    else if ((_angle >= 3*PI/2) && (_angle <= 2*PI))
        qx = -sin(_angle - 3*PI/2);
    
    return (qx);
}

float angle2qz(const float _angle)
{
    float qz = 0;
    
    if ((_angle >= 0) && (_angle < PI/2))
        qz = sin(_angle);
    else if ((_angle >= PI/2) && (_angle < PI))
        qz = cos(_angle - PI/2);
    else if ((_angle >= PI) && (_angle < 3*PI/2))
        qz = -sin(_angle - PI);
    else if ((_angle >= 3*PI/2) && (_angle <= 2*PI))
        qz = -cos(_angle - 3*PI/2);
    
    return (qz);
}

// Sofar used in 2D only

float qatso(const float _so, const float _angle, int nRay, fcoord s, fcoord r, fcoord *rayReference3D, float *slowness, icoord size, float uo)
{
    float slow, sl, deltaS, x, z;
    float qatsol;
    float greenTwoP = 0;
    int i;
    float qMulGradU1;
    fcoord slownessGradient;
    float gradu1x, gradu1z;
    float qx, qz;
    
    sl = sqrt(pow((r.x-s.x),2) + pow((r.z-s.z),2) + pow((r.y-s.y),2));
    
    if ((sl <= 0) || (nRay <= 1))
    {
        return 0;
    }
    
    deltaS = sl/(nRay-1);
//    uo = referenceSlowness(slowness, size, nRay, r, s);

    qatsol = 0;
    for (i = 0; i < nRay; i++)
    {
        slow = i*deltaS;
        x = rayReference3D[i].x;
        z = rayReference3D[i].z;
        
        if (slow <= _so)
            greenTwoP = -(1 - _so/sl)*slow/uo;
        else
            greenTwoP = -_so*(1-slow/sl)/uo;
        
        slownessGradient = getSlownessGradient(x, z, slowness, size);
        gradu1x = slownessGradient.x;
        gradu1z = slownessGradient.z;
        
        if ((_angle >= 0) && (_angle < PI/2)) {
            qx = -cos(_angle);
            qz = sin(_angle);
        }
        else if ((_angle >= PI/2) && (_angle < PI)) {
            qx = sin(_angle - PI/2);
            qz = cos(_angle - PI/2);
        }
        else if ((_angle >= PI) && (_angle < 3*PI/2)) {
            qx = cos(_angle - PI);
            qz = -sin(_angle - PI);
        }
        else if ((_angle >= 3*PI/2) && (_angle <= 2*PI)) {
            qx = -sin(_angle - 3*PI/2);
            qz = -cos(_angle - 3*PI/2);
        }

        qMulGradU1 = qx*gradu1x + qz*gradu1z;
        qatsol += greenTwoP*qMulGradU1*deltaS;
    }

    return(qatsol);
}

float getdT2(const float _x, const float _z, const float _so, const float _angle, const float _ds, int nRay, fcoord s, fcoord r, fcoord *rayReference3D, float *slowness, icoord size, float uo)
{
    float T2 = 0;
    float qatsol;
    float qMulGradU1l;

 //   fprintf(stderr,"getdT2: calling qatso nRay=%d\n",nRay);

    qatsol = qatso(_so, _angle, nRay, s, r, rayReference3D, slowness, size, uo);
    
//    fprintf(stderr,"getdT2: calling qMulGradU1\n");

    qMulGradU1l = qMulGradU1(_x, _z, _angle, slowness, size);

    T2 = qatsol*qMulGradU1l*_ds;
    
    return(T2);
}

float greenTwoP(const float _so, const float _slow, const float _sL, int nRay, fcoord s, fcoord r, float *slowness, icoord size)
{
    float greenTwoP = 0;
    float uo = referenceSlowness(slowness, size, nRay, r, s);
    
//    fprintf(stderr,"greenTwoP: slowness = %f nRay=%d\n",uo,nRay);

    if (_sL <= 0)
    {
        return(0);
    }
    
    if (_slow <= _so)
        greenTwoP = -(1 - _so/_sL)*_slow/uo;
    else
        greenTwoP = -_so*(1-_slow/_sL)/uo;
    
    return(greenTwoP);
}

float qMulGradU1(const float _x, const float _z, const float _angle, float *slowness, icoord size)
{
    float qMulGradU1;
    float gradu1x, gradu1z;
    float qx, qz;
    fcoord slownessGradient;
    
    slownessGradient = getSlownessGradient(_x, _z, slowness, size);
    gradu1x = slownessGradient.x;
    gradu1z = slownessGradient.z;
    
    qx = angle2qx(_angle);
    qz = angle2qz(_angle);
    
    qMulGradU1 = qx*gradu1x + qz*gradu1z;
    
    return(qMulGradU1);
}

float referenceSlowness(float *slowness, icoord size, int nRay, fcoord r, fcoord s)
{
    float x, y, z;
    float uo = 0;
    int i;
    
    for (i = 0; i < nRay; i++)
    {
        x = s.x + (r.x - s.x)*i/(nRay-1);
        z = s.z + (r.z - s.z)*i/(nRay-1);

        if (size.y == 1) // 2D
            uo += ModelInterpolation_slowness2D(slowness, size, x, z);
        else
        {
            y = s.y + (r.y - s.y)*i/(nRay-1);
            uo += ModelInterpolation_slowness3D(slowness, size, x, z, y);
        }
    }
    
    uo /= nRay;
    
    return(uo);
}

fcoord getSlownessGradient(const float _x, const float _z, float *slowness, icoord size)
{
    float dx, dz, x1, x2, z1, z2;
    float slow2, slow1;
    float gradu1x, gradu1z;
    fcoord slownessGradient;
    
    dx = lGradient*L/(size.x-1);
    dz = lGradient*H/(size.z-1);
    
    x1 = _x-dx;
    x2 = _x+dx;
    
    if (x1 <= 0)
        x1 = EPSMIN;
    
    if (x2 >= L)
        x2 = L - EPSMIN;
    
    if (size.y == 1)
    {
        slow1 = ModelInterpolation_slowness2D(slowness, size, x1, _z);
        slow2 = ModelInterpolation_slowness2D(slowness, size, x2, _z);
    }
    else
    {
        slow1 = ModelInterpolation_slowness3D(slowness, size, x1, _z, 0);
        slow2 = ModelInterpolation_slowness3D(slowness, size, x2, _z, 0);
    }
    
    if (fabs(slow2-slow1) < minValueGradient)
        gradu1x = 0;
    else
        gradu1x = (slow2 - slow1)/(x2-x1);
    
    z1 = _z-dz;
    z2 = _z+dz;
    
    if (z1 <= 0)
        z1 = EPSMIN;
    
    if (z2 >= H)
        z2 = H - EPSMIN;
    
    if (size.y == 1)
    {
        slow1 = ModelInterpolation_slowness2D(slowness, size, _x, z1);
        slow2 = ModelInterpolation_slowness2D(slowness, size, _x, z2);
    }
    else
    {
        slow1 = ModelInterpolation_slowness3D(slowness, size, _x, z1, 0);
        slow2 = ModelInterpolation_slowness3D(slowness, size, _x, z2, 0);
    }
    
    if (fabs(slow2-slow1) < minValueGradient)
        gradu1z = 0;
    else
        gradu1z = (slow2 - slow1)/(z2-z1);
    
    slownessGradient.x=gradu1x;
    slownessGradient.z=gradu1z;
    slownessGradient.y=0;

    return(slownessGradient);
}

int xPointIndex(const float _x, int nx, float L)
{
    int i;
    
    if (_x <= 0)
        return(0);
    
    if (_x >= L)
        i = nx - 1;
    else
    {
        if (0 < L)
            i = _x*nx*iL;
        else
            i = 0;
    }
    
    return(i);
}

int zPointIndex(const float _z, int nz, float H)
{
    int i;
    
    if (_z <= 0) return(0);
    
    if (_z >= H)
        i = nz - 1;
    else
    {
        if (0 < H)
            i = _z*nz*iH;
        else
            i = 0;
    }
    
    return(i);
}

int yPointIndex(const float _y, int ny, float W)
{
    int i;
    
    if (_y <= -0.5*W)
        return(0);
    
    if (_y >= 0.5*W)
        i = ny - 1;
    else
    {
        if (0 < W)
            i = ny*(_y*iW + 0.5);
        else
            i = 0;
    }
    
    return(i);
}

float ModelInterpolation_slowness2D(float *slowness, icoord size, const float _x, const float _z)
{
    float slow;
    float f11, f12, f21, f22;
    float t, j;
    float x1, x2;
    float z1, z2;
    int nx, nz, ix, iz, ixMin, ixMax, izMin, izMax;
    int ixCoordinate, izCoordinate;
    
    slow = f11 = f12 = f21 = f22 = 0;
    nx = size.x;
    nz = size.z;
    
    ixCoordinate = (int)(_x*nx)*iL;
    
    if (ixCoordinate >= nx)
    {
        x1 = (float) L*(nx-1)/nx;
        x2 = (float) L;
    }
    else if (ixCoordinate <= 0)
    {
        x1 = 0;
        x2 = (float) L/nx;
    }
    else
    {
        x1 = (float) L*ixCoordinate/nx;
        x2 = (float) L*(ixCoordinate+1)/nx;
    }
    
    izCoordinate = (int) _z*nz*iH;
    
    if (izCoordinate >= nz)
    {
        z1 = (float) H*(nz-1)/nz;
        z2 = (float) H;
    }
    else if (izCoordinate <= 0)
    {
        z1 = 0;
        z2 = (float) H/nz;
    }
    else
    {
        z1 = (float) H*izCoordinate/nz;
        z2 = (float) H*(izCoordinate+1)/nz;
    }
    
    ix = xPointIndex(_x, nx, L);
    iz = zPointIndex(_z, nz, H);
    
    if (ix == 0)
    {
        ixMin = 0;
        ixMax = 1;
    }
    else if (ix == nx-1)
    {
        ixMin = nx-2;
        ixMax = nx-1;
    }
    else
    {
        ixMin = ix-1;
        ixMax = ix+1;
    }
    
    if (iz == 0)
    {
        izMin = 0;
        izMax = 1;
    }
    else if (iz == nz-1)
    {
        izMin = nz-2;
        izMax = nz-1;
    }
    else
    {
        izMin = iz-1;
        izMax = iz+1;
    }
    
    f11 = slowness[ixMin*size.z+izMin];
    f21 = slowness[ixMax*size.z+izMin];
    f12 = slowness[ixMin*size.z+izMax];
    f22 = slowness[ixMax*size.z+izMax];
    
    t = (_x-x1)/(x2-x1);
    j = (_z-z1)/(z2-z1);
    
    slow = f11*(1-t)*(1-j) + f21*t*(1-j) + f12*(1-t)*j + f22*t*j;
    
    return (slow);
}

float ModelInterpolation_slowness3D(float *slowness, icoord size, const float _x, const float _z, const float _y)
{
    float slow;
    float f111, f112, f212, f211;
    float f121, f122, f222, f221;
    float t, j, r;
    float x1, x2;
    float y1, y2;
    float z1, z2;
    int ix, iy, iz, ixMin, ixMax, iyMin, iyMax, izMin, izMax;
    int nx, nz, ny, nxz;
    int ixCoordinate, iyCoordinate, izCoordinate;
    
    nx = size.x;
    nz = size.z;
    ny = size.y;
    nxz = nx*nz;

    slow = f111 = f112 = f212 = f211 = f121 = f122 = f222 = f221 = 0;
    
    ixCoordinate = _x*nx*iL;
    
    if (ixCoordinate >= nx)
        ixCoordinate =  nx;
    
    if (ixCoordinate == nx)
    {
        x1 = (float) L*(ixCoordinate-1)/nx;
        x2 = L;
    }
    else if (ixCoordinate <= 0)
    {
        x1 = 0;
        x2 = (float) L/nx;
    }
    else
    {
        x1 = (float) L*ixCoordinate/nx;
        x2 = (float) L*(ixCoordinate+1)/nx;
    }
    
    if (x1 < 0)
        x1 = 0;
    
    if (x1 > L)
        x1 = L;
    
    if (x2 < 0)
        x2 = 0;
    
    if (x2 > L)
        x2 = L;
    
    izCoordinate = _z*nz*iH;
    
    if (izCoordinate >= nz)
        izCoordinate = nz;
    
    if (izCoordinate == nz)
    {
        z1 = H*(izCoordinate-1)/nz;
        z2 = H;
    }
    else if (izCoordinate <= 0)
    {
        z1 = 0;
        z2 = (float) H/nz;
    }
    else
    {
        z1 = (float) H*izCoordinate/nz;
        z2 = (float) H*(izCoordinate+1)/nz;
    }
    
    if (z1 < 0)
        z1 = 0;
    
    if (z1 > H)
        z1 = H;
    
    if (z2 < 0)
        z2 = 0;
    
    if (z2 > H)
        z2 = H;
    
    iyCoordinate = ny*(_y*iW + 0.5);
    
    if (iyCoordinate >= ny)
        iyCoordinate = ny;
    
    if (iyCoordinate == ny)
    {
        y1 = (float) W*(iyCoordinate-1-0.5*ny)/ny;
        y2 = 0.5*W;
    }
    else if (iyCoordinate <= 0)
    {
        y1 = -0.5*W;
        y2 = (float) W*(1-0.5*ny)/ny;
    }
    else
    {
        y1 = (float) W*(iyCoordinate-0.5*ny)/ny;
        y2 = (float) W*(iyCoordinate+1-0.5*ny)/ny;
    }
    
    if (y1 < -0.5*W)
        y1 = -0.5*W;
    
    if (y1 > 0.5*W)
        y1 = 0.5*W;
    
    if (y2 < -0.5*W)
        y2 = -0.5*W;
    
    if (y2 > 0.5*W)
        y2 = 0.5*W;
    
    ix = xPointIndex(_x, size.x, L);
    iy = yPointIndex(_y, size.y, W);
    iz = zPointIndex(_z, size.z, H);
    
    if (ix == 0)
    {
        ixMin = 0;
        ixMax = 1;
    }
    else if (ix == nx-1)
    {
        ixMin = nx-2;
        ixMax = nx-1;
    }
    else
    {
        ixMin = ix-1;
        ixMax = ix+1;
    }
    
    if (iz == 0)
    {
        izMin = 0;
        izMax = 1;
    }
    else if (iz == nz-1)
    {
        izMin = nz-2;
        izMax = nz-1;
    }
    else
    {
        izMin = iz-1;
        izMax = iz+1;
    }
    
    if (iy == 0)
    {
        iyMin = 0;
        iyMax = 1;
    }
    else if (iy == ny-1)
    {
        iyMin = ny-2;
        iyMax = ny-1;
    }
    else
    {
        iyMin = iy-1;
        iyMax = iy+1;
    }
    
    nxz = nx*nz;
    f111 = slowness[iyMin*nxz+ixMin*nz+izMin];
    f211 = slowness[iyMax*nxz+ixMin*nz+izMin];
    f121 = slowness[iyMin*nxz+ixMax*nz+izMin];
    f221 = slowness[iyMax*nxz+ixMax*nz+izMin];
    f112 = slowness[iyMin*nxz+ixMin*nz+izMax];
    f212 = slowness[iyMax*nxz+ixMin*nz+izMax];
    f122 = slowness[iyMin*nxz+ixMax*nz+izMax];
    f222 = slowness[iyMax*nxz+ixMax*nz+izMax];
    
    //    cout << "slowness3D 6 "  << endl;
    
    r = (_z-z1)/(z2-z1);
    t = (_x-x1)/(x2-x1);
    j = (_y-y1)/(y2-y1);
    
    slow = f111*(1-t)*(1-j)*(1-r) + f112*(1-t)*(1-j)*r + f211*t*(1-j)*(1-r) + f212*t*(1-j)*r + f121*(1-t)*j*(1-r) + f122*(1-t)*j*r + f222*t*j*r + f221*t*j*(1-r);
    
    slow = f111*(1-r)*(1-t)*(1-j) + f112*(1-r)*(1-t)*j + f211*r*(1-t)*(1-j) + f212*r*(1-t)*j + f121*(1-r)*t*(1-j) + f122*(1-r)*t*j + f222*r*t*j + f221*r*t*(1-j);
    
    
    //    if (slow != slow)
    /*
    if (slow <= 0)
    {
        cout << "                  ModelInterpolation::slowness3D " << 1/slow << "  " << 1/f111 << "  " << 1/f112 << "  " << 1/f211 << "  " << 1/f212 << "  " << 1/f121 << "  " << 1/f122 << "  " << 1/f222 << "  " << 1/f211 << "  " << r << "  " << t << "  " << j << "  " << ixCoordinate << "  "  << x1 << "  " << x2 << "  " << _x << "  " << nx << "  "  << L <<  endl;
        cout << "                        ModelInterpolation::slowness3D, x1, x2 = " << x1 << "  " << x2 << endl;
        cout << "                        ModelInterpolation::slowness3D, y1, y2 = " << y1 << "  " << y2 << endl;
        cout << "                        ModelInterpolation::slowness3D, z1, z2 = " << z1 << "  " << z2 << "  " << _z << endl;
        
        
        exit(EXIT_FAILURE);
    }
    */
    
    return (slow);
}

float slownessA(float *slowness, icoord size, float _x, float _z, float _y)
{
    float slow;
    
    if (size.y == 1)
        slow = ModelInterpolation_slowness2D(slowness, size, _x, _z);
    else
        slow = ModelInterpolation_slowness3D(slowness, size, _x, _z, _y);
    
    return(slow);
}

float greenIntP(const float _so, const float _s, const float _sL, float *slowness, icoord size, int nRay, fcoord r, fcoord s)
{
    float greenIntP;
    float uo = referenceSlowness(slowness, size, nRay, r, s);
    
    if (_sL <= 0)
    {
        greenIntP = 0;
        return(greenIntP);
    }
    
    if (_s <= _so)
        greenIntP = (_so - _s)/uo;
    else
        greenIntP = 0;
    
    return(greenIntP);
}

float secondDerivativeU1(float *slowness, icoord size, const float _x, const float _z, const float _angle, fcoord r, fcoord s)
{
    float secondDerivativeU1 = 0;
    float dphi, sl;
    float qx, qz;
    float dh, x1, z1, x2, z2;
    
    dphi = DPHI_ANGLE*PI/180.0;
    sl = sqrt(pow((r.x-s.x),2) + pow((r.z-s.z),2) + pow((r.y-s.y),2));
    
    // Here qx and qz are perpendicular to the raz direction
    
    qx = angle2qx(_angle);
    qz = angle2qz(_angle);
    
    dh = sl*tan(2*dphi);
    x2 = _x + dh*qx;
    z2 = _z + dh*qz;
    
    x1 = _x - dh*qx;
    z1 = _z - dh*qz;
    
    if (x1 <= 0)
        x1 = EPSMIN;
    
    if (x1 >= L)
        x1 = L - EPSMIN;
    
    if (x2 <= 0)
        x2 = EPSMIN;
    
    if (x2 >= L)
        x2 = L - EPSMIN;
    
    if (z1 <= 0)
        z1 = EPSMIN;
    
    if (z1 >= H)
        z1 = H - EPSMIN;
    
    if (z2 <= 0)
        z2 = EPSMIN;
    
    if (z2 >= H)
        z2 = H - EPSMIN;
    
    secondDerivativeU1 = (slownessA(slowness, size, x2, z2, 0) + slownessA(slowness, size, x1, z1, 0) - 2*slownessA(slowness, size, _x, _z, 0))/(4*pow(dphi, 2));
    
    if (fabs(secondDerivativeU1) <= minValueSecondDerivativeU1)
        secondDerivativeU1 = 0;
    
    return(secondDerivativeU1);
}

// Moving average filter
void applyMovingAverageFilter(float *slowness, icoord size, int window, int dim, float *averageModel)
{
    float   averageFilter;
    int     nsamp, iAverageX, iAverageY, iAverageZ;
    int     jWindowX, jWindowZ, jWindowY, ix, iy, iz, nw;

    nw = window;
	if (dim == 2) {
        for (ix = 0; ix < size.x; ix++) {
            for (iz = 0; iz < size.z; iz++) {
                averageFilter =  0;
                nsamp = 0;
                for (jWindowX = -nw; jWindowX <= nw; jWindowX++) {
                    iAverageX = nw + ix + jWindowX;	

//					if (iAverageX < 0) iAverageX = 0;
//                    if (iAverageX > size.x-1) iAverageX = size.x-1;

                    for (jWindowZ = -nw; jWindowZ <= nw; jWindowZ++) {
                        iAverageZ = nw + iz + jWindowZ;

//                        if (iAverageZ < 0) iAverageZ = 0;
//                        if (iAverageZ > size.z-1) iAverageZ = size.z-1;

                        averageFilter += slowness[iAverageX*size.z+iAverageZ];
                        nsamp += 1;
                    }
                }
				if (nsamp > 0) {
                    averageFilter /= nsamp;
				    averageModel[ix*size.z+iz] = averageFilter;
                }
                else 
				    averageModel[ix*size.z+iz] = slowness[(ix+nw)*size.z+iz+nw];
			}
		}
	}
/* 3D ray-tracer not yet implemented 
	else {
	    for (iz = 0; iz < size.z; iz++) {
			for (ix = 0; ix < size.x; ix++) {
                for (iy = 0; iy < size.y; iy++) {
                    averageFilter =  0;
                    nsamp = 0;

					for (jWindowZ = -window; jWindowZ <= window; jWindowZ++) {
                        iAverageZ = iz + jWindowZ;

						if (iAverageZ < 0) iAverageZ = 0;
	                    if (iAverageZ > size.z-1) iAverageZ = size.z-1;

                        for (jWindowX = -window; jWindowX <= window; jWindowX++) {
                            iAverageX = ix + jWindowX;	

							if (iAverageX < 0) iAverageX = 0;
                            if (iAverageX > size.x-1) iAverageX = size.x-1;

                            for (jWindowY = -window; jWindowY <= window; jWindowY++) {
                                iAverageY = iy + jWindowY;

                                if (iAverageY < 0) iAverageY = 0;
                                if (iAverageY > size.y-1) iAverageY = size.y-1;

                                averageFilter += slowness[iAverageZ+iAverageX*size.z+iAverageY*size.z*size.x];
                                nsamp += 1;
                            }
                        }
					}
					
					if (nsamp > 0) {
                        averageFilter /= nsamp;
				        averageModel[iz+ix*size.z+iy*size.z*size.x] = averageFilter;
                    }
                    else {
                       averageModel[iz+ix*size.z+iy*size.z*size.x] = slowness[iz+ix*size.z+iy*size.z*size.x];
                    }
				}
			}
		}
	}	
*/

	return;
}