/*
 * Copyright University of Reims Champagne-Ardenne
 * Authors and Contributors: Akilan RAJAMANI, Corentin LEFEBVRE, Johanna KLEIN,
 *                           Emmanuel PLUOT, Gaetan RUBEZ, Hassan KHARTABIL,
 *                           Jean-Charles BOISSON and Eric HENON
 * (24/07/2017)
 * jean-charles.boisson@univ-reims.fr, eric.henon@univ-reims.fr
 *
 * This software is a computer program whose purpose is to
 * detect and quantify interactions from electron density
 * obtained either internally from promolecular density or
 * calculated from an input wave function input file. It also
 * prepares for the visualization of isosurfaces representing
 * several descriptors (dg) coming from the IGM methodology.
 *
 * This software is governed by the CeCILL-C license under French law and
 * abiding by the rules of distribution of free software.  You can  use,
 * modify and/ or redistribute the software under the terms of the CeCILL-C
 * license as circulated by CEA, CNRS and INRIA at the following URL
 * "http://www.cecill.info".
 *
 * As a counterpart to the access to the source code and  rights to copy,
 * modify and redistribute granted by the license, users are provided only
 * with a limited warranty  and the software's author,  the holder of the
 * economic rights,  and the successive licensors  have only  limited
 * liability.
 *
 * In this respect, the user's attention is drawn to the risks associated
 * with loading,  using,  modifying and/or developing or reproducing the
 * software by the user in light of its specific status of free software,
 * that may mean  that it is complicated to manipulate,  and  that  also
 * therefore means  that it is reserved for developers  and  experienced
 * professionals having in-depth computer knowledge. Users are therefore
 * encouraged to load and test the software's suitability as regards their
 * requirements in conditions enabling the security of their systems and/or
 * data to be ensured and,  more generally, to use and operate it in the
 * same conditions as regards security.
 *
 * The fact that you are presently reading this means that you have had
 * knowledge of the CeCILL-C license and that you accept its terms.
 *
 * */

/**
 * @file output.cpp
 * @brief Implementation of methods declared in output.h
 * @author Gaetan & Emmanuel */

#ifndef _OUTPUT_CPP_
#define _OUTPUT_CPP_

// LOCAL
#include <output.h>
#include <string>

using namespace std;

//! The computation of the current index (using i, j, k)
#define INDEX (k*data->getNbSteps(0)*data->getNbSteps(1)+j*data->getNbSteps(0)+i)

void
outCube(int cubeType, param_t * params, ProgData * data, double * cubeRho, double * cube)
{
  /* PROMOLECULAR MODE */
  /* function dedicated to print a cube file for dgInter and dgIntra descriptors only */
  /*                                      or for dgInter/rho and dgIntra/rho          */

  // cubeType: an integer : defined as the number after all TYPE_DELTA_G in the CUBE_TYPES enum type (see includes/general.h)
  //           <-> the number of deltag_grids to be written to cube files
  // params  : object containing all information about parameters of the run
  // ProgData: ProgData object: atomTypes, atom positions, nbStep, max, min
  // cubeRho : a cube array in memory containg the ED values at grid nodes
  // cube    : a cube array in memory containg another property (dg, dgIntra ...) at grid nodes
  
  /* Building filename */
  ostringstream os;
  if (not isdgSCALED())
  {os << params->outputName << "-" << CUBE_NAMES[cubeType] << ".cube";}
  else
  {os << params->outputName << "-" << CUBE_NAMES[cubeType] << "S.cube";}
	
  /* Opening writing stream */
  ofstream writer(os.str().c_str());
    
  /* Setting stream precision to 6 numbers */
  writer << fixed << setprecision(6) << CUBE_NAMES[cubeType] + " cube \nGenerated by IGMPlot\n";

  /* determining which kind of cube, to set the correct cutplotIGM	 */
  double lowerlimit = 0.0;
  double upperlimit = 0.0;
  if (CUBE_NAMES[cubeType]== "dgIntra")
    {
      if (isPeakFocusIntraActivated()) {
         lowerlimit = params->cutpeakIntra[0];
         upperlimit = params->cutpeakIntra[1];
      } else {
         lowerlimit = -params->cutplotIGM[0] ;
         upperlimit =  params->cutplotIGM[0] ;
      }
    } // end of dgIntra treatment

  else
    {
      if (CUBE_NAMES[cubeType]== "dgInter")
	{
          if (isPeakFocusInterActivated()) {
             lowerlimit = params->cutpeakInter[0];
             upperlimit = params->cutpeakInter[1];
          } else {
             lowerlimit = -params->cutplotIGM[1] ;
             upperlimit =  params->cutplotIGM[1] ;
          } // end of isPeakFocusInterActivated
       } // end of dgInter treatment
      else // any other grid data
        {
    	  lowerlimit =-params->cutplotIGM[0] ;
          upperlimit = params->cutplotIGM[0] ;
        }
    } // end of if else if (CUBE_NAMES[cubeType]== "dgIntra")
 
  /* Writing number of atom and min coordinates */
  writer << data->getNbAtom() << " " << data->getMinCoord(0) << " " << data->getMinCoord(1) << " " << data->getMinCoord(2) << "\n";
	
  /* Writing steps and increments by axis */
  writer << data->getNbSteps(0) 	<< 	" " 					
         << params->increments[0]	<<	" 0.000000 0.000000" 	<< "\n";
  writer << data->getNbSteps(1)	<<	" 0.000000 " 			<< 	params->increments[1] 	
         << " 0.000000" 				<< "\n";
  writer << data->getNbSteps(2)	<<	" 0.000000 0.000000 " 	<< 	params->increments[2] 	<< "\n";

  /* Looping through all atoms  */
  for( int i(0) ; i < data->getNbAtom() ; ++i )
    {
      /* Writing both type and coordinates for each atom */
      writer << data->atomTypes[i] + 1 << " 0.0 " << data->atomPositions.xValues[i] << " " 
             << data->atomPositions.yValues[i] << " " << data->atomPositions.zValues[i] << " " << "\n";
    }
	
  /* Writing next values with scientific convention */
  writer << fixed << scientific;
	
  /* Index value initilization */
  // NOT REALLY USED
  //int index(0);
  
  /* ================ T R I P L E   L O O P   O V E R    t h e   G R I D   =========*/	
  /* Looping over the first axis */
  for( int i(0) ; i < data->getNbSteps(0) ; ++i )
    {
      /* Looping over the second axis */
      for( int j(0) ; j < data->getNbSteps(1) ; ++j )
	{
	  /* Looping over the third axis */
	  for( int k(0) ; k < data->getNbSteps(2) ; ++k )
	    {
	      /* Writing value (those inside plots) */
              if (     (cubeRho[INDEX] < lowerlimit) 
                   or  (cubeRho[INDEX] > upperlimit)
                 )
              {
                    writer << " " << 0.0;
              }
              else 
              {
                    if (cube[INDEX] >= 0)
                       {
                         writer << " " ;          
                       }
                    // MODIF ERIC 02/29/2024 for dgSCALED 
                    if (not isdgSCALED())
                      {writer <<  cube[INDEX];}
                    else
                       {writer <<  cube[INDEX]/std::abs(cubeRho[INDEX]);}

              }
              writer <<  " ";
             
	      
	      /* Updating index */
	      // NOT REALLY USED
	      //++index;

              if(k%6==5)
                {
                  writer << "\n";
                }

	      
	    } // end of  for( int k(0) ; k < data->getNbSteps(2) ; ++k )
	  
	  writer << "\n";
			
	} // end of for( int j(0) ; j < data->getNbSteps(1) ; ++j )
		
      writer << "\n";

    } // end of for( int i(0) ; i < data->getNbSteps(0) ; ++i )
  
  /* Closing writing stream */
  writer.close();
}

void
writeCube(param_t * params, ProgData * data, const char* cubeFileName, double *** rho, double *** cube,  const bool usingRhoThreshold, const double lowerlimit, const double upperlimit, const double defaultValue)
{
  /* function dedicated to print a cube file for dgInter and dgIntra descriptors only */
  // ProgData      : input --> The pointer to the array to be written to the cube file
  // cubeFileName  : input --> a String specifying the output filename for this cube file.
  // usingRhoThreshold: input --> if true, the ED will be used to parse values according to lowlimit  and upperlimit
  // lowlimit      : input --> if the electron density is larger than this threshold: print the data to the cube file
  // upperlimit    : input --> if the electron density is lower  than this threshold: print the data to the cube file
  // default       : default value to print to the cube file when threshold criteria are not respected
  //

  /* Building filename */
  ostringstream os;
  os << params->outputName << "-" << cubeFileName << ".cube";
	
  /* Opening writing stream */
  ofstream writer(os.str().c_str());
    
  /* Setting stream precision to 6 numbers */
  writer << fixed << setprecision(6) << cubeFileName << " cube \nGenerated by IGMPlot\n";

  /* Writing number of atom and min coordinates */
  writer << data->getNbAtom() << " " << data->getMinCoord(0) << " " << data->getMinCoord(1) << " " << data->getMinCoord(2) << "\n";
	
  /* Writing steps and increments by axis */
  writer << data->getNbSteps(0) << 	" " 			<<	params->increments[0]	<<	" 0.000000 0.000000" 	<< "\n";
  writer << data->getNbSteps(1)	<<	" 0.000000 " 		<< 	params->increments[1] 	<< " 0.000000" 			<< "\n";
  writer << data->getNbSteps(2)	<<	" 0.000000 0.000000 " 	<< 	params->increments[2]  	<< "\n";

  /* Looping through all atoms  */
  unsigned int* user2ADF = wf_user2ADF_atomOrder();
  for( int iatUser=0 ; iatUser < data->getNbAtom() ; ++iatUser )
    {
      // First, convert the atomic user numering to the WF atomic numbering
      unsigned int iat;
      if (isRKFmodeActivated()) { // ADF requires a specific treatment
          iat = user2ADF[iatUser]-1;  // user2ADF takes a user_0_based atomic index and returns a WF_1_based atomic index
      } else {iat = iatUser;} // in WFX and WFN, user atom numbering is preserved in WF data



      /* Writing both type and coordinates for each atom */
      writer << data->atomTypes[iat] + 1 << " 0.0 " << data->atomPositions.xValues[iat] << " " << 
                data->atomPositions.yValues[iat] << " " << data->atomPositions.zValues[iat] << " " << "\n";
    } // end for over of atom data
	
  /* Writing next values with scientific convention */
  writer << fixed << scientific;
	

  /* Looping over the first axis */
  for( int i=0 ; i < data->getNbSteps(0) ; ++i )
    {
      /* Looping over the second axis */
      for( int j=0 ; j < data->getNbSteps(1) ; ++j )
	{
	  /* Looping over the third axis */
	  for( int k=0 ; k < data->getNbSteps(2) ; ++k )
	    {
	        if( usingRhoThreshold and ( (rho[i][j][k] < lowerlimit) or (rho[i][j][k] > upperlimit) ) )
		{
	  	  writer << defaultValue;
		}
	        else
		{
		  if( cube[i][j][k] >= 0.0 )
		    {
		      writer << " " ;
		    }
		    writer << cube[i][j][k];
//DBUG ERIC
//if (cube[i][j][k] != cube[i][j][k]) {
//std::cout << "NaN found ! " << std::endl;
//std::cout << "i,j,k = " << i << " " << j << " " << k << std::endl;
//}
		}
	      
	      writer << " " ;

	      if(k%6==5)
		{
		  writer << "\n";
		}
	      
	    }
	  
	  writer << "\n";
			
	}
		
      writer << "\n";

    }
  
  /* Closing writing stream */
  writer.close();
}

void
outCubeRDG(param_t * params, ProgData * data, double * cube, double * rho, bool * shouldPrint)
{ //PROMOLECULAR TREATMENT

  shouldPrint[0] = shouldPrint[0];	
  /* Building filename */
  ostringstream os;
  os << params->outputName << "-RDG.cube";
	
  /* Opening writing stream */
  ofstream writer(os.str().c_str());
    
  /* Setting stream precision to 6 numbers */
  writer << fixed << setprecision(6) << "RDG Cube" << endl << "Generated by IGMPlot" << endl;
	
    
  /* Writing number of atom and min coordinates */
  writer << data->getNbAtom() << " " << data->getMinCoord(0) << " " 
         << data->getMinCoord(1) << " " << data->getMinCoord(2) << endl;
	
  /* Writing steps and increments by axis */
  writer << data->getNbSteps(0) 	<< 	" " 					
         << params->increments[0]	<<	" 0.000000 0.000000" 	<< endl;
  writer << data->getNbSteps(1)	<<	" 0.000000 " 			
         << params->increments[1] 	<< " 0.000000" 				<< endl;
  writer << data->getNbSteps(2)	<<	" 0.000000 0.000000 " 	<< 	params->increments[2] 								<< endl;

  /* Looping through all atoms  */
  for( int i(0) ; i < data->getNbAtom() ; ++i )
    {
		
      /* Writing both type and coordinates for each atom */
      writer << data->atomTypes[i] + 1 << " 0.0 " << data->atomPositions.xValues[i] 
             << " " << data->atomPositions.yValues[i] << " " << data->atomPositions.zValues[i] << " " << endl;

    }
  rho[0]=rho[0];
	
  /* Writing next values with scientific convention */
  writer << scientific;
	
  /* Index value initilization */
  // NOT REALLY USED
  //int index(0);
	
  /* Looping over the first axis */
  for( int i(0) ; i < data->getNbSteps(0) ; ++i )
    {
		
      /* Looping over the second axis */
      for( int j(0) ; j < data->getNbSteps(1) ; ++j )
	{
			
	  /* Looping over the third axis */
	  for( int k(0) ; k < data->getNbSteps(2) ; ++k )
	    {
				
	      /* Writing value  */
	      writer << ( std::abs(rho[INDEX]) > params->cutplot[0] || !shouldPrint[INDEX] ? 100.0 : cube[INDEX] ) 
                     << " ";
				 
	      /* Updating index */
	      // NOT REALLY USED
	      //++index;
				
	    }
			
	  writer << endl;
			
	}
		
      writer << endl;

    }

  /* Closing writing stream */
  writer.close();

}

void
outCubeRho(param_t * params, ProgData * data, double * cube)
{ //PROMOLECULAR TREATMENT
	

  /* Building filename */
  ostringstream os;
  os << params->outputName << "-dens.cube";
	
  /* Opening writing stream */
  ofstream writer(os.str().c_str());
    
  /* Setting stream precision to 6 numbers */
  writer << fixed << setprecision(6) << "Electron density Cube" << endl << "Generated by IGMPlot" << endl ;
	
    
  /* Writing number of atom and min coordinates */
  writer << data->getNbAtom() << " " << data->getMinCoord(0) << " " << data->getMinCoord(1) << " " << data->getMinCoord(2) << endl;
	
  /* Writing steps and increments by axis */
  writer << data->getNbSteps(0) 	<< 	" " 					<<	params->increments[0]	<<	" 0.000000 0.000000" 	<< endl;
  writer << data->getNbSteps(1)	<<	" 0.000000 " 			<< 	params->increments[1] 	<< " 0.000000" 				<< endl;
  writer << data->getNbSteps(2)	<<	" 0.000000 0.000000 " 	<< 	params->increments[2] 								<< endl;

  /* Looping through all atoms  */
  for( int i(0) ; i < data->getNbAtom() ; ++i )
    {
      
      /* Writing both type and coordinates for each atom */
      writer << data->atomTypes[i] + 1 << " 0.0 " << data->atomPositions.xValues[i] << " " << data->atomPositions.yValues[i] << " " << data->atomPositions.zValues[i] << " " << endl;
      
    }
	
  /* Writing next values with scientific convention */
  writer << scientific;
	
  /* Index value initilization */
  // NOT REALLY USED
  //int index(0);
	
  /* Looping over the first axis */
  for( int i(0) ; i < data->getNbSteps(0) ; ++i )
    {
		
      /* Looping over the second axis */
      for( int j(0) ; j < data->getNbSteps(1) ; ++j )
	{
			
	  /* Looping over the third axis */
	  for( int k(0) ; k < data->getNbSteps(2) ; ++k )
	    {
				
	      /* Writing value  */
	      writer << cube[INDEX] << " ";
				 
	      /* Updating index */
	      // NOT REALLY USED
	      //++index;
				
	    }
			
	  writer << endl;
			
	}
		
      writer << endl;

    }

  /* Closing writing stream */
  writer.close();

}

void
outDat(param_t * params, ProgData * data, Results&	results)
{ //PROMOLECULAR TREATMENT
	
  /* Gathering pointers */
  double * cubeRho		= results.get(TYPE_RHO);
  double * cubeRDG		= results.get(TYPE_RDG);
  double * cubedgIntra  = results.get(TYPE_DELTA_G_INTRA);
  double * cubedgInter	= results.get(TYPE_DELTA_G_INTER);
  bool * shouldPrint	= results.getShouldPrint();
	
  /* Building filename */
  ostringstream os;
  os << params->outputName << "-nci.dat";
	
  /* Opening writing stream */
  ofstream writerNCI(os.str().c_str());
	
  /* Building second filename */
  os.clear();
  os.str("");
  os << params->outputName << "-igm.dat";
	
  /* Opening writing stream */
  ofstream writerIGM(os.str().c_str());
		
  /* Setting stream precision to 6 numbers */
  writerNCI << fixed << setprecision(6);
  writerIGM << fixed << setprecision(6);
	
	
  /* Writing next values with scientific convention */
  writerNCI << scientific;
  writerIGM << scientific;
	

  /* Writing header */
  writerNCI << "   RHO            RDG\n";
  writerIGM << "   RHO            RDG         dg_intra        dg_inter         qg_intra        qg_inter\n";
	
  /* Looping over the first axis */
  for( int i(0) ; i < data->getNbSteps(0) ; ++i )
    {
		
      /* Looping over the second axis */
      for( int j(0) ; j < data->getNbSteps(1) ; ++j )
	{
			
	  /* Looping over the third axis */
	  for( int k(0) ; k < data->getNbSteps(2) ; ++k )
	    {

	      /* Checks if the value is in the cutoffs */
	      if ( std::abs(cubeRho[INDEX]) <= params->cutoffs[0]  && cubeRDG[INDEX] <= params->cutoffs[1])
		{
                       // preliminary calculations to retrieve qg values from known data
                       // since gradnorm, gradrhoIGMIntra and gradrhoIGMInter have not been stored in arrays
                       // it is necessary to retrieve them from mathematical expressions using other arrays
                       double tmprho     = cubeRho[INDEX];
                       double tmpdgInter = cubedgInter[INDEX];  
                       double tmpdgIntra = std::max<double>(cubedgIntra[INDEX], 1e-30); // <-> dgIntra
                       double gradnorm    = cubeRDG[INDEX] * (constValue * std::pow(std::abs(tmprho), FOUR_THIRD));//retrieve gradnorm
                       double gradrhoIGMIntra = tmpdgIntra + gradnorm;
                       double gradrhoIGMInter = tmpdgInter + gradnorm;
                       gradrhoIGMInter = std::max<double>(gradrhoIGMInter, 1e-30);
                       gradnorm = std::max<double>(gradnorm, 1e-30);
                       double qgIntra         = gradrhoIGMIntra/gradnorm;
                       double qgInter         = gradrhoIGMInter/gradnorm;

		  /* Writing value NCI */
		  if(shouldPrint[INDEX]) writerNCI << cubeRho[INDEX] << "  " << cubeRDG[INDEX] << "\n";
				
		  /* Writing value IGM */
		  writerIGM << cubeRho[INDEX] << "  " << cubeRDG[INDEX] << "  ";
                  if (not isdgSCALED()) {  writerIGM << cubedgIntra[INDEX];}
                  else                  {  writerIGM << cubedgIntra[INDEX]/std::abs(cubeRho[INDEX]);}
                  writerIGM << "  ";
                  if (not isdgSCALED()) {  writerIGM << cubedgInter[INDEX];}
                  else                  {  writerIGM << cubedgInter[INDEX]/std::abs(cubeRho[INDEX]);}
                  writerIGM << "    " << qgIntra << "    " << qgInter << std::endl;
				 
				 
		} // if rho in the desired range
	    }
	}
    }

  /* Closing writing streams */
  writerNCI.close();
  writerIGM.close();

  // each time the igm.dat is written, two gnuplot input files are generated as well
  // ================ 2D-dgIntra ================
  os.str("");
  if (not isdgSCALED())
  {os << params->outputName << "-dgIntra.gnu";}
  else
  {os << params->outputName << "-dgIntraS.gnu";}
  std::ofstream datWritergnuIntra(os.str().c_str());
  datWritergnuIntra << "# setting the terminal" << std::endl;
  datWritergnuIntra << "# uncomment the two following lines to generate an postscript image" << std::endl;
  datWritergnuIntra << "#############################################" << std::endl;
  datWritergnuIntra << "# set terminal postscript eps color enhanced \"Helvetica\" 12 size 7 in, 4.9 in" << std::endl;
  datWritergnuIntra << "# set output \"fig.eps\"" << std::endl;
  datWritergnuIntra << "#############################################" << std::endl;
  datWritergnuIntra << "set border lw 1" << std::endl;
  datWritergnuIntra << "set xtics nomirror scale 0.5 out # xtics marks half reduced and outside"  << std::endl;
  datWritergnuIntra << "set xtics format \"%.2f\" " << std::endl;
  datWritergnuIntra << "#set ytics 0.020 nomirror" << std::endl;
  datWritergnuIntra << "set ytics format \"%.2f\""<< std::endl;
  datWritergnuIntra << "set key left font \"Helvetica,14\"" << std::endl;
  datWritergnuIntra << "set xlabel \"sign({/Symbol l}_2){/Symbol r} (a.u.)\" font \"Helvetica,18\" offset 0.0, 0.1" << std::endl;

 if (not isdgSCALED()) {
  datWritergnuIntra << "set ylabel \"{/Symbol d}g (a.u.)\" font \"Helvetica,14\" offset 0.2, 0.0" << std::endl;
 } else {
  datWritergnuIntra << "set ylabel \"{/Symbol d}g/{/Symbol r} (a.u.)\" font \"Helvetica,14\" offset 0.2, 0.0" << std::endl;
 }
  datWritergnuIntra << "# to colour according to the qg value" << std::endl;
  datWritergnuIntra << "set palette rgbformulae 22,13,-31" << std::endl;
  datWritergnuIntra << "set cbrange[1:4]" << std::endl;
  datWritergnuIntra << "set xrange[-0.4:0.1]" << std::endl;
  datWritergnuIntra << "# in igm.dat: column 4 = dgInter, column6 = qgInter" << std::endl;
  datWritergnuIntra << "plot \"" << params->outputName << "-igm.dat\" every 1 u 1:3:5 notitle pointtype 7 ps 0.8 palette" << std::endl;
  datWritergnuIntra << "# comment the following line to generate the postscript image" << std::endl;
  datWritergnuIntra << "pause -1;" << std::endl;
  datWritergnuIntra.close();

  // ================ 2D-dgInter ================
  os.str("");
  if (not isdgSCALED())
  {os << params->outputName << "-dgInter.gnu";}
  else
  {os << params->outputName << "-dgInterS.gnu";}
  std::ofstream datWritergnuInter(os.str().c_str()); 
  datWritergnuInter << "# setting the terminal" << std::endl;
  datWritergnuInter << "# uncomment the two following lines to generate an postscript image" << std::endl;
  datWritergnuInter << "#############################################" << std::endl;
  datWritergnuInter << "# set terminal postscript eps color enhanced \"Helvetica\" 12 size 7 in, 4.9 in" << std::endl;
  datWritergnuInter << "# set output \"fig.eps\"" << std::endl;
  datWritergnuInter << "#############################################" << std::endl;
  datWritergnuInter << "set border lw 1" << std::endl;
  datWritergnuInter << "set xtics nomirror scale 0.5 out # xtics marks half reduced and outside"  << std::endl;
  datWritergnuInter << "set xtics format \"%.2f\" " << std::endl;
  datWritergnuInter << "#set ytics 0.020 nomirror" << std::endl;
  datWritergnuInter << "set ytics format \"%.2f\""<< std::endl;
  datWritergnuInter << "set key left font \"Helvetica,14\"" << std::endl;
  datWritergnuInter << "set xlabel \"sign({/Symbol l}_2){/Symbol r} (a.u.)\" font \"Helvetica,18\" offset 0.0, 0.1" << std::endl;
  if (not isdgSCALED()) {
    datWritergnuInter << "set ylabel \"{/Symbol d}g (a.u.)\" font \"Helvetica,14\" offset 0.2, 0.0" << std::endl;
  } else {
    datWritergnuInter << "set ylabel \"{/Symbol d}g/{/Symbol r} (a.u.)\" font \"Helvetica,14\" offset 0.2, 0.0" << std::endl;
  }
  datWritergnuInter << "# to colour according to the qg value" << std::endl;
  datWritergnuInter << "set palette rgbformulae 22,13,-31" << std::endl;
  datWritergnuInter << "set cbrange[1:4]" << std::endl;
  datWritergnuInter << "set xrange[-0.1:0.07]" << std::endl;
  datWritergnuInter << "# in igm.dat: column 4 = dgInter, column6 = qgInter" << std::endl;
  datWritergnuInter << "plot \"" << params->outputName << "-igm.dat\" every 1 u 1:4:6 notitle pointtype 7 ps 0.8 palette" << std::endl;
  datWritergnuInter << "# comment the following line to generate the postscript image" << std::endl;
  datWritergnuInter << "pause -1;" << std::endl;
  datWritergnuInter.close();

} // end of outDat(: PROMOLECULAR MODE


// PROMOLECULAR MODE:
void
outPercent(param_t * params, Results &results, ProgData * data)
{
// write atomic percentage  to AtContribInter.dat file (to be used by VMD later on)
	
  /* Building filename */
  ostringstream os;
  os << params->outputName << "-AtContribInter.dat";
	
  /* Opening writing stream */
  ofstream writer(os.str().c_str());

  // calculate the total sum for each fragment:
  // remember that in Promolecular mode, atoms are necessary ordered:
  // Fragment A first, and subsequently atoms of Fragment B:
  double totalFragA = 0.0;
  double totalFragB = 0.0;

  // compute elementary volume of the rectangular grid 
  // noticed that total and Results::DGSIE have not yet been multiplied by dv
  double dv = params->increments[0] * params->increments[1] * params->increments[2];

//  for (unsigned int i=0; i<nbAtoms; ++i)
    for (int i=0; i<data->getNbAtomMolA(); ++i)
    {
          totalFragA = totalFragA + results.get(i, Results::DGSIE)*dv; 
    }
    for (int i=data->getNbAtomMolA(); i<data->getNbAtom(); ++i)
    {     
          totalFragB = totalFragB + results.get(i, Results::DGSIE)*dv; 
    }

    if ( (totalFragA != 0) and (totalFragB != 0) )
  {
       /* header */
       writer << "  Atom           deltagInterAtomicContrib      Percent   Fragment" << endl;
     
          // compute elementary volume of the rectangular grid 
          // noticed that total and Results::DGSIE have not yet been multiplied by dv
          double dv = params->increments[0] * params->increments[1] * params->increments[2];
     
          /* Looping through all atoms of FRAGMENT A first */
          for( int i(0) ; i < data->getNbAtomMolA(); ++i )
            {
              /* Writing participation percentage for each atom */
              std::string  atomName = getAtomName(data->atomTypes[i]);
              writer << std::setw(5) << std::right << i+1;
              writer << "   " << std::setw(2) << atomName <<  "               ";
              writer << std::setw(5) << fixed << setprecision(4) << std::right;
              writer << results.get(i, Results::DGSIE)*dv << "              ";
              writer << std::setw(8) << fixed << setprecision(4) << std::right;
              writer << ( ( results.get(i, Results::DGSIE)*dv / totalFragA ) * 100.0 );
              writer << "   " << std::setw(8);
              writer << "1" << std::endl;
            } // end over atoms of FRAGA

          /* Looping through all atoms of FRAGMENT B next */
          for( int i(data->getNbAtomMolA()) ; i < data->getNbAtom(); ++i )
            {
              /* Writing participation percentage for each atom */
              std::string  atomName = getAtomName(data->atomTypes[i]);
              writer << std::setw(5) << std::right << i+1;
              writer << "   " << std::setw(2) << atomName  << "               ";
              writer << std::setw(5) << fixed << setprecision(4) << std::right;
              writer << results.get(i, Results::DGSIE)*dv << "              ";
              writer << std::setw(8) << fixed << setprecision(4) << std::right;
              writer << ( ( results.get(i, Results::DGSIE)*dv / totalFragB ) * 100.0 );
              writer << "   " << std::setw(8);
              writer << "2" << std::endl;
            } // end over atoms of FRAGB

    } // end of     if ( (totalFragA != 0) and (totalFragB != 0) )
   else
    {
      writer << "       No INTER interactions detected ..." << endl;
    }

  
  /* Closing writing stream */
  writer.close();
} // end of outPercent PROMOLECULAR MODE


// QM MODE
void outPercentQM(param_t params, ProgData *data, double *dgInterAtSum, double dv, unsigned int nbAtoms) // WFX/N mode
{
// write atomic percentage  to AtContribInter.dat file (to be used by VMD later on)
	
  /* Building filename */
  ostringstream os;
  os << params.outputName << "-AtContribInter.dat";
	
  /* Opening writing stream */
  ofstream writer(os.str().c_str());
    
  /* header */
  writer << "  Atom           deltagInterAtomicContrib      Percent   Fragment" << endl;

  // calculate the total sum:
  double totalFragA = 0.0;
  double totalFragB = 0.0;
  for (unsigned int i=0; i<nbAtoms; ++i)
    {
      if (isInMoleculeA(i+1))  // isImoleculeA takes a 1-based numbered atom index parameter
       {
          totalFragA = totalFragA + dgInterAtSum[i];
       }
      else if (isInMoleculeB(i+1)) 
       {
          totalFragB = totalFragB + dgInterAtSum[i];
       }
    }

//  if (total != 0) 
    if ( (totalFragA != 0) and (totalFragB != 0) )
  {
     /* Looping through all atoms */
     unsigned int* user2ADF = wf_user2ADF_atomOrder();
     for (unsigned int iatUser=0; iatUser<nbAtoms; ++iatUser)
     {
   
         // First, convert the atomic user numering to the WF atomic numbering
         unsigned int iat;
         if (isRKFmodeActivated()) { // ADF requires a specific treatment
           iat = user2ADF[iatUser]-1;  // user2ADF takes a user_0_based atomic index and returns a WF_1_based atomic index
         } else {iat = iatUser;} // in WFX and WFN, user atom numbering is preserved in WF data


      /* Writing participation percentage for each atom */
      std::string  atomName = getAtomName(data->atomTypes[iat]);
      writer << std::setw(5) << std::right << iatUser+1;
      writer << "   " << std::setw(2) << atomName <<  "               ";
      writer << std::setw(5) << std::fixed << setprecision(4) << std::right;
      writer << dgInterAtSum[iat] << "              ";
      writer << std::setw(8) << std::fixed << setprecision(4) << std::right;
      if (isInMoleculeA(iat+1)) // isImoleculeA takes a 1-based numbered atom index parameter
       {
         writer << ( (  dgInterAtSum[iat] / totalFragA ) * 100.0 );
       }
      else if (isInMoleculeB(iat+1)) // isImoleculeB takes a 1-based numbered atom index parameter
       {
         writer << ( (  dgInterAtSum[iat] / totalFragB ) * 100.0 );
       }
      else {
         writer << (  dgInterAtSum[iat] * 0.0 ); // atom not belonging to fragA and B
       }
       writer << "   " << std::setw(8); 
      if (isInMoleculeA(iat+1)) 
      {
       writer << "1";
      } else if (isInMoleculeB(iat+1))
      {
       writer << "2";
      }
      writer << std::endl;



      
     } // end of for over atoms
   } // end of if ( (totalFragA != 0) and (totalFragB != 0) )
   else 
    {
      writer << "       No INTER interactions detected ..." << endl;
    }
  
  /* Closing writing stream */
  writer.close();
} // end of outPercentQM Quantum Mecha. Mode


string getWorkPath()
{
  char buffer[FILENAME_MAX];
  //string dir(getDir(buffer, FILENAME_MAX));
  string dir(getcwd(buffer, FILENAME_MAX));
  dir += "/";
  
  //printf("Working   Path: %s\n",dir.c_str());
  
  return dir;
}


void
outVMD(param_t * params, ProgData * data, double max, double deltagIntraMaximumValue, double deltagInterMaximumValue, double dgAtWeakMaxValue)
{ // ==================  PROMOLECULAR MODE ================== //

// this routine is called in two cases: OUTPUT type 4 or 5
  
  /* Getting current paths */
//  string currWork = getWorkPath();
  string currWork = "";


  /**************************************/
  /*******COMPLEX FILE GENERATION********/
  /**************************************/

 // only if fragmentation scheme is enabled
 if ( not ( (data->getNbAtomMolA() == 0)  or  (data->getNbAtomMolB() == 0) ) ) 
  {

  /* Opening molecule related streams */
  ifstream molA(params->molAFileName.c_str());
  ifstream molB(params->molBFileName.c_str());
  ofstream comp(std::string(params->outputName+"-coord.xyz").c_str());
	

  if(molA.good() && comp.good() && (  ( !params->molBFileName.empty() && molB.good() ) || params->molBFileName.empty()  )  )
    {
	
      { // Printing number of atoms
			
	int temp, total(0);
	
	/* Getting number of atom */
	molA >> temp;
	total+=temp;
	if (!params->molBFileName.empty())
	  {
	    molB >> temp;
	    total+=temp;
	  }
				
	comp << "  " << total << endl << endl;
				
				
	/* Ignoring comment lines */
	string line;
	std::getline(molA, line);
	std::getline(molB, line);
	/* No valid without -std=c++11
	 * molA.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
	 * molA.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); */
	if (!params->molBFileName.empty())
	  {
	    std::getline(molA, line);
	    std::getline(molB, line);
	    /* No valid without -std=c++11
	     * molB.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
	     * molB.ignore(std::numeric_limits<std::streamsize>::max(), '\n');*/
	  }
			
      } // end of Printing number of atoms
		
      { // Printing molecules' atoms' coordinates
		
	string symbol;
	double posX, posY, posZ;
	
	/* Handling molecule A */
	while(molA >> symbol)
	  {
			
	    /* Printing atom's symbol */
	    comp << " " << symbol << "        ";
	    
	    /* Getting atom's position */
	    molA >> posX;
	    molA >> posY;
	    molA >> posZ;
	    
	    /* Printing atom's position */
	    comp << posX << "        ";
	    comp << posY << "        ";
	    comp << posZ << endl;
	    
	  }
			
			
	/* Handling molecule B if it is not empty */
	if (!params->molBFileName.empty())
	  {
	    while(molB >> symbol)
	      {
			
		/* Printing atom's symbol */
		comp << " " << symbol << "        ";
		
		/* Getting atom's position */
		molB >> posX;
		molB >> posY;
		molB >> posZ;
		
		/* Printing atom's position */
		comp << posX << "        ";
		comp << posY << "        ";
		comp << posZ << endl;
		
	      }  // end of while(molB >> symbol)
	  } // end of if (!params->molBFileName.empty())
	
      } // end of Printing molecules' atoms' values
		
    } // end of if(molA.good() && comp.good() && (  ( !params->molBFileName.empty() && molB.good() ) || params->molBFileName.empty()  )  )
  else
    {
      
      cerr << "[ERROR] Problem while opening streams." << endl;
      cerr << "[ERROR] Make sure the program has the necessary rights." << endl;
      cerr << "[ERROR] The program will now exit." << endl;
      exit(EXIT_FAILURE);	
    }
	
  /* Closing streams */
  molA.close();
  molB.close();
  comp.close();	

} // end of if fragmentation scheme


  /**************************************/
  /********VMD SCRIPT GENERATION*********/
  /**************************************/
  // PROMOL MODE
    
    
  string rep[SIZE_KEYWORDS];
  ostringstream oss;
  oss << currWork << params->outputName << "-dgInter.cube";
  rep[DGINTER]= oss.str();
  oss.str("");

  oss << currWork << params->outputName << "-dgIntra.cube";
  rep[DGINTRA]= oss.str();
  oss.str("");

  oss << currWork << params->outputName << "-dens.cube";
  rep[DENS] = oss.str();
  oss.str("");

  oss << currWork  << params->outputName <<  "-RDG.cube";
  rep[RDG]  = oss.str();
  oss.str("");

  oss << currWork <<  params->outputName << "-AtContribInter.dat";
  rep[PERCENT] = oss.str();
  oss.str("");

  oss << currWork << params->outputName << "-coord.xyz";
  rep[COMPLEX] = oss.str();
  oss.str("");

  oss << params->cutplot[0];
  rep[CUTPLOT1] = oss.str();
  oss.str("");

  oss << params->cutplot[1];
  rep[CUTPLOT2] = oss.str();
  oss.str("");
  
  oss << params->cutplotIGM[0];
  rep[CUTPLOTIGM1] = oss.str();
  oss.str("");

  oss << params->cutplotIGM[1];
  rep[CUTPLOTIGM2] = oss.str();
  oss.str("");

  //The isovalue is defined as 40% (ratiopeak) of the maximum peak height of the signature
  deltagIntraMaximumValue*=ratioPeak;
  deltagInterMaximumValue*=ratioPeak;
  
  oss << deltagIntraMaximumValue;
  rep[CUTPLOTIGM3] = oss.str();
  oss.str("");

  oss << deltagInterMaximumValue;
  rep[CUTPLOTIGM4] = oss.str();
  oss.str("");

  oss << params->vmdcolrangIGM[0];
  rep[VMDCOLRANGIGM1] = oss.str();
  oss.str("");

  oss << params->vmdcolrangIGM[1];
  rep[VMDCOLRANGIGM2] = oss.str();
  oss.str("");

  oss << params->cutoffs[0];
  rep[CUTOFFS1] = oss.str();
  oss.str("");

  oss << params->cutoffs[1];
  rep[CUTOFFS2] = oss.str();
  oss.str("");

//  oss << data->getNbAtomMolA() -1;
//  rep[LAST_LIG_INDEX] = oss.str();
  oss << data->getNbAtomMolA();
  rep[FRAG1Def] = "serial 1 to " + oss.str(); // serial <-> 1-based atom index
  oss.str("");

//  oss << data->getNbAtomMolA();
//  rep[FIRST_PROT_INDEX] = oss.str();
//  oss.str("");
//  oss << data->getNbAtom() -1;
//  rep[LAST_COMPLEX_INDEX] = oss.str();
  string tmp;
  oss << data->getNbAtomMolA()+1;
  tmp = "serial " + oss.str(); // serial <-> 1-based atom index
  oss.str("");
  oss << data->getNbAtom();
  tmp = tmp + " to " + oss.str();
  rep[FRAG2Def] = tmp; 
  oss.str("");

  oss << max;
  rep[MAX_PERCENT] 	= oss.str();
  oss.str("");

  oss << currWork <<  params->outputName << "-AtomDOI.dat";
  rep[ATOMDOIDAT] = oss.str();
  oss.str("");

  oss << currWork <<  params->outputName << ".xyz";
  rep[XYZ] = oss.str();
  oss.str("");

  oss << dgAtWeakMaxValue;
  rep[DGATWEAKMAX] = oss.str();
  oss.str("");

  if ( not ( (data->getNbAtomMolA() == 0)  or  (data->getNbAtomMolB() == 0) ) ) // fragmentation scheme enabled
     {generateVMDAtContribInterFile(rep, false);} // PROMOLECULAR MODE

  if (params->outputType==5) 
   {
     generateVMDigmFile(rep);
     generateVMDnciFile(rep);
   }
  
	  
}// end of outVMD =====  PROMOLECULAR MODE ================== //


// ===================== QM MODE ===============================
void
writeVMDfiles(param_t * params, ProgData * data, double deltagIntraMaximumValue, 
              double deltagInterMaximumValue, double dgAtWeakMaxValue,
              std::vector<criticalpoint>  cpList, double max, double selfmaxrange)
{ // ==================  QM MODE ================== //
	
  /* Getting current paths */
// MODIF ERIC 06/2024
//  string currWork = getWorkPath();
    string currWork =""; // VMD is now able to deal with relative path
	
  /**************************************/
  /********VMD SCRIPT GENERATION*********/
  /**************************************/

  // =========================================================================== //
  //                         f i l l    k e y w o r d s                          //
  //                         Q M        M O D E
  // =========================================================================== //
  string rep[SIZE_KEYWORDS];
  ostringstream oss;

  if ( (not isPauli()) and (not isdgSCALED()) )
  {
     oss << currWork << params->outputName << "-dgInter.cube";
     rep[DGINTER]= oss.str();
     oss.str("");

     oss << currWork << params->outputName << "-dgIntra.cube";
     rep[DGINTRA]= oss.str();
     oss.str("");
  }
  else if ( isPauli() or isdgSCALED() )
  {
     oss << currWork << params->outputName << "-dgInterS.cube";
     rep[DGINTER]= oss.str();
     oss.str("");

     oss << currWork << params->outputName << "-dgIntraS.cube";
     rep[DGINTRA]= oss.str();
     oss.str("");
  }


  oss << currWork << params->outputName << "-dens.cube";
  rep[DENS] = oss.str();
  oss.str("");

  oss << currWork  << params->outputName <<  "-RDG.cube";
  rep[RDG]  = oss.str();
  oss.str("");

  oss << currWork  << params->outputName <<  "-ELF.cube";
  rep[ELFcube]  = oss.str();
  oss.str("");

  oss << currWork  << params->outputName <<  "-ELF_IGMcolor.cube";
  rep[ELF_IGMcolor]  = oss.str();
  oss.str("");

  oss << elfIsovalue;
  rep[ELF_isovalue] = oss.str();
  oss.str("");


// ======== P A U L I ======================================

  oss << currWork  << params->outputName <<  "-SELF.cube";
  rep[SELFcube]  = oss.str();
  oss.str("");

  oss << currWork  << params->outputName <<  "-selfatom.dat";
  rep[SELFATOMDAT]  = oss.str();
  oss.str("");

  oss << deltagInterMaximumValue; 
  rep[SELFMAX]  = oss.str();
  oss.str("");

  oss << deltagIntraMaximumValue;
  rep[SELFMIN]  = oss.str();
  oss.str("");


  oss << dgAtWeakMaxValue;
  rep[DGSCALEDMAX]  = oss.str();
  oss.str("");

  if (max == 0.0) {
    rep[isSELFAtomic]="FALSE";
  }
  else {
    rep[isSELFAtomic]="TRUE";
  }

  oss << selfmaxrange;
  rep[SELFMAXCOLOR]  = oss.str();
  oss.str("");


// =========================================================




  oss << currWork <<  params->outputName << "-AtContribInter.dat";
  rep[PERCENT] = oss.str();
  oss.str("");

  oss << currWork << params->outputName << "-coord.xyz";
  rep[COMPLEX] = oss.str();
  oss.str("");

  oss << params->cutplot[0];
  rep[CUTPLOT1] = oss.str();
  oss.str("");

  oss << params->cutplot[1];
  rep[CUTPLOT2] = oss.str();
  oss.str("");
  
  oss << params->cutplotIGM[0];
  rep[CUTPLOTIGM1] = oss.str();
  oss.str("");

  oss << params->cutplotIGM[1];
  rep[CUTPLOTIGM2] = oss.str();
  oss.str("");
  
  //The isovalue is defined as 40% (ratiopeak) of the maximum peak height of the signature
  deltagIntraMaximumValue*=ratioPeak;
  deltagInterMaximumValue*=ratioPeak;
  
  oss << deltagIntraMaximumValue;
  rep[CUTPLOTIGM3] = oss.str(); // can also represent the upperlimit of pauli range to color dg/rho iso-surfaces
  oss.str("");

  oss << deltagInterMaximumValue;
  rep[CUTPLOTIGM4] = oss.str();
  oss.str("");

  oss << params->vmdcolrangIGM[0];
  rep[VMDCOLRANGIGM1] = oss.str();
  oss.str("");

  oss << params->vmdcolrangIGM[1];
  rep[VMDCOLRANGIGM2] = oss.str();
  oss.str("");

  oss << params->cutoffs[0];
  rep[CUTOFFS1] = oss.str();
  oss.str("");

  oss << params->cutoffs[1];
  rep[CUTOFFS2] = oss.str();
  oss.str("");

//  oss << data->getNbAtomMolA();
//  rep[FIRST_PROT_INDEX] = oss.str();
//  oss.str("");

string descA = getCondensedDescriptionOfMoleculeA();
string descB = getCondensedDescriptionOfMoleculeB();
string atomSelectA = convertToVMDAtomSelect(descA);
string atomSelectB = convertToVMDAtomSelect(descB);


//  oss << data->getNbAtom() -1;
//  rep[LAST_COMPLEX_INDEX] = oss.str();
  oss << atomSelectA; 
  rep[FRAG1Def] = oss.str();
  oss.str("");
  
//  oss << data->getNbAtomMolA() -1;
//  rep[LAST_LIG_INDEX] = oss.str();
  oss << atomSelectB;
  rep[FRAG2Def] = oss.str();
  oss.str("");

  oss << max;
  rep[MAX_PERCENT]      = oss.str();
  oss.str("");

  oss << currWork <<  params->outputName << "-AtomDOI.dat"; // note that this files also contains
                                                            // atomic contributions to weak interactions
  rep[ATOMDOIDAT] = oss.str();
  oss.str("");

  oss << currWork <<  params->outputName << "-coord.xyz";
  rep[XYZ] = oss.str();
  oss.str("");

  oss << dgAtWeakMaxValue;
  rep[DGATWEAKMAX] = oss.str();
  oss.str("");


  if ( (not isCriticmodeActivated()) and (not isELFmodeActivated()) and (not isPauli()) )
    {
      generateVMDigmFile(rep);  
      generateVMDnciFile(rep);
    
      if ( (data->getNbAtomMolA() == 0)  or  (data->getNbAtomMolB() == 0) ) // no fragmentation scheme
        {generateVMDAtomDOIFile(rep);} // note that the DOI atomic value are not used here,
                                       // but rather the atomic contributions to weak interactions
                                       // when no fragment are defined; these values 
                                       //are also stored in the DOI.dat file ...
      else  // fragmentation scheme enabled within QM MODE
       {generateVMDAtContribInterFile(rep, true);} // here, atomic contributions to 
                                                   // the intermolecular interaction
                                                   // will be colored when two fragments are defined

    } // end of if (not isCriticmodeActivated())

  // if critical points have been requested and found:
  else if (isCriticmodeActivated())
    {
       if (cpList.size()>0) {generateVMDCriticPointsFile(rep, cpList);}
    }

  // ELF mode
  else if (isELFmodeActivated())
    {
       generateVMDelfFile(rep);
    }

  // PAULI mode
  else if (isPauli())
    {
       generateVMDPAULIFile(rep);
    }

} // end of writeVMDfiles

void writeXYZ(param_t &params, ProgData *data, unsigned int nbAtoms, positions_t &atomCoordinates)
{
      // New ostringstream 
      std::ostringstream os;

      /* Building filename and Opening writing stream */
      os << params.outputName << "-coord.xyz";
      std::ofstream xyzWriter(os.str().c_str());

     // first, generate the XYZ molecular file: needed for AtomDOI.vmd and criticalpoint.vmd
     // Header and coordinates
     xyzWriter << " " << nbAtoms <<  std::endl;
     xyzWriter << std::endl;
     unsigned int* user2ADF = wf_user2ADF_atomOrder();
     for (unsigned int iatUser=0; iatUser<nbAtoms; ++iatUser)
        {
            // First, convert the atomic user numering to the WF atomic numbering
            unsigned int iat;
            if (isRKFmodeActivated()) { // ADF requires a specific treatment
                iat = user2ADF[iatUser]-1;  // user2ADF takes a user_0_based atomic index and returns a WF_1_based atomic index
            } else {iat = iatUser;} // in WFX and WFN, user atom numbering is preserved in WF data
            std::string  atomName = getAtomName(data->atomTypes[iat]);
            xyzWriter << std::right << std::setw(2) << atomName;
            xyzWriter << std::fixed << std::setprecision(9);
            xyzWriter << std::setw(13) << atomCoordinates.xValues[iat]*BOHRTOA;
            xyzWriter << std::setw(13) << atomCoordinates.yValues[iat]*BOHRTOA;
            xyzWriter << std::setw(13) << atomCoordinates.zValues[iat]*BOHRTOA << std::endl;

        } // end of loop over atoms

     xyzWriter.close();
} // end of routine  writeXYZ  

void writeCPTXT(param_t &params, std::vector<criticalpoint>  cpList) 
{
            // Building filename 
            std::ostringstream os;
            os.str("");
            os << params.outputName << "-cp.txt";

            // Opening writing stream 
            std::ofstream cpWriter(os.str().c_str());

            // Header
            cpWriter << "CP   Signat.    X       Y       Z       Rho      GradRho     dg      qg      L1      L2      L3    Laplacian       G       V          H     Ellipticity (3,-1)" << std::endl;
            cpWriter << "              (Angs)  (Angs)  (Angs) (e-/bohr^3) ..(e-/bohr^4)..           ......(e-/bohr^5)..... (e-/bohr^5) .......Hartree/bohr^3........ (L1/L2 - 1.0)      " << std::endl;

            cpWriter << "__________________________________________________________________________________________________________________________________________________________" << std::endl;

                 if (cpList.size() >0)
                    {

                      for(unsigned int icp = 0; icp < cpList.size(); icp++)
                        {  // CP number
                           cpWriter << std::left  << std::setw(4) << icp+1 << " ";
                           
                           // CP Signature 
                           if (cpList[icp].type < 0)
                              {
                           cpWriter << "(3,";
                           cpWriter << std::right << std::setw(2);
                           cpWriter << cpList[icp].type << ")"; 
                              }
                           else 
                              {
                           cpWriter << "(3,+";
                           cpWriter << std::right << std::setw(1);
                           cpWriter << cpList[icp].type << ")";
                              }

                           // CP xyz
                           double x = cpList[icp].pos[0]*BOHRTOA;
                           double y = cpList[icp].pos[1]*BOHRTOA;
                           double z = cpList[icp].pos[2]*BOHRTOA;

                           cpWriter << std::right << std::setw(8);
                           cpWriter << std::fixed << std::setprecision(3);
                           cpWriter << x;   

                           cpWriter << std::right << std::setw(8);
                           cpWriter << std::fixed << std::setprecision(3);
                           cpWriter << y; 

                           cpWriter << std::right << std::setw(8);
                           cpWriter << std::fixed << std::setprecision(3);
                           cpWriter << z << " ";

                           // CP Rho 
                           cpWriter << std::right << std::setw(12);
                           cpWriter << std::scientific << std::setprecision(5);
                           cpWriter << cpList[icp].RHO;

                           // CP gradRho magnitude
                           cpWriter << std::right << std::setw(9);
                           cpWriter << std::scientific << std::setprecision(1);
                           cpWriter << cpList[icp].GRADRHO;

                           // CP dg 
                           cpWriter << std::right << std::setw(8);
                           cpWriter << std::fixed << std::setprecision(3);
                           cpWriter << cpList[icp].dg;

                           // CP qg                
                           cpWriter << std::right << std::setw(10);
                           cpWriter << std::scientific << std::setprecision(2);
                           cpWriter << cpList[icp].qg;


                           // L1,L2,L3            
                           cpWriter << std::right << std::setw(7);
                           cpWriter << std::fixed << std::setprecision(3);
                           cpWriter << cpList[icp].L123[0] << " ";

                           cpWriter << std::right << std::setw(7);
                           cpWriter << std::fixed << std::setprecision(3);
                           cpWriter << cpList[icp].L123[1] << " ";

                           cpWriter << std::right << std::setw(7);
                           cpWriter << std::fixed << std::setprecision(3);
                           cpWriter << cpList[icp].L123[2];

                           // Laplacian           
                           cpWriter << std::right << std::setw(10);
                           cpWriter << std::fixed << std::setprecision(5);
                           cpWriter << cpList[icp].laplac << " ";

                           // G, H, V             
                           cpWriter << std::right << std::setw(11);
                           cpWriter << std::scientific << std::setprecision(3);
                           cpWriter << cpList[icp].G;


                           cpWriter << std::right << std::setw(11);
                           cpWriter << std::scientific << std::setprecision(3);
                           cpWriter << cpList[icp].V;

                           cpWriter << std::right << std::setw(11);
                           cpWriter << std::scientific << std::setprecision(3);
                           cpWriter << cpList[icp].H << "  ";

                           //  Ellipticity for (3,-1) bcp
                           if (cpList[icp].type == -1)
                             {
                           cpWriter << std::right << std::setw(9);
                           cpWriter << std::fixed << std::setprecision(4);
                           cpWriter << (cpList[icp].L123[0]/cpList[icp].L123[1] -1.0);
                             }

                           cpWriter << std::endl;
                        } // end over cp
                    } // end of if (cpList.size() >0)
                 else
                    {
                      cpWriter << " ---- No critical point found ----" << std::endl;
                    }
                 cpWriter << "__________________________________________________________________________________________________________________________________________________________" << std::endl;
             cpWriter.close();
} // end of writeCPTXT routine

void writeAtomDOI(param_t params, ProgData *data, double *dgAtSum, double *dgAtSumW, 
                  double *dgAtSumW2, double dv, unsigned int nbAtoms)
{
   // =================  AtomDOI.dat ==================================== //
   // writing atomic decomposition result on AtomDOI.dat file
       /* Building filename */
      std::ostringstream os; 
      os << params.outputName << "-AtomDOI.dat";
      /* Opening writing stream */
      std::ofstream nccWriter(os.str().c_str());
      nccWriter << "Atom    Element    Weak_dgSum   Strong_Weak_Ovlap      DOI    "  <<  std::endl;
      unsigned int* user2ADF = wf_user2ADF_atomOrder();
      for (unsigned int iatUser=0; iatUser<nbAtoms; ++iatUser)
          {
              // First, convert the atomic user numering to the WF atomic numbering
              unsigned int iat;
              if (isRKFmodeActivated()) { // ADF requires a specific treatment
                  iat = user2ADF[iatUser]-1;  // user2ADF takes a user_0_based atomic index and returns a WF_1_based atomic index
              } else {iat = iatUser;} // in WFX and WFN, user atom numbering is preserved in WF data

              dgAtSum[iat]    = dgAtSum[iat]     * dv;
              dgAtSumW[iat]   = dgAtSumW[iat]    * dv;
              dgAtSumW2[iat]  = dgAtSumW2[iat]   * dv;
              // compute the derivative of the dgAtSumW function according to the
              // ED threshold chosen to separate cov from NCC
              double atDeriv = (dgAtSumW2[iat] - dgAtSumW[iat]) / EDepsilon;
              //    retrieve the current atom name
              std::string  atomName = getAtomName(data->atomTypes[iat]);
              nccWriter << std::left << std::fixed << std::setprecision(6);
              nccWriter << std::setw(5) << iatUser+1 << "   " << std::setw(2) << atomName << "         "  << dgAtSumW[iat];
              nccWriter << "         "  << atDeriv;
              nccWriter << "         "  << std::left << std::fixed << std::setprecision(6) << dgAtSum[iat] <<  std::endl;
          } // end over atoms
       nccWriter.close();
} // end of writeAtomDOI() routine

void writeSelfAtomDat(param_t params, ProgData *data, double *pauliAT,unsigned int nbAtoms)
{
   // =================  selfatom.dat =================================== //
   // writing atomic decomposition of the SELF analysis 
       /* Building filename */
      std::ostringstream os; 
      os << params.outputName << "-selfatom.dat";
      /* Opening writing stream */
      std::ofstream selfWriter(os.str().c_str());
      selfWriter << "Atom                    SELF_Contribution    Fragment" << std::endl;

      // for ADF users, user atomic numbering to recover ...
      unsigned int* user2ADF = wf_user2ADF_atomOrder();

      // loop over atoms to write results
      for (unsigned int iatUser=0; iatUser<nbAtoms; ++iatUser)
          {
              // First, convert the atomic user numering to the WF atomic numbering
              unsigned int iat;
              if (isRKFmodeActivated()) { // ADF requires a specific treatment
                  iat = user2ADF[iatUser]-1;  // user2ADF takes a user_0_based atomic index and returns a WF_1_based atomic index
              } else {iat = iatUser;} // in WFX and WFN, user atom numbering is preserved in WF data

              std::string  atomName = getAtomName(data->atomTypes[iat]);
              selfWriter << std::left << std::fixed << std::setprecision(6);
              selfWriter << std::setw(5) << iatUser+1 << "   " << std::setw(2) << atomName << "         ";
              selfWriter << std::setw(15) << std::fixed << setprecision(2) << std::right;
              selfWriter << pauliAT[iat]<<  "              ";
              if (isInMoleculeA(iat+1))
              {
               selfWriter << "1";
              } else if (isInMoleculeB(iat+1))
              {
               selfWriter << "2";
              }
              selfWriter << std::endl;

          } // end over atoms
       selfWriter.close();
} // end of writeAtomDOI() routine
#endif
