/*
    DFT++ is a density functional package developed by the research group
    of Professor Tomas Arias

    Copyright 1996-2003 Sohrab Ismail-Beigi

    This file is part of DFT++.

    DFT++ is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    DFT++ is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with DFT++; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

    Please see the file CREDITS for a list of authors.

    For academic users, we request that publications using results obtained with
    this software reference

    "New algebraic formulation of density functional calculation," by Sohrab Ismail-Beigi
    and T.A. Arias, Computer Physics Communications 128:1-2, 1-45 (June 2000).

    and, if using the wavelet basis, further reference

    "Multiresolution analysis of electronic structure: semicardinal and wavelet bases,"
    T.A. Arias, Reviews of Modern Physics 71:1, 267-311 (January 1999).

    and 

    "Robust ab initio calculation of condensed matter: transparent convergence through
    semicardinal multiresolution analysis,'' I.P. Daykov, T.A. Arias, and
    Torkel D. Engeness, Physical Review Letters, 90:21, 216402 (May 2003).

    For your convenience, preprints of the above articles may be obtained from
    http://arXiv.org/abs/cond-mat/9909130, 9805262, and 0204411, respectively.
*/

/************************************************************************/
/*	                                                                */
/*            Tairan Wang                       Nov. 6, 1997            */
/*	                                                                */
/*  A set of timer access routines for MPI performance profiling        */
/*									*/
/************************************************************************/

/* $Id: timer.cpp,v 1.14.2.4 2003/05/29 18:54:35 ivan Exp $ */

#include "header.h"
#include "parallel.h"

#ifdef DFT_PROFILING

#define ON              1
#define OFF		0
#define NUM_TIMERS	64
#define NUM_COUNTERS    64

int    timerState[NUM_TIMERS];
char   timerTitle[NUM_TIMERS][DFT_MSG_LEN];
double  lastStart[NUM_TIMERS],accTime[NUM_TIMERS];
int  num_timers;

double counterState[NUM_COUNTERS];
char counterTitle[NUM_TIMERS][DFT_MSG_LEN];
int num_counters;

//////////////////////////////////////////////////////////////////

/*  To turn on a timer.  Returns the prior state of the timer.	*/

int
timerOn(int timer)
{
  int priorState = timerState[timer];

  if (priorState == OFF)
    {
      timerState[timer] = ON;
      lastStart[timer] = System::get_time();
    }
//  SIB 27/1/00: commented out for now... this is NOT thread safe
//               i.e. when running threads, many threads
//               call the same timer at the same time, and as
//               variables are global, we get WARNINGs galore
//   else
//     {
//       dft_log("\nWARNING: timer %d already on at:%f",
// 	      timer,lastStart[timer]);
//     }
  return(priorState);
}

/*  To turn a timer off.  Again, returns the prior state.	*/

int
timerOff(int timer)
{
  int priorState = timerState[timer];

  if (priorState == ON)
    {
      timerState[timer] = OFF;
      accTime[timer] += System::get_time() - lastStart[timer];
    }
  return(priorState);
}

/*  To set the timer to a given state.	Returns prior state.	*/

int
timerSetState(int timer, int state)
{
  if (state == ON)
    return timerOn(timer);
  else
    return timerOff(timer);
}

/*  To initialize all timers.					*/
void
timerInitAll(void)
{
  num_timers = 0;
  for (int i = 0; i < NUM_TIMERS; i++)
    timerInit(i);
}

/*  To initialize a timer.					*/
void
timerInit(int timer, char* title)
{
  timerState[timer] = OFF;
  accTime[timer]    = 0;
  strcpy(timerTitle[timer], title);
  if ((timer+1) > num_timers)
    num_timers = timer+1;
}

void
timerInit(int timer)
{
  timerState[timer] = OFF;
  accTime[timer]    = 0;
}


/*  To reset a timer, possibly running.				*/
int
timerReset(int timer)
{
  int priorState = timerOff(timer);

  timerInit(timer);
  timerSetState(timer,priorState);
  return(priorState);
}


/*  To read a timer, regardless of state.			*/
double
timerRead(int timer)
{
  double timeTemp;

  timeTemp = accTime[timer];
  if (timerState[timer] == ON)
    timeTemp += System::get_time() - lastStart[timer];
  return(timeTemp);
}


/*  To get the title of a timer */
char *
timerGetTitle(int timer)
{
  return (timerTitle[timer]);
}


/*  To get the number of initialized timers */
int
timerGetTotal(void)
{
  return num_timers;
}


/* 
 * Convenience routines to turn on all the timers 
 */
int
timerActivateAll(void)
{
  timerInitAll();
  timerInit(0, "Initializations");
  timerInit(1, "Total computation");
  timerInit(2, "Minimization routines");
  timerInit(3, "Y1^Y2");
  timerInit(4, "Y*M");
  timerInit(5, "Column bundle transpose");
  timerInit(6, "diagouterI");
  timerInit(7, "diaginner"); 
  timerInit(8, "memory allocation");
  timerInit(9, "do_linmin");
  timerInit(10, "calc_elecgrad_Hsub");
  timerInit(11, "calc_UVCn_d_elec_dependent_e");
  timerInit(12, "Y1^Y2 MPI_Allreduce");
  timerInit(13, "Other MPI_Allreduce");
  timerInit(14, "\tcalc_U");
  timerInit(15, "\tcalc_C");
  timerInit(16, "\tcalc_n");
  timerInit(17, "\tsolve_poisson");
  timerInit(18, "\tcalc_KE");
  timerInit(19, "\tcalc_Eloc");
  timerInit(20, "\tcalc_Enl");
  timerInit(21, "\tcalc_EH");
  timerInit(22, "\tcalc_Exc");
  timerInit(23, "\tcalc_Ecore");
  timerInit(24, "\tcalc_Eewald");
  timerInit(25, "\tcalc_Vscloc");
  timerInit(26, "\tapply_Hsp");
  timerInit(27, "\tdiagonalize_herm");
  timerInit(28, "\tmatrix*matrix");
  timerInit(29, "\tO");
  timerInit(30, "\tPbar");
  timerInit(31, "\tL");
  timerInit(32, "\tinvL");
  timerInit(33, "\tprecond");
  timerInit(34, "\tI");
  timerInit(35, "\tJ");
  timerInit(36, "\tIdag");
  timerInit(37, "\tJdag");
  timerInit(38,"diaginner  MPI_Allreduce");
  timerInit(39,"diagouterI MPI_Allreduce");
  timerInit(40,"FFT3D");
  timerInit(41,"calc_V");
  timerInit(42,"apply_Hsp_loc");
  timerInit(43,"apply_Hsp_nloc");
  timerInit(50,"operator \"=\"");
  timerInit(51,"operator \"+\"");
  timerInit(52,"operator \"-\"");

  // For wavelet profiling, it's important to count the time we spend doing
  // data conversion.
#ifdef WAVELETS
  timerInit(60,"GetRe, PutRe, GetIm, PutIm");
#endif // WAVELETS
  return 43;
}

/* 
 * Report the timer content
 */
void
timerReport(int iter)
{
  int itimer, n_timers = timerGetTotal();
  if (n_timers > 0)
    {
      dft_log("\nTiming reports at iteration %d:\n",iter);
      for (itimer = 0; itimer < n_timers; itimer++) 
	dft_log("\t%4d %s :\t\t%f sec\n",
		itimer,
		timerGetTitle(itimer), 
		timerRead(itimer));
      dft_log("\n");
      dft_log_flush();
    }
}

/*
 * Report the timer content and counter content:
 */
void
timer_counter_Report(int iter)
{
  timerReport(iter);
  counterReport(iter);
}

/////////////////////////////////////////////////////////////////

void
counterReport(int iter)
{
  int n_counters = counterGetTotal(); 
  if (n_counters > 0)
    {
      dft_log("\nCounter reports at iteration %d:\n",iter);
      for (int icounter = 0; icounter < n_counters; icounter++)
	if (counterRead(icounter)>1.0e6)
	  dft_log("\t%4d %s :\t\t%e\n",
		  icounter,
		  counterGetTitle(icounter),
		  counterRead(icounter));
	else
	  dft_log("\t%4d %s :\t\t%f\n",
		  icounter,
		  counterGetTitle(icounter),
		  counterRead(icounter));
      dft_log("\n");
      dft_log_flush();
    }
}

int
counterActivateAll(void)
{
  counterInitAll();

  counterInit(0, "Y1^Y2 flop count");
  counterInit(1, "Y1^Y2 total count");
  counterInit(2, "Y1^Y2 distributed count");
  counterInit(3, "Y1^Y2 MPI_Allreduce bytes");

  counterInit(4, "Y*M flop count");
  counterInit(5, "Y*M total count");
  counterInit(6, "Y*M distributed count");

  counterInit(7, "Tranpose count");
  counterInit(8, "Transpose bytes");

  counterInit(9,  "matrix*matrix flop count");
  counterInit(10, "matrix*matrix count");
  counterInit(11, "diagonalization count");

  counterInit(12, "I count");
  counterInit(13, "Idag count");
  counterInit(14, "J count");
  counterInit(15, "Jdag count");
  counterInit(16, "FFT3D count");

  counterInit(17, "diaginner  MPI_Allreduce count");
  counterInit(18, "diaginner  MPI_Allreduce bytes");
  counterInit(19, "diagouterI MPI_Allreduce count");
  counterInit(20, "diagouterI MPI_Allreduce bytes");
  counterInit(21, "other MPI_Allreduce count");
  counterInit(22, "other MPI_Allreduce bytes");

  // My additions to the operator counts.
  counterInit(23, "L count");
  counterInit(24, "O count");
  
#ifdef WAVELETS
  counterInit(25, "Get/Put count");
#endif // WAVELETS

  return 24;
}

void
counterInitAll(void)
{
  num_counters = 0;
  for (int i=0; i < NUM_COUNTERS; i++)
    counterState[i] = 0.0;
}

void
counterInit(int counter, char* title)
{
  counterState[counter] = 0.0;
  strcpy(counterTitle[counter], title);
  if ((counter+1) > num_counters) {
    num_counters = counter + 1;
  }
}

void
counterIncr(int counter)
{
  counterState[counter] += 1.0;
}

void
counterIncr(int counter, double amount)
{
  counterState[counter] += amount;
}

double
counterRead(int counter)
{
  return counterState[counter];
}

char *
counterGetTitle(int counter)
{
  return (counterTitle[counter]);
}

int
counterGetTotal(void)
{
  return num_counters;
}

#endif // DFT_PROFILING
