/* ----------------------------------------------------------------------
   LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator

   Original Version:
   http://lammps.sandia.gov, Sandia National Laboratories
   Steve Plimpton, sjplimp@sandia.gov

   See the README file in the top-level LAMMPS directory.

   -----------------------------------------------------------------------

   USER-CUDA Package and associated modifications:
   https://sourceforge.net/projects/lammpscuda/

   Christian Trott, christian.trott@tu-ilmenau.de
   Lars Winterfeld, lars.winterfeld@tu-ilmenau.de
   Theoretical Physics II, University of Technology Ilmenau, Germany

   See the README file in the USER-CUDA directory.

   This software is distributed under the GNU General Public License.
------------------------------------------------------------------------- */

#ifndef _CUDA_SHARED_H_
#define _CUDA_SHARED_H_
#include "cuda_precision.h"

#define CUDA_MAX_DEBUG_SIZE 1000 //size of debugdata array (allows for so many doubles or twice as many int)

struct dev_array {
  void* dev_data;			// pointer to memory address on cuda device
  unsigned dim[3];		// array dimensions
};

struct cuda_shared_atom {	// relevent data from atom class
  dev_array dx; 			// cumulated distance for binning settings
  dev_array x;			// position
  dev_array v;			// velocity
  dev_array f;			// force
  dev_array tag;
  dev_array type; 		// global ID number, there are ghosttype = ntypes  (ntypescuda=ntypes+1)
  dev_array mask;
  dev_array image;
  dev_array q;			// charges
  dev_array mass;			// per-type masses
  dev_array rmass;		// per-atom masses
  dev_array radius;		// per-atom radius
  dev_array density;
  dev_array omega;
  dev_array torque;
  dev_array molecule;

  dev_array special;
  int maxspecial;
  dev_array nspecial;
  int* special_flag;
  int molecular;

  dev_array eatom;		// per-atom energy
  dev_array vatom;		// per-atom virial
  int need_eatom;
  int need_vatom;

  dev_array x_type;		// position + type in X_FLOAT4 struct
  dev_array v_radius;		// velociyt + radius in V_FLOAT4 struct currently only used for granular atom_style
  dev_array omega_rmass;		// velociyt + radius in V_FLOAT4 struct currently only used for granular atom_style

  double* mass_host;		// remember per-type host pointer to masses
  //int natoms;				// total # of atoms in system, could be 0
  int nghost;				// and ghost atoms on this proc
  int nlocal;				// # of owned
  int nall;			    // total # of atoms in this proc
  int nmax;				// max # of owned+ghost in arrays on this proc
  int ntypes;
  int q_flag;				// do we have charges?
  int rmass_flag;			// do we have per-atom masses?
  int firstgroup;
  int nfirst;

  int update_nlocal;
  int update_nmax;
  int update_neigh;

  dev_array xhold;	    // position at last neighboring
  X_FLOAT triggerneighsq;		// maximum square movement before reneighboring
  int reneigh_flag;		// is reneighboring necessary
  int maxhold;			// size of xhold
  int dist_check; 		//perform distance check for reneighboring
  dev_array binned_id;    //id of each binned atom (not tag!!)
  dev_array binned_idnew; //new id of each binned atom for sorting basically setting atom[binned_id[k]] at atom[binned_newid[k]]
  float bin_extraspace;
  int bin_dim[3];
  int bin_nmax;
  dev_array map_array;
};

struct cuda_shared_pair {	// relevent data from pair class
  char cudable_force;		// check for (cudable_force!=0)
  X_FLOAT cut_global;
  X_FLOAT cut_inner_global;
  X_FLOAT cut_coul_global;
  double** cut;			// type-type cutoff
  double** cutsq;			// type-type cutoff
  double** cut_inner;			// type-type cutoff for coul
  double** cut_coul;			// type-type cutoff for coul
  double** coeff1;		// tpye-type pair parameters
  double** coeff2;
  double** coeff3;
  double** coeff4;
  double** coeff5;
  double** coeff6;
  double** coeff7;
  double** coeff8;
  double** coeff9;
  double** coeff10;
  double** offset;
  double* special_lj;
  double* special_coul;
  dev_array virial; // ENERGY_FLOAT
  dev_array eng_vdwl; // ENERGY_FLOAT
  dev_array eng_coul; // ENERGY_FLOAT
  X_FLOAT cut_coulsq_global;
  F_FLOAT g_ewald, kappa;
  int freeze_group_bit;

  dev_array coeff1_gm;
  dev_array coeff2_gm;
  dev_array coeff3_gm;
  dev_array coeff4_gm;
  dev_array coeff5_gm;
  dev_array coeff6_gm;
  dev_array coeff7_gm;
  dev_array coeff8_gm;
  dev_array coeff9_gm;
  dev_array coeff10_gm;

  int lastgridsize;
  int n_energy_virial;
  int collect_forces_later;
  int use_block_per_atom;
  int override_block_per_atom;
  bool neighall;

};

struct cuda_shared_domain {	// relevent data from domain class
  X_FLOAT sublo[3];			// orthogonal box -> sub-box bounds on this proc
  X_FLOAT subhi[3];
  X_FLOAT boxlo[3];
  X_FLOAT boxhi[3];
  X_FLOAT prd[3];
  int periodicity[3];		// xyz periodicity as array

  int triclinic;
  X_FLOAT xy;
  X_FLOAT xz;
  X_FLOAT yz;
  X_FLOAT boxlo_lamda[3];
  X_FLOAT boxhi_lamda[3];
  X_FLOAT prd_lamda[3];
  X_FLOAT h[6];
  X_FLOAT h_inv[6];
  V_FLOAT h_rate[6];
  int update;
};

struct cuda_shared_pppm {
  char cudable_force;
#ifdef FFT_CUFFT
  FFT_FLOAT* work1;
  FFT_FLOAT* work2;
  FFT_FLOAT* work3;
  PPPM_FLOAT* greensfn;
  PPPM_FLOAT* fkx;
  PPPM_FLOAT* fky;
  PPPM_FLOAT* fkz;
  PPPM_FLOAT* vg;
#endif
  int* part2grid;
  PPPM_FLOAT* density_brick;
  int* density_brick_int;
  PPPM_FLOAT density_intScale;
  PPPM_FLOAT* vdx_brick;
  PPPM_FLOAT* vdy_brick;
  PPPM_FLOAT* vdz_brick;
  PPPM_FLOAT* density_fft;
  ENERGY_FLOAT* energy;
  ENERGY_FLOAT* virial;
  int nxlo_in;
  int nxhi_in;
  int nxlo_out;
  int nxhi_out;
  int nylo_in;
  int nyhi_in;
  int nylo_out;
  int nyhi_out;
  int nzlo_in;
  int nzhi_in;
  int nzlo_out;
  int nzhi_out;
  int nx_pppm;
  int ny_pppm;
  int nz_pppm;
  PPPM_FLOAT qqrd2e;
  int order;
  // float3 sublo;
  PPPM_FLOAT* rho_coeff;
  int nmax;
  int nlocal;
  PPPM_FLOAT* debugdata;
  PPPM_FLOAT delxinv;
  PPPM_FLOAT delyinv;
  PPPM_FLOAT delzinv;
  int nlower;
  int nupper;
  PPPM_FLOAT shiftone;
  PPPM_FLOAT3* fH;
};

struct cuda_shared_comm {
  int maxswap;
  int maxlistlength;
  dev_array pbc;
  dev_array slablo;
  dev_array slabhi;
  dev_array multilo;
  dev_array multihi;
  dev_array sendlist;
  int grow_flag;
  int comm_phase;

  int nsend;
  int* nsend_swap;
  int* send_size;
  int* recv_size;
  double** buf_send;
  void** buf_send_dev;
  double** buf_recv;
  void** buf_recv_dev;
  void* buffer;
  int buffer_size;
  double overlap_split_ratio;
};

struct cuda_shared_neighlist { // member of CudaNeighList, has no instance in cuda_shared_data
  int maxlocal;
  int inum;                // # of I atoms neighbors are stored for local indices of I atoms
  int inum_border2;
  dev_array inum_border;         // # of atoms which interact with border atoms
  dev_array ilist;
  dev_array ilist_border;
  dev_array numneigh;
  dev_array numneigh_inner;
  dev_array numneigh_border;
  dev_array firstneigh;
  dev_array neighbors;
  dev_array neighbors_border;
  dev_array neighbors_inner;
  int maxpage;
  dev_array page_pointers;
  dev_array* pages;
  int maxneighbors;
  int neigh_lists_per_page;
  double** cutneighsq;
  CUDA_FLOAT* cu_cutneighsq;
  int* binned_id;
  int* bin_dim;
  int bin_nmax;
  float bin_extraspace;
  double maxcut;
  dev_array ex_type;
  int nex_type;
  dev_array ex1_bit;
  dev_array ex2_bit;
  int nex_group;
  dev_array ex_mol_bit;
  int nex_mol;

};

struct cuda_compile_settings {	// this is used to compare compile settings (i.e. precision) of the cu files, and the cpp files
  int prec_glob;
  int prec_x;
  int prec_v;
  int prec_f;
  int prec_pppm;
  int prec_fft;
  int cufft;
  int arch;
};

struct cuda_timings_struct {
  //Debug:
  double test1;
  double test2;
  //transfers
  double transfer_upload_tmp_constr;
  double transfer_download_tmp_deconstr;

  //communication
  double comm_forward_total;
  double comm_forward_mpi_upper;
  double comm_forward_mpi_lower;
  double comm_forward_kernel_pack;
  double comm_forward_kernel_unpack;
  double comm_forward_kernel_self;
  double comm_forward_upload;
  double comm_forward_download;

  double comm_exchange_total;
  double comm_exchange_mpi;
  double comm_exchange_kernel_pack;
  double comm_exchange_kernel_unpack;
  double comm_exchange_kernel_fill;
  double comm_exchange_cpu_pack;
  double comm_exchange_upload;
  double comm_exchange_download;

  double comm_border_total;
  double comm_border_mpi;
  double comm_border_kernel_pack;
  double comm_border_kernel_unpack;
  double comm_border_kernel_self;
  double comm_border_kernel_buildlist;
  double comm_border_upload;
  double comm_border_download;

  //pair forces
  double pair_xtype_conversion;
  double pair_kernel;
  double pair_virial;
  double pair_force_collection;

  //neighbor
  double neigh_bin;
  double neigh_build;
  double neigh_special;

  //PPPM
  double pppm_particle_map;
  double pppm_make_rho;
  double pppm_brick2fft;
  double pppm_poisson;
  double pppm_fillbrick;
  double pppm_fieldforce;
  double pppm_compute;

};

struct cuda_shared_data {	// holds space for all relevent data from the different classes
  void* buffer; //holds temporary GPU data [data used in subroutines, which has not to be consistend outside of that routine]
  int buffersize; //maxsize of buffer
  int buffer_new; //should be 1 if the pointer to buffer has changed
  void* flag;
  void* debugdata;  //array for easily collecting debugdata from device class cuda contains the corresponding cu_debugdata and host array
  cuda_shared_atom atom;
  cuda_shared_pair pair;
  cuda_shared_domain domain;
  cuda_shared_pppm pppm;
  cuda_shared_comm comm;
  cuda_compile_settings compile_settings;
  cuda_timings_struct cuda_timings;
  int exchange_dim;
  int me; //mpi rank
  unsigned int datamask;
  int overlap_comm;
};


#endif // #ifndef _CUDA_SHARED_H_
