/*
memory_gpu.cu (CUDA)
*/

#include "oth.h"
#include "oth_cuda.h"
#include "cuda_complex.h"
#include "oth_prototype.h"

void memalloc_gpu()
{
	// mesh factor

	cuda_malloc(GPU, UM, (void **)&d_DXn, (Nx + 1) * sizeof(double));
	cuda_malloc(GPU, UM, (void **)&d_DYn, (Ny + 1) * sizeof(double));
	cuda_malloc(GPU, UM, (void **)&d_DZn, (Nz + 1) * sizeof(double));
	cuda_malloc(GPU, UM, (void **)&d_DXc, (Nx + 0) * sizeof(double));
	cuda_malloc(GPU, UM, (void **)&d_DYc, (Ny + 0) * sizeof(double));
	cuda_malloc(GPU, UM, (void **)&d_DZc, (Nz + 0) * sizeof(double));

	cuda_malloc(GPU, UM, (void **)&d_KRXn, (Nx + 1) * sizeof(double));
	cuda_malloc(GPU, UM, (void **)&d_KRYn, (Ny + 1) * sizeof(double));
	cuda_malloc(GPU, UM, (void **)&d_KRZn, (Nz + 1) * sizeof(double));
	cuda_malloc(GPU, UM, (void **)&d_KRXc, (Nx + 0) * sizeof(double));
	cuda_malloc(GPU, UM, (void **)&d_KRYc, (Ny + 0) * sizeof(double));
	cuda_malloc(GPU, UM, (void **)&d_KRZc, (Nz + 0) * sizeof(double));

	cuda_malloc(GPU, UM, (void **)&d_KDXn, (Nx + 1) * sizeof(double));
	cuda_malloc(GPU, UM, (void **)&d_KDYn, (Ny + 1) * sizeof(double));
	cuda_malloc(GPU, UM, (void **)&d_KDZn, (Nz + 1) * sizeof(double));
	cuda_malloc(GPU, UM, (void **)&d_KDXc, (Nx + 0) * sizeof(double));
	cuda_malloc(GPU, UM, (void **)&d_KDYc, (Ny + 0) * sizeof(double));
	cuda_malloc(GPU, UM, (void **)&d_KDZc, (Nz + 0) * sizeof(double));

	cuda_malloc(GPU, UM, (void **)&d_KRXp, (Nx + 1) * sizeof(double));
	cuda_malloc(GPU, UM, (void **)&d_KRYp, (Ny + 1) * sizeof(double));
	cuda_malloc(GPU, UM, (void **)&d_KRZp, (Nz + 1) * sizeof(double));
	cuda_malloc(GPU, UM, (void **)&d_KRXm, (Nx + 1) * sizeof(double));
	cuda_malloc(GPU, UM, (void **)&d_KRYm, (Ny + 1) * sizeof(double));
	cuda_malloc(GPU, UM, (void **)&d_KRZm, (Nz + 1) * sizeof(double));

	// Material

	cuda_malloc(GPU, UM, (void **)&d_Material, NMaterial * NFrequency * sizeof(material_t));
	cuda_malloc(GPU, UM, (void **)&d_mE, NE * sizeof(int));
	cuda_malloc(GPU, UM, (void **)&d_mH, NH * sizeof(int));

	// PML

	if (Abc == PML) {
		cuda_malloc(GPU, UM, (void **)&d_Fxpml, NFxpml * sizeof(double));
		cuda_malloc(GPU, UM, (void **)&d_Fypml, NFypml * sizeof(double));
		cuda_malloc(GPU, UM, (void **)&d_Fzpml, NFzpml * sizeof(double));
	}
}

// CG work array
void memalloc_cg_gpu()
{
	const size_t size = NE * sizeof(cuDoubleComplex);

	cuda_malloc(GPU, UM, (void **)&d_Ecg, size);
	cuda_malloc(GPU, UM, (void **)&d_Bcg, size);
	cuda_malloc(GPU, UM, (void **)&d_Rcg, size);
	cuda_malloc(GPU, UM, (void **)&d_Pcg, size);
	cuda_malloc(GPU, UM, (void **)&d_Qcg, size);
	cuda_malloc(GPU, UM, (void **)&d_Tcg, size);
	cuda_malloc(GPU, UM, (void **)&d_Ucg, size);
	cuda_malloc(GPU, UM, (void **)&d_R0cg, size);

	if (MATRIX) {
		cuda_malloc(GPU, UM, (void **)&d_Acg, NNZ * size);
	}
}

// frequency independent
void memcopy_gpu()
{
	cuda_memcpy(GPU, d_DXn, DXn, (Nx + 1) * sizeof(double), cudaMemcpyHostToDevice);
	cuda_memcpy(GPU, d_DYn, DYn, (Ny + 1) * sizeof(double), cudaMemcpyHostToDevice);
	cuda_memcpy(GPU, d_DZn, DZn, (Nz + 1) * sizeof(double), cudaMemcpyHostToDevice);
	cuda_memcpy(GPU, d_DXc, DXc, (Nx + 0) * sizeof(double), cudaMemcpyHostToDevice);
	cuda_memcpy(GPU, d_DYc, DYc, (Ny + 0) * sizeof(double), cudaMemcpyHostToDevice);
	cuda_memcpy(GPU, d_DZc, DZc, (Nz + 0) * sizeof(double), cudaMemcpyHostToDevice);

	cuda_memcpy(GPU, d_Material, Material, NMaterial * NFrequency * sizeof(material_t), cudaMemcpyHostToDevice);
	cuda_memcpy(GPU, d_mE, mE, NE * sizeof(int), cudaMemcpyHostToDevice);
	cuda_memcpy(GPU, d_mH, mH, NH * sizeof(int), cudaMemcpyHostToDevice);

	if (Abc == PML) {
		cuda_memcpy(GPU, d_Fxpml, Fxpml, NFxpml * sizeof(double), cudaMemcpyHostToDevice);
		cuda_memcpy(GPU, d_Fypml, Fypml, NFypml * sizeof(double), cudaMemcpyHostToDevice);
		cuda_memcpy(GPU, d_Fzpml, Fzpml, NFzpml * sizeof(double), cudaMemcpyHostToDevice);
	}
}

// frequency dependent
void memcopy2_gpu()
{
	cuda_memcpy(GPU, d_KRXn, KRXn, (Nx + 1) * sizeof(double), cudaMemcpyHostToDevice);
	cuda_memcpy(GPU, d_KRYn, KRYn, (Ny + 1) * sizeof(double), cudaMemcpyHostToDevice);
	cuda_memcpy(GPU, d_KRZn, KRZn, (Nz + 1) * sizeof(double), cudaMemcpyHostToDevice);
	cuda_memcpy(GPU, d_KRXc, KRXc, (Nx + 0) * sizeof(double), cudaMemcpyHostToDevice);
	cuda_memcpy(GPU, d_KRYc, KRYc, (Ny + 0) * sizeof(double), cudaMemcpyHostToDevice);
	cuda_memcpy(GPU, d_KRZc, KRZc, (Nz + 0) * sizeof(double), cudaMemcpyHostToDevice);

	cuda_memcpy(GPU, d_KDXn, KDXn, (Nx + 1) * sizeof(double), cudaMemcpyHostToDevice);
	cuda_memcpy(GPU, d_KDYn, KDYn, (Ny + 1) * sizeof(double), cudaMemcpyHostToDevice);
	cuda_memcpy(GPU, d_KDZn, KDZn, (Nz + 1) * sizeof(double), cudaMemcpyHostToDevice);
	cuda_memcpy(GPU, d_KDXc, KDXc, (Nx + 0) * sizeof(double), cudaMemcpyHostToDevice);
	cuda_memcpy(GPU, d_KDYc, KDYc, (Ny + 0) * sizeof(double), cudaMemcpyHostToDevice);
	cuda_memcpy(GPU, d_KDZc, KDZc, (Nz + 0) * sizeof(double), cudaMemcpyHostToDevice);

	cuda_memcpy(GPU, d_KRXp, KRXp, (Nx + 1) * sizeof(double), cudaMemcpyHostToDevice);
	cuda_memcpy(GPU, d_KRYp, KRYp, (Ny + 1) * sizeof(double), cudaMemcpyHostToDevice);
	cuda_memcpy(GPU, d_KRZp, KRZp, (Nz + 1) * sizeof(double), cudaMemcpyHostToDevice);
	cuda_memcpy(GPU, d_KRXm, KRXm, (Nx + 1) * sizeof(double), cudaMemcpyHostToDevice);
	cuda_memcpy(GPU, d_KRYm, KRYm, (Ny + 1) * sizeof(double), cudaMemcpyHostToDevice);
	cuda_memcpy(GPU, d_KRZm, KRZm, (Nz + 1) * sizeof(double), cudaMemcpyHostToDevice);
}

// setup parameters
void setup_parameter_gpu()
{
	h_Param.Nx = Nx;
	h_Param.Ny = Ny;
	h_Param.Nz = Nz;

	h_Param.NEXI = NEXI;
	h_Param.NEXJ = NEXJ;
	h_Param.NEXK = NEXK;
	h_Param.NEX0 = NEX0;

	h_Param.NEYI = NEYI;
	h_Param.NEYJ = NEYJ;
	h_Param.NEYK = NEYK;
	h_Param.NEY0 = NEY0;

	h_Param.NEZI = NEZI;
	h_Param.NEZJ = NEZJ;
	h_Param.NEZK = NEZK;
	h_Param.NEZ0 = NEZ0;

	h_Param.NHXI = NHXI;
	h_Param.NHXJ = NHXJ;
	h_Param.NHXK = NHXK;
	h_Param.NHX0 = NHX0;

	h_Param.NHYI = NHYI;
	h_Param.NHYJ = NHYJ;
	h_Param.NHYK = NHYK;
	h_Param.NHY0 = NHY0;

	h_Param.NHZI = NHZI;
	h_Param.NHZJ = NHZJ;
	h_Param.NHZK = NHZK;
	h_Param.NHZ0 = NHZ0;

	h_Param.NMaterial = NMaterial;

	h_Param.SCALING = SCALING;
	h_Param.MATRIX = MATRIX;

	h_Param.NFeed = NFeed;

	h_Param.IPlanewave = IPlanewave;
	h_Param.Planewave_isemi = Planewave.isemi;

	h_Param.Abc    = Abc;
	h_Param.Lpml   = Lpml;
	h_Param.Xpbc   = Pbc.xpbc;
	h_Param.Ypbc   = Pbc.ypbc;
	h_Param.Zpbc   = Pbc.zpbc;
	h_Param.pbc_fx = make_cuDoubleComplex(Pbc.fx.r, Pbc.fx.i);
	h_Param.pbc_fy = make_cuDoubleComplex(Pbc.fy.r, Pbc.fy.i);
	h_Param.pbc_fz = make_cuDoubleComplex(Pbc.fz.r, Pbc.fz.i);
}

// free
void memfree_cg_gpu()
{
	cuda_free(GPU, d_Ecg);
	cuda_free(GPU, d_Bcg);
	cuda_free(GPU, d_Rcg);
	cuda_free(GPU, d_Pcg);
	cuda_free(GPU, d_Qcg);
	cuda_free(GPU, d_Tcg);
	cuda_free(GPU, d_Ucg);

	if (MATRIX) {
		cuda_free(GPU, d_Acg);
	}
}
