// user function
inline void update_halo_kernel2_yvel_plus_2_left(double *yvel0, double *yvel1,
                                                 const int *fields) {
  if (fields[FIELD_YVEL0] == 1)
    yvel0[OPS_ACC0(0, 0, 0)] = yvel0[OPS_ACC0(2, 0, 0)];
  if (fields[FIELD_YVEL1] == 1)
    yvel1[OPS_ACC1(0, 0, 0)] = yvel1[OPS_ACC1(2, 0, 0)];
}
inline void update_halo_kernel2_zvel_plus_2_bot(double *zvel0, double *zvel1,
                                                const int *fields) {
  if (fields[FIELD_ZVEL0] == 1)
    zvel0[OPS_ACC0(0, 0, 0)] = zvel0[OPS_ACC0(0, 2, 0)];
  if (fields[FIELD_ZVEL1] == 1)
    zvel1[OPS_ACC1(0, 0, 0)] = zvel1[OPS_ACC1(0, 2, 0)];
}
// user function
inline void drhouupdx_kernel(const double *rhou_new, const double *rho_new,
                             const double *rhoE_new, double *rhou_res) {

  double fni =
      rhou_new[OPS_ACC0(0)] * rhou_new[OPS_ACC0(0)] / rho_new[OPS_ACC1(0)];
  double p = gam1 * (rhoE_new[OPS_ACC2(0)] - 0.5 * fni);
  fni = fni + p;
  double fnim1 =
      rhou_new[OPS_ACC0(-1)] * rhou_new[OPS_ACC0(-1)] / rho_new[OPS_ACC1(-1)];
  p = gam1 * (rhoE_new[OPS_ACC2(-1)] - 0.5 * fnim1);
  fnim1 = fnim1 + p;
  double fnim2 =
      rhou_new[OPS_ACC0(-2)] * rhou_new[OPS_ACC0(-2)] / rho_new[OPS_ACC1(-2)];
  p = gam1 * (rhoE_new[OPS_ACC2(-2)] - 0.5 * fnim2);
  fnim2 = fnim2 + p;
  double fnip1 =
      rhou_new[OPS_ACC0(1)] * rhou_new[OPS_ACC0(1)] / rho_new[OPS_ACC1(1)];
  p = gam1 * (rhoE_new[OPS_ACC2(1)] - 0.5 * fnip1);
  fnip1 = fnip1 + p;
  double fnip2 =
      rhou_new[OPS_ACC0(2)] * rhou_new[OPS_ACC0(2)] / rho_new[OPS_ACC1(2)];
  p = gam1 * (rhoE_new[OPS_ACC2(2)] - 0.5 * fnip2);
  fnip2 = fnip2 + p;

  double deriv = (fnim2 - fnip2 + 8.0 * (fnip1 - fnim1)) / (12.00 * dx);
  rhou_res[OPS_ACC3(0)] = deriv;
}
inline void update_halo_kernel2_yvel_minus_4_a(double *yvel0, double *yvel1,
                                               const int *fields) {
  if (fields[FIELD_YVEL0] == 1)
    yvel0[OPS_ACC0(0, 0)] = -yvel0[OPS_ACC0(0, 4)];
  if (fields[FIELD_YVEL1] == 1)
    yvel1[OPS_ACC1(0, 0)] = -yvel1[OPS_ACC1(0, 4)];
}
// user function
inline void update_halo_kernel2_zvel_minus_4_back(double *zvel0, double *zvel1,
                                                  const int *fields) {
  if (fields[FIELD_ZVEL0] == 1)
    zvel0[OPS_ACC0(0, 0, 0)] = -zvel0[OPS_ACC0(0, 0, 4)];
  if (fields[FIELD_ZVEL1] == 1)
    zvel1[OPS_ACC1(0, 0, 0)] = -zvel1[OPS_ACC1(0, 0, 4)];
}
//user function
inline 
void init_kernel(double *val, int *idx){
  if(idx[0]==0 || idx[0]==nx-1 || idx[1]==0 || idx[1]==ny-1 || idx[2]==0 || idx[2]==nz-1)
    val[OPS_ACC0(0,0,0)] = 1.0;
  else
    val[OPS_ACC0(0,0,0)] = 0.0;
}
inline void update_halo_kernel2_xvel_plus_2_top(double *xvel0, double *xvel1,
                                                const int *fields) {
  if (fields[FIELD_XVEL0] == 1)
    xvel0[OPS_ACC0(0, 0, 0)] = xvel0[OPS_ACC0(0, -2, 0)];
  if (fields[FIELD_XVEL1] == 1)
    xvel1[OPS_ACC1(0, 0, 0)] = xvel1[OPS_ACC1(0, -2, 0)];
}
inline void update_halo_kernel2_xvel_minus_4_b(double *xvel0, double *xvel1,
                                               const int *fields) {
  if (fields[FIELD_XVEL0] == 1)
    xvel0[OPS_ACC0(0, 0)] = -xvel0[OPS_ACC0(-4, 0)];
  if (fields[FIELD_XVEL1] == 1)
    xvel1[OPS_ACC1(0, 0)] = -xvel1[OPS_ACC1(-4, 0)];
}
// user function
inline void update_halo_kernel3_plus_4_a(double *vol_flux_x,
                                         double *mass_flux_x,
                                         const int *fields) {
  if (fields[FIELD_VOL_FLUX_X] == 1)
    vol_flux_x[OPS_ACC0(0, 0)] = vol_flux_x[OPS_ACC0(0, 4)];
  if (fields[FIELD_MASS_FLUX_X] == 1)
    mass_flux_x[OPS_ACC1(0, 0)] = mass_flux_x[OPS_ACC1(0, 4)];
}
inline void update_halo_kernel4_plus_2_b(double *vol_flux_y,
                                         double *mass_flux_y,
                                         const int *fields) {
  if (fields[FIELD_VOL_FLUX_Y] == 1)
    vol_flux_y[OPS_ACC0(0, 0, 0)] = vol_flux_y[OPS_ACC0(-2, 0, 0)];
  if (fields[FIELD_MASS_FLUX_Y] == 1)
    mass_flux_y[OPS_ACC1(0, 0, 0)] = mass_flux_y[OPS_ACC1(-2, 0, 0)];
}
// user function
inline void update_halo_kernel3_minus_4_a(double *vol_flux_x,
                                          double *mass_flux_x,
                                          const int *fields) {
  if (fields[FIELD_VOL_FLUX_X] == 1)
    vol_flux_x[OPS_ACC0(0, 0, 0)] = -(vol_flux_x[OPS_ACC0(4, 0, 0)]);
  if (fields[FIELD_MASS_FLUX_X] == 1)
    mass_flux_x[OPS_ACC1(0, 0, 0)] = -(mass_flux_x[OPS_ACC1(4, 0, 0)]);
}
// user function
inline void update_halo_kernel5_plus_4_left(double *vol_flux_z,
                                            double *mass_flux_z,
                                            const int *fields) {
  if (fields[FIELD_VOL_FLUX_Z] == 1)
    vol_flux_z[OPS_ACC0(0, 0, 0)] = (vol_flux_z[OPS_ACC0(4, 0, 0)]);
  if (fields[FIELD_MASS_FLUX_Z] == 1)
    mass_flux_z[OPS_ACC1(0, 0, 0)] = (mass_flux_z[OPS_ACC1(4, 0, 0)]);
}
inline void update_halo_kernel4_minus_2_a(double *vol_flux_y,
                                          double *mass_flux_y,
                                          const int *fields) {
  if (fields[FIELD_VOL_FLUX_Y] == 1)
    vol_flux_y[OPS_ACC0(0, 0)] = -(vol_flux_y[OPS_ACC0(0, 2)]);
  if (fields[FIELD_MASS_FLUX_Y] == 1)
    mass_flux_y[OPS_ACC1(0, 0)] = -(mass_flux_y[OPS_ACC1(0, 2)]);
}
inline void update_halo_kernel5_minus_2_back(double *vol_flux_z,
                                             double *mass_flux_z,
                                             const int *fields) {
  if (fields[FIELD_VOL_FLUX_Z] == 1)
    vol_flux_z[OPS_ACC0(0, 0, 0)] = -vol_flux_z[OPS_ACC0(0, 0, 2)];
  if (fields[FIELD_MASS_FLUX_Z] == 1)
    mass_flux_z[OPS_ACC1(0, 0, 0)] = -mass_flux_z[OPS_ACC1(0, 0, 2)];
}
// user function
inline void advec_mom_kernel2_x(double *vel1, const double *node_mass_post,
                                const double *node_mass_pre,
                                const double *mom_flux) {

  vel1[OPS_ACC0(0, 0, 0)] =
      (vel1[OPS_ACC0(0, 0, 0)] * node_mass_pre[OPS_ACC2(0, 0, 0)] +
       mom_flux[OPS_ACC3(-1, 0, 0)] - mom_flux[OPS_ACC3(0, 0, 0)]) /
      node_mass_post[OPS_ACC1(0, 0, 0)];
}
// user function
inline void initialise_chunk_kernel_celly(const double *vertexy, double *celly,
                                          double *celldy) {

  double d_y;
  d_y = (grid.ymax - grid.ymin) / (double)grid.y_cells;

  celly[OPS_ACC1(0, 0)] =
      0.5 * (vertexy[OPS_ACC0(0, 0)] + vertexy[OPS_ACC0(0, 1)]);
  celldy[OPS_ACC2(0, 0)] = d_y;
}
// user function
inline void drhoudx_kernel(const double *rhou_new, double *rho_res) {

  double fnim1 = rhou_new[OPS_ACC0(-1)];
  double fnim2 = rhou_new[OPS_ACC0(-2)];
  double fnip1 = rhou_new[OPS_ACC0(1)];
  double fnip2 = rhou_new[OPS_ACC0(2)];

  double deriv = (fnim2 - fnip2 + 8.0 * (fnip1 - fnim1)) / (12.00 * dx);
  rho_res[OPS_ACC1(0)] = deriv;
}
// user function
inline void calvar_kernel(const double *rho_new, const double *rhou_new,
                          const double *rhoE_new, double *workarray2,
                          double *workarray3) {
  double p, rhoi, u;
  rhoi = 1 / rho_new[OPS_ACC0(0)];
  u = rhou_new[OPS_ACC1(0)] * rhoi;
  p = gam1 * (rhoE_new[OPS_ACC2(0)] - 0.5 * rho_new[OPS_ACC0(0)] * u * u);

  workarray2[OPS_ACC3(0)] = p + rhou_new[OPS_ACC1(0)] * u;
  workarray3[OPS_ACC4(0)] = (p + rhoE_new[OPS_ACC2(0)]) * u;
}
// user function
inline void checkop_kernel(const double *rho_new, const double *x,
                           const double *rhoin, double *pre, double *post,
                           int *num) {
  double diff;
  diff = (rho_new[OPS_ACC0(0)] - rhoin[OPS_ACC2(0)]);
  if (fabs(diff) < 0.01 && x[OPS_ACC1(0)] > -4.1) {
    *post = *post + diff * diff;
    *num = *num + 1;

  } else
    *pre = *pre + (rho_new[OPS_ACC0(0)] - rhol) * (rho_new[OPS_ACC0(0)] - rhol);
}
inline void advec_mom_kernel_post_pre_advec_y( double *node_mass_post, const double *post_vol,
                                  const double *density1, double *node_mass_pre, const double *node_flux) {



  node_mass_post[OPS_ACC0(0,0)] = 0.25 * ( density1[OPS_ACC2(0,-1)] * post_vol[OPS_ACC1(0,-1)] +
                              density1[OPS_ACC2(0,0)]   * post_vol[OPS_ACC1(0,0)]   +
                              density1[OPS_ACC2(-1,-1)] * post_vol[OPS_ACC1(-1,-1)] +
                              density1[OPS_ACC2(-1,0)]  * post_vol[OPS_ACC1(-1,0)]  );

  node_mass_pre[OPS_ACC3(0,0)] = node_mass_post[OPS_ACC0(0,0)] - node_flux[OPS_ACC4(0,-1)] + node_flux[OPS_ACC4(0,0)];

}
// user function
inline void advec_cell_kernel1_ydir(double *pre_vol, double *post_vol,
                                    const double *volume,
                                    const double *vol_flux_x,
                                    const double *vol_flux_y) {

  pre_vol[OPS_ACC0(0, 0)] =
      volume[OPS_ACC2(0, 0)] +
      (vol_flux_y[OPS_ACC4(0, 1)] - vol_flux_y[OPS_ACC4(0, 0)] +
       vol_flux_x[OPS_ACC3(1, 0)] - vol_flux_x[OPS_ACC3(0, 0)]);
  post_vol[OPS_ACC1(0, 0)] =
      pre_vol[OPS_ACC0(0, 0)] -
      (vol_flux_y[OPS_ACC4(0, 1)] - vol_flux_y[OPS_ACC4(0, 0)]);
}
// user function
inline void tea_leaf_cg_calc_w_reduce_kernel(double *w, const double *Kx,
                                             const double *Ky, const double *p,
                                             const double *rx, const double *ry,
                                             double *pw) {
  w[OPS_ACC0(0, 0)] = (1.0 + (*ry) * (Ky[OPS_ACC2(0, 1)] + Ky[OPS_ACC2(0, 0)]) +
                       (*rx) * (Kx[OPS_ACC1(1, 0)] + Kx[OPS_ACC1(0, 0)])) *
                          p[OPS_ACC3(0, 0)] -
                      (*ry) * (Ky[OPS_ACC2(0, 1)] * p[OPS_ACC3(0, 1)] +
                               Ky[OPS_ACC2(0, 0)] * p[OPS_ACC3(0, -1)]) -
                      (*rx) * (Kx[OPS_ACC1(1, 0)] * p[OPS_ACC3(1, 0)] +
                               Kx[OPS_ACC1(0, 0)] * p[OPS_ACC3(-1, 0)]);
  *pw = *pw + w[OPS_ACC0(0, 0)] * p[OPS_ACC3(0, 0)];
}
inline void update_halo_kernel1_fr2(double *density0, double *density1,
                          double *energy0, double *energy1,
                          double *pressure, double *viscosity,
                          double *soundspeed , const int* fields) {
  if(fields[FIELD_DENSITY0] == 1) density0[OPS_ACC0(0,0,0)] = density0[OPS_ACC0(0,0,-3)];
  if(fields[FIELD_DENSITY1] == 1) density1[OPS_ACC1(0,0,0)] = density1[OPS_ACC1(0,0,-3)];
  if(fields[FIELD_ENERGY0] == 1) energy0[OPS_ACC2(0,0,0)] = energy0[OPS_ACC2(0,0,-3)];
  if(fields[FIELD_ENERGY1] == 1) energy1[OPS_ACC3(0,0,0)] = energy1[OPS_ACC3(0,0,-3)];
  if(fields[FIELD_PRESSURE] == 1) pressure[OPS_ACC4(0,0,0)] = pressure[OPS_ACC4(0,0,-3)];
  if(fields[FIELD_VISCOSITY] == 1) viscosity[OPS_ACC5(0,0,0)] = viscosity[OPS_ACC5(0,0,-3)];
  if(fields[FIELD_SOUNDSPEED] == 1) soundspeed[OPS_ACC6(0,0,0)] = soundspeed[OPS_ACC6(0,0,-3)];

}
// user function
inline void calc_dt_kernel_print(const double *xvel0, const double *yvel0,
                                 const double *zvel0, const double *density0,
                                 const double *energy0, const double *pressure,
                                 const double *soundspeed, double *output) {
  output[0] = xvel0[OPS_ACC0(0, 0, 0)];
  output[1] = yvel0[OPS_ACC1(0, 0, 0)];
  output[2] = zvel0[OPS_ACC2(0, 0, 0)];
  output[3] = xvel0[OPS_ACC0(1, 0, 0)];
  output[4] = yvel0[OPS_ACC1(1, 0, 0)];
  output[5] = zvel0[OPS_ACC2(0, 0, 0)];
  output[6] = xvel0[OPS_ACC0(1, 1, 0)];
  output[7] = yvel0[OPS_ACC1(1, 1, 0)];
  output[8] = zvel0[OPS_ACC2(0, 0, 0)];
  output[9] = xvel0[OPS_ACC0(0, 1, 0)];
  output[10] = yvel0[OPS_ACC1(0, 1, 0)];
  output[11] = zvel0[OPS_ACC2(0, 0, 0)];
  output[12] = xvel0[OPS_ACC0(0, 0, 1)];
  output[13] = yvel0[OPS_ACC1(0, 0, 1)];
  output[14] = zvel0[OPS_ACC2(0, 0, 1)];
  output[15] = xvel0[OPS_ACC0(1, 0, 1)];
  output[16] = yvel0[OPS_ACC1(1, 0, 1)];
  output[17] = zvel0[OPS_ACC2(0, 0, 1)];
  output[18] = xvel0[OPS_ACC0(1, 1, 1)];
  output[19] = yvel0[OPS_ACC1(1, 1, 1)];
  output[20] = zvel0[OPS_ACC2(0, 0, 1)];
  output[21] = xvel0[OPS_ACC0(0, 1, 1)];
  output[22] = yvel0[OPS_ACC1(0, 1, 1)];
  output[23] = zvel0[OPS_ACC2(0, 0, 1)];
  output[24] = density0[OPS_ACC3(0, 0, 0)];
  output[25] = energy0[OPS_ACC4(0, 0, 0)];
  output[26] = pressure[OPS_ACC5(0, 0, 0)];
  output[27] = soundspeed[OPS_ACC6(0, 0, 0)];
}
inline void advec_cell_kernel4_xdir( double *density1, double *energy1,
                         const double *mass_flux_x, const double *vol_flux_x,
                         const double *pre_vol, const double *post_vol,
                         double *pre_mass, double *post_mass,
                         double *advec_vol, double *post_ener,
                         const double *ener_flux) {

  pre_mass[OPS_ACC6(0,0,0)] = density1[OPS_ACC0(0,0,0)] * pre_vol[OPS_ACC4(0,0,0)];
  post_mass[OPS_ACC7(0,0,0)] = pre_mass[OPS_ACC6(0,0,0)] + mass_flux_x[OPS_ACC2(0,0,0)] - mass_flux_x[OPS_ACC2(1,0,0)];
  post_ener[OPS_ACC9(0,0,0)] = ( energy1[OPS_ACC1(0,0,0)] * pre_mass[OPS_ACC6(0,0,0)] + ener_flux[OPS_ACC10(0,0,0)] - ener_flux[OPS_ACC10(1,0,0)])/post_mass[OPS_ACC7(0,0,0)];
  advec_vol[OPS_ACC8(0,0,0)] = pre_vol[OPS_ACC4(0,0,0)] + vol_flux_x[OPS_ACC3(0,0,0)] - vol_flux_x[OPS_ACC3(1,0,0)];
  density1[OPS_ACC0(0,0,0)] = post_mass[OPS_ACC7(0,0,0)]/advec_vol[OPS_ACC8(0,0,0)];
  energy1[OPS_ACC1(0,0,0)] = post_ener[OPS_ACC9(0,0,0)];

}
inline void update_halo_kernel1_r2(double *density0, double *energy0,
                                   double *energy1, double *u, double *p,
                                   double *sd, const int *fields) {
  if (fields[FIELD_DENSITY] == 1)
    density0[OPS_ACC0(0, 0)] = density0[OPS_ACC0(-3, 0)];
  if (fields[FIELD_ENERGY0] == 1)
    energy0[OPS_ACC1(0, 0)] = energy0[OPS_ACC1(-3, 0)];
  if (fields[FIELD_ENERGY1] == 1)
    energy1[OPS_ACC2(0, 0)] = energy1[OPS_ACC2(-3, 0)];
  if (fields[FIELD_U] == 1)
    u[OPS_ACC3(0, 0)] = u[OPS_ACC3(-3, 0)];
  if (fields[FIELD_P] == 1)
    p[OPS_ACC4(0, 0)] = p[OPS_ACC4(-3, 0)];
  if (fields[FIELD_SD] == 1)
    sd[OPS_ACC5(0, 0)] = sd[OPS_ACC5(-3, 0)];
}
// user function
inline void field_summary_kernel(const double *volume, const double *density0,
                                 const double *energy0, const double *pressure,
                                 const double *xvel0, const double *yvel0,
                                 double *vol, double *mass, double *ie,
                                 double *ke, double *press) {

  double vsqrd, cell_vol, cell_mass;

  vsqrd = 0.0;
  vsqrd = vsqrd +
          0.25 * (xvel0[OPS_ACC4(0, 0)] * xvel0[OPS_ACC4(0, 0)] +
                  yvel0[OPS_ACC5(0, 0)] * yvel0[OPS_ACC5(0, 0)]);
  vsqrd = vsqrd +
          0.25 * (xvel0[OPS_ACC4(1, 0)] * xvel0[OPS_ACC4(1, 0)] +
                  yvel0[OPS_ACC5(1, 0)] * yvel0[OPS_ACC5(1, 0)]);
  vsqrd = vsqrd +
          0.25 * (xvel0[OPS_ACC4(0, 1)] * xvel0[OPS_ACC4(0, 1)] +
                  yvel0[OPS_ACC5(0, 1)] * yvel0[OPS_ACC5(0, 1)]);
  vsqrd = vsqrd +
          0.25 * (xvel0[OPS_ACC4(1, 1)] * xvel0[OPS_ACC4(1, 1)] +
                  yvel0[OPS_ACC5(1, 1)] * yvel0[OPS_ACC5(1, 1)]);

  cell_vol = volume[OPS_ACC0(0, 0)];
  cell_mass = cell_vol * density0[OPS_ACC1(0, 0)];
  *vol = *vol + cell_vol;
  *mass = *mass + cell_mass;
  *ie = *ie + cell_mass * energy0[OPS_ACC2(0, 0)];
  *ke = *ke + cell_mass * 0.5 * vsqrd;
  *press = *press + cell_vol * pressure[OPS_ACC3(0, 0)];
}
inline void advec_cell_kernel2_zdir( double *pre_vol, double *post_vol, const double *volume,
                        const double *vol_flux_z) {

  pre_vol[OPS_ACC0(0,0,0)] = volume[OPS_ACC2(0,0,0)] + vol_flux_z[OPS_ACC3(0,0,1)] - vol_flux_z[OPS_ACC3(0,0,0)];
  post_vol[OPS_ACC1(0,0,0)] = volume[OPS_ACC2(0,0,0)];

}
inline void advec_mom_kernel_mass_flux_y(double *node_flux,
                                         const double *mass_flux_y) {

  node_flux[OPS_ACC0(0, 0)] =
      0.25 * (mass_flux_y[OPS_ACC1(-1, 0)] + mass_flux_y[OPS_ACC1(0, 0)] +
              mass_flux_y[OPS_ACC1(-1, 1)] + mass_flux_y[OPS_ACC1(0, 1)]);
}
// user function
inline void save_kernel(double *rho_old, double *rhou_old, double *rhoE_old,
                        const double *rho_new, const double *rhou_new,
                        const double *rhoE_new) {
  rho_old[OPS_ACC0(0)] = rho_new[OPS_ACC3(0)];
  rhou_old[OPS_ACC1(0)] = rhou_new[OPS_ACC4(0)];
  rhoE_old[OPS_ACC2(0)] = rhoE_new[OPS_ACC5(0)];
}