/*
monitor.c
*/

#include "oth.h"

static int memory_size_cpu(int);
static int memory_size_gpu();
static int output_size(void);


// title or message
static void monitor1_(FILE *fp, const char msg[])
{
	fprintf(fp, "%s\n", msg);
	fflush(fp);
}


// condition
static void monitor2_(FILE *fp, int gpu, int commsize)
{
	time_t now;
	time(&now);

	fprintf(fp, "%s", ctime(&now));
	fprintf(fp, "Title = %s\n", Title);
	fprintf(fp, "Cells = %d x %d x %d = %lld\n", Nx, Ny, Nz, (long long int)(Nx * Ny * Nz));
	fprintf(fp, "No. of Materials   = %d\n", NMaterial);
	fprintf(fp, "No. of Geometries  = %d\n", NGeometry);
	fprintf(fp, "No. of Frequencies = %d\n", NFrequency);
	fprintf(fp, "Plane wave (0/1)   = %d\n", IPlanewave);
	fprintf(fp, "No. of Feeds       = %d\n", NFeed);
	fprintf(fp, "No. of Points      = %d\n", NPoint);
	fprintf(fp, "CPU Memory %s = %d [MB] (%s)\n", (commsize > 1 ? "/ proc." : "       "), memory_size_cpu(gpu), (MATRIX ? "matrix" : "no matrix"));
	if (gpu) fprintf(fp, "GPU Memory %s = %d [MB] (%s)\n", (commsize > 1 ? "/ proc." : "       "), memory_size_gpu(), (MATRIX ? "matrix" : "no matrix"));
	fprintf(fp, "Output filesize    = %d [MB]\n", output_size());
	if (Abc == MUR) fprintf(fp, "ABC = Mur-1st\n");
	if (Abc == PML) fprintf(fp, "ABC = PML (L=%d)\n", Lpml);
	if (Pbc.xpbc || Pbc.ypbc || Pbc.zpbc) fprintf(fp, "PBC = %s%s%s\n", (Pbc.xpbc ? "X" : ""), (Pbc.ypbc ? "Y" : ""), (Pbc.zpbc ? "Z" : ""));
	if (IPlanewave && Planewave.ifull) fprintf(fp, "Full-space = %d\n", Planewave.mfull);
	if (IPlanewave && Planewave.isemi) fprintf(fp, "Semi-space : %s[m] = %g (%d->%d)\n", ((Planewave.isemi == 1) ? "X" : (Planewave.isemi == 2) ? "Y" : "Z"), Planewave.psemi, Planewave.msemi1, Planewave.msemi2);
	fprintf(fp, "Iteration = %d, %d, %10.8f\n", Solver.maxiter, Solver.nout, Solver.converg);
	fprintf(fp, "%s\n", "=== iteration start ===");
	fflush(fp);
}


// output files
static void monitor3_(FILE *fp)
{
	fprintf(fp, "=== output files ===\n");
	fprintf(fp, "%s, %s\n", FN_log, FN_out);
	fflush(fp);
}


// cpu time
static void monitor4_(FILE *fp, const double cpu[])
{
	time_t now;
	time(&now);

	fprintf(fp, "%s\n", "=== cpu time [sec] ===");
	fprintf(fp, "  part-1 : %11.3f\n", cpu[2] - cpu[1]);
	fprintf(fp, "  part-2 : %11.3f\n", (cpu[1] - cpu[0]) + (cpu[3] - cpu[2]));
	fprintf(fp, "  %s\n", "--------------------");
	fprintf(fp, "  total  : %11.3f\n", cpu[3] - cpu[0]);
	fprintf(fp, "%s\n", "=== normal end ===");
	fprintf(fp, "%s", ctime(&now));
	fflush(fp);
}


// iteration header
static void monitor5_(FILE *fp, int ifreq)
{
	char str[BUFSIZ], str2[BUFSIZ];

	sprintf(str, "  frequency[Hz] = %.3e", Frequency[ifreq]);
	if (NFrequency > 1) {
		sprintf(str2, " (%d/%d)", ifreq + 1, NFrequency);
		strcat(str, str2);
	}
	fprintf(fp, "%s\n", str);

	strcpy(str, "  iteration     residual        <E>");
	fprintf(fp, "%s\n", str);

	fflush(fp);
}


// iteraion
static void monitor6_(FILE *fp, int iter, double residue, double xnrm)
{
	int lx = (Abc == PML) ? Lpml : 0;
	int ly = (Abc == PML) ? Lpml : 0;
	int lz = (Abc == PML) ? Lpml : 0;
	int64_t ntotal = (Nx + 2 * lx + 0) * (Ny + 2 * ly + 1) * (Nz + 2 * lz + 1)
	               + (Nx + 2 * lx + 1) * (Ny + 2 * ly + 0) * (Nz + 2 * lz + 1)
	               + (Nx + 2 * lx + 1) * (Ny + 2 * ly + 1) * (Nz + 2 * lz + 0);

	fprintf(fp, "    %7d %13.8f %12.7f\n", iter, residue, xnrm / sqrt(ntotal / 3.0));

	fflush(fp);
}


void monitor1(FILE *fp, const char msg[])
{
	monitor1_(fp,     msg);
	monitor1_(stdout, msg);
}


void monitor2(FILE *fp, int gpu, int commsize)
{
	monitor2_(fp,     gpu, commsize);
	monitor2_(stdout, gpu, commsize);
}


void monitor3(FILE *fp)
{
	monitor3_(fp);
	monitor3_(stdout);
}


void monitor4(FILE *fp, const double cpu[])
{
	monitor4_(fp,     cpu);
	monitor4_(stdout, cpu);
}


void monitor5(FILE *fp, int ifreq)
{
	monitor5_(fp,     ifreq);
	monitor5_(stdout, ifreq);
}

void monitor6(FILE *fp, int iter, double residue, double xnrm)
{
	monitor6_(fp,     iter, residue, xnrm);
	monitor6_(stdout, iter, residue, xnrm);
}


// CPU memory size [MB]
static int memory_size_cpu(int gpu)
{
	const int64_t num
		= NFrequency * 3 * Nx * Ny * Nz * sizeof(f_complex_t)  // Ex, Ey, Ez
		+ 7                  * NE       * sizeof(d_complex_t)  // E, B, R, P, Q, T, U
		+ (gpu ? 2 : 0)      * NE       * sizeof(d_complex_t)  // E, B
		+ (MATRIX ? NNZ : 0) * NE       * sizeof(d_complex_t)  // A
		+ (NE + NH)                     * sizeof(int)          // mE, mH
		+ NGeometry                     * sizeof(geometry_t);  // Geometry

	return (int)(num / 1024 / 1024) + 1;
}


// GPU memory size [MB]
static int memory_size_gpu(void)
{
	const int64_t num
		= 7                  * NE * sizeof(d_complex_t)        // E, B, R, P, Q, T, U
		+ (MATRIX ? NNZ : 0) * NE * sizeof(d_complex_t)        // A
		+ (NE + NH)               * sizeof(int);               // mE, mH

	return (int)(num / 1024 / 1024) + 1;
}


// output filesize [MB]
static int output_size(void)
{
	const int64_t num
		= NFrequency * 3 * Nx * Ny * Nz * sizeof(f_complex_t)  // Ex, Ey, Ez
		+ 6 * Nx * Ny * Nz              * sizeof(int);         // mE, mH

	return (int)(num / 1024 / 1024) + 1;
}
