/*
 * Copyright University of Reims Champagne-Ardenne
 * Authors and Contributors: Akilan RAJAMANI, Corentin LEFEBVRE, Johanna KLEIN, Hugo Roussel,
 *                           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 NCISolver.cpp
 * @brief Implementation of NCISolver.h definition */

#ifndef _NCI_SOLVER_CPP_
#define _NCI_SOLVER_CPP_

// LOCAL
#include <NCISolver.h>
#include <sys/time.h>

/* ============================  C O N S T R U C T O R    o f   N C I S o l v e r  =================================== */

// an object NCISolver is created and initialized with param.igm
NCISolver::NCISolver (const char *parameterFileName, bool logParam):
log (logParam),
nbThreads (1)
{

#ifndef No_OpenMP
#pragma omp parallel
  {
#pragma omp single
    nbThreads = omp_get_num_threads ();
  }
#endif

  //JC : initialization of LI structure in a classical C way
  initializeLI ();
  initializeL3();

  /* Read the content of the file passed in argument of the function's call */
  // params : member of NCISolver --> results of readParam
  readParam (parameterFileName, &params);

  /* verify the compatibility of keywords */
  checkKeywords (&params);

  if (isWFmodeActivated ())
/* ==========================  Q M   P R E P A R A T I O N  ========================================================== */
    {

      // read the WFN(or WFX or RKF) input file; among others, cartesian coordinates are read in bohr
      wf_load_file (params.molAFileName);

      if (!wf_file_already_loaded ())
	{
	  std::cerr << std::endl;
	  std::cerr << "[ERROR] Could not open file " << params.
	    molAFileName << std::
	    endl << "[ERROR] The program will now exit." << std::endl;
	  exit (EXIT_FAILURE);
	}


      // no ligand concept in QM treatment
      params.numLigand = -1; // number of fragments

      //    MoleculeA= FRAG1 in the molecular system
      //    convert the FRAG1 string given by the user into a list of atom indexes defining the FRAG1 fragment (moleculeAatoms vector)
      //    and accordingly the FRAG2 system (default = remainder)
      //    Note that if FRAG1 and FRAG2 are not defined : FRAG1 = the whole system, FRAG2 = 0.
      //    in the IBSI treatment, no FRAG1/2 can be passed by the user : FRAG1 = the whole system (see analyseSetFRAG1FRAG2)
      analyseSetFRAG1FRAG2 (getMoleculeAatomDefinition (), // string definition of FRAG1 (user defined)
			    getMoleculeBatomDefinition (), // string definition of FRAG2 (user defined)
			    wf_number_of_nuclei (), &params);

      // set the npri property to the size of the basis set read from the WFN/WFX
      npri    = wf_number_of_primitives(); // field of NCISolver

      // set of MOs expressions read from WFN/WFX
      molecularOrbitals=wf_molecular_orbitals(); // field of NCISolver

      // set up the accuracy for primitive calculations (J. Pilme procedure)
      // default is maxElecAccurlev1
      if (isfullAOAccActivated()) {maxElectron = maxElecAccurlev4;}
      else if (isCriticmodeActivated()) {maxElectron = maxElecAccurlev3;} // for CRITIC calculation 
                                                                          // enhance the accuracy for primitive calculations
      else if (isIBSImodeActivated ()) {maxElectron = maxElecAccurlev2;} // for IBSI calculation
      

      // find the maximum primitive coefficient accross the MOs
      // this procedure is common to every QM calculation and must be performed prior any QM calculation
      maxc=new double[npri]; // field of NCISolver
      findmaxc();


      // set the list of primitives to be used for the calculation
      // primitives are filtered according to either te fragment definitions (CUBE mode)
      // or within a cutoff of each atom pair (IBSI MODE)
      if (not isIBSImodeActivated ())	// QM - CUBE
	{			// set the prim list and the arrays to define the membership of each primitive
	  //  according to fragment A or B
	  setPrimList ();
	}
      else			// QM CYLINDERS   
	{			// set the prim list according to the possible cutoff around each bond
	  /*========== S P E E D  U P  T H E   C O D E   W I T H   C U T O F F   o n    P R I M I T I V E S ========*
           *   ! limiting calculations to those primitives in a radius of r angstroms
           *   ! around both atoms of the considered bond  
           *   ! Build the list of atoms to be considered for each bond */
	  setIBSIPrimList ();
	}

      // create a new ProgData object: atomTypes, atom positions, nbStep, max, min
      //data = new ProgData(wfn_getMoleculeAnbAtoms() ,  wfn_getMoleculeBnbAtoms() , params.numLigand, getChosenBonds().size());
      
      // QM mode:	
      data = new ProgData (wfn_getMoleculeAnbAtoms (),
			   wfn_getMoleculeBnbAtoms (),
			   wf_number_of_nuclei (), params.numLigand,
			   getChosenBonds ());

      //  get the centers information already read before
      //  all the center are recorded (no concept of fragments here)
      centerData *wfnReadCenters = wf_center_data ();

      // loop over all the atoms of the system to fill the data object of NCISolver
      // MIN and MAX coordinate of the gridBox are determined here
      // these MIN and MAX can be adjusted after if CUBE or RADIUS keywords have been used 
      // or will be definitely refined in the validate routine of ProData
      // by adding a buffer around the box (default = 3 angstroms)
      // Note that 'i' index refer to wf numbering : not the user numbering for ADF/RKF !!
      for (unsigned int i = 0; i < wf_number_of_nuclei (); i++)
	{
	  data->addAtom (conversionAtom (*(wfnReadCenters[i].element)), // convert element string name to Z-1
			 wfnReadCenters[i].coordinates[0],        // coordinate from WFN normally in bohr
			 wfnReadCenters[i].coordinates[1],
			 wfnReadCenters[i].coordinates[2], false, true, isHirshmodeActivated());

	}			// end of loop over atoms

      /* If CUBE option has been used */
      // ==> adjust min and max coordinates of the box to the user-selected cube
      if (params.cubeUsed)
	{

	  /* Setting the min/max coordinates  according to the CUBE information give by user */
	  data->setCube (params.cube);
	}
      else if (params.radiusUsed)
	{
	  /* Setting the min/max coordinates  according to the  RADIUS info */
	  data->setRadius (params.radius);
	}

      /* /\* Check validity of data *\/ */
      // will, among others, set a buffer around the molecule (if radius and cube keywords have not been used)
      // and also, set the nbStep0,1,2 according to the cube definition (including or not the buffer)
      // increments read from param.igm are passed here. Note that if CRITIC mode has been activated,
      // new increments are chosen to build the promolecular grid that will be used to find seeds for NEwton-Raphson procedure
      // within the critical point search:
      if (isCriticmodeActivated()) // provide new increments in angstroms 
        {  

          // first, test Z for every atom 
          // Since the promolecular ED will be used to determine critical points
          // and that only atoms of the three first rows are available (promol ED is 
          // avail. for larger atoms but only to describe ED beyond vdW radius...)
          unsigned int nbAtoms = data->getNbAtom ();
          IGMSeeds=true; // default is to use the IGM Promol. seeding strategy
          for  (unsigned iat=0; iat<nbAtoms; ++iat) 
            {
             if (data->atomTypes[iat]> CRITICPROMOLAtomLimit) // atomTypes=Z-1, CRITICPROMOLAtomLimit: 0-based range
               {
                  IGMSeeds=false;
               }
            } // end over atoms

          if (isCriticFINEmodeActivated())
            {
              params.increments[0] = incrCRITICFINEx; // no matter the increments supplied by the user
              params.increments[1] = incrCRITICFINEy;
              params.increments[2] = incrCRITICFINEz;
            }
          else if (isCriticULTRAFINEmodeActivated())
            {
              params.increments[0] = incrCRITICULTRAFINEx; // no matter the increments supplied by the user
              params.increments[1] = incrCRITICULTRAFINEy;
              params.increments[2] = incrCRITICULTRAFINEz;
            }
	  else // MEDIUM or CRITICaddSEEDS
            {
              params.increments[0] = incrCRITICMEDIUMx; // no matter the increments supplied by the user
              params.increments[1] = incrCRITICMEDIUMy;
              params.increments[2] = incrCRITICMEDIUMz;
            }


        } // end of if (isCriticmodeActivated()) 
       
       // VALIDATE QM parameters
       data->validate(params.increments, params.radiusRoundLigand, false, true); // increments are converted in bohr	


      // if a BONDS section has been read, check atom indexes validity
      if (atomBondsHaveBeenRegistered ())
	{
	  bool bondsValid = true;
	  std::vector < std::pair < unsigned int,unsigned int > >::iterator it = getChosenBonds ().begin ();

	  while (bondsValid && it != getChosenBonds ().end ())
	    {

	      unsigned int atom = (*it).first;

	      // Index of the atom is a natural index i.e. in [1,nbAtoms]
	      if (atom < 1 || atom > wf_number_of_nuclei ())
		{
		  std::cerr << std::endl;
		  std::
		    cerr <<
		    "[ERROR] First atom of a bond have an invalid value  " <<
		    atom << std::endl;
		  bondsValid = false;
		}
	      else
		{
		  atom = (*it).second;

		  // Index of the atom is a natural index i.e. in [1,nbAtoms]
		  if (atom < 1 || atom > wf_number_of_nuclei ())
		    {
		      std::cerr << std::endl;
		      std::
			cerr <<
			"[ERROR] Second atom of a bond have an invalid value  "
			<< atom << std::endl;
		      bondsValid = false;
		    }
		}

	      it++;
	    }			// end of while( bondsValid && it!=getChosenBonds().end() )

	  if (!bondsValid)
	    {
	      std::cerr << "[ERROR] The program will now exit." << std::endl;
	      exit (EXIT_FAILURE);
	    }

	}			// end of if(atomBondsHaveBeenRegistered())

      // if the IBSI mode is activated: prepare the cylinder definition 
      // Pz = 100 points, Pr = 100 points, Ptheta = 50 points
      if (isIBSImodeActivated ())
	{ // for a cylindrical grid, the optimimum number of points
	  // along Z, r and theta has been studied (J. Klein M2 graduation)
	  // Pz = 100, Pr = 100, Ptheta = 50 
	  data->setNbSteps (cylPz, cylPr, cylPtheta);
	}

      // establish the chemical formula and set the atomTypeList and atomTypeNb !
      setChemFormula ();

      // count the number of core orbitals and electrons
      // (this routine no longer reorganizes the MO in ascending order of energy
      //  since valence electrons are no longer used)
      // both for IBSi and cubic grids:
      setCOREMOandELEC();


    }  // end of if(isWFNmodeActivated())

/* ==========================  P R O M O L E C U L A R    P R E P A R A T I O N ====================================== */
  else				// promolecular electron density
    {
      /* Read file(s) .xyz and find min and max (more remote atoms) */
      //  and deduce NbSteps(0), NbSteps(1), and NbSteps(2)
      //  cartesian coordinates are read in angstroms
      data = readxyzMinMax (&params);
    }
/* ------------------------------------------------------------------------------------------------------------------ */


  /* estimation of the size of the treatment */
  fullSize =
    data->getNbSteps (0) * data->getNbSteps (1) * data->getNbSteps (2);


  /* Information box printing */
  std::string emptyLine
    ("   *                                                                   *");
  size_t lineSize = emptyLine.length ();

  std::string leftPart ("   *                          : ");
  size_t leftPartSize = leftPart.length ();
  size_t rightPartSize = lineSize - leftPartSize;

  /* ==========================  P R E L I M I N A R Y   O U T P U T  ================================= */
  /** HEAD **/

  std::
    cout <<
    "   ---------------------------------------------------------------------"
    << std::endl;
  std::
    cout <<
    "   *                                                                   *"
    << std::endl;
  std::
    cout <<
    "   *                         I G M P L O T                             *"
    << std::endl;
  std::
    cout <<
    "   *           v3.17   - Build Git-enabled  August 2025                *"
    << std::endl;
  std::
    cout <<
    "   *           Identifying and Characterizing Interactions             *"
    << std::endl;
  std::
    cout <<
    "   *                          in Molecules                             *"
    << std::endl;
  std::
    cout <<
    "   *                                                                   *"
    << std::endl;
  std::
    cout <<
    "   *                PROGRAM AUTHORS and CONTRIBUTORS                   *"
    << std::endl;
   std::
    cout <<
    "   *                   .     Akilan Rajamani                           *"
    << std::endl;
  std::
    cout <<
    "   *                   .     Corentin Lefebvre                         *"
    << std::endl;
  std::
    cout <<
    "   *                   .     Johanna Klein                             *"
    << std::endl;

  std::
    cout << 
    "   *                   .     Hugo Roussel                              *"
    << std::endl;

  std::
    cout <<
    "   *                   .     Pluot Emmanuel                            *"
    << std::endl;
  std::
    cout <<
    "   *                   .     Rubez Gaetan                              *"
    << std::endl;
  std::
    cout <<
    "   *                   .     Khartabil Hassan                          *"
    << std::endl;
  std::
    cout <<
    "   *                   .     Boisson Jean-Charles                      *"
    << std::endl;
  std::
    cout <<
    "   *                   .     Henon Eric                                *"
    << std::endl;
  std::
    cout <<
    "   *                                                                   *"
    << std::endl;
  std::
    cout <<
    "   * ------------  Reims Champagne-Ardenne University  --------------  *"
    << std::endl;
  std::
    cout <<
    "   *                                                                   *"
    << std::endl;
  std::
    cout <<
    "   *                            Citations                              *"
    << std::endl ;

  std::cout << emptyLine << std::endl;

  std::
    cout <<
    "   *                        The PROGRAM IGMPlot                        *"
    << std::endl;
  std::
    cout <<
    "   *                 https://doi.org/10.1002/jcc.27123                 *"
  << std::endl;
  std::cout <<
    "   *              J. Comp. Chem 2023, 44(20), 1750-1766                *"
  << std::endl;

  std::cout << emptyLine << std::endl;

  std::
    cout <<
    "   *                            IGM concept                            *"
    << std::endl;
  std::
    cout <<
    "   *        Phys. Chem. Chem. Phys. 2017, 19(27), 17928-17936          *"
  << std::endl;

  std::cout << emptyLine << std::endl;

  std::
    cout <<
    "   *                         QM theory of IGM                          *"
    << std::endl;
  std::
    cout <<
    "   *        Chem. Phys. Chem.       2018, 19    , 724-735              *"
  << std::endl;

  std::cout << emptyLine << std::endl;

  std::
    cout <<
    "   *                             I B S I                               *"  
  << std::endl;
  std::
    cout <<
    "   *        J. Phys. Chem. A        2020, 124(9), 1850-1860            *"
  << std::endl;

  std::cout << emptyLine << std::endl;

  std::
    cout <<
    "   *               Atomic Decomp. of intermol. interactions            *"
  << std::endl;
  std::
    cout <<
    "   *        J. Chem. Inf. Model.    2020, 60(1) , 268-278              *"
    << std::endl;
  std::
    cout <<
    "   *                                                                   *"
    << std::endl;
  std::
    cout <<
    "   *                     Degree of Interaction (DoI)                   *"
  << std::endl;
  std::
    cout <<
    "   *          Phys. Chem. Chem. Phys, 2023, 25, 11398-11409            *"
    << std::endl;
  std::
    cout <<
    "   *                                                                   *"
    << std::endl;

  std::
    cout <<
    "   *                    Pair Density Asymmetry (PDA) index             *"
  << std::endl;
  std::
    cout <<
    "   *  J. Comp. Chem. 2023, 44, 1750  https://doi.org/10.1002/jcc.27123 *"
    << std::endl;
  std::
    cout <<
    "   *                                                                   *"
    << std::endl;
  std::
    cout <<
    "   *                                ELF & IGM                          *"
  << std::endl;
  std::
    cout <<
    "   *  J. Comp. Chem. 2025,46,e70146  https://doi.org/10.1002/jcc.70146 *"
    << std::endl;
  std::
    cout <<
    "   *                                                                   *"
    << std::endl;
  std::
    cout <<
    "   *                          STERIC effects (SELF)                    *"
  << std::endl;
  std::
    cout <<
    "   *  Article in preparation, preprint available on ChemRXiv           *"
    << std::endl;
    std::
    cout <<
    "   *  https://doi.org/10.26434/chemrxiv-2025-br97q                     *"
    << std::endl;
  std::
    cout <<
    "   * ----------------------------------------------------------------  *"
    << std::endl;



 //   ============== J O B   T Y P E =============================================  //  
  if (isPauli())
    { 
      std::cout << "   *   STERIC effect analysis : SELF descriptor                        *" << std      ::endl;
      if (isPauliAtomic()) 
      {
        std::cout << "   *                            with atomic details                    *" << std      ::endl;
      }
    } // end of if isPauli
  else if  (isCriticmodeActivated())
   {
      std::cout << "   *   Critical Point Search  : ";
      if (isCriticMEDIUMmodeActivated()) {std::cout << "CRITIC                                 *" << std::endl;}
      else if (isCriticFINEmodeActivated()) {std::cout << "CRITICFINE                             *" << std::endl;}
      else if (isCriticULTRAFINEmodeActivated()) {std::cout << "CRITICULTRAFINE                        *" << std::endl;}
   } // end of isCRITIC
  else if  ( isELFmodeActivated() )  // ELF analysis
   {
      std::cout << "   *   ELF iso-surfaces       : ";
      std::cout << "colored according to 1/qg^2            *" << std::endl;
      std::cout << "   *                            ";
      std::cout << "(qg descript. = grad Rho^RIGM/grad Rho)*" << std::endl;
      std::cout << "   *                            ";
      std::cout << "with BGR color scheme                  *" << std::endl;
      std::cout << "   *                            ";
      std::cout << "  . red <=> core or monosynaptic basin *" << std::endl;
      std::cout << "   *                            ";
      std::cout << "    (no ED sharing)                    *" << std::endl;
      std::cout << "   *                            ";
      std::cout << "  . blue-green: polysynaptic basin     *" << std::endl;
      std::cout << "   *                            ";
      std::cout << "    (   ED sharing)                    *" << std::endl;
      std::cout << emptyLine << std::endl;
      std::cout << "   *   ED Grad Atom partition : Gradient-Based (GBP) to color basins   *" << std::endl;
   } // end of ELF
  else if (isParseRKF())
   {  
      std::cout << "   *   RKF PARSING            : Analysis of an ADF calculation         *" << std      ::endl;
   } // end of isParseRKF
  else 
   {
      std::cout << "   *   IGM                    : Interaction analysis                   *" << std      ::endl;
   }


//    ============== D E N S I T Y    T Y P E ====================================  // 
  if (isWFmodeActivated() and (not isParseRKF()) )
   {
      std::cout << emptyLine << std::endl;
      if (!isRKFmodeActivated()) { // GTO primitive family
         std::cout << "   *   Density type           : QUANTUM, GTO basis functions           *" << std::endl;
         std::cout << emptyLine << std::endl;
      // indicate the accuracy used to compute primitives
         std::cout << "   *   GTO pruning threshold  : ";
         unsigned int precis=8;
         if (maxElectron==maxElecAccurlev1) {precis = 8;}
         else if (maxElectron==maxElecAccurlev2) {precis =  9;}
         else if (maxElectron==maxElecAccurlev3) {precis = 11;}
         else if (maxElectron==maxElecAccurlev4) {precis = 21;}
         std::cout << std::setw(precis) << std::setprecision(precis-2)<< std::fixed << std::left << maxElectron;
         std::cout << " e-";
         space (lineSize - 36 - precis);
         std::cout << "*" << std::endl;
      }
      else // STO primitive family
      {
         std::cout << "   *   Density type           : QUANTUM, STO basis functions           *" << std::endl;
      }
      std::cout << emptyLine << std::endl;

   } // end of isWFmodeActivated and (not isParseRKF()) )
  else if (not isParseRKF())
    {
      std::
        cout <<
        "   *   Density type           : PROMOLECULAR                           *"
        << std::endl;
    }


//    ============== P A R T I T I O N   T Y P E =================================  // 
  if (isWFmodeActivated() and (not isParseRKF()) )
   {
     if ( not isCriticmodeActivated() ) // CRITIC does not use atomic partition
           {
             if (isPauli())
              { 
                if (not isHirshmodeActivated()) // GBP partition used for atomic partition and iso-surface
                {std::cout << "   *   ED Grad Atom partition : Gradient-Based (GBP)                   *" << std::endl;}
                else                            // Hirshfeld  partition used for atomic partition and iso-surface 
                {std::cout << "   *   ED Grad Atom partition : Hirshfeld-Based (HBP)                  *" << std::endl;}
              } 
             else if (isELFmodeActivated())
              {
                // ELF iso-surfaces are colored according to Qg!
                std::cout << "   *   ED Grad Atom partition : Gradient-Based (GBP)                   *" << std::endl;
              }
             else if (not isHirshmodeActivated()) // general IGM case
              {   // GBP
                std::cout << "   *   ED Grad Atom partition : Gradient-Based (GBP)                   *" << std::endl;
              }
             else // Hirshfeld for IGM
              {
                std::cout << "   *   ED Grad Atom partition : Hirshfeld-Based (HBP)                  *" << std::endl;
              } // end of GBP or HIRSH
   
           } // end of if ( (not isCriticmodeActivated() and (not isELFmodeActivated()) ) )
   
     std::cout << emptyLine << std::endl;
   } // end of if (isWFmodeActivated() and (not isParseRKF()) )

  std::stringstream oss;
  std::string field;


  //    ============== I G M     M O D E L    a n d    M O L E C U L E     D E T A I L S ===  //

  // QM electron density, WFN input file has been read
  if (isWFmodeActivated () and (not isParseRKF()) )
    {
      if (isIBSImodeActivated ())
	{			// IBSI/PDA    integration score calculated

	  // ######  IGM Model HEADER  ########
	  field = "   *   IGM Model              : Atom pair dg descriptor ";
	  std::cout << field;
	  space (lineSize - field.length () - 1);
	  std::cout << "*" << std::endl;
	  field = "   *                            Cylindrical grid        ";
	  std::cout << field;
	  space (lineSize - field.length () - 1);
	  std::cout << "*" << std::endl;
	  field = "   *                            Integration --> IBSI,PDA   ";
	  std::cout << field;
	  space (lineSize - field.length () - 1);
	  std::cout << "*" << std::endl;
	  std::cout << emptyLine << std::endl;

	  // ######  NbFiles used ########
	  field = "   *   Input NbFiles          : 1                       ";
	  std::cout << field;
	  space (lineSize - field.length () - 1);
	  std::cout << "*" << std::endl;

	  // ######  FileName    ########
	  size_t value = params.molAFileName.rfind ("/");
	  std::string shortFileName;
	  if (value != std::string::npos
	      && value != params.molAFileName.length () - 1)
	    {
	      shortFileName = params.molAFileName.substr (value + 1);
	    }
	  else
	    {
	      shortFileName = params.molAFileName;
	    }
	  field = std::string ("   *   ==> File name          : ");
	  std::cout << field << shortFileName;
	  // -1 due to the add of '*' character)
	  space (lineSize - field.size () - shortFileName.length () - 1);
	  std::cout << "*" << std::endl;

	  // ######  Chem. Formula ######
	  field = std::string ("   *   ==> Chemical formula   : ");
	  std::cout << field;

	  for (unsigned int iType = 0; iType < atomTypeList.size (); ++iType)
	    {
              if (atomTypeNb[iType] > 1)
	      {
                std::cout << ATOM_NAMES[atomTypeList[iType]] << atomTypeNb[iType];
                oss << ATOM_NAMES[atomTypeList[iType]] << atomTypeNb[iType];
              }
              else if (atomTypeNb[iType] == 1)
              {
                std::cout << ATOM_NAMES[atomTypeList[iType]]; 
                oss << ATOM_NAMES[atomTypeList[iType]];
              }
	    } // end of or (unsigned int iType = 0;
	  space (lineSize - field.size () - oss.str ().length () - 1);
	  std::cout << "*" << std::endl;
	  oss.str ("");


	  // ######  Nb Atoms    ########
	  field = std::string ("   *   ==> Atoms              : ");
	  std::cout << field << data->getNbAtom ();
	  oss << data->getNbAtom ();
	  space (lineSize - field.size () - oss.str ().length () - 1);
	  std::cout << "*" << std::endl;
	  oss.str ("");

	  // #######  Nb of primitives per bond ######
	  // if no cutoff applied for bonds: simply display the total number of primitives
	  if (params.bondcut == 0.0)	// <-> no cutoff
	    {
	      field = std::string ("   *   ==> Primitives         : ");
	      std::cout << field << wf_number_of_primitives ();
	      oss << wf_number_of_primitives ();
	      space (lineSize - field.size () - oss.str ().length () - 1);
	      std::cout << "*" << std::endl;
	      oss.str ("");
	    }

	  else			// a cutoff has been applied ==> the number of primitives per bond is displayed
	    {
	      field = std::string ("   *   ==> Primitives         : ");
	      std::cout << field << std::setw (6) << std::left << wf_number_of_primitives ();
	      std::cout << "| Bond cutoff set to " << std::fixed << std::setprecision (1) << std::setw (4) << params.bondcut;
	      std::cout << " (Angs) *" << std::endl;

	      for (unsigned int ibond = 0; ibond < getChosenBonds ().size ();
		   ibond++)
		{
		  std::cout << "   *                            bond " << THREE;
		  std::cout << std::right << getChosenBonds ()[ibond].first;
		  std::cout << " - " << THREE << std::left << getChosenBonds ()[ibond].second << " Nprim = ";
		  std::cout << std::setw (6) << std::fixed << nprimbond[ibond];
		  std::cout << "          *" << std::endl;
		}		// end of for (ibond)

	    }			// end of else of if (params.bondcut==0.0)

          // ##### MO number (singly or doubly or partially occupied in wfn/wfx)
          std::cout << "   *   ==> MO number (WFN/WFX): ";
          std::cout << std::setw(6) << std::fixed << std::setprecision(0)<< std::left << MOoccupied;
          std::cout << " (occupancy > 0.1)               *" << std::endl;

          // ######  WFN electr.                       ########
          field = std::string ("   *   ==> e- (WFN/WFX)       : ");
          std::cout << field << std::setw(7) << std::setprecision(1)<< std::fixed << std::left << WFNelecNumb;
          std::cout << " e-                             *" << std::endl; 
          oss.str ("");


/*
	  if (not COREHeavyAtomWarning)	// if the Z upper limit for the BDA has not been exceeded by one of the atoms
	    {
	      // ######  WFN electr.                       ########
	      field = std::string ("   *   ==> e- (WFN/WFX)       : ");
              std::cout << field << std::setw(7) << std::setprecision(1)<< std::fixed << std::left << WFNelecNumb;
	      std::cout << " e-                             *" << std::endl; 
	      oss.str ("");

              std::cout << "   *                           -------                                 *" << std::endl;

              // ######  Nb electrons in neutral system    ########
              field = std::string ("   *   ==>   Full neutral sys.: ");
              std::cout << field << std::setw(6) << std::setprecision(1)<< std::fixed << std::right << elecNumb;
              std::cout << "  e-                             *" << std::endl;
              oss.str ("");

              // ######  CHARGE of the SYSTEM     ########
              field = std::string ("   *   ==> - CHARGE           : ");
              std::cout << field << std::setw(4) << std::fixed << std::right << -(wf_netCharge());
              std::cout << "    e-                             *" << std::endl;
              oss.str ("");

              // ######  PSEUDO                            ########
              // remind that: WFN e- = Total neutre elec  - CHARGE   -  pseudo
              // ==> pseudo = Total neutre elec  - CHARGE - WFN e-
              field = std::string ("   *   ==> - PSEUDOPOT. e-    : ");
              std::cout << field << std::setw(6) << std::setprecision(1)<< std::fixed << std::right << (WFNelecNumb-elecNumb+wf_netCharge());
              oss << std::setw(6) << std::setprecision(1) << std::fixed << std::left << elecNumb;
              std::cout << "  e-                             *" << std::endl;
              oss.str ("");


             std::cout << emptyLine << std::endl;
             // ######  CORE AOs / e-                       ########
             // from database stored in IGMPlot
             std::cout <<  "   *   For BDA Analysis       :                                        * " << std::endl;
             std::cout <<  "   *      .Full CORE          :   e- / AOs      Atoms                  *" << std::endl;

             for (unsigned int iType = 0; iType < atomTypeList.size (); ++iType)
               {
                 if (iType == 0) 
                   {
                     std::cout << "   *       ---------           ";
                   }
                 else
                   {
                     std::cout << "   *                           ";
                   }
                 std::cout << std::setw(5) << std::right;
                 std::cout << 2*ATOM_COREORB[atomTypeList[iType]]*atomTypeNb[iType] << " / ";
                 std::cout << std::setw(8) << std::left  <<  ATOM_COREORB[atomTypeList[iType]]*atomTypeNb[iType];
                 std::cout << "  " << std::right << std::setw(2)<<  ATOM_NAMES[atomTypeList[iType]] << " x ";
                 std::cout << std::left << std::setw(4) << atomTypeNb[iType];
                 std::cout << "             *" << std::endl;
               } // end of for( unsigned int iType=0 
             std::cout <<  "   *                              ------                               * " << std::endl;
             std::cout << "   *                           ";
             std::cout << std::setw(5) << std::right << 2*coreAONumber << " / ";
             std::cout << std::setw(8) << std::left  << coreAONumber;
             std::cout << " Total                  *" << std::endl;


              if (MOSorted)
               {
                 std::cout <<  "   *   ==> MOs rearranged by ascending order of energy                 * " << std::endl;
               }

//            std::cout << emptyLine << std::endl; 
              if ( (elecNumb-WFNelecNumb-wf_netCharge())>0)  // if a pseudo exists
                    { 
                        std::cout << "   *      .IGMPlot criteria accounting for PSEUDOPOT.                  *" << std::endl;
                    }
                  else
                    {
                        std::cout << "   *      .IGMPlot criteria                                            *" << std::endl;
                    }
    
              std::cout << "   *       ----------------                                            *" << std::endl;
              std::cout << "   *       Core e-  left out  :    ";
              std::cout << std::setw(6) << std::fixed << std::setprecision(1) << std::right << coreElecWFN;
              std::cout << "                              *" << std::endl;
              std::cout << "   *       Core MOs left out  :    ";
              std::cout << std::setw(4) << std::right << coreMOWFN;
              std::cout << "   (1 - " << std::setw(4) << std::left << coreMOWFN << ")                   *" << std::endl;
         
	    }	// end of if (not COREHeavyAtomWarning) before else
	  else
	    {
              // ######  Z upperlimit exceeded  INFO       ########
              field = std::string ("   *   ==> Z upper limit      : ");
              std::stringstream sszlimit;
              sszlimit << (BDAAtomLimit + 1);	// BDAAtomLimit counting from 0 ...
              field.append (sszlimit.str ());
              field.append (", exceeded here for BDA calculation");
              std::cout << field;
              space (lineSize - field.size () - 1);
              std::cout << "*" << std::endl;
	    } // end of else of if (not COREHeavyAtomWarning)
*/

	}			// end of if IBSI HEADER ..................................................

      else			// ======= CUBIC GRID (no IBSI/PDA    integration score calculated) ========
	{
 
          if ( (not isCriticmodeActivated()) and (not isELFmodeActivated()) and (not isParseRKF())  )
            {
               if ( (not isdgSCALED()) and (not isPauli()) )
                {
           	  // ######  IGM Model HEADER  ########
           	  field =
           	    "   *   IGM Model              : dg, dgInter, dgIntra descriptors ";
           	  std::cout << field;
           	  space (lineSize - field.length () - 1);
           	  std::cout << "*" << std::endl;
                  std::cout << "   *                            + Atomic Degree of Interaction (DOI)   *" << std::endl;
           	  field = "   *                            Cubic grid              ";
           	  std::cout << field;
           	  space (lineSize - field.length () - 1);
           	  std::cout << "*" << std::endl;
           	  std::cout << emptyLine << std::endl;
                } // end of if (not isdgSCALED()) and (not isPauli())
               else if (isdgSCALED()) // dgSCALED mode
                {
                  // ######  IGM Model HEADER  ########
                  field =
                    "   *   IGM Model              : dgInter/rho, dgIntra/rho descrip.";
                  std::cout << field;
                  space (lineSize - field.length () - 1);
                  std::cout << "*" << std::endl;
                  std::cout << "   *                            + Atomic Degree of Interaction (DOI)   *" << std::endl;
                  field = "   *                            Cubic grid              ";
                  std::cout << field;
                  space (lineSize - field.length () - 1);
                  std::cout << "*" << std::endl;
                  std::cout << emptyLine << std::endl;
                 } // end of else if (isdgSCALED())
                else if (isPauli())
                 {
                  // ######  IGM Model HEADER  ########
                  field =
                    "   *   IGM Model              : dgInter/rho descriptor";
                  std::cout << field;
                  space (lineSize - field.length () - 1);
                  std::cout << "*" << std::endl;
                  field = "   *                            Cubic grid              ";
                  std::cout << field;
                  space (lineSize - field.length () - 1);
                  std::cout << "*" << std::endl;
                  std::cout << emptyLine << std::endl;
                 } // end of else if (isPauli())        

       
            } // end of if (not isCriticmodeActivated()) and (not isELFmodeActivated())  and (not isParseRKF())

          else if (isELFmodeActivated()) 
            {
               
               std::cout << "   *   IGM Model              : qg descrip. (to color ELF basins)      *" << std::endl;
               std::cout << "   *                            Cubic grid                             *"  << std::endl;
               std::cout << emptyLine << std::endl;
            }

	  if (wfn_getMoleculeAnbAtoms () == 0
	      || wfn_getMoleculeBnbAtoms () == 0)
	    {
	      field =
		"   *   Input NbFiles          : 1, no fragment partition";
	      std::cout << field;
	      space (lineSize - field.length () - 1);
	      std::cout << "*" << std::endl;
              if ( (not isCriticmodeActivated()) and (not isELFmodeActivated()) )
	       {
                field = "   *                            dgIntra = dg, dgInter = 0 ";
                std::cout << field;
                space (lineSize - field.length () - 1);
                std::cout << "*" << std::endl;
	       }
            }
	  else
	    {
	      field =
		"   *   Input NbFiles          : 1, fragmentation scheme  ";
              std::cout << field;
              space (lineSize - field.length () - 1);
              std::cout << "*" << std::endl;
	    }

	  std::cout << emptyLine << std::endl;

	  size_t value = params.molAFileName.rfind ("/");
	  std::string shortFileName;
	  if (value != std::string::npos
	      && value != params.molAFileName.length () - 1)
	    {
	      shortFileName = params.molAFileName.substr (value + 1);
	    }
	  else
	    {
	      shortFileName = params.molAFileName;
	    }

	  std::string condensedDescriptionOfMolecule;


	  // ######  Chem. Formula ######
	  field = std::string ("   *   Chemical formula       : ");
	  std::cout << field;
	  for (unsigned int iType = 0; iType < atomTypeList.size (); ++iType)
	    {
              if (atomTypeNb[iType] > 1)
              {
                std::cout << ATOM_NAMES[atomTypeList[iType]] << atomTypeNb[iType];
                oss << ATOM_NAMES[atomTypeList[iType]] << atomTypeNb[iType];
              }
              else if (atomTypeNb[iType] == 1)
              {
                std::cout << ATOM_NAMES[atomTypeList[iType]];
                oss << ATOM_NAMES[atomTypeList[iType]];
              }
	    }
	  space (lineSize - field.size () - oss.str ().length () - 1);
	  std::cout << "*" << std::endl;
	  oss.str ("");
          std::cout << emptyLine << std::endl;


	  if (wfn_getMoleculeBnbAtoms () == 0)	// no fragment partitionning
	    {
	      field = std::string ("   *   Molecule                 ");
	      std::cout << field;
	      space (lineSize - field.size () - 1);
	      std::cout << "*" << std::endl;
	    }
	  else			// molecule A defined (FRAG1 defined)
	    {
	      field = std::string ("   *   Fragment 1               ");
	      std::cout << field;
	      space (lineSize - field.size () - 1);
	      std::cout << "*" << std::endl;
	    }

/* =========== Molecule or FRAGMENT 1  if defined  =============================== */
	  field = std::string ("   *   ==> File name          : ");
	  std::cout << field << shortFileName;
	  // -1 due to the add of '*' character)
	  space (lineSize - field.size () - shortFileName.length () - 1);
	  std::cout << "*" << std::endl;

	  field = std::string ("   *   ==> Number of atoms    : ");
	  std::cout << field << data->getNbAtomMolA () << " / ";
	  std::cout << data->getNbAtom ();
	  oss << data->getNbAtomMolA () << " / " << data->getNbAtom ();
	  space (lineSize - field.size () - oss.str ().length () - 1);
	  std::cout << "*" << std::endl;
	  oss.str ("");

	  condensedDescriptionOfMolecule =
	    getCondensedDescriptionOfMoleculeA();

	  field = std::string ("   *   ==> Atom indexes       : ");
	  size_t shift = rightPartSize - 2;

	  std::cout << field;

	  while (shift < condensedDescriptionOfMolecule.length ())
	    {
	      while (condensedDescriptionOfMolecule[shift] != ' ')
		shift--;

	      std::cout << condensedDescriptionOfMolecule.substr (0, shift);
	      space (rightPartSize - shift - 1);
	      std::cout << "*" << std::endl;
	      std::cout << leftPart;
	      condensedDescriptionOfMolecule =
		condensedDescriptionOfMolecule.substr (shift + 1);
	    }

	  std::cout << condensedDescriptionOfMolecule;
	  space (rightPartSize - condensedDescriptionOfMolecule.length () -
		 1);
	  std::cout << "*" << std::endl;

          // if IGM mode AND fragments defined
          if (isIGMmodeActivated() and (wfn_getMoleculeBnbAtoms () > 0) ) {
              field = std::string ("   *   ==> Primitives         : ");
              std::cout << field << nbPrimInA << " / ";
              std::cout << wf_number_of_primitives ();
              oss << nbPrimInA << " / " << wf_number_of_primitives ();
              space (lineSize - field.size () - oss.str ().length () - 1);
              std::cout << "*" << std::endl;
              oss.str ("");
          } // end of if (isIGMmodeActivated() {


/* =========== FRAGMENT 2  if defined  =============================== */

	  if (wfn_getMoleculeBnbAtoms () > 0)	// a frag2 has been defined
	    {

	      std::cout << emptyLine << std::endl;
	      field = std::string ("   *   Fragment 2               ");
	      std::cout << field;
	      space (lineSize - field.size () - 1);
	      std::cout << "*" << std::endl;

	      field = std::string ("   *   ==> File name          : ");
	      std::cout << field << shortFileName;
	      space (lineSize - field.size () - shortFileName.length () - 1);
	      std::cout << "*" << std::endl;

	      field = std::string ("   *   ==> Number of atoms    : ");
	      std::cout << field << data->getNbAtomMolB () << " / ";
	      std::cout << data->getNbAtom ();
	      oss << data->getNbAtomMolB () << " / " << data->getNbAtom ();
	      space (lineSize - field.size () - oss.str ().length () - 1);
	      std::cout << "*" << std::endl;
	      oss.str ("");


	      condensedDescriptionOfMolecule =
		getCondensedDescriptionOfMoleculeB ();

	      field = std::string ("   *   ==> Atom indexes       : ");
	      size_t shift = rightPartSize - 2;

	      std::cout << field;

	      while (shift < condensedDescriptionOfMolecule.length ())
		{
		  while (condensedDescriptionOfMolecule[shift] != ' ')
		    {
		      shift--;
		    }
		  std::cout << condensedDescriptionOfMolecule.substr (0,
								      shift);
		  space (rightPartSize - shift - 1);
		  std::cout << "*" << std::endl;
		  std::cout << leftPart;
		  condensedDescriptionOfMolecule =
		    condensedDescriptionOfMolecule.substr (shift + 1);
		}

	      std::cout << condensedDescriptionOfMolecule;
	      space (rightPartSize -
		     condensedDescriptionOfMolecule.length () - 1);
	      std::cout << "*" << std::endl;

              if (isIGMmodeActivated()) {
                  field = std::string ("   *   ==> Primitives         : ");
                  std::cout << field << nbPrimInB << " / ";
                  std::cout << wf_number_of_primitives ();
                  oss << nbPrimInB << " / " << wf_number_of_primitives ();
                  space (lineSize - field.size () - oss.str ().length () - 1);
                  std::cout << "*" << std::endl;
                  oss.str ("");
              } // end of  if isIGMmodeActivated()


	    }			// end of if(wfn_getMoleculeBnbAtoms() > 0)

/* =========== Specify the total number of atoms considered ====================== */
/*              if a fragmentation scheme has been required                        */
/*              and IGMmode or SELF mode                                           */
	  if ((wfn_getMoleculeAnbAtoms () !=
	       0) and (wfn_getMoleculeBnbAtoms () != 0))
	    {
	      std::cout << emptyLine << std::endl;
	      field =
		std::
		string ("   *   Total number of atoms considered :      ");
	      std::cout << field << data->getNbAtomMolA () +
		data->getNbAtomMolB () << " / ";
	      std::cout << data->getNbAtom ();
	      oss << data->getNbAtomMolA () +
		data->getNbAtomMolB () << " / " << data->getNbAtom ();
	      space (lineSize - field.size () - oss.str ().length () - 1);
	      std::cout << "*" << std::endl;
	      oss.str ("");
	    }


/* =========== Specify the total number of primitives considered ================= */
/*              which can be limited and lower than the total AO nb of primitives  */
/*              if a fragmentation scheme has been required in the IGM Mode        */
	  if ((wfn_getMoleculeAnbAtoms () != 0) and 
	      (wfn_getMoleculeBnbAtoms () != 0) and
              isIGMmodeActivated()
             )
	    {
	      field =
		std::
		string ("   *   Total number of primitives considered : ");
	      std::cout << field << nbPrimInA + nbPrimInB << " / ";
	      std::cout << wf_number_of_primitives ();
	      oss << nbPrimInA +
		nbPrimInB << " / " << wf_number_of_primitives ();
	      space (lineSize - field.size () - oss.str ().length () - 1);
	      std::cout << "*" << std::endl;
	      oss.str ("");
	    } 

/* =========== Specify the total number of primitives considered ================= */
/*             for any other calculations than IGM , including SELF (which uses    */
/*             all AOs), or IGM calculation without fragmentation scheme           */
          if ((wfn_getMoleculeAnbAtoms () == 0) or  
              (wfn_getMoleculeBnbAtoms () == 0) or  
              isPauli()
             )
            { 
              std::cout << emptyLine << std::endl;
              field = std::string ("   *   ==> Primitives         : ");
              std::cout << field <<  std::setw(10) << std::left;
              std::cout << wf_number_of_primitives ();
              space (lineSize - field.size () - 11);
              std::cout << "*" << std::endl;
              oss.str ("");
            } 



          
          // ##### MO number (singly or doubly or partially occupied in wfn/wfx)
          std::cout << emptyLine << std::endl;
          std::cout << "   *   ==> MO number (WFN/WFX): ";
          std::cout << std::setw (6) << std::fixed << std::setprecision(0) << std::left << MOoccupied;
          std::cout << " (occupancy > 0.1)               *" << std::endl;

          // ######  WFN electr.                       ########
          field = std::string ("   *   ==> e- (WFN/WFX)       : ");
          std::cout << field << std::setw(7) << std::setprecision(1)<< std::fixed << std::left << WFNelecNumb;
          std::cout << " e-                             *" << std::endl;
          oss.str ("");


	}	// end of else CUBIC GRID                     
    }	// end of if(isWFNmodeActivated()  and not isParseRKF()
        // ....................................... END  QM TREATMENT ................

  else if (not isParseRKF()) // else of if(isWFNmodeActivated()) => PROMOLECULAR DENSITY 
    {
      // ======= P R O M O L E C U L A R     O U T P U T  ========

      // ######  IGM Model HEADER  ########
      field =
	"   *   IGM Model              : dg, dgInter, dgIntra, dgAt descriptors ";
      std::cout << field;
      space (lineSize - field.length () - 1);
      std::cout << "*" << std::endl;
      field = "   *                            Cubic grid              ";
      std::cout << field;
      space (lineSize - field.length () - 1);
      std::cout << "*" << std::endl;
      field =
	"   *                            Atomic contributions calculated";
      std::cout << field;
      space (lineSize - field.length () - 1);
      std::cout << "*" << std::endl;
      std::cout << emptyLine << std::endl;


      /** NB FILE **/
      if (params.nbFiles == 1)
	{
	  std::
	    cout <<
	    "   *   Input NbFiles          : 1, no fragment partition  " <<
	    "             *" << std::endl;
	  std::
	    cout <<
	    "   *                          : dgIntra = dg, dgInter = 0 " <<
	    "             *" << std::endl;

	}
      else
	{
	  std::
	    cout << "   *   Input NbFiles          : 2, fragmentation scheme"
	    << "                *" << std::endl;
	}
      std::cout << emptyLine << std::endl;

      size_t value = params.molAFileName.rfind ("/");
      std::string shortFileName;
      if (value != std::string::npos
	  && value != params.molAFileName.length () - 1)
	{
	  shortFileName = params.molAFileName.substr (value + 1);
	}
      else
	{
	  shortFileName = params.molAFileName;
	}

      std::cout << "   *   MoleculeFile [0]       : " << shortFileName;
      space (21 - shortFileName.length ());
      std::cout << "/ " << FIVE << data->
	getNbAtomMolA () << " atoms     *" << std::endl;

      /** MOLECULE B DESCRIPTION **/
      if (params.nbFiles == 2)
	{
	  value = params.molBFileName.rfind ("/");
	  if (value != std::string::npos
	      && value != params.molBFileName.length () - 1)
	    {
	      shortFileName = params.molBFileName.substr (value + 1);
	    }
	  else
	    {
	      shortFileName = params.molBFileName;
	    }

	  std::cout << "   *   MoleculeFile [1]       : " << shortFileName;
	  space (21 - shortFileName.length ());
	  std::cout << "/ " << FIVE << data->
	    getNbAtomMolB () << " atoms     *" << std::endl;
	}// end of if(params.nbFiles == 2)
    }	// end of promolecular density: else of if(isWFNmodeActivated())  
        // ................. END  PROMOLECULAR ..............


  std::cout << emptyLine << std::endl;
  field = std::string ("   *   Number of thread(s)    : ");
  std::cout << field;
  oss << nbThreads;
  std::cout << oss.str ();
  space (lineSize - field.size () - oss.str ().length () - 1);
  std::cout << "*" << std::endl;
  oss.str ("");

  /** SEPARATOR **/
  std::cout << emptyLine << std::endl;

  if (isIBSImodeActivated ())
    {				// ################  IBSI  HEADER  ##############################

	 /** BOX DEFINITION **/
      std::
	cout <<
	"   *   GridBox Definition     :  Cylindrical (by bond)                 *"
	<< std::endl;

	 /** CYLINDRICAL FEATURES **/
      std::
	cout <<
	"   *   Cylindrical features   :                                        *"
	<< std::endl;
      std::cout << "   *                            dr = ";
      std::cout << std::setprecision (2) << std::fixed << std::left;
      std::cout << "   " << NINE << R_MAX / data->getNbSteps (1) << " (bohr)";
      std::cout << "               *" << std::endl;
      std::cout << "   *                        dtheta = ";
      std::cout << "   " << NINE << THETA_MAX /
	data->getNbSteps (2) << " (radian)";
      std::cout << "             *" << std::endl;

    } // end of if (isIBSImodeActivated()) 

  else if (not isParseRKF())	// CUBIC GRID 
      {
      /** BOX DEFINITION **/
      std::cout << "   *   gridBox Definition     : " << std::setw (6) << std::left << data->getGridBoxDefinition();
        if (data->getGridBoxDefinition().compare("LIGAND") != 0)
          {
            std::cout << std::right << "                                 *" << std::endl;
          }
        else
          {
           if (params.nbFiles == 1)  
             {
               std::cout << std::right << " (box built around molecule)     *" << std::endl; 
             }
           else if (params.nbFiles == 2) 
             {
               std::cout << std::right << " (box built around molecule 1)   *" << std::endl;
             }
          } // end of if (data->getGridBoxDefinition().compare("LIGAND") != 0)

      /** MINIMUM COORDINATE VALUES **/
      std::cout << "   *   gridBox MIN (bohr)     : ";
      std::cout << std::setprecision (4) << std::fixed;
      std::cout << " " << NINE << data->getMinCoord (0);
      std::cout << " " << NINE << data->getMinCoord (1);
      std::cout << " " << NINE << data->getMinCoord (2);
      std::cout << "         *" << std::endl;

      /** MAXIMUM COORDINATE VALUES **/
      std::cout << "   *   gridBox MAX (bohr)     : ";
      std::cout << " " << NINE << data->getMaxCoord (0);
      std::cout << " " << NINE << data->getMaxCoord (1);
      std::cout << " " << NINE << data->getMaxCoord (2);
      std::cout << "         *" << std::endl;

      /** INCREMENTS **/
      std::cout << "   *   Increments  (bohr)     : ";
      std::cout << " " << NINE << params.increments[0];
      std::cout << " " << NINE << params.increments[1];
      std::cout << " " << NINE << params.increments[2];
      std::cout << "         *" << std::endl;

      std::cout << emptyLine << std::endl;    

      /** MINIMUM COORDINATE VALUES **/
      std::cout << "   *   gridBox MIN (Angs)     : ";
      std::cout << std::setprecision (4) << std::fixed;
      std::cout << " " << NINE << data->getMinCoord (0)*BOHRTOA;
      std::cout << " " << NINE << data->getMinCoord (1)*BOHRTOA;
      std::cout << " " << NINE << data->getMinCoord (2)*BOHRTOA;
      std::cout << "         *" << std::endl;

      /** MAXIMUM COORDINATE VALUES **/
      std::cout << "   *   gridBox MAX (Angs)     : ";
      std::cout << " " << NINE << data->getMaxCoord (0)*BOHRTOA;
      std::cout << " " << NINE << data->getMaxCoord (1)*BOHRTOA;
      std::cout << " " << NINE << data->getMaxCoord (2)*BOHRTOA;
      std::cout << "         *" << std::endl;

      /** INCREMENTS **/
      std::cout << "   *   Increments  (Angs)     : ";
      std::cout << " " << NINE << params.increments[0]*BOHRTOA;
      std::cout << " " << NINE << params.increments[1]*BOHRTOA;
      std::cout << " " << NINE << params.increments[2]*BOHRTOA;
      std::cout << "         *" << std::endl;


      /** NB STEPS **/
      std::cout << "   *   NbSteps                : ";
      std::cout << " " << NINE << data->getNbSteps (0);
      std::cout << " " << NINE << data->getNbSteps (1);
      std::cout << " " << NINE << data->getNbSteps (2);
      std::cout << "         *" << std::endl;

      /** SEPARATOR **/
      std::
	cout <<
	"   *                                                                   *"
	<< std::endl;

      if ( (not isCriticmodeActivated()) and (not isELFmodeActivated()) and (not isPauli()) )
         { 
          /*===================== PARAMETERS ==============================*/
          std::cout << "   *   IGM PARAMETERS         :       V A L U E           I M P A C T  *" << std::endl;
          std::cout << "   *   --------------         :       ---------           -----------  *" << std::endl;
         
          /** INTERMOLECULAR **/
          if (not isWFmodeActivated ()) //PROMOLECULAR MODE
            {
               std::cout << std::setprecision (2) << std::fixed;
               std::cout << "   *   INTERMOLECULAR %       : " << FIVE << params.intermolecular * 100 
    	            << " %(promol. NCI) ->  RDG     cube *" << std::endl;
            }
   
          std::cout << std::setprecision (2) << std::fixed; 
          /** RHO RANGE (.dat) **/
          std::cout << "   *   cutoffs rho       (r1) : [" << NINE << std::
          scientific << -params.cutoffs[0] << ":" << NINE << params.
          cutoffs[0] << "] ->  nci+igm dat  *" << std::endl;
    
          /** RDG RANGE (.dat) **/
          std::cout << "   *   cutoffs RDG       (r2) : [ 0.00e+00:" << NINE
    	  << params.cutoffs[1] << "] ->  nci     dat  *" << std::endl;
    
          /** RHO RANGE (.cube) **/
          std::cout << "   *   cutplot rho       (r3) : [" << NINE << -params.
          cutplot[0] << ":" << NINE << params.
          cutplot[0] << "] ->  RDG     cube *" << std::endl;
    
          if (!isPeakFocusIntraActivated()) { 
             /** RHO RANGE (.cube) **/
             std::cout << "   *   cuptlot_igm rho   (r5) : [" << NINE << -params.
             cutplotIGM[0] << ":" << NINE << params.
             cutplotIGM[0] << "] ->  dgIntra cube *" << std::endl;
          }
          else {
          /** RHO RANGE (.cube) **/
            std::cout << "   *   peakfocusIntra (r9,r10): [" << NINE << params.
            cutpeakIntra[0] << ":" << NINE << params.
            cutpeakIntra[1] << "] ->  dgIntra cube *" << std::endl;
          }
    
          if (!isPeakFocusInterActivated()) {
          /** RHO RANGE (.cube) **/
            std::cout << "   *   cutplot_igm rho   (r6) : [" << NINE << -params.
    	cutplotIGM[1] << ":" << NINE << params.
    	cutplotIGM[1] << "] ->  dgInter cube *" << std::endl;
          }
          else {
          /** RHO RANGE (.cube) **/
            std::cout << "   *   peakfocusInter(r10,r11): [" << NINE << params.
            cutpeakInter[0] << ":" << NINE << params.
            cutpeakInter[1] << "] ->  dgInter cube *" << std::endl;
          }
    
          /** RDG isovalue (.vmd) **/
          std::cout << "   *   cutplot VMD RDG   (r4) :  " << NINE << params.
    	cutplot[1] << "            ->  RDG      vmd *" << std::endl;
    
          /** dg_intra isovalue (.vmd) **/
          std::cout << "   *   vmd_range_igm rho (r7) : [" << NINE << -params.
            vmdcolrangIGM[0] << ":"<< NINE << params.vmdcolrangIGM[0] << "] ->  dgIntra  vmd *" << std::endl;
    
          /** dg_inter isovalue (.vmd) **/
          std::cout << "   *   vmd_range_igm rho (r8) : [" << NINE << -params.
            vmdcolrangIGM[1] << ":"<< NINE << params.vmdcolrangIGM[1] << "] ->  dgInter  vmd *" << std::endl;
    
          /** SEPARATOR **/
          std::
    	cout <<
    	"   *                                                                   *"
    	<< std::endl;
    
          /** OUTPUT OPTION **/
    
    	std::cout << "   *   OUTPUT type            : " << params.outputType
    	    << "                                      *" << std::endl;
    
          // Predicted size of the calculation
          // empirical rule    
          predictedSize = 0.0;
          double toMo = 1024 * 1024;
    
          unsigned int nbAtom = data->getNbAtom ();
    
          /** depending on outputtype, different files are generated : **/
        int ifile = 0;
// ========================= 1 ================================== //
        if (params.outputType == 1)
        { 
          ifile++;
          std::cout << "   *   File " << std::setfill('0') << std::setw(2) << ifile << "                : " << params.
            outputName + "-nci.dat";
          space (31 - params.outputName.length ());
          std::cout << "*" << std::endl;

          // Must be more adapted (add /2)
          predictedSize += ((fullSize * 28) + 22) / (2 * toMo);
          //std::cout << ( (fullSize*28)+22 ) / (2*toMo) << std::endl;

          ifile++;
          std::cout << "   *   File " << std::setfill('0') << std::setw(2) << ifile << "                : " << params.
            outputName + "-igm.dat";
          space (31 - params.outputName.length ());
          std::cout << "*" << std::endl;

    	  // Must be more adapted (add /2)
    	  predictedSize += ((fullSize * 28) + 22) / (2 * toMo);
   
          ifile++;
          std::cout << "   *   File " << std::setfill('0') << std::setw(2) << ifile << "                : " << params.
            outputName + "-dgIntra.gnu";
          space (27 - params.outputName.length ());
          std::cout << "*" << std::endl;

          ifile++;
          std::cout << "   *   File " << std::setfill('0') << std::setw(2) << ifile << "                : " << params.
            outputName + "-dgInter.gnu";
          space (27 - params.outputName.length ());
          std::cout << "*" << std::endl;
 

          if (isWFmodeActivated ())
           {
             ifile++;
             std::cout << "   *   File " << std::setfill('0') << std::setw(2) << ifile << "                : " << params.
             outputName + "-AtomDOI.dat";
             space (27 - params.outputName.length ());
             std::cout << "*" << std::endl;
           }

    
    	  // Must be more adapted (add /2)
    	  predictedSize += ((fullSize * 52) + 4) / (2 * toMo);


          if ( (data->getNbAtomMolA() != 0)  and  (data->getNbAtomMolB() != 0) ) // fragmentation scheme enabled
          {
          ifile++;
          std::cout << "   *   File " << std::setfill('0') << std::setw(2) << ifile << "                : " << params.
            outputName + "-AtContribInter.dat";
          space (20 - params.outputName.length ());
          std::cout << "*" << std::endl;
          }// end of if ( (data->getNbAtomMolA() != 0) ...
    	} // end of if (params.outputType == 1)
   
// ========================= 2 ================================== // 
          if (params.outputType == 2)
    	{ 
          ifile++;
          std::cout << "   *   File " << std::setfill('0') << std::setw(2) << ifile << "                : " << params.
            outputName + "-dens.cube";
          space (29 - params.outputName.length ());
          std::cout << "*" << std::endl;

          predictedSize += ((fullSize * 16) + (nbAtom * 45) + 219) / toMo;

          ifile++;
          std::cout << "   *   File " << std::setfill('0') << std::setw(2) << ifile << "                : " << params.
            outputName + "-RDG.cube";
          space (30 - params.outputName.length ());
          std::cout << "*" << std::endl;

    	  predictedSize += ((fullSize * 16) + (nbAtom * 45) + 219) / toMo;
    	}
   

// ========================= 3 ================================== // 
          if (params.outputType == 3)
    	{ ifile++;
    	  std::cout << "   *   File " << std::setfill('0') << std::setw(2) << ifile << "                : " << params.
    	    outputName + "-dens.cube";
    	  space (29 - params.outputName.length ());
    	  std::cout << "*" << std::endl;
    
    	  predictedSize += ((fullSize * 16) + (nbAtom * 45) + 219) / toMo;
    
          ifile++;
    	  std::cout << "   *   File " << std::setfill('0') << std::setw(2) << ifile << "                : " << params.
    	    outputName + "-dgInter.cube";
    	  space (26 - params.outputName.length ());
    	  std::cout << "*" << std::endl;
    
    	  predictedSize += ((fullSize * 16) + (nbAtom * 45) + 219) / toMo;
   
          ifile++; 
    	  std::cout << "   *   File " << std::setfill('0') << std::setw(2) << ifile << "                : " << params.
    	    outputName + "-dgIntra.cube";
    	  space (26 - params.outputName.length ());
    	  std::cout << "*" << std::endl;
    
    	  predictedSize += ((fullSize * 16) + (nbAtom * 45) + 219) / toMo;
    	}
   

// ========================= 4 ================================== // 
// D E P R E C A T E D
          if (params.outputType == 4) // fragmentation mode enabled has been already tested (in reader.cpp)
    	{ 
          ifile++;
    	  std::cout << "   *   File " << ifile << "                 : " << "AtContribInter.vmd";
    	  space (21);
    	  std::cout << "*" << std::endl;
    
    	  predictedSize += 0.05;
   
          ifile++; 
    	  std::cout << "   *   File " << ifile << "                 : " << params.
    	    outputName + "-coord.xyz";
    	  space (29 - params.outputName.length ());
    	  std::cout << "*" << std::endl;
    
    	  predictedSize += ((nbAtom + 2) * 100) / toMo;
   

          ifile++; 
    	  std::cout << "   *   File " << ifile << "                 : " << params.
    	  outputName + "-AtContribInter.dat";
    	  space (20 - params.outputName.length ());
    	  std::cout << "*" << std::endl;
    
    	  // Must be more adapted (add /4)
    	  predictedSize += ((nbAtom * 50) / (4 * toMo));

    	} // end of if (params.outputType == 4)



   
// ========================= 5 ================================== // 
          if (params.outputType == 5)
    	{

// ================ P R O M O L   & &   W F X/W F N =======================
          ifile++;
    	  std::cout << "   *   File " << std::setfill('0') << std::setw(2) << ifile << "                : " << params.
    	    outputName + "-nci.dat";
    	  space (31 - params.outputName.length ());
    	  std::cout << "*" << std::endl;
    
    	  // Must be more adapted (add /2)
    	  predictedSize += ((fullSize * 28) + 22) / (2 * toMo);
    	  //std::cout << ( (fullSize*28)+22 ) / (2*toMo) << std::endl;
   
          ifile++; 
    	  std::cout << "   *   File " << std::setfill('0') << std::setw(2) << ifile << "                : " << params.
    	    outputName + "-igm.dat";
    	  space (31 - params.outputName.length ());
    	  std::cout << "*" << std::endl;

          ifile++;
          std::cout << "   *   File " << std::setfill('0') << std::setw(2) << ifile << "                : " << params.
            outputName + "-dgIntra.gnu";
          space (27 - params.outputName.length ());
          std::cout << "*" << std::endl;

          ifile++;
          std::cout << "   *   File " << std::setfill('0') << std::setw(2) << ifile << "                : " << params.
            outputName + "-dgInter.gnu";
          space (27 - params.outputName.length ());
          std::cout << "*" << std::endl;
    
    	  // Must be more adapted (add /2)
    	  predictedSize += ((fullSize * 52) + 4) / (2 * toMo);
    	  //std::cout << ( (fullSize*52)+4 ) / (2*toMo) << std::endl;
    
          ifile++;
    	  std::cout << "   *   File " << std::setfill('0') << std::setw(2) << ifile << "                : " << params.
    	    outputName + "-dens.cube";
    	  space (29 - params.outputName.length ());
    	  std::cout << "*" << std::endl;
    
    	  predictedSize += ((fullSize * 16) + (nbAtom * 45) + 219) / toMo;
    	  //std::cout << ( (fullSize*16) + (nbAtom*45) + 219 ) / toMo << std::endl;
   
          ifile++; 
    	  std::cout << "   *   File " << std::setfill('0') << std::setw(2) << ifile << "                : " << params.
    	    outputName + "-RDG.cube";
    	  space (30 - params.outputName.length ());
    	  std::cout << "*" << std::endl;
    
    	  predictedSize += ((fullSize * 16) + (nbAtom * 45) + 219) / toMo;
    
          ifile++;
    	  std::cout << "   *   File " << std::setfill('0') << std::setw(2) << ifile << "                : " << params.
    	    outputName + "-dgInter.cube";
    	  space (26 - params.outputName.length ());
    	  std::cout << "*" << std::endl;
    
    	  predictedSize += ((fullSize * 16) + (nbAtom * 45) + 219) / toMo;
    
          ifile++;
    	  std::cout << "   *   File " << std::setfill('0') << std::setw(2) << ifile << "                : " << params.
    	    outputName + "-dgIntra.cube";
    	  space (26 - params.outputName.length ());
    	  std::cout << "*" << std::endl;
    
    	  predictedSize += ((fullSize * 16) + (nbAtom * 45) + 219) / toMo;
   
          ifile++; 
    	  std::cout << "   *   File " << std::setfill('0') << std::setw(2) << ifile << "                : " << "nci.vmd";
    	  space (32);
    	  std::cout << "*" << std::endl;
    
    	  predictedSize += 0.05;
    
          ifile++;
    	  std::cout << "   *   File " << std::setfill('0') << std::setw(2) << ifile << "                : " << "igm.vmd";
    	  space (32);
    	  std::cout << "*" << std::endl;

    	  predictedSize += 0.05;

          //  ========== Q M    M O D E    O N L Y  ============
          if (isWFmodeActivated ())
           {

             ifile++;
             std::cout << "   *   File " << std::setfill('0') << std::setw(2) << ifile << "                : " << params.
             outputName + "-coord.xyz";
             space (26);
             std::cout << "*" << std::endl; 

             ifile++;
             std::cout << "   *   File " << std::setfill('0') << std::setw(2) << ifile << "                : " << params.
             outputName + "-AtomDOI.dat";
             space (27 - params.outputName.length ());
             std::cout << "*" << std::endl;

             if ( (data->getNbAtomMolA() == 0)  or  (data->getNbAtomMolB() == 0) ) // no fragmentation scheme
               {
                  ifile++; 
                  std::cout << "   *   File " << std::setfill('0') << std::setw(2) << ifile << "                : weakAtomContrib.vmd";
                  space (20);
                  std::cout << "*" << std::endl;
               }
             else // fragmentation scheme enabled
               {
                  ifile++;
                  std::cout << "   *   File " << std::setfill('0') << std::setw(2) << ifile << "                : " << params.
                  outputName + "-AtContribInter.dat";
                  space (20 - params.outputName.length ());
                  std::cout << "*" << std::endl;
    
                  ifile++;
                  std::cout << "   *   File " << std::setfill('0') << std::setw(2) << ifile << "                : AtContribInter.vmd";
                  space (21);
                  std::cout << "*" << std::endl;
               } // end of  fragmentation scheme enabled
           } // end of  Q M    M O D E  

          //  ========== P R O M O L E C U L A R    M O D E    O N L Y  ============ 
    	  if (!isWFmodeActivated ()) 
    	    { if ( (data->getNbAtomMolA() != 0)  and (data->getNbAtomMolB() != 0) ) // fragmentation mode enabled
                 {
          	      ifile++;
                      std::cout << "   *   File " << std::setfill('0') << std::setw(2) << ifile << "                : AtContribInter.vmd";
          	      space (21);
          	      std::cout << "*" << std::endl;
          
          	      predictedSize += 0.05;
          
                      ifile++;
          	      std::cout << "   *   File " << std::setfill('0') << std::setw(2) << ifile << "                : " << params.
                        outputName + "-coord.xyz";
          	      space (29 - params.outputName.length ());
          	      std::cout << "*" << std::endl;
          
          	      predictedSize += ((nbAtom + 2) * 100) / toMo;
         
                      ifile++; 
          	      std::cout << "   *   File " << std::setfill('0') << std::setw(2) << ifile << "                : " << params.
                    outputName + "-AtContribInter.dat";
          	      space (20 - params.outputName.length ());
          	      std::cout << "*" << std::endl;
          
          	      // Must be more adapted (add /4)
          	      predictedSize += ((nbAtom * 50) / (4 * toMo));

                 } // end of fragmentation mode enabled
             }	// end of P R O M O L E C U L A R    M O D E
           } // end of if (params.outputType==5)

         } // end of  if ( (not isCriticmodeActivated()) and (not isELFmodeActivated()) )
           // ..............................................................................................


      else  if (isCriticmodeActivated() )// critical point search generated two files: cp.vmd and mol-cp.txt
         {
           std::cout << "   *   OUTPUT files";space(52); std::cout << "*" << std::endl;
           std::cout << "   *   File 1                 : cp.vmd                                 *" << std::endl;
           std::cout << "   *   File 2                 : " << params.outputName + "-cp.txt";
           space (32 - params.outputName.length ());
           std::cout << "*" << std::endl; 
           std::cout << "   *   File 3                 : " << params.outputName + "-cp.xyz";
           space (32 - params.outputName.length ());
           std::cout << "*" << std::endl;
         }

      else  if ( isELFmodeActivated() )//
        {
           std::cout << "   *   OUTPUT files";space(52); std::cout << "*" << std::endl;
           std::cout << "   *   File 1                 : elf.vmd                                *" << std::endl;
           std::cout << "   *   File 2                 : " << params.outputName + "-ELF.cube";
           space (30 - params.outputName.length ());
           std::cout << "*" << std::endl;
           std::cout << "   *   File 3                 : " << params.outputName + "-ELF_IGMcolor.cube";
           space (21 - params.outputName.length ());
           std::cout << "*" << std::endl;
        }

      else  if ( isPauli() )//
        {
           std::cout << "   *   OUTPUT files";space(52); std::cout << "*" << std::endl;
           std::cout << "   *   File 1                 : self.vmd                               *" << std::endl;
           std::cout << "   *   File 2                 : " << params.outputName + "-dgInterS.cube";
           space (25 - params.outputName.length ());
           std::cout << "*" << std::endl; 
           std::cout << "   *   File 3                 : " << params.outputName + "-SELF.cube";
           space (29 - params.outputName.length ());
           std::cout << "*" << std::endl;
        }


       // reset the setfill formatting command
       std::cout << std::setfill(' ');

      // Estimate the size of the calculation based on the grid point number 
      if ((data->getNbSteps (0) > 1000) || (data->getNbSteps (1) > 1000)
	  || (data->getNbSteps (2) > 1000))
	{
	  if (!isWFmodeActivated ())
	    {
	      std::cout << std::endl
		<<
		"[INFO ] One (or more) IGM grid dimension(s) is greater that 1 000"
		<< std::
		endl <<
		"[INFO ] Maybe the \"integer\" maximum value will be overtaken"
		<< std::
		endl <<
		"[INFO ] in the current version of IGMPlot, beware ..." <<
		std::endl;
	    }
	  else
	    {
	      std::cout << std::endl
		<<
		"[INFO ] One (or more) IGM grid dimension(s) is greater that 1 000"
		<< std::
		endl <<
		"[INFO ] Maybe the the memory will be not sufficent to manage the current configuration"
		<< std::
		endl <<
		"[INFO ] or the computation time will be very long, beware ..."
		<< std::endl;
	    }
	}


      //  ============= Preparing results storage for PROMOLECULAR calculations ====================== 
      if (!isWFmodeActivated ())
	{
	  posGrid = new axis_t[nbThreads];

	  /* Preparing storage for deltag values (initializing variables) */
	  results = new Results (data->getNbAtom (), fullSize);

	  /* Instanciation of the threads' nodes */
	  nodes = new Node *[nbThreads];
	  for (unsigned int i (0); i < nbThreads; ++i)
	    {
	      nodes[i] =
		new Node (data->getNbAtom (), data->getNbAtomMolA (), *data);
	    }
	}

    }// end of else of if (isIBSImodeActivated()) :  CUBIC GRID (QM or PROMOLECULAR)

 
    // ######################## P A R S E     R K F ###################### 
    if ( isParseRKF() )
     {
     std::cout << "   *   RKF Binary parsed                                               *" << std::endl;  
     std::cout << "   *   File 1                 : ";
     std::cout << params.outputName + "-coord.xyz";
     space (29 - params.outputName.length());
     std::cout << "*" << std::endl;
     std::cout << "   *   File 2                 : adf-rkf.txt                            *" << std::endl;
     std::cout << emptyLine << std::endl;
     std::cout <<
    "   * ----------------------------------------------------------------  *";
     std::cout << std::endl;

     writeXYZ(params, data, data->getNbAtom(), data->atomPositions);
     }
      // ...................... END isParseRKF ............

  // initialize the runinfo file state to false (true = runinfo file created by printCurrentStet)
  runinfostate = false;

}               // END OF CONSTRUCTOR of NCISOlver

/* -------------------------------------------------------------------------------------------------------------*/

NCISolver::~NCISolver ()
{
  /* Deleting objects */
  delete data;

  if (!isWFmodeActivated ())
    {
      delete results;

      /* Destruction of the threads' nodes */
      for (unsigned int i (0); i < nbThreads; ++i)
	{
	  delete nodes[i];
	}
      delete[]nodes;

      delete[]posGrid;
    }


  // JC WFN to validate at the end ...
  if (isWFmodeActivated ())
    {
      wf_cleaning ();
    }
}


/* ===================  C A L C P R O P S _ W F N    M E T H O D ====================================*/
void
NCISolver::calcprops_wfn()
{
  unsigned int counter=0;
  // get the starting time
  gettimeofday(&start,NULL);
  gettimeofday(&interm,NULL);

//  printCurrentState();
      
  unsigned int nbAtoms        = data->getNbAtom();
  unsigned int nbSteps0       = data->getNbSteps(0);
  unsigned int nbSteps1       = data->getNbSteps(1);
  unsigned int nbSteps2       = data->getNbSteps(2);
  positions_t atomCoordinates = data->atomPositions;


  // variable for main RESULTS:
  double*** rho=NULL;
  double*** RDG=NULL;
  double*** deltag=NULL;
  double*** deltaginter=NULL;
  double*** ELFarray = nullptr;
  double*** KE = nullptr; // Kinetic excess (G-GW)
  double*** ELFColor = nullptr; // to color code the ELF iso-surface according to the SELF strategy
  double*** deltagFC=NULL;     // same as deltag      but accounting for frame change
  double*** deltaginterFC=NULL;  // same as deltaginter but accounting for frame change
  double**  D=NULL; // density matrix for Pauli calculation

  // It is the more critical part in memory allocation
  try
    {
      D                  = new double*[fragNbPrim]; // density matrix symetrical array
      setDensityMatrixForPauli(D);                  // Note that for ELF tretament fragNbPrim = allPrim !

      rho                = new double**[nbSteps0];
      RDG                = new double**[nbSteps0];
      deltag             = new double**[nbSteps0];
      deltaginter        = new double**[nbSteps0];
      deltagFC           = new double**[nbSteps0];
      deltaginterFC      = new double**[nbSteps0];
	  
      for(unsigned int i=0;i<nbSteps0;++i)
	{
	      
	  rho[i]           = new double*[nbSteps1];
	  RDG[i]           = new double*[nbSteps1];
	  deltag[i]        = new double*[nbSteps1];
	  deltaginter[i]   = new double*[nbSteps1];
          deltagFC[i]      = new double*[nbSteps1];
          deltaginterFC[i] = new double*[nbSteps1];
	      
	  for(unsigned int j=0;j<nbSteps1;++j)
	    {
	      rho[i][j]           = new double[nbSteps2];
	      RDG[i][j]           = new double[nbSteps2];
	      deltag[i][j]        = new double[nbSteps2];
	      deltaginter[i][j]   = new double[nbSteps2];
              deltagFC[i][j]      = new double[nbSteps2];
              deltaginterFC[i][j] = new double[nbSteps2];

		  
	      for(unsigned int k=0;k<nbSteps2;++k)
		{
		  rho[i][j][k]           = 0.0;
		  RDG[i][j][k]           = 0.0;
		  deltag[i][j][k]        = 0.0;
		  deltaginter[i][j][k]   = 0.0;
                  deltagFC[i][j][k]      = 0.0;
                  deltaginterFC[i][j][k] = 0.0;

		} // end over k
	    } // end over j
	} // end over i
	  
    } // end of  try allocating global arrays
  catch (const std::bad_alloc& bad_alloc_error)
    {
      std::cerr << std::endl
		<< "[ERROR] Allocation failed: bad_alloc error catched" << std::endl
		<< "[ERROR] The chosen increments have generated a too high amount of data for the shared data structures" << std::endl
		<< "[ERROR] that the current version of IGMplot is not able to manage, sorry." << std::endl
		<< "[ERROR] The program will now exit." << std::endl << std::endl;
      exit(EXIT_FAILURE);
    }

      
  double** heigs = NULL;
      
  double*** gradPrim = NULL;
      
  double**** hess = NULL;
  double**** eigenVect = NULL;
      
  double*** dx = NULL;
  double*** dy = NULL;
  double*** dz = NULL;
  double*** d2 = NULL;
      
   // INTEGRATIONS per processor
  // General Atomic Decomposition 
  double** dgAtSum_proc = NULL;
  double** dgAtSumW_proc = NULL;
  double** dgAtSumW2_proc = NULL;
  double** dgInterAtSum_proc = NULL;

  // PAULI atomic decomposition
     double*** pauli = NULL;

  // Atomic decomposition
  double***  ggIGMAt=NULL;

  try
    {
      //Allocation for making    private pointer shareable

      // INTEGRATIONS per processor
      dgAtSum_proc      = new double*[nbThreads];
      dgAtSumW_proc     = new double*[nbThreads];
      dgAtSumW2_proc    = new double*[nbThreads];
      dgInterAtSum_proc = new double*[nbThreads];

      // SELF analysis (kinetic excess)
      if (isPauliAtomic() or isTOPELF() ) {
         pauli        = new double**[nbThreads];
         for(unsigned int ithread=0;ithread<nbThreads;++ithread)
            {
                pauli[ithread] = new double*[nbAtoms];
                for (unsigned iat=0; iat<nbAtoms; ++iat) 
                   {
                      pauli[ithread][iat] = new double[nbAtoms];
                      for (unsigned jat=0; jat<nbAtoms; ++jat) 
                         {
                           pauli[ithread][iat][jat] = 0.0; 
                         } // end of for (unsigned jat=0;
                   } // end of for (unsigned iat=0; 
            } // end over threads
      } // end of if (isPauliAtomic())


      // atomic decomposition
      ggIGMAt = new double**[nbThreads];
      for(unsigned int ithread=0;ithread<nbThreads;++ithread)
        { 
           dgAtSum_proc[ithread]      = new double[nbAtoms];
           dgAtSumW_proc[ithread]     = new double[nbAtoms];
           dgAtSumW2_proc[ithread]    = new double[nbAtoms];
           dgInterAtSum_proc[ithread] = new double[nbAtoms];
           
           for (unsigned iat=0; iat<nbAtoms; iat++)
              {
                dgAtSum_proc[ithread][iat]      = 0.0;
                dgAtSumW_proc[ithread][iat]     = 0.0;
                dgAtSumW2_proc[ithread][iat]    = 0.0;
                dgInterAtSum_proc[ithread][iat] = 0.0;
              }


           ggIGMAt[ithread] = new double*[nbAtoms]; 
           for (unsigned int iat=0; iat<nbAtoms; ++iat)
                {
                 ggIGMAt[ithread][iat] = new double[3];
                }
        }// end over threads

      
      heigs = new double*[nbThreads];
      
      gradPrim = new double**[nbThreads];

      hess = new double***[nbThreads];
      eigenVect = new double***[nbThreads];

      dx = new double**[nbThreads];
      dy = new double**[nbThreads];
      dz = new double**[nbThreads];
      d2 = new double**[nbThreads];

      for(unsigned int i=0;i<nbThreads;++i)
	{
	  heigs[i] = new double[3];

	  gradPrim[i] = new double*[npri];

	  for( unsigned int j=0;j<npri;++j)
	    {
	      gradPrim[i][j] = new double[3];
	    }

	  hess[i] = new double**[nbSteps0];
          eigenVect[i] = new double**[nbSteps0];
	  
	  for( unsigned int j=0 ; j < nbSteps0 ; ++j )
	    {
	      hess[i][j]=new double*[3];
              eigenVect[i][j]=new double*[3];
	      for(unsigned int k=0;k<3;++k)
		{
		  hess[i][j][k]= new double[3];
                  eigenVect[i][j][k]= new double[3];
		}
	    }
	  
	  dx[i] = new double*[nbSteps0];
	  dy[i] = new double*[nbSteps0];
	  dz[i] = new double*[nbSteps0];
	  d2[i] = new double*[nbSteps0];


	  for( unsigned int j=0 ; j < nbSteps0 ; ++j )
	    {
	      dx[i][j]=new double[nbAtoms];
	      dy[i][j]=new double[nbAtoms];
	      dz[i][j]=new double[nbAtoms];
	      d2[i][j]=new double[nbAtoms];
	      
	    }

	}
    } // end of trying allocating local arrays
  catch (const std::bad_alloc& bad_alloc_error)
    {
      std::cerr << std::endl
		<< "[ERROR] Allocation failed: bad_alloc error catched" << std::endl
		<< "[ERROR] The chosen increments have generated a too high amount of data for the thread private data structures" << std::endl
		<< "[ERROR] that the current version of IGMplot is not able to manage, sorry." << std::endl
		<< "[ERROR] The program will now exit." << std::endl << std::endl;
      exit(EXIT_FAILURE);
    }

if (not isCriticmodeActivated())
{ 
  unsigned int jp, kp, ip, threadID;
#ifndef No_OpenMP	  
#pragma omp parallel for private(jp, kp, ip, threadID) shared(rho, RDG, deltag, deltaginter, deltagFC, deltaginterFC, D, KE, ELFColor, ELFarray, nbSteps0, nbSteps1, nbAtoms, atomCoordinates, counter, heigs, gradPrim, hess, dx, dy, dz, d2, ggIGMAt, dgAtSum_proc, dgAtSumW_proc, dgAtSumW2_proc, dgInterAtSum_proc, pauli) schedule(dynamic) collapse(2)
#endif
  //loop over Y     dimension ======================================== Y  axis ===============    
  for ( int gridYAxis=0; gridYAxis < data->getNbSteps(1); ++gridYAxis )
    {
      //loop over Z     dimension ======================================== Z  axis ===============
      for ( int gridZAxis=0; gridZAxis < data->getNbSteps(2); ++gridZAxis )
	{
	  //for index compatibility with older code:
          jp=gridYAxis;
	  kp=gridZAxis;
          
#ifndef No_OpenMP
          threadID = omp_get_thread_num();
#else
          threadID = 0;
#endif

          //loop over X     dimension ======================================== X  axis ===============
	  for( unsigned int i=0 ; i < nbSteps0 ; ++i )
	    {
	      for(unsigned int j=0; j<3;++j)
		{
		  for(unsigned int k=0;k<3;++k)
		    {
		      hess[threadID][i][j][k]      = 0.0;
                      eigenVect[threadID][i][j][k] = 0.0;
		    }
		      
		}
	    } // end of X axis ......................................................................

			      
	  // calculate distances between current grid point and every atom
	  for(unsigned int iat=0; iat<nbAtoms; ++iat)
	    {
              //loop over X     dimension ======================================== X  axis ===============
	      for(unsigned int i=0 ; i < nbSteps0 ; ++i )
		{
		  //for index compatibility with older code:
		  ip = i;

		  // JC : based on Emmanuel code (CAST_double, params.increments, ...)
		  dx[threadID][ip][iat]= data->getMinCoord(0) + i * params.increments[0] - atomCoordinates.xValues[iat];

		  dy[threadID][ip][iat]= data->getMinCoord(1) + gridYAxis * params.increments[1] - atomCoordinates.yValues[iat];

		  dz[threadID][ip][iat]= data->getMinCoord(2) + gridZAxis * params.increments[2] - atomCoordinates.zValues[iat];


		  d2[threadID][ip][iat] =
		    dx[threadID][ip][iat]*dx[threadID][ip][iat] +
		    dy[threadID][ip][iat]*dy[threadID][ip][iat] +
		    dz[threadID][ip][iat]*dz[threadID][ip][iat];
		} // end of X axis ......................................................................

	    } // end of LOOP over atoms


          // ===========================================================================
          // L O C A L   V A R I A B L E S   f o r   t h e   C U R R E N T   T H R E A D
          // ==========================================================================

          // local array to store the primitives at current grid point
	  double** chi = new double*[npri];
	  for(unsigned int i=0;i<npri;++i)
	    {
	      chi[i] =  new double[10];
	    }
          // General and Inter atomic decomposition
          double *deltagAt  = new double[nbAtoms];			      
	  double *dgInterAt = new double[nbAtoms];
			      
          // local array to store the Molecular Orbitals at current grid point
          // ! if FRAG1+2 < whole system ==> MO limited to fragments 1 and 2 primitives
	  double** phi     = new double*[molecularOrbitals.size()];
	  for(std::vector<moleculeOrbital>::size_type imo=0 ; imo< molecularOrbitals.size() ; ++imo)
	    {
	      phi[imo]     = new double[10];
	    }
          double rhoValue; // ED value

          // distance between the current node and the current atom needed for the calculation of the promol ED
          double* dist = new double[nbAtoms];

          // quantities needed to compute the Hirschfeld ED Gradients
          double* rhoFree = new double[nbAtoms]; // Atomic contrib to the total promol ED
          double* pregradFree = new double[nbAtoms]; // Scalar quantity common to the 3 promol ED Grad components 

          // real gradient of the FRAG1 + FRAG2 system
          double gg[3];
          double gradnorm = 0.0;

          // Hirshfeld Atomic Electron Density
          double* rhoAtom = new double[nbAtoms];
          
          // Hirschfeld or GBP atomic ED gradient
          double** gradAtom = new double*[nbAtoms];
          for (unsigned int iat=0; iat<nbAtoms; ++iat) {
              gradAtom[iat] = new double[3]; //for x,y,z components
          }

          double deltagIntraCURRENT, qgIntraCURRENT;
          double deltagInterCURRENT, qgInterCURRENT;
          
          // nb of primitives considered for calculating total rho, total ED grad, and total MO,
          // which will depend on dgscaled option
          // if dgscaled option is enabled (to divide deltag by rho or within SELF formalism dividing
          // (8 rho G - nabla Rho^2 ) by rho, wee need the real total rho value !
          // --> in that case, basisSize and basisSet cover the overall set of AOs
          int            basisSize;
          unsigned int*  basisSet;// = new unsigned int[npri];
          bool           fullRHOGRAD; // says if the ED and its gradient are for the full system or for fragments


          // ... end of L O C A L   V A R I A B L E S .............................................


          //loop over X     dimension ======================================== X  axis ===============
	  // calculate primitives at the points along X axis
	  for( unsigned int i=0 ; i < nbSteps0 ; ++i )
	    {
              // JC for reading compatibility 
              ip = i; 

              // INITIALIZATIONS for CURRENT grid point
              gg[0] = 0.0;
              gg[1] = 0.0;
              gg[2] = 0.0;
              deltagIntraCURRENT =0.0;
              deltagInterCURRENT =0.0;
                  qgIntraCURRENT =0.0;
                  qgInterCURRENT =0.0;


              for (unsigned int iat=0; iat<nbAtoms; ++iat)
                  {
                     dist[iat]        = 0.0;
                     rhoFree[iat]     = 0.0;
                     pregradFree[iat] = 0.0;
                  }

              // ============================================================================================
              //         C O M P U T E      M O s, p r i m i t i v e s, r h o, h e s s
              //         CPU time-consuming part 
              // ============================================================================================
              // if dgscaled or isPAuli or isELF is invoked by user: the total rho & ED grad and phi[imo] have to be calculated
              // even though FRAG1+FRAG2 < ALL
              // computing rho&gradrho_total is the general case (since FRAG1+2 generally represent the whole system)
              // we force here all AOs to be computed and involved in ED and grad ED and IGM and PAULI calculations
              if (isdgSCALED() or isPauli() or isELFmodeActivated())  // ALL AO are considered to obtain rho, grad and hessian
              {
                 basisSize = npri;
                 basisSet  = allPrim;
                 fullRHOGRAD=true;
              } 
              else  // IGM mode and only AO of FRAG1+FRAG2 are considered (which can represent the whole system if FRAG1+2 = ALL)
              {
                 basisSize = fragNbPrim; // fragNbPrim <=npri
                 basisSet  = fragPrim;
                 fullRHOGRAD=false;
              }
              calcQMAO(basisSize, basisSet, d2[threadID][ip], dx[threadID][ip], dy[threadID][ip], dz[threadID][ip], chi);
              calcQMPROP(basisSize, basisSet, chi, phi, rhoValue, gg, hess[threadID][ip]);
              rhoValue = std::max<double>(rhoValue, 1e-30);
              /*  .................... END calculating MO, rho and hessian .......................................... */


       // ==================================================================================
       // I   G    M                              t    r    e    a    t    m    e    n    t
       //                    C A R T E S I A N    
       // ==================================================================================
       //       
       //                      w i t h    U S E R    O R I E N T A T I O N
       //....................................................................................
       if ( (not isPauli()) and (not isELFmodeActivated()) ) 
       { // PURE IGM treatment dg OR dgscaled

             // HIRSHFELD PARTITION
             if (isHirshmodeActivated()) {
    
                  // =================================================================
                  // H I R S H F E L D   d e l t a g
                  // =================================================================
    
                  for (unsigned int iat=0; iat<nbAtoms; ++iat) 
                      {
                         dist[iat] = std::sqrt(d2[threadID][ip][iat]);
                         dist[iat] = (dist[iat] < R_TRESHOLD ? R_TRESHOLD : dist[iat]);
                      }
    
                  // compute the individual atomic quantities rhoFree and pregradFree needed for rhopro and gradpro
                  // for ALL atoms (even if FRAG1+FRAG2< whole system)
                  this-> rhoFree__pregradFree(dist, rhoFree, pregradFree);
    
                  // get gradAtom for atoms
                  // from rho and grad limited to (FRAG1+FRAG2) or not limited at all (whole system) 
                  // (depending on fullRHOGRAD)      
                  this ->gradAtomHirsh(fullRHOGRAD,threadID, rhoValue, gg, // rhovalue and gg come from calcQM
                                       dx, dy, dz, ip, rhoFree, pregradFree, gradAtom, rhoAtom); // rhoAtom not used so far
    
                  // get deltags from previously calculated gradAtom
                  // does no depend on dgscaled option at this step (done before to obtain gradAtomi used as input here)
                  this->IGMH(gradAtom, deltagIntraCURRENT, deltagInterCURRENT,qgIntraCURRENT,qgInterCURRENT);
             } //  ................................................ end of H I R S H F E L D  
    
             else { // G B P   PARTITION    
    
                  // ===================================================================
                  // G R A D I E N T     B A S E D     P A R T I T I O N    d e l t a g 
                  // ===================================================================
    
                  // get deltags, qgs  and gradPrim (gradPrim won't be used for the moment)
                  // if pure dg  mode: basisSize = fragNbPrim (only fragments are considered), 
                  // if dgscaled mode: basisSize = nprim (=all)
                  this->IGM(chi, phi, dx[threadID][ip], dy[threadID][ip], dz[threadID][ip], gradPrim[threadID], 
                            deltagIntraCURRENT, deltagInterCURRENT, qgIntraCURRENT, qgInterCURRENT);
                  } //  ................................................ end of  G B P
    
    
             // =================================================================
             //  S I G N E D    E L E C T R O N    D E N S I T Y                    
             // =================================================================
             // compute the signed electron density:
             // ! hessian eigenvalue and sign of rho
             heigs[threadID][0]=0;
             heigs[threadID][1]=0;
             heigs[threadID][2]=0;
    
             // we need heigs here (eigenvectors, although returned, are not employed immediately, but after (basis change)) 
             this->getLambdaOfHessian(hess[threadID][ip],heigs[threadID],eigenVect[threadID][ip]);
    
             // SIGNED Electron Density:
             // Note that if dgscaled option is disabled, Electron Density is limited to FRAG1+FRAG2 (which can be < whole system)
             // otherwise, if dgscaled option is enabled, Electron Density is the ED of the whole system in anycase
             if(heigs[threadID][1]<0.0)
               {
                 rho[ip][jp][kp]=-rhoValue;
               }
             else
               {
                 rho[ip][jp][kp]=rhoValue;
               } 
             //  ...................................... end of   E L E C T R O N   D E N S I T Y
    
             // =================================================================
             //  d e l t a g      o r    d e l t a g / r h o
             //  =================================================================
             // save the deltagIntra and Inter contributions in cube (to be saved in cube files)
             if (not isdgSCALED()) // pure dg
               {
                  deltag[ip][jp][kp]      = deltagIntraCURRENT;
                  deltaginter[ip][jp][kp] = deltagInterCURRENT;
               }
             else  // deltag is rescaled by 1/rho
               {
                  deltag[ip][jp][kp]      = deltagIntraCURRENT / (rhoValue+epsrhodgscaled); // for graphical purposes 
                                                                        // to draw dgIntra/rho isosurfaces
                  // note that rhoValue is the true total ED in that case (option dgscaled enabled)
                  // {deltaginter[ip][jp][kp] = deltagInterCURRENT / (rhoValue);} // for graphical purposes 
                  deltaginter[ip][jp][kp] = deltagInterCURRENT/(rhoValue+epsrhodgscaled); // for graphical purposes 
                                                                      // to draw dgInter/rho isosurfaces later on
               } // end of mode dgScaled 
    
             // ========================================================================
             //  G R A D I E N T 
             //  of FRAG1+FRAG2 (which can be the whole system) if dgscaled option disabled
             //  else of the whole system if dgscaled option enabled
             // ========================================================================
    	     gradnorm = std::sqrt(gg[0]*gg[0] + gg[1]*gg[1] + gg[2]*gg[2]); // gg come from calcQM
             gradnorm = std::max<double>(gradnorm, 1e-30);
    
             //  ......................................... end of  R E A L   G R A D I E N T
    
    
             // =====================================================
             //     R   D   G 
             // of FRAG1+FRAG2 (which can be the whole system) if dgscaled option disabled
             // else of the whole system if dgscaled option enabled
             // =====================================================
             RDG[ip][jp][kp]    = gradnorm / (constValue * std::pow(rhoValue, FOUR_THIRD));
    
    
             // ==================================================================================
             // I   G    M                              t    r    e    a    t    m    e    n    t
             //                    C A R T E S I A N    
             // ==================================================================================
             // 
             // w i t h    R E O R I E N T A T I O N   f o r    S C O R I N G      C A L C U L A T I O N S
             //...........................................................................................
    
    
             // =================================================================
             //  B A S I S   C H A N G E                             
             // =================================================================
             // Change the orientation of the vectors primitive gradients and dx,dy,dz in the new reference frame
             // defined by the eigenvector of the ED hessien
             
             // for VECTORS PROPERTIES ONLY !! (chi', dx, phi', rho')
             // eric: IGM model, change to the new reference frame defined by the three Hessian eigenvectors
             // --> requires the transformation of primitive gradients AND displacements dx from current axis system (wfn)
             // to the UNIQUE axis system relying on the ED Hessian eigenvectors                  
             
             // basis change for the density gradient vector
             double matrixHessianEigVec[3][3];
             // define the change-of-basis matrix to go from the WFN reference frame to the Curvature reference frame 
             // assuming that the 3 eigenvectors of the ED hessian are set in columns in eigenVect structure and
             // all eigenvectors are orthonormal ==> the change-of-basis matrix to go from the WFN reference frame 
             // to the Curvature reference frame
             // is the transposed matrix (i.e. the inverse here) of the matrix formed with the 3 eigenVect and 
             // is called now: matrixHessianEigVec
             getTransposed(eigenVect[threadID][ip], matrixHessianEigVec);
    
    
             // xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
             // ==> basis change for the primitive gradient chi (atomic orbital gradient)
             //                          Molecular orbitals derivatives
             //                          position vectors dx,y,z
             //                          ED gradient
             // xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
             // ============================================================================================
             // basisSize, basisSet depends on option dgscaled (if enabled: basisSize=full size npri,
             // basisSet=full allPrim, otherwise they are limited to FRAG1+FRAG2)
             basischange(basisSize, basisSet, matrixHessianEigVec, chi,
             	 dx[threadID][ip], dy[threadID][ip], dz[threadID][ip], phi, gg);
             
             //  ...................................... end of B A S I S   C H A N G E            
             
             // =================================================================
             // H I R S H F E L D   d e l t a g   B A S I S   C H A N G E D
             // =================================================================
             if (isHirshmodeActivated()) {
             
              // dist has already been evaluated (not orientation dependent)
              // rhoFree, pregradFree have already been calculated (not orientation dependent)
             
             
              // get gradAtom for atoms
              // from total rho and grad limited to (FRAG1+FRAG2) or not limited at all (whole system) 
              // (depending on fullRHOGRAD)     
              this ->gradAtomHirsh(fullRHOGRAD, threadID, rhoValue, gg,
             		 dx, dy, dz, ip, rhoFree, pregradFree, gradAtom, rhoAtom); // rhoAtom not used so far
             
              // get deltags from previously calculated gradAtom
              deltagIntraCURRENT =0.0;
              deltagInterCURRENT =0.0;
              // does no depend on dgscaled option at this step (done before to obtain gradAtom used as input here)
              this->IGMH(gradAtom, deltagIntraCURRENT, deltagInterCURRENT,qgIntraCURRENT,qgInterCURRENT);
             
             } //  ................................................ end of H I R S H F E L D 
    
             else { // G B P   d e l t a g 
              // ===============================================================================================
              // G R A D I E N T     B A S E D     P A R T I T I O N    d e l t a g    B A S I S   C H A N G E D
              // ===============================================================================================
             
              // get deltags and gradPrim (required for the atomic decomposition in the following)
              deltagIntraCURRENT =0.0;
              deltagInterCURRENT =0.0; 
              // does no depend here on fullRHOGRAD option at this step (it was done before to obtain phi used as input here)
              // --> gradPrim(input) were calculated before either from the total MOs or from MOs limited to FRAG1+FRAG2
              this->IGM(chi, phi, dx[threadID][ip], dy[threadID][ip], dz[threadID][ip], gradPrim[threadID],
             		 deltagIntraCURRENT, deltagInterCURRENT, qgIntraCURRENT, qgInterCURRENT);
             
              // get the atom gradients for next Atomic decomposition (prerequisite: gradPrim)
              gradAtomGBP(fragNbPrim, fragPrim, gradPrim[threadID], gradAtom);
             
             } //  ................................................ end of  G B P
             
             // save the deltagIntra and Inter contributions in cube (to  obtain the rotational independent scores)
             deltagFC[ip][jp][kp]      = deltagIntraCURRENT;  // FC <-> Frame Changed
             deltaginterFC[ip][jp][kp] = deltagInterCURRENT;  // FC <-> Frame Changed
    
    
             // ============================================================
             //  G E N E R A L     A T O M I C    D e c o m p o s i t i o n     
             // ============================================================
             
             // prerequisite: gradAtom  AND real gradient (for FRAG1+FRAG2)
             // compute the non-interacting reference for each atom
             // initialization for atomic decomposition
             for(unsigned int iat=0; iat<nbAtoms; ++iat)
             {
              ggIGMAt[threadID][iat][0] = 0.0;
              ggIGMAt[threadID][iat][1] = 0.0;
              ggIGMAt[threadID][iat][2] = 0.0;
             } // end of for(unsigned int iat=0; iat<nbAtoms; ++iat)
             for(unsigned int iat=0; iat<nbAtoms; ++iat)
             {
              ggIGMAt[threadID][iat][0]  = std::abs(gradAtom[iat][0]) + 
             	 std::abs( gg[0] - gradAtom[iat][0] );
              ggIGMAt[threadID][iat][1]  = std::abs(gradAtom[iat][1]) + 
             	 std::abs( gg[1] - gradAtom[iat][1] );
              ggIGMAt[threadID][iat][2]  = std::abs(gradAtom[iat][2]) + 
             	 std::abs( gg[2] - gradAtom[iat][2] );
             }// end over atoms
    
    
             // deltagAt                     
             for(unsigned int iat=0;iat<nbAtoms;++iat)
             {
              deltagAt[iat] = 0.0;
             } // end over atoms
             for(unsigned int iat=0; iat<nbAtoms; ++iat)
             {
              double grad2IGMAt    =  ggIGMAt[threadID][iat][0]*ggIGMAt[threadID][iat][0] +
             	 ggIGMAt[threadID][iat][1]*ggIGMAt[threadID][iat][1] +
             	 ggIGMAt[threadID][iat][2]*ggIGMAt[threadID][iat][2];
             
              // contribution of Atom At to the total interaction
              deltagAt[iat] = std::sqrt(grad2IGMAt)  - gradnorm; 
             
             } // end over atoms
             
             //  ........ end of G E N E R A L     A T O M I C     D E C O M P O S I T I O N 
             
             
             // ==================================================================================== //
             //              I N T E R        A T O M I C    D E C O M P O S I T I O N               // 
             // ==================================================================================== //
             
             // LOCAL VARIABLES
             double gradA[3], gradB[3], grad1[3], grad2[3];
             
             // initializations
             gradA[0]=0.0;
             gradA[1]=0.0;
             gradA[2]=0.0;
             
             gradB[0]=0.0;
             gradB[1]=0.0;
             gradB[2]=0.0;
             
             grad1[0]=0.0;
             grad1[1]=0.0;
             grad1[2]=0.0;
             
             grad2[0]=0.0;
             grad2[1]=0.0;
             grad2[2]=0.0;
             
             // prepare the calculation by getting ED grad of each fragment 
             for (unsigned int iat=0; iat<nbAtoms; ++iat)
             {
              // compute ED gradient of fragment A
              if (isInMoleculeA(iat+1) )  // isInmoleculeA takes a 1-based numbered atom index parameter
              {
             	 gradA[0] = gradA[0] + gradAtom[iat][0];
             	 gradA[1] = gradA[1] + gradAtom[iat][1];
             	 gradA[2] = gradA[2] + gradAtom[iat][2];
              }
             
              else if (isInMoleculeB(iat+1) )  // isImoleculeB takes a 1-based numbered atom index parameter
              {
             	 gradB[0] = gradB[0] + gradAtom[iat][0];
             	 gradB[1] = gradB[1] + gradAtom[iat][1];
             	 gradB[2] = gradB[2] + gradAtom[iat][2];
              }
             } // end over iat
             
             // calculate 2 references for each atom: (1) non interacting with other fragment
             //                                       (2) interacting     with other fragment
             // Equations (5) and (6) of Paper: J. Chem. Info. Model 2020, 60, 1, 268–278 https://doi.org/10.1021/acs.jcim.9b01016
             
             for (unsigned int iat=0; iat<nbAtoms; ++iat)
             {
              // compute ED gradient of fragment A
              if (isInMoleculeA(iat+1) )  // isImoleculeA takes a 1-based numbered atom index parameter
              {
             	 grad1[0] = std::abs(gradAtom[iat][0]) + std::abs(gradB[0]) + std::abs(gradA[0]-gradAtom[iat][0]);
             	 grad1[1] = std::abs(gradAtom[iat][1]) + std::abs(gradB[1]) + std::abs(gradA[1]-gradAtom[iat][1]);
             	 grad1[2] = std::abs(gradAtom[iat][2]) + std::abs(gradB[2]) + std::abs(gradA[2]-gradAtom[iat][2]);
             
             	 grad2[0] = std::abs(gradAtom[iat][0]  +          gradB[0]) + std::abs(gradA[0]-gradAtom[iat][0]);
             	 grad2[1] = std::abs(gradAtom[iat][1]  +          gradB[1]) + std::abs(gradA[1]-gradAtom[iat][1]);
             	 grad2[2] = std::abs(gradAtom[iat][2]  +          gradB[2]) + std::abs(gradA[2]-gradAtom[iat][2]);
             	 dgInterAt[iat] = getNormOfVector(grad1) - getNormOfVector(grad2);
              } // end fragA
              else if (isInMoleculeB(iat+1) )  // isImoleculeB takes a 1-based numbered atom index parameter
              {
             	 grad1[0] = std::abs(gradAtom[iat][0]) + std::abs(gradA[0]) + std::abs(gradB[0]-gradAtom[iat][0]);
             	 grad1[1] = std::abs(gradAtom[iat][1]) + std::abs(gradA[1]) + std::abs(gradB[1]-gradAtom[iat][1]);
             	 grad1[2] = std::abs(gradAtom[iat][2]) + std::abs(gradA[2]) + std::abs(gradB[2]-gradAtom[iat][2]);
             
             	 grad2[0] = std::abs(gradAtom[iat][0]  +          gradA[0]) + std::abs(gradB[0]-gradAtom[iat][0]);
             	 grad2[1] = std::abs(gradAtom[iat][1]  +          gradA[1]) + std::abs(gradB[1]-gradAtom[iat][1]);
             	 grad2[2] = std::abs(gradAtom[iat][2]  +          gradA[2]) + std::abs(gradB[2]-gradAtom[iat][2]);
             	 dgInterAt[iat] = getNormOfVector(grad1) - getNormOfVector(grad2);
              }
              else {
             	 dgInterAt[iat] = 0.0;
              } // end of else 
             
             } // end over iat 
             
             //  ........ end of I N T E R    A T O M I C     D E C O M P O S I T I O N
    
    
    
             // ================================================================================================
             // I   N    T    E    G    R    A    T    I    O    N    S             
             // ================================================================================================
             
             // ==================================================================================== //
             //              G e n e r a l    A T O M I C    D E C O M P O S I T I O N               // 
             // ==================================================================================== //
             
             // "Non-Bonding" interactions domain (two criteria: rho and qg) 
             for (unsigned int iat=0; iat<nbAtoms; ++iat)
             { 
              // total sum for the current atom:
              dgAtSum_proc[threadID][iat] = dgAtSum_proc[threadID][iat] + deltagAt[iat];
             
              // calculate the atom qg value (depends on the atom !)
              // qg = gradRhoIGM,At / gradRho  (since dgAt = gradRhoIGM,At - gradRho))
              double qgAtom = (deltagAt[iat]+gradnorm) / gradnorm;
             
              // only keep points in peaks (not in the flat part of dg)
              if (qgAtom > qgThresh) {
             	 // "Non-Bonding" interactions domain ((two criteria: rho and qg) 
             	 //if (tmprho>0.00) {
             	 //    dgAtSumNB[iat] = dgAtSumNB[iat] + deltagAt[iat];
             	 //}// end of if (tmprho>0.00)
             
             	 // Non-Covalent interactions domain (two criteria: rho and qg) 
             	 //else if ( (tmprho>-0.09) and (tmprho<0) ) 
             	 if ( (rho[ip][jp][kp]>qgRhoThresh) and (rho[ip][jp][kp]<0) ) {
             		 dgAtSumW_proc[threadID][iat] = dgAtSumW_proc[threadID][iat] + deltagAt[iat];
             	 }// end of if (tmprho>-0.09) and (tmprho<0) 
             
             	 if ( (rho[ip][jp][kp]>qgRhoThresh-EDepsilon) and (rho[ip][jp][kp]<0) ) {
             		 dgAtSumW2_proc[threadID][iat] = dgAtSumW2_proc[threadID][iat] + deltagAt[iat];
             	 }// end of if (tmprho>-0.09) and (tmprho<0)  
             
             	 // Covalent interactions domain (two criteria: rho and qg)
             	 //else if (tmprho<=-0.09) 
             	 //     dgAtSumS[threadID][iat] = dgAtSumS[threadID][iat] + deltagAt[iat];
             	 //   //end of if (tmprho<=-0.09) 
              } // end of if (qgAtom > qgThresh) 
             
             } // end over atoms
             
             // ................. E N D  O F   G E N E R A L  A T O M I C  D E C O M P O S I T I O N ..........
             
             
             // ==================================================================================== //
             //              I n t e r    A T O M I C    D E C O M P O S I T I O N               // 
             // ==================================================================================== //
             for (unsigned int iat=0; iat<nbAtoms; ++iat)
             { 
              // total sum for the current atom:
              dgInterAtSum_proc[threadID][iat] = dgInterAtSum_proc[threadID][iat] + dgInterAt[iat];
             } // end over atoms
    

             // ................. E N D  O F   I N T E R  A T O M I C  D E C O M P O S I T I O N ..........

             //............................... E N D   I N T E G R A T I O N S    ......................................

         } // end of if ( (not isPauli()) and (not isELFmodeActivated()) )




    	 // ===============================================================
    	 //         E L F                                                   
    	 // ===============================================================
    	 if (isELFmodeActivated())
    	 {
    		 // ... Kinetic energy density (Fermion)
    		 // compute kinetic energy density using all the MOs 
    		 // beyond the thresholdMO cutoff and using primitives 
    		 // of atoms within the IBSI cutoff (default = all)
    
    		 double G = 0.0;
    		 for( unsigned int imo=0 ; imo< molecularOrbitals.size() ; ++imo)
    		 {
    			 G = G + molecularOrbitals[imo].occupancy*(phi[imo][1]*phi[imo][1] +  // contrib. of MO x gradient
    					 phi[imo][2]*phi[imo][2] +  // contrib. of MO y gradient
    					 phi[imo][3]*phi[imo][3]);  // contrib. of MO z gradient
    		 } // end of Ekin calculation
    		 G = G / 2.0; // +1/2 ni (nabla Psi).(nabla Psi)
                 
    
    		 // ... Weizsaker (bosonic) kinetic energy density 
                 gradnorm = std::sqrt(gg[0]*gg[0] + gg[1]*gg[1] + gg[2]*gg[2]); // gg comes from calcQMPROP with total ED
    		 double GW = gradnorm*gradnorm / rhoValue;
    		 GW = GW / 8.0;
    
    		 // ... D 
    		 double DD = G - GW;

                 // we squatt deltagFC array to store KE (kinetic excess)
                 KE = deltagFC;
                 KE[ip][jp][kp] = DD;
    		 DD = DD + 2.871E-5; // shifting constant according to Savin to avoid numerical instabilities
    		 // when DD tends to zero faster than rhoValue !
    		 // This constrains ELF to be less than 0.5 for rhoValue <= 0.001 a.u. .
    		 // Savin 1996 "Topological analysis of the electron loc alization function" 
    		 // 4_synapticDEFINIT.pdf 
    
    		 // ... Dh
    		 double Dh = ELF_const * std::pow(rhoValue, FIVE_THIRD);
    
    		 // ELF
                 double ELFvalue = 1 / (1 + std::pow((DD/Dh), 2));

                 ELFarray = deltaginter;  
    		 ELFarray[ip][jp][kp] = ELFvalue; // we squatt the deltagInter array to save memory
                
                 rho[ip][jp][kp] = rhoValue; // positive


                 // =====================================================================
                 //     C O L O R I N G    M E T H O D    O F    E L F     B A S I N S 
                 //                        d e l t a g
                 // =====================================================================

                 if (not isTOPELF() ) { // FAST ELF&IGM coloring method to color ELF basins  

                    // to color basins we need IGM (qgIntraCURRENT): 
                    // note that here (ELF treatment) chi, phi, ... have been obtained for all the primitives ! and no basis change
                    if (isHirshmodeActivated()) {
                       // compute dg^HIRSHFELD
                       // =================================================================
                            // H I R S H F E L D   d e l t a g
                            // =================================================================
                            for (unsigned int iat=0; iat<nbAtoms; ++iat)
                            {
                                    dist[iat] = std::sqrt(d2[threadID][ip][iat]);
                                    dist[iat] = (dist[iat] < R_TRESHOLD ? R_TRESHOLD : dist[iat]);
                            }
     
                            // compute the individual atomic quantities rhoFree and pregradFree needed for rhopro and gradpro
                            // for ALL atoms (even if FRAG1+FRAG2< whole system)
                            this-> rhoFree__pregradFree(dist, rhoFree, pregradFree);
     
                            // get gradAtom for atoms
                            // from real rho and grad (whole system) 
                            this->gradAtomHirsh(fullRHOGRAD, threadID, rhoValue, gg, // rhoValue and gg are for the FULL system       
                                            dx, dy, dz, ip, rhoFree, pregradFree, gradAtom, rhoAtom); // rhoAtom not used so far
       
                            // get deltags from previously calculated gradAtom
                            this->IGMH(gradAtom, deltagIntraCURRENT, deltagInterCURRENT,qgIntraCURRENT,qgInterCURRENT);
                            deltag[ip][jp][kp] = 1/(qgIntraCURRENT*qgIntraCURRENT); // used to color ELF iso-surfaces
                    } // end of if (isHirshmodeActivated())
                    else {
                            this->IGM(chi, phi, dx[threadID][ip], dy[threadID][ip], dz[threadID][ip], gradPrim[threadID],
                                      deltagIntraCURRENT, deltagInterCURRENT, qgIntraCURRENT, qgInterCURRENT);
   	        	 deltag[ip][jp][kp] = 1/(qgIntraCURRENT*qgIntraCURRENT); // used to color ELF iso-surfaces
                    }
                    // alias for encoding the color information into deltag
                    ELFColor = deltag;

                 } // end of if (not isTOPELF() )
                        
                         // ==================================================

    	 } // end of if isELFmodeActivated()
    	 // ............... end E L F ..........................................................

	 // ============================================================
	 //   P A U L I   E S T I M A T I O N                               
	 // ============================================================

	 // PAULI does not require a basis change  (unlike IGM)
	 if (isPauli())
	 {
		 // .....................................................................................................................//
		 //       S E L F   A N A L Y S I S                                                                                      //
		 //       Intermolecular expression                                                                                      //
		 //       A) PAULI_22:                                                                                                   //
		 //  SELF = 8G1rho2 + 8G2rho1 + 32 G12rho12 - rho'12^2 -rho'21^2 -2rho'1rho'2-2rho'12rho'21                              //
		 //  SELF = 4[2G2rho1+2G1rho2+8G12rho12-(HALFrho'12)^2-(HALFrho'21)^2-2(HALFrho'1)(HALFrho'2)-2(HALFrho'12)(HALFrho'21)] //
		 //       B) PAULI_31:                                                                                                   //
		 //  with:                                                                                                               //
		 //    . 2G2       = Sum_a2b2 a'2b'2 Da2b2  (on fragment 2)                                                              //
		 //    . 2G1       = Sum_a1b1 a'1b'1 Da1b1  (on fragment 1)                                                              //
		 //    . rho1      = Sum_a1b1 a1 b1  Da1b1  (on fragment 1)                                                              //
		 //    . rho2      = Sum_a2b2 a2 b2  Da2b2  (on fragment 2)                                                              //
                 //    . rho'1     = 2 Sum_a1b1 a1'b1  Da1b1  (on fragment 1) !!(rho1)' =  Sum_a1b1 (a1'b1+a1b1')Da1b1                   //
                 //                                                                     =2xSum_a1b1 (a1'b1      )Da1b1                   //
                 //                                                           since a1'b1 and a1b1' MATRICES are transposed !!           //
                 //    . rho'2     = 2 Sum_a2b2 a2'b2  Da2b2  (on fragment 2) !!(rho2)' =  Sum_a2b2 (a2'b2+a2b2' )Da2b2                  //
                 //                                                                     =2xSum_a2b2 (a2'b2       )Da2b2                  //
                 //                                                           since a2'b2 and a2b2' MATRICES are transposed !!           //
                 //    . HALFrho'1 = Sum_a1b1 a'1b1  Da1b1  (on fragment 1)                                                              //
                 //    . HALFrho'2 = Sum_a2b2 a'2b2  Da2b2  (on fragment 2)                                                              //
		 //    . 2G12      = Sum_a1b2 a'1b'2 Da1b2  (on fragment 1 and 2)  = G21                                                 //
		 //    . rho12     = Sum_a1b2 a1 b2  Da1b2  (on fragment 1 and 2)  = rho21                                               //
		 //    . HALFrho'12= Sum_a1b2 a'1b2  Da1b2  (on fragment 1 and 2) !! a'1b2 and a'2b1 are not transposed !!               //
		 //    . HALFrho'21= Sum_a2b1 a'2b1  Da2b1  (on fragment 2 and 1) -> to me, HALFrho'12 should be renamed rho'12          // 
                 //                                                                  and   HALFrho'21 should be renamed rho'21           //
		 // .....................................................................................................................//


                 // ........................................................................
                 // First, calculate the iso-surface on which Pauli repulsion will be projected
                 // ============================================== //
                 //           dg           / rho_ALL               //
                 // ============================================== //
                 // to draw dgInter/rho isosurfaces later on and to project on it the Pauli descriptor
                 // here dg/rho = dg/rhoTOTAL (not limited to fragments)

                 if (isHirshmodeActivated()) {
                    // compute dg^HIRSHFELD
                    // =================================================================
			 // H I R S H F E L D   d e l t a g
			 // =================================================================
			 for (unsigned int iat=0; iat<nbAtoms; ++iat)
			 {
				 dist[iat] = std::sqrt(d2[threadID][ip][iat]);
				 dist[iat] = (dist[iat] < R_TRESHOLD ? R_TRESHOLD : dist[iat]);
			 }

			 // compute the individual atomic quantities rhoFree and pregradFree needed for rhopro and gradpro
			 // for ALL atoms (even if FRAG1+FRAG2< whole system)
			 this-> rhoFree__pregradFree(dist, rhoFree, pregradFree);

			 // get gradAtom for atoms
			 // from real rho and grad (whole system) 
			 this->gradAtomHirsh(fullRHOGRAD, threadID, rhoValue, gg, // rhoValue and gg are for the FULL system       
					 dx, dy, dz, ip, rhoFree, pregradFree, gradAtom, rhoAtom); // rhoAtom not used so far

			 // get deltags from previously calculated gradAtom
			 this->IGMH(gradAtom, deltagIntraCURRENT, deltagInterCURRENT,qgIntraCURRENT,qgInterCURRENT);
		 } // end of if (isHirshmodeActivated())
		 else {
                         // compute dg^GBP 
                         // =================================================================
                         // G B P   d e l t a g
                         // =================================================================
                         // note that here (Pauli treatment) chi, phi, ... have been obtained for all the primitives ! 
                         // and no basis change
			 this->IGM(chi, phi, dx[threadID][ip], dy[threadID][ip], dz[threadID][ip], gradPrim[threadID],
					 deltagIntraCURRENT, deltagInterCURRENT, qgIntraCURRENT, qgInterCURRENT);

                         // get the atom gradients for the atomic decomposition of SELF (SELFATOMIC)      
                         // within SELF (PAULI) treatment all primitives are considered
                         gradAtomGBP(basisSize, basisSet, gradPrim[threadID], gradAtom);
		 } // end of GBP

		 // store dg / rho_ALL      
                 deltaginter[ip][jp][kp] = deltagInterCURRENT/(rhoValue+epsrhodgscaled); // deltaginter is squatted to store dgscaled

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


                 // rhovalue used in the following test must be the total ED (not limited to FRAG1+2)
                 if (rhoValue > PauliRhoThresh)  // to limit the SELF analysis to regions with enough ED
		 {

			 // 1) PRELIMINARY calculations : MATRIX elements (the symmetrical DENSITY MATRIX, ...)

			 // ------------  O V   D O V    D ------------------------


			 double SELF = 0.0;
			 //                double PAULI40 = 0.0;
			 //                double PAULI04 = 0.0;
			 //                double PAULI31 = 0.0;
			 //                double PAULI13 = 0.0;
			 double** DOV  = NULL; //symmetrical  matrix of SCALAR PRODUCTS a'.b' (between two AO derivatives a' and b')
			 double** OV   = NULL; //symmetrical  matrix of        PRODUCTS a .b  (between two AO values      a  and b )
			 double*** COV = NULL; //asymmetrical matrix of VECTOR          a'.b  (between                    a' and b )
                         // note that COV[a][b] differs from COV[b][a] for 2 different AO a and b.

			 try            
			 {         
				 DOV = new double*[fragNbPrim]; // a'.b'
				 OV  = new double*[fragNbPrim]; // a .b
				 COV = new double**[fragNbPrim]; // a'.b 
				 // we limit the calculation to FRAG1+FRAG2 primitives:
				 for(unsigned int ipria=0;ipria<fragNbPrim; ++ipria)
                                 {
					 DOV[ipria] = new double[fragNbPrim];
					 OV[ipria]  = new double[fragNbPrim];
					 COV[ipria] = new double*[fragNbPrim];
					 for(unsigned int iprib=0;iprib<fragNbPrim; ++iprib)
					 {  
						 DOV[ipria][iprib]  = 0.0;
						 OV[ipria][iprib]   = 0.0;
						 COV[ipria][iprib]  = new double[3];
						 for(unsigned int ix=0;ix<3; ++ix) // three directions x,y,z
						 { 
							 COV[ipria][iprib][ix]  = 0.0;
						 }
					 } // end of loop over a
				 } // end of loop over b
			 } // end of trying allocating local arrays
			 catch (const std::bad_alloc& bad_alloc_error)
			 { 
				 std::cerr << std::endl
					 << "[ERROR] Allocation of density matrix failed: bad_alloc error catched" << std::endl
					 << "[ERROR] The program will now exit." << std::endl << std::endl;
				 exit(EXIT_FAILURE);
			 } 

			 // set the DOV, OV, COV matrices
			 // with the MAPPING:  Dxy in the xy original chi numbering ==> Dab in the ab fragPrim numbering hereafter
			 for(unsigned int ipria=0; ipria<fragNbPrim; ++ipria) // ipria in the range [0:nA+nB] (nA+nB <= n)
			 {  unsigned int apri = fragPrim[ipria]; // original apri returned in the range[0:nbprim-1] 
				 for(unsigned int jpria=0; jpria<fragNbPrim; ++jpria) // jpria in the range [0:nA+nB] (nA+nB <= n)
				 {  unsigned int bpri = fragPrim[jpria]; // original bpri returned in the range[0:nbprim-1] 
					 DOV[ipria][jpria]     =  chi[apri][1]*chi[bpri][1] +
                                                                  chi[apri][2]*chi[bpri][2] +
                                                                  chi[apri][3]*chi[bpri][3];
					 OV [ipria][jpria]     =  chi[apri][0]*chi[bpri][0];
					 COV[ipria][jpria][0]  =  chi[apri][1]*chi[bpri][0]; 
					 COV[ipria][jpria][1]  =  chi[apri][2]*chi[bpri][0]; 
					 COV[ipria][jpria][2]  =  chi[apri][3]*chi[bpri][0];
				 } // !end over jpria
			 } // !end over ipria


			 // ------------ E N D    O F     O V   D O V    D ------------------------


                         // ------------------- P R U N E       p r i m i t i v e s    i n    A   and   B  (n o t    n u l l)  -------------
                         //                     to speedup calculations according to J. Pilme algorithm

                          // ............ E X A M P L E .............................. //
                          // ipri                        1  2  3  4  5  6  7  8  9  10
                          // FRAG                        B  A  B  x  A  A  B  B  A  B  (two fragments are considered here, with FRAG1+2 < ALL)
                          // value                       x  x  0  0  0  x  x  x  0  0  (x = not null)
                          
                          //                             1  2  3  4  5  6  7  8  9
                          // fragPrim                    1  2  3  5  6  7  8  9  10
                          // FRAG                        B  A  B  A  A  B  B  A  B
                          // value                       x  x  0  0  x  x  x  0  0
                          
                          
                          // fragAPrim                   2  5  6  9
                          // fragBPrim                   1  3  7  8  10
                          // allPrim                     1  2  3  4  5  6  7  8  9  10
                          // fragAIndexToFragPrimIndex   2  4  5  8
                          // fragBIndexToFragPrimIndex   1  3  6  7  9
                          
                          // after speed-up refinement:
                          // fragAPrim                   2  6           
                          // fragBPrim                   1  7  8        
                          // fragAIndexToFragPrimIndex   2  5           
                          // fragBIndexToFragPrimIndex   1  6  7        
                          // ......................................................... //

                          // define a vectors to store the primitives of Frag A and B, which are not null
                          std::vector<unsigned int> reducedFragAPrim;
                          std::vector<unsigned int> reducedFragAIndexToFragPrimIndex;
                          unsigned int reducednbPrimInA;
                          std::vector<unsigned int> reducedFragBPrim;
                          std::vector<unsigned int> reducedFragBIndexToFragPrimIndex;
                          unsigned int reducednbPrimInB;
                      
                          unsigned int* icenter = wf_primitive_centers(); // within WF atom ordering
                          unsigned int iat ;
                     
                          // Initialize 
                          reducedFragAPrim.clear();
                          reducedFragAIndexToFragPrimIndex.clear();
                          reducednbPrimInA = 0;
                          reducedFragBPrim.clear();
                          reducedFragBIndexToFragPrimIndex.clear();
                          reducednbPrimInB = 0;
                      
                          // fill the new vectors with non-null primitives of FRAGA
                          for (unsigned int ipri = 0; ipri < nbPrimInA; ++ipri) {
                              iat = icenter[fragAPrim[ipri]] - 1; // the atom bearing the current primitive
                              if (std::sqrt(d2[threadID][ip][iat]) < primRmin[fragAPrim[ipri]]) { // if primitive is not too small
                                  reducedFragAPrim.push_back(fragAPrim[ipri]);
                                  reducedFragAIndexToFragPrimIndex.push_back(fragAIndexToFragPrimIndex[ipri]);
                                  ++reducednbPrimInA;
                              } // end of if
                          } // end of for over primitive of fragA
                      
                          // fill the new vectors with non-null primitives of FRAGB
                          for (unsigned int ipri = 0; ipri < nbPrimInB; ++ipri) {
                              iat = icenter[fragBPrim[ipri]] - 1; // the atom bearing the current primitive
                              if (std::sqrt(d2[threadID][ip][iat]) < primRmin[fragBPrim[ipri]]) { // if primitive is not too small
                                  reducedFragBPrim.push_back(fragBPrim[ipri]);
                                  reducedFragBIndexToFragPrimIndex.push_back(fragBIndexToFragPrimIndex[ipri]);
                                  ++reducednbPrimInB;
                              } // end of if
                          } // end of for over primitive of fragB


                         // ....................end of P R U N E    p r i m i t i v e s    A and B ......



			 if (not isPauliAtomic()) // the SELF direct way is used (without atomic contribution estimate)
			 {
				 // CALCULATE G1, rho1, rho'1 ---------------------------------------------------------
				 double _2G1  = 0.0;
				 double rho1  = 0.0;
				 double* HALFrho1D= new double[3];
				 HALFrho1D[0] = 0.0;
				 HALFrho1D[1] = 0.0;
				 HALFrho1D[2] = 0.0;
				 for(unsigned int ipria=0; ipria<reducednbPrimInA; ++ipria)
				 {  unsigned int a = reducedFragAIndexToFragPrimIndex[ipria]; // a returned in the range[0:fragNbPrim-1]
					 for(unsigned int jpria=0; jpria<reducednbPrimInA; ++jpria)
					 {  unsigned int b = reducedFragAIndexToFragPrimIndex[jpria]; // b returned in the range[0:fragNbPrim-1]
						 _2G1         = _2G1     + DOV[a][b]    * D[a][b];
						 rho1         = rho1     +  OV[a][b]    * D[a][b];
						 HALFrho1D[0] = HALFrho1D[0] + COV[a][b][0] * D[a][b];
						 HALFrho1D[1] = HALFrho1D[1] + COV[a][b][1] * D[a][b];
						 HALFrho1D[2] = HALFrho1D[2] + COV[a][b][2] * D[a][b];
					 } // end over b1
				 } // !end over a1   


				 // CALCULATE G2, rho2, rho'2 ---------------------------------------------------------
				 double _2G2  = 0.0;
				 double rho2  = 0.0;
				 double* HALFrho2D= new double[3];
				 HALFrho2D[0] = 0.0;
				 HALFrho2D[1] = 0.0;
				 HALFrho2D[2] = 0.0;
				 for(unsigned int ipria=0; ipria<reducednbPrimInB; ++ipria)
				 {  unsigned int a = reducedFragBIndexToFragPrimIndex[ipria]; // a returned in the range[0:fragNbPrim-1]
					 for(unsigned int jpria=0; jpria<reducednbPrimInB; ++jpria)
					 {  unsigned int b = reducedFragBIndexToFragPrimIndex[jpria]; // b returned in the range[0:fragNbPrim-1]
						 _2G2         = _2G2         + DOV[a][b]    * D[a][b];
						 rho2         = rho2         +  OV[a][b]    * D[a][b];
						 HALFrho2D[0] = HALFrho2D[0] + COV[a][b][0] * D[a][b];
						 HALFrho2D[1] = HALFrho2D[1] + COV[a][b][1] * D[a][b];
						 HALFrho2D[2] = HALFrho2D[2] + COV[a][b][2] * D[a][b];
					 } // end over b2
				 } // !end over a2  

				 // CALCULATE 2G12 , rho12, rho'12, rho'21 ----------------------------------
				 double _2G12   = 0.0;
				 double _8G12   = 0.0;
				 double rho12   = 0.0;
				 double* HALFrho12D = new double[3];
				 HALFrho12D[0] = 0.0;
				 HALFrho12D[1] = 0.0;
				 HALFrho12D[2] = 0.0;

				 double* HALFrho21D = new double[3];
				 HALFrho21D[0] = 0.0;
				 HALFrho21D[1] = 0.0;
				 HALFrho21D[2] = 0.0;

				 for(unsigned int ipria=0; ipria<reducednbPrimInA; ++ipria)
				 {  unsigned int a = reducedFragAIndexToFragPrimIndex[ipria]; // a returned in the range[0:fragNbPrim-1]
					 for(unsigned int jpria=0; jpria<reducednbPrimInB; ++jpria)
					 {  unsigned int b = reducedFragBIndexToFragPrimIndex[jpria]; // b returned in the range[0:fragNbPrim-1]
						 _2G12 = _2G12 + DOV[a][b] * D[a][b];
						 rho12 = rho12 +  OV[a][b] * D[a][b];

						 HALFrho12D[0] = HALFrho12D[0] + COV[a][b][0] * D[a][b];
						 HALFrho12D[1] = HALFrho12D[1] + COV[a][b][1] * D[a][b];
						 HALFrho12D[2] = HALFrho12D[2] + COV[a][b][2] * D[a][b];

						 HALFrho21D[0] = HALFrho21D[0] + COV[b][a][0] * D[a][b];
						 HALFrho21D[1] = HALFrho21D[1] + COV[b][a][1] * D[a][b];
						 HALFrho21D[2] = HALFrho21D[2] + COV[b][a][2] * D[a][b];
					 } // end over b2
				 } // !end over a1     
				 _8G12 = 4*_2G12;


				 // all the below quantities will be scaled by 4 later on
				 // PAULI22
				 SELF =   (_2G2*rho1+_2G1*rho2+_8G12*rho12-
						 getScalarProduct(HALFrho12D,HALFrho12D)-
						 getScalarProduct(HALFrho21D,HALFrho21D)-
						 2*getScalarProduct(HALFrho1D,HALFrho2D)-
						 2*getScalarProduct(HALFrho12D,HALFrho21D) );

				 //                   PAULI40 = _2G1*rho1 - getScalarProduct(HALFrho1D,HALFrho1D);
				 //                   PAULI04 = _2G2*rho2 - getScalarProduct(HALFrho2D,HALFrho2D);
				 //                   PAULI31 = 2*_2G1*rho12 + 2*_2G12*rho1 - 
				 //                             2*getScalarProduct(HALFrho1D,HALFrho12D) -
				 //                             2*getScalarProduct(HALFrho1D,HALFrho21D);
				 //                   PAULI13 = 2*_2G2*rho12 + 2*_2G12*rho2 - 
				 //                             2*getScalarProduct(HALFrho2D,HALFrho12D) -
				 //                             2*getScalarProduct(HALFrho2D,HALFrho21D);
                                 // SELF = SELF + PAULI31 + PAULI13;



		 // D E A L L O C A T E
				 delete[] HALFrho1D;
				 delete[] HALFrho2D; 
				 delete[] HALFrho12D;
				 delete[] HALFrho21D;


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

			 else { // second way to assess PAULI repulsion: expensive one, but with atom. contrib. 


				 // ...................................................................................//
				 //       S E L F   A N A L Y S I S: D E T A I L E D                                   //
				 // The SELF descriptor = [8xrhoxG - grad(rho)^2]/[8xrho]                              //
				 //                     = [4xSum_(a,b,c,d) [Dab.Dcd - Dac.Dbd] x a'.b'.c.d]/[8xrho]    //
				 //                     with: a,b,c,d running over the set of atomic orbitals          //
				 //                     a'  = first derivative of AO a                                 //
				 //                     b'  = first derivative of AO b                                 //
				 //                     Dab = Density matrix element for AOs a and b                   //
				 //  SELF = 0, strictly, when electrons behave as bosons, else SELF > 0                //
				 //  Sum_(a,b,c,d) [Dab.Dcd - Dac.Dbd] x a'.b'.c.d] can be decomposed in intra and     //
				 //  intermolecular terms according to the atoms carrying a,b,c,d in the expression.   //
				 //  a) when a,b,c,d belong to only one single fragment --> INTRAMOLECULAR term        //
				 //     This is called the partition '4/0'                                             //
				 //  b) Partition '3/1' corresponds normally to intermolecuar term, but we notice that //
				 //     it occurs only in intramolecular regions                                       //
				 //  c) Partition '2/2' corresponds to intermolecular term, in between 2 fragments     //
				 //     -> can be subsequently divided in the following cases:                         //
				 //        a'2b'2c1d1 : AO derivatives a and b are on frag2, c and d are on frag1      //
				 //        a'1b'1c2d2 ok                                                               //
				 //        a'1b'2c1d2 ok                                                               //
				 //        a'1b'2c2d1 ok                                                               //
				 //        a'2b'1c2d1 ok                                                               //
				 //        a'2b'1c1d2 ok                                                               //
				 //  d) To speed-up the calculation, the first case a'2b'2c1d1 can be splitted as:     //
				 //    ***  a'2b'2c1d1 : a'2, b'2>a2,   c1, d1>c1  ***                                 //
				 //                      Off-diagonal x Off-diagonal                                   //
				 //     --> [4xSum_(a,b,c,d) [Dab.Dcd - Dac.Dbd] x a'.b'.cxd] reduces to:              //
				 //         [4xSum_c,d>c Sum_a,b>a [4Dab.Dcd-2Dac.Dbd-2Dad.Dbc] x a'.b'xcxd]           //
				 //                                                                                    //
				 //    ***  a'2b'2c1d1 : a'2, b'2>a2,   c1=d1      ***                                 //
				 //                      Off-diagonal x diagonal                                       //
				 //     --> [4xSum_(a,b,c,d) [Dab.Dcd - Dac.Dbd] x a'.b'.cxd] reduces to:              //
				 //         [4xSum_c     Sum_a,b>a [2Dab.Dcc-2Dac.Dbc]          x a'.b'xcxc]           //
				 //                                                                                    //
				 //    ***  a'2b'2c1d1 : a'2=b'2    ,   c1,d1>c1   ***                                 //
				 //                      diagonal     x Off-diagonal                                   //
				 //     --> [4xSum_(a,b,c,d) [Dab.Dcd - Dac.Dbd] x a'.b'.cxd] reduces to:              //
				 //         [4xSum_c,d>c Sum_a     [2Daa.Dcd-2Dac.Dad]          x a'.a'xcxd]           //
				 //                                                                                    //
				 //    ***  a'2b'2c1d1 : a'2=b'2    ,   c1=d1      ***                                 //
				 //                      diagonal     x diagonal                                       //
				 //     --> [4xSum_(a,b,c,d) [Dab.Dcd - Dac.Dbd] x a'.b'.cxd] reduces to:              //
				 //         [4xSum_c     Sum_a     [ Daa.Dcc- Dac.Dac]          x a'.a'xcxc]           //
				 //                                                                                    //
				 //     Similarly for the second case a'1b'1c2d2                                       // 
				 //                                                                                    //
				 //  e) To save CPU time, only [Sum_(a,b,c,d) [Dab.Dcd - Dac.Dbd] x a'.b'.c.d]/[rho]   //
				 //     is calculated, instead of [4xSum_(a,b,c,d)[Dab.Dcd-Dac.Dbd]xa'.b'.c.d]/[8xrho] //
				 //     ==> in the end, the factor 4/8 will be applied                                 //
				 //                                                                                    //
				 //  f) Moreover, for atomic contributions estimation, since the array pauli[iat][jat] //
				 //     is filled twice for each quadruplet abcd, we will have to divide by two each   //
				 //     element of this array later on (in the end, while calculating atomic           //
				 //     contributions pauli[iat][jat]                                                  //
				 //                                                                                    //
				 //  g) Atomic contribution: for a given AO quadruplet abcd, for the 2/2 partition     //
				 //     (2 AO in frag1, 2 AO in frag2), a given SELF value is attributed to the two    //
				 //     atom pairs involved in abcd. For instance, if abcd AO are carried by atoms     //
				 //     1,2,3,4 (a1b2c3d4) of frag1 (1,2) and frag2 (3,4) ==> the array pauli will be  //
				 //     filled as follows: pauli[1][2] and pauli[3][4] will receive the SELF value.    //
				 //     For a1b1c3d4 the array elements pauli[1][1] and pauli[3][4] will be filled.    //
				 //     Since abcd are not traversed in a progressive order during the algorithm,      //
				 //     elements pauli[i][j] and pauli[j][i] are not complementary but have to be added//
				 //     And also, the sum of all elements of pauli will give 2 x SELF                  //
				 //     ==> at the end (while calculating the atomic contributions), pauli[i][j] and   //        
				 //     pauli[j][i] will be added and divided by 2.                                    //
				 // ...................................................................................//

				 // ............. SELF descriptor ..............................

                                 // ------------  A T O M I C    W E I G H T S  -----------
                                 //                  (using GBP partition)
                                 double** weight = new double*[nbAtoms]; // weights that will be used to distribute
                                                                         // a local SELF value over two atoms of the same fragment
                                                                         // weight[iat][jat] : the weight for atom iat in tha atom pair (iat,jat)
                                 for(unsigned int iat=0; iat<nbAtoms; ++iat) // iat in WFN/WFX/RKF numbering
                                 {                            
                                    weight[iat] = new double[nbAtoms];
                                    for(unsigned int jat=0; jat<nbAtoms; ++jat)
                                    {                                         
                                      weight[iat][jat] = 0.0;                  
                                    } // end over jat                         
                                 } // end over iat                                    

                                 // if GBP:
                                 if (not (isHirshmodeActivated())) {
                                    double iatNorm = 0.0; // ED gradient norm for atom iat
                                    double jatNorm = 0.0; // ED gradient norm for atom jat 
                                    for(unsigned int iat=0; iat<nbAtoms; ++iat)                  
                                    {                                                    
                                      weight[iat][iat] = 1.0;
                                      iatNorm = getNormOfVector(gradAtom[iat]);          
                                      for(unsigned int jat=iat+1; jat<nbAtoms; ++jat)    
                                        {                                                
                                           jatNorm = getNormOfVector(gradAtom[jat]);     
                                           if ( (iatNorm+jatNorm) > 1E-20 ) {
                                              weight[iat][jat] = iatNorm/(iatNorm+jatNorm); 
                                              weight[jat][iat] = jatNorm/(iatNorm+jatNorm);       
                                           }
                                           else {
                                              weight[iat][jat] = 0.0;
                                              weight[jat][iat] = 0.0;
                                           }
   
                                        }// end over atoms  jat                          
                                    } // end over atoms  iat                                   
                                  } // end of GBP
                                  else { // HIRSHFELD atomic partition of SELF, base on promolecular ED
                                    for(unsigned int iat=0; iat<nbAtoms; ++iat)
                                    {
                                      double rhoIAT = rhoFree[iat];
                                      weight[iat][iat] = 1.0;
                                      for(unsigned int jat=iat+1; jat<nbAtoms; ++jat)
                                        {
                                           double rhoJAT = rhoFree[jat]; 
                                           double rhoIAT_JAT = rhoIAT + rhoJAT;
                                           if ( (rhoIAT + rhoJAT) > 1E-20 ) {
                                              weight[iat][jat] = rhoIAT/rhoIAT_JAT;
                                              weight[jat][iat] = rhoJAT/rhoIAT_JAT;;
                                           }
                                           else {
                                              weight[iat][jat] = 0.0;
                                              weight[jat][iat] = 0.0;
                                           }

                                        }// end over atoms  jat                          
                                    } // end over atoms  iat                                   
                                  } // end of HIRSHFELD

                                 // E N D   O F    A T O M I C    W E I G H T S  -----------
   

				 // ########################  I N T E R ##########################################
				 // ........................   3/1 partition ........................

				 //                    a'1b'1c1d2
				 //            for(unsigned int ipria=0; ipria<reducednbPrimInA; ++ipria)
				 //               {  unsigned int a = reducedFragAIndexToFragPrimIndex[ipria]; // a returned in the range[0:reducednbPrimInA-1] 
				 //                  for(unsigned int iprib=0; iprib<reducednbPrimInA; ++iprib)
				 //                     {  unsigned int b = reducedFragAIndexToFragPrimIndex[iprib]; 
				 //                        if (std::abs(DOV[a][b]) > OV_thresh)
				 //                            {
				 //			     for(unsigned int ipric=0; ipric<reducednbPrimInA; ++ipric)
				 //                          {
				 //                              unsigned int c = reducedFragAIndexToFragPrimIndex[ipric];
				 //                              for(unsigned int iprid=0; iprid<reducednbPrimInB; ++iprid)
				 //                                 {  unsigned int d = reducedFragBIndexToFragPrimIndex[iprid];
				 //         
				 //                                     SELF = SELF + (D[a][b]*D[c][d]-
				 //                                                    D[a][c]*D[b][d]) *
				 //                                                    DOV[a][b]*OV[c][d];
				 //                                 } // !end over iprid
				 //                           } // !end over ipric
				 //                              }
				 //                     } // !end over iprib
				 //               } // !end over ipria
				 ////            std::cout << " Element a'1b'1c1d2 achieved, SELF = " << SELF << std::endl;
				 //
				 //            //                    a'1b'1c2d1
				 //            for(unsigned int ipria=0; ipria<reducednbPrimInA; ++ipria)
				 //               {  unsigned int a = reducedFragAIndexToFragPrimIndex[ipria]; // a returned in the range[0:reducednbPrimInA-1] 
				 //                  for(unsigned int iprib=0; iprib<reducednbPrimInA; ++iprib)
				 //                     {  unsigned int b = reducedFragAIndexToFragPrimIndex[iprib];
				 //                        if (std::abs(DOV[a][b]) > OV_thresh)
				 //                            {
				 //                             for(unsigned int ipric=0; ipric<reducednbPrimInB; ++ipric)
				 //                           {  unsigned int c = reducedFragBIndexToFragPrimIndex[ipric];
				 //                              for(unsigned int iprid=0; iprid<reducednbPrimInA; ++iprid)
				 //                                 {  unsigned int d = reducedFragAIndexToFragPrimIndex[iprid];
				 //
				 //                                     SELF = SELF + (D[a][b]*D[c][d]-
				 //                                                    D[a][c]*D[b][d]) *
				 //                                                    DOV[a][b]*OV[c][d];
				 //                                 } // !end over iprid
				 //                           } // !end over ipric
				 //                            }
				 //                     } // !end over iprib
				 //               } // !end over ipria
				 //              //std::cout << " Element a'1b'1c2d1 achieved, SELF = " << SELF << std::endl;
				 //
				 //            //                    a'1b'2c1d1
				 //            for(unsigned int ipria=0; ipria<reducednbPrimInA; ++ipria)
				 //               {  unsigned int a = reducedFragAIndexToFragPrimIndex[ipria]; // a returned in the range[0:reducednbPrimInA-1] 
				 //                  for(unsigned int iprib=0; iprib<reducednbPrimInB; ++iprib)
				 //                     {  unsigned int b = reducedFragBIndexToFragPrimIndex[iprib];
				 //                        if (std::abs(DOV[a][b]) > OV_thresh)
				 //                            {
				 //                             for(unsigned int ipric=0; ipric<reducednbPrimInA; ++ipric)
				 //                           {  unsigned int c = reducedFragAIndexToFragPrimIndex[ipric];
				 //                              for(unsigned int iprid=0; iprid<reducednbPrimInA; ++iprid)
				 //                                 {  unsigned int d = reducedFragAIndexToFragPrimIndex[iprid];
				 //
				 //                                     SELF = SELF + (D[a][b]*D[c][d]-
				 //                                                    D[a][c]*D[b][d]) *
				 //                                                    DOV[a][b]*OV[c][d];
				 //                                 } // !end over iprid
				 //                           } // !end over ipric
				 //                            }
				 //                     } // !end over iprib
				 //               } // !end over ipria
				 //              //std::cout << " Element a'1b'2c1d1 achieved, SELF = " << SELF << std::endl;
				 //
				 //            //                    a'2b'1c1d1
				 //            for(unsigned int ipria=0; ipria<reducednbPrimInB; ++ipria)
				 //               {  unsigned int a = reducedFragBIndexToFragPrimIndex[ipria]; // a returned in the range[0:reducednbPrimInA-1] 
				 //                  for(unsigned int iprib=0; iprib<reducednbPrimInA; ++iprib)
				 //                     {  unsigned int b = reducedFragAIndexToFragPrimIndex[iprib];
				 //                        if (std::abs(DOV[a][b]) > OV_thresh)
				 //                            {
				 //                             for(unsigned int ipric=0; ipric<reducednbPrimInA; ++ipric)
				 //                           {  unsigned int c = reducedFragAIndexToFragPrimIndex[ipric];
				 //                              for(unsigned int iprid=0; iprid<reducednbPrimInA; ++iprid)
				 //                                 {  unsigned int d = reducedFragAIndexToFragPrimIndex[iprid];
				 //
				 //                                     SELF = SELF + (D[a][b]*D[c][d]-
				 //                                                    D[a][c]*D[b][d]) *
				 //                                                    DOV[a][b]*OV[c][d];
				 //                                 } // !end over iprid
				 //                           } // !end over ipric
				 //                            }
				 //                     } // !end over iprib
				 //               } // !end over ipria
				 //              //std::cout << " Element a'2b'1c1d1 achieved, SELF = " << SELF << std::endl;
				 //
				 //            //                    a'2b'2c2d1
				 //            for(unsigned int ipria=0; ipria<reducednbPrimInB; ++ipria)
				 //               {  unsigned int a = reducedFragBIndexToFragPrimIndex[ipria]; // a returned in the range[0:reducednbPrimInA-1] 
				 //                  for(unsigned int iprib=0; iprib<reducednbPrimInB; ++iprib)
				 //                     {  unsigned int b = reducedFragBIndexToFragPrimIndex[iprib];
				 //                        if (std::abs(DOV[a][b]) > OV_thresh)
				 //                            {
				 //                             for(unsigned int ipric=0; ipric<reducednbPrimInB; ++ipric)
				 //                           {  unsigned int c = reducedFragBIndexToFragPrimIndex[ipric];
				 //                              for(unsigned int iprid=0; iprid<reducednbPrimInA; ++iprid)
				 //                                 {  unsigned int d = reducedFragAIndexToFragPrimIndex[iprid];
				 //
				 //                                     SELF = SELF + (D[a][b]*D[c][d]-
				 //                                                    D[a][c]*D[b][d]) *
				 //                                                    DOV[a][b]*OV[c][d];
				 //                                 } // !end over iprid
				 //                           } // !end over ipric
				 //                            }
				 //                     } // !end over iprib
				 //               } // !end over ipria
				 //              //std::cout << " Element a'2b'2c2d1 achieved, SELF = " << SELF << std::endl;
				 //
				 //            //                    a'2b'2c1d2
				 //            for(unsigned int ipria=0; ipria<reducednbPrimInB; ++ipria)
				 //               {  unsigned int a = reducedFragBIndexToFragPrimIndex[ipria]; // a returned in the range[0:reducednbPrimInA-1] 
				 //                  for(unsigned int iprib=0; iprib<reducednbPrimInB; ++iprib)
				 //                     {  unsigned int b = reducedFragBIndexToFragPrimIndex[iprib];
				 //                        if (std::abs(DOV[a][b]) > OV_thresh)
				 //                            {
				 //                             for(unsigned int ipric=0; ipric<reducednbPrimInA; ++ipric)
				 //                           {  unsigned int c = reducedFragAIndexToFragPrimIndex[ipric];
				 //                              for(unsigned int iprid=0; iprid<reducednbPrimInB; ++iprid)
				 //                                 {  unsigned int d = reducedFragBIndexToFragPrimIndex[iprid];
				 //
				 //                                     SELF = SELF + (D[a][b]*D[c][d]-
				 //                                                    D[a][c]*D[b][d]) *
				 //                                                    DOV[a][b]*OV[c][d];
				 //                                 } // !end over iprid
				 //                           } // !end over ipric
				 //                            }
				 //                     } // !end over iprib
				 //               } // !end over ipria
				 //              //std::cout << " Element a'2b'2c1d2 achieved, SELF = " << SELF << std::endl;
				 //
				 //            //                    a'2b'1c2d2
				 //            for(unsigned int ipria=0; ipria<reducednbPrimInB; ++ipria)
				 //               {  unsigned int a = reducedFragBIndexToFragPrimIndex[ipria]; // a returned in the range[0:reducednbPrimInA-1] 
				 //                  for(unsigned int iprib=0; iprib<reducednbPrimInA; ++iprib)
				 //                     {  unsigned int b = reducedFragAIndexToFragPrimIndex[iprib];
				 //                        if (std::abs(DOV[a][b]) > OV_thresh)
				 //                            {
				 //                             for(unsigned int ipric=0; ipric<reducednbPrimInB; ++ipric)
				 //                           {  unsigned int c = reducedFragBIndexToFragPrimIndex[ipric];
				 //                              for(unsigned int iprid=0; iprid<reducednbPrimInB; ++iprid)
				 //                                 {  unsigned int d = reducedFragBIndexToFragPrimIndex[iprid];
				 //
				 //                                     SELF = SELF + (D[a][b]*D[c][d]-
				 //                                                    D[a][c]*D[b][d]) *
				 //                                                    DOV[a][b]*OV[c][d];
				 //                                 } // !end over iprid
				 //                           } // !end over ipric
				 //                            }
				 //                     } // !end over iprib
				 //               } // !end over ipria
				 //              //std::cout << " Element a'2b'1c2d2 achieved, SELF = " << SELF << std::endl;
				 //
				 //
				 //            //                    a'1b'2c2d2
				 //     for(unsigned int ipria=0; ipria<reducednbPrimInA; ++ipria)
				 //               {  unsigned int a = reducedFragAIndexToFragPrimIndex[ipria]; // a returned in the range[0:reducednbPrimInA-1] 
				 //                  for(unsigned int iprib=0; iprib<reducednbPrimInB; ++iprib)
				 //                     {  unsigned int b = reducedFragBIndexToFragPrimIndex[iprib];
				 //                        if (std::abs(DOV[a][b]) > OV_thresh)
				 //                            {
				 //                             for(unsigned int ipric=0; ipric<reducednbPrimInB; ++ipric)
				 //                           {  unsigned int c = reducedFragBIndexToFragPrimIndex[ipric];
				 //                              for(unsigned int iprid=0; iprid<reducednbPrimInB; ++iprid)
				 //                                 {  unsigned int d = reducedFragBIndexToFragPrimIndex[iprid];
				 //
				 //                                     SELF = SELF + (D[a][b]*D[c][d]-
				 //                                                    D[a][c]*D[b][d]) *
				 //                                                    DOV[a][b]*OV[c][d];
				 //                                 } // !end over iprid
				 //                           } // !end over ipric
				 //                            }
				 //                     } // !end over iprib
				 //               } // !end over ipria
				 //              //std::cout << " Element a'1b'2c2d2 achieved, SELF = " << SELF << std::endl;


				 // ........................   2/2 partition ........................

                                 // Note that the array pauli[threadID][iat][jat] does not need to be reset since we want to assess
                                 // the pauli interaction between atoms iat and jat over the whole grid !

				 // ||||||||||||||     a'2b'2c1d1     ||||||||||||||
				 double tmpSELF = 0.0;
                                 unsigned int ia,ib,ic,id;

				 // ----- a'2,b'2>a'2   c1,d1>c1 -----//
				 //      Off-diagonal x Off-diagonal // 
               //		 for(unsigned int ipric=0; ipric<reducednbPrimInA-1; ++ipric)
                                 for(unsigned int ipric=0; ipric<reducednbPrimInA; ++ipric)
				 {  unsigned int cpri = reducedFragAPrim[ipric]; // cpri returned in the range[0:nbPrim-1] 
					 unsigned int c    = reducedFragAIndexToFragPrimIndex[ipric]; // c returned in the range[0:fragNbPrim-1]
						 for(unsigned int iprid=ipric+1; iprid<reducednbPrimInA; ++iprid)
						 {  unsigned int dpri = reducedFragAPrim[iprid]; 
							 unsigned int d    = reducedFragAIndexToFragPrimIndex[iprid]; 
                                                                 for(unsigned int ipria=0; ipria<reducednbPrimInB; ++ipria)
								 {       unsigned int apri = reducedFragBPrim[ipria]; // apri returned in the range[0:nbPrim-1]       
									 unsigned int a    = reducedFragBIndexToFragPrimIndex[ipria]; // a returned in the range[0:fragNbPrim-1]
									 for(unsigned int iprib=ipria+1; iprib<reducednbPrimInB; ++iprib)
									 {  unsigned int bpri = reducedFragBPrim[iprib]; 
										 unsigned int b    = reducedFragBIndexToFragPrimIndex[iprib]; 
										 tmpSELF =      (4*D[a][b]*D[c][d]-
												 2*D[a][c]*D[b][d]-
												 2*D[a][d]*D[b][c]) *
											 DOV[a][b]*OV[c][d];
										 SELF = SELF + tmpSELF;

										 // store atomic pair decomposition for each fragment
										 //---- Fragment 1 ----//
                                                                                 ic = icenter[cpri]-1;
                                                                                 id = icenter[dpri]-1; 
										 pauli[threadID][ic][id] = pauli[threadID][ic][id] 
                                                                                       + (tmpSELF/rhoValue)*weight[ic][id] ;
                                                                                 if (ic != id) {
                                                                                 pauli[threadID][id][ic] = pauli[threadID][id][ic]             
                                                                                       + (tmpSELF/rhoValue)*weight[id][ic] ;}


										 //---- Fragment 2 ----//
                                                                                 ia = icenter[apri]-1;
                                                                                 ib = icenter[bpri]-1;
										 pauli[threadID][ia][ib] = pauli[threadID][ia][ib] 
                                                                                       + (tmpSELF/rhoValue)*weight[ia][ib] ; 
                                                                                 if (ia != ib) {
                                                                                 pauli[threadID][ib][ia] = pauli[threadID][ib][ia] 
                                                                                       + (tmpSELF/rhoValue)*weight[ib][ia] ;}


									 } // !end over iprib
								 } // !end over ipria
						  } // !end over iprid
				 } // !end over ipric


				 // ----- a'2,b'2>a'2   c1=d1    -----//
				 //      Off-diagonal x  diagonal    // 
				 for(unsigned int ipric=0; ipric<reducednbPrimInA; ++ipric)
				 {       unsigned int cpri = reducedFragAPrim[ipric]; // cpri returned in the range[0:nbPrim-1]  
					 unsigned int c    = reducedFragAIndexToFragPrimIndex[ipric]; 
                                         for(unsigned int ipria=0; ipria<reducednbPrimInB; ++ipria)
					 {  unsigned int apri = reducedFragBPrim[ipria];
                                            unsigned int a    = reducedFragBIndexToFragPrimIndex[ipria];
                                            for(unsigned int iprib=ipria+1; iprib<reducednbPrimInB; ++iprib)
                                            {  unsigned int bpri = reducedFragBPrim[iprib];                  
                                               unsigned int b    = reducedFragBIndexToFragPrimIndex[iprib];
                                               tmpSELF =     (2*D[a][b]*D[c][c]-2*D[a][c]*D[b][c])*DOV[a][b]*OV[c][c];
                                               SELF = SELF + tmpSELF;
                                 
                                               // store atomic pair decomposition for each fragment
                                               //---- Fragment 1 ----//
                                               ic = icenter[cpri]-1;
                                               pauli[threadID][ic][ic] = pauli[threadID][ic][ic] 
                                                     + (tmpSELF/rhoValue); 

                                               //---- Fragment 2 ----//
                                               ia = icenter[apri]-1;
                                               ib = icenter[bpri]-1;
                                               pauli[threadID][ia][ib] = pauli[threadID][ia][ib] 
                                                     + (tmpSELF/rhoValue)*weight[ia][ib] ;
                                               if (ia != ib) {
                                               pauli[threadID][ib][ia] = pauli[threadID][ib][ia] 
                                                     + (tmpSELF/rhoValue)*weight[ib][ia] ;}
              
                                            } // !end over iprib
                                         } // !end over ipria
				 } // !end over ipric


				 // ----- a'2=b'2       c1,d1>c1 -----//
				 //        diagonal   x Off-diagonal  // 
				 for(unsigned int ipric=0; ipric<reducednbPrimInA; ++ipric)
				 {       unsigned int cpri = reducedFragAPrim[ipric]; // cpri returned in the range[0:nbPrim-1]    
					 unsigned int c    = reducedFragAIndexToFragPrimIndex[ipric]; 
					 for(unsigned int iprid=ipric+1; iprid<reducednbPrimInA; ++iprid)
					 {  unsigned int dpri = reducedFragAPrim[iprid];
                                            unsigned int d    = reducedFragAIndexToFragPrimIndex[iprid];
                                            for(unsigned int ipria=0; ipria<reducednbPrimInB; ++ipria)
                                            {   unsigned int apri = reducedFragBPrim[ipria];
                                                unsigned int a    = reducedFragBIndexToFragPrimIndex[ipria];
                                                tmpSELF =     (2*D[a][a]*D[c][d]-2*D[a][c]*D[a][d])*DOV[a][a]*OV[c][d];
                                                SELF = SELF + tmpSELF;
                                            
                                                // store atomic pair decomposition for each fragment   
                                                //---- Fragment 1 ----//
                                                ic = icenter[cpri]-1;
                                                id = icenter[dpri]-1;
                                                pauli[threadID][ic][id] = pauli[threadID][ic][id] 
                                                      + (tmpSELF/rhoValue)*weight[ic][id] ;
                                                if (ic != id) {
                                                pauli[threadID][id][ic] = pauli[threadID][id][ic] 
                                                      + (tmpSELF/rhoValue)*weight[id][ic] ;}

                                                //---- Fragment 2 ----//
                                                ia = icenter[apri]-1;
                                                pauli[threadID][ia][ia] = pauli[threadID][ia][ia] 
                                                      + (tmpSELF/rhoValue);

                                             } // !end over ipria
                                          }// !end over iprid
				 } // !end over ipric


				 // ----- a'2=b'2       c1=d1    -----//
				 //         diagonal  x   diagonal   // 
				 for(unsigned int ipric=0; ipric<reducednbPrimInA; ++ipric)
				 {  unsigned int cpri = reducedFragAPrim[ipric]; // cpri returned in the range[0:nbPrim-1]    
				    unsigned int c    = reducedFragAIndexToFragPrimIndex[ipric];
                                    for(unsigned int ipria=0; ipria<reducednbPrimInB; ++ipria)
                                    {  unsigned int apri = reducedFragBPrim[ipria];
                                       unsigned int a    = reducedFragBIndexToFragPrimIndex[ipria];
                                       tmpSELF =     (D[a][a]*D[c][c]- D[a][c]*D[a][c])*DOV[a][a]*OV[c][c];
                                       SELF = SELF + tmpSELF;
                                    
                                       // store atomic pair decomposition for each fragment
                                       //---- Fragment 1 ----//
                                       ic = icenter[cpri]-1;
                                       pauli[threadID][ic][ic] = pauli[threadID][ic][ic] 
                                             + (tmpSELF/rhoValue);


                                       //---- Fragment 2 ----//
                                       ia = icenter[apri]-1;
                                       pauli[threadID][ia][ia] = pauli[threadID][ia][ia] 
                                             + (tmpSELF/rhoValue);


                                      
                                     } // !end over ipria
				 } // !end over ipric


				 // ||||||||||||||     a'1b'2c1d2    ||||||||||||||
				 for(unsigned int ipric=0; ipric<reducednbPrimInA; ++ipric)
				 {  unsigned int cpri = reducedFragAPrim[ipric]; // cpri returned in the range[0:nbPrim-1]    
                                    unsigned int c    = reducedFragAIndexToFragPrimIndex[ipric];
                                    for(unsigned int iprid=0; iprid<reducednbPrimInB; ++iprid)
                                     {  unsigned int dpri = reducedFragBPrim[iprid];
                                        unsigned int d    = reducedFragBIndexToFragPrimIndex[iprid];
                                        for(unsigned int ipria=0; ipria<reducednbPrimInA; ++ipria)
                                  	 {  unsigned int apri = reducedFragAPrim[ipria];
                                             unsigned int a    = reducedFragAIndexToFragPrimIndex[ipria];
                                    	     for(unsigned int iprib=0; iprib<reducednbPrimInB; ++iprib)
                                    		 {  unsigned int bpri = reducedFragBPrim[iprib];
                                                    unsigned int b    = reducedFragBIndexToFragPrimIndex[iprib];
                                    		    tmpSELF =     (D[a][b]*D[c][d]-D[a][c]*D[b][d]) *DOV[a][b]*OV[c][d];
                                                     SELF = SELF + tmpSELF;
                                    
                                                     // store atomic pair decomposition for each fragment
                                                     //---- Fragment 1 ----//
                                                     ia = icenter[apri]-1;
                                                     ic = icenter[cpri]-1;
                                                     pauli[threadID][ia][ic] = pauli[threadID][ia][ic] 
                                                           + (tmpSELF/rhoValue)*weight[ia][ic] ;
                                                     if (ia != ic) {
                                                     pauli[threadID][ic][ia] = pauli[threadID][ic][ia] 
                                                           + (tmpSELF/rhoValue)*weight[ic][ia] ;}


                                                     //---- Fragment 2 ----//
                                                     ib = icenter[bpri]-1;
                                                     id = icenter[dpri]-1;
                                                     pauli[threadID][ib][id] = pauli[threadID][ib][id] 
                                                           + (tmpSELF/rhoValue)*weight[ib][id] ;
                                                     if (ib != id) {
                                                     pauli[threadID][id][ib] = pauli[threadID][id][ib] 
                                                           + (tmpSELF/rhoValue)*weight[id][ib] ;}


                                    		 } // !end over iprib
                                          } // !end over ipria
                                      } // !end over iprid
				  } // !end over ipric


				 // ||||||||||||||     a'2b'1c1d2    ||||||||||||||
				 for(unsigned int ipric=0; ipric<reducednbPrimInA; ++ipric)
				 {  unsigned int cpri = reducedFragAPrim[ipric]; // cpri returned in the range[0:nbPrim-1]    
                                    unsigned int c    = reducedFragAIndexToFragPrimIndex[ipric];
                                    for(unsigned int iprid=0; iprid<reducednbPrimInB; ++iprid)
                                    {  unsigned int dpri = reducedFragBPrim[iprid];
                                       unsigned int d    = reducedFragBIndexToFragPrimIndex[iprid];
                                       for(unsigned int ipria=0; ipria<reducednbPrimInB; ++ipria)
                                   	 {  unsigned int apri = reducedFragBPrim[ipria];
                                            unsigned int a    = reducedFragBIndexToFragPrimIndex[ipria];
                                   	    for(unsigned int iprib=0; iprib<reducednbPrimInA; ++iprib)
                                   	    {  unsigned int bpri = reducedFragAPrim[iprib];
                                   	       unsigned int b    = reducedFragAIndexToFragPrimIndex[iprib];
                                   	       tmpSELF =     (D[a][b]*D[c][d]-D[a][c]*D[b][d]) *DOV[a][b]*OV[c][d];
                                   	       SELF = SELF + tmpSELF;

                                               // store atomic pair decomposition for each fragment
                                               //---- Fragment 1 ----//
                                               ib = icenter[bpri]-1;
                                               ic = icenter[cpri]-1;
                                               pauli[threadID][ib][ic] = pauli[threadID][ib][ic] 
                                                     + (tmpSELF/rhoValue)*weight[ib][ic] ;
                                               if (ib != ic) {
                                               pauli[threadID][ic][ib] = pauli[threadID][ic][ib] 
                                                     + (tmpSELF/rhoValue)*weight[ic][ib] ;}


                                               //---- Fragment 2 ----//
                                               ia = icenter[apri]-1;
                                               id = icenter[dpri]-1;
                                               pauli[threadID][ia][id] = pauli[threadID][ia][id] 
                                                     + (tmpSELF/rhoValue)*weight[ia][id] ;
                                               if (ia != id) {
                                               pauli[threadID][id][ia] = pauli[threadID][id][ia] 
                                                     + (tmpSELF/rhoValue)*weight[id][ia] ;}

                                            } // !end over iprib
                                         } // !end over ipria
                                    } // !end over iprid
				 } // !end over ipric


				 // ||||||||||||||     a'1b'2c2d1    ||||||||||||||
                                 for(unsigned int ipric=0; ipric<reducednbPrimInB; ++ipric)
                                 {  unsigned int cpri = reducedFragBPrim[ipric]; // cpri returned in the range[0:nbPrim-1]    
                                    unsigned int c    = reducedFragBIndexToFragPrimIndex[ipric];
                                    for(unsigned int iprid=0; iprid<reducednbPrimInA; ++iprid)
                                    {  unsigned int dpri = reducedFragAPrim[iprid];
                                       unsigned int d    = reducedFragAIndexToFragPrimIndex[iprid];
                                       for(unsigned int ipria=0; ipria<reducednbPrimInA; ++ipria)
                                       {  unsigned int apri = reducedFragAPrim[ipria];
                                    	  unsigned int a    = reducedFragAIndexToFragPrimIndex[ipria];
                                  	  for(unsigned int iprib=0; iprib<reducednbPrimInB; ++iprib)
                                 	  {  unsigned int bpri = reducedFragBPrim[iprib];
                                             unsigned int b    = reducedFragBIndexToFragPrimIndex[iprib];
                                 	     tmpSELF =     (D[a][b]*D[c][d]-D[a][c]*D[b][d])*DOV[a][b]*OV[c][d];
                                 	     SELF = SELF + tmpSELF;
                                             
                                             // store atomic pair decomposition for each fragment
                                             //---- Fragment 1 ----//
                                             ia = icenter[apri]-1;
                                             id = icenter[dpri]-1;
                                             pauli[threadID][ia][id] = pauli[threadID][ia][id] 
                                                   + (tmpSELF/rhoValue)*weight[ia][id] ;
                                             if (ia != id) {
                                             pauli[threadID][id][ia] = pauli[threadID][id][ia]
                                                   + (tmpSELF/rhoValue)*weight[id][ia] ;}


                                             //---- Fragment 2 ----//
                                             ib = icenter[bpri]-1;
                                             ic = icenter[cpri]-1;
                                             pauli[threadID][ib][ic] = pauli[threadID][ib][ic] 
                                                   + (tmpSELF/rhoValue)*weight[ib][ic] ;
                                             if (ib != ic) {
                                             pauli[threadID][ic][ib] = pauli[threadID][ic][ib] 
                                                   + (tmpSELF/rhoValue)*weight[ic][ib] ;}


                                          } // !end over iprib
                                       } // !end over ipria
                                    } // !end over iprid
                                 } // !end over ipric


				 // ||||||||||||||     a'2b'1c2d1    ||||||||||||||
				 for(unsigned int ipric=0; ipric<reducednbPrimInB; ++ipric)
				 {  unsigned int cpri = reducedFragBPrim[ipric]; // cpri returned in the range[0:nbPrim-1]    
                                    unsigned int c    = reducedFragBIndexToFragPrimIndex[ipric];
                                    for(unsigned int iprid=0; iprid<reducednbPrimInA; ++iprid)
                                    {     unsigned int dpri = reducedFragAPrim[iprid];
                                          unsigned int d    = reducedFragAIndexToFragPrimIndex[iprid];
                                          for(unsigned int ipria=0; ipria<reducednbPrimInB; ++ipria)
                                          {  unsigned int apri = reducedFragBPrim[ipria];
                                             unsigned int a    = reducedFragBIndexToFragPrimIndex[ipria];
                                             for(unsigned int iprib=0; iprib<reducednbPrimInA; ++iprib)
                                             {  unsigned int bpri = reducedFragAPrim[iprib];
                                                unsigned int b    = reducedFragAIndexToFragPrimIndex[iprib];
                                                tmpSELF =     (D[a][b]*D[c][d]-D[a][c]*D[b][d]) * DOV[a][b]*OV[c][d];
                                                SELF = SELF + tmpSELF;
                                               
                                                // store atomic pair decomposition for each fragment
                                                //---- Fragment 1 ----//
                                                ib = icenter[bpri]-1;
                                                id = icenter[dpri]-1;
                                                pauli[threadID][ib][id] = pauli[threadID][ib][id] 
                                                      + (tmpSELF/rhoValue)*weight[ib][id] ;
                                                if (ib != id) {
                                                pauli[threadID][id][ib] = pauli[threadID][id][ib] 
                                                      + (tmpSELF/rhoValue)*weight[id][ib] ;}


                                                //---- Fragment 2 ----//
                                                ia = icenter[apri]-1;
                                                ic = icenter[cpri]-1;
                                                pauli[threadID][ia][ic] = pauli[threadID][ia][ic] 
                                                      + (tmpSELF/rhoValue)*weight[ia][ic] ;
                                               if (ia != ic) {
                                                pauli[threadID][ic][ia] = pauli[threadID][ic][ia] 
                                                      + (tmpSELF/rhoValue)*weight[ic][ia] ;}
                                            
                                             } // !end over iprib
                                          } // !end over ipria
                                     } // !end over iprid
                                 } // !end over ipric

				 // ||||||||||||||     a'1b'1c2d2    ||||||||||||||

				 // ----- a'1,b'1>a'1   c2,d2>c2 -----//
				 //      Off-diagonal x Off-diagonal // 
                                 for(unsigned int ipric=0; ipric<reducednbPrimInB; ++ipric)
                                  {  unsigned int cpri = reducedFragBPrim[ipric]; // cpri returned in the range[0:nbPrim-1]    
                                     unsigned int c    = reducedFragBIndexToFragPrimIndex[ipric];
                                     for(unsigned int iprid=ipric+1; iprid<reducednbPrimInB; ++iprid)
                                     {  unsigned int dpri = reducedFragBPrim[iprid];
                                        unsigned int d    = reducedFragBIndexToFragPrimIndex[iprid];
                                        for(unsigned int ipria=0; ipria<reducednbPrimInA; ++ipria)
                                        {  unsigned int apri = reducedFragAPrim[ipria];
                                           unsigned int a    = reducedFragAIndexToFragPrimIndex[ipria];
                                           for(unsigned int iprib=ipria+1; iprib<reducednbPrimInA; ++iprib)
                                           {  unsigned int bpri = reducedFragAPrim[iprib];
                                              unsigned int b    = reducedFragAIndexToFragPrimIndex[iprib];
                                              tmpSELF =     (4*D[a][b]*D[c][d]-2*D[a][c]*D[b][d]-2*D[a][d]*D[b][c])* DOV[a][b]*OV[c][d];
                                              SELF = SELF + tmpSELF;
                                             
                                              // store atomic pair decomposition for each fragment
                                              //---- Fragment 1 ----//
                                              ia = icenter[apri]-1;
                                              ib = icenter[bpri]-1;
                                              pauli[threadID][ia][ib] = pauli[threadID][ia][ib] 
                                                    + (tmpSELF/rhoValue)*weight[ia][ib] ;
                                              if (ia != ib) {
                                              pauli[threadID][ib][ia] = pauli[threadID][ib][ia] 
                                                    + (tmpSELF/rhoValue)*weight[ib][ia] ;}


                                              //---- Fragment 2 ----//
                                              ic = icenter[cpri]-1;
                                              id = icenter[dpri]-1;
                                              pauli[threadID][ic][id] = pauli[threadID][ic][id] 
                                                    + (tmpSELF/rhoValue)*weight[ic][id] ;
                                              if (ic != id) {
                                              pauli[threadID][id][ic] = pauli[threadID][id][ic] 
                                                    + (tmpSELF/rhoValue)*weight[id][ic] ;}


                                           } // !end over iprib
                                        } // !end over ipria
                                     } // !end over iprid
                                  } // !end over ipric


				 // ----- a'1,b'1>a'1   c2=d2    -----//
				 //      Off-diagonal x diagonal     // 
                                 for(unsigned int ipric=0; ipric<reducednbPrimInB; ++ipric)
                                 {  unsigned int cpri = reducedFragBPrim[ipric]; // cpri returned in the range[0:nbPrim-1]       
                                    unsigned int c    = reducedFragBIndexToFragPrimIndex[ipric];
                                    for(unsigned int ipria=0; ipria<reducednbPrimInA; ++ipria)
                                    {  unsigned int apri = reducedFragAPrim[ipria];
                                       unsigned int a    = reducedFragAIndexToFragPrimIndex[ipria];
                                       for(unsigned int iprib=ipria+1; iprib<reducednbPrimInA; ++iprib)
                                       {  unsigned int bpri = reducedFragAPrim[iprib];
                                          unsigned int b    = reducedFragAIndexToFragPrimIndex[iprib];
                                          tmpSELF =     (2*D[a][b]*D[c][c]-2*D[a][c]*D[b][c]) * DOV[a][b]*OV[c][c];
                                          SELF = SELF + tmpSELF;
                                         
                                          // store atomic pair decomposition for each fragment
                                          //---- Fragment 1 ----//
                                          ia = icenter[apri]-1;
                                          ib = icenter[bpri]-1;
                                          pauli[threadID][ia][ib] = pauli[threadID][ia][ib] 
                                                + (tmpSELF/rhoValue)*weight[ia][ib] ;
                                          if (ia != ib) {
                                          pauli[threadID][ib][ia] = pauli[threadID][ib][ia] 
                                                + (tmpSELF/rhoValue)*weight[ib][ia] ;}


                                          //---- Fragment 2 ----//
                                          ic = icenter[cpri]-1;
                                          pauli[threadID][ic][ic] = pauli[threadID][ic][ic] 
                                                + (tmpSELF/rhoValue);

                                        } // !end over iprib
                                    } // !end over ipria
                                 } // !end over ipric


				 // ----- a'1=b'1       c2,d2>c2 -----//
				 //        diagonal   x Off-diagonal // 
                                 for(unsigned int ipric=0; ipric<reducednbPrimInB; ++ipric)
                                  {  unsigned int cpri = reducedFragBPrim[ipric]; // cpri returned in the range[0:nbPrim-1]       
                                     unsigned int c    = reducedFragBIndexToFragPrimIndex[ipric];
                                     for(unsigned int iprid=ipric+1; iprid<reducednbPrimInB; ++iprid)
                                     {  unsigned int dpri = reducedFragBPrim[iprid];
                                        unsigned int d    = reducedFragBIndexToFragPrimIndex[iprid];
                                        for(unsigned int ipria=0; ipria<reducednbPrimInA; ++ipria)
                                        {  unsigned int apri = reducedFragAPrim[ipria];
                                           unsigned int a    = reducedFragAIndexToFragPrimIndex[ipria];
                                           tmpSELF =     (2*D[a][a]*D[c][d]-2*D[a][c]*D[a][d])*DOV[a][a]*OV[c][d];
                                           SELF = SELF + tmpSELF;
                                          
                                           // store atomic pair decomposition for each fragment
                                           //---- Fragment 1 ----//
                                           ia = icenter[apri]-1;
                                           pauli[threadID][ia][ia] = pauli[threadID][ia][ia] 
                                                 + (tmpSELF/rhoValue);

                                           //---- Fragment 2 ----//
                                           ic = icenter[cpri]-1;
                                           id = icenter[dpri]-1;
                                           pauli[threadID][ic][id] = pauli[threadID][ic][id] 
                                                 + (tmpSELF/rhoValue)*weight[ic][id] ;
                                           if (ic != id) {
                                           pauli[threadID][id][ic] = pauli[threadID][id][ic] 
                                                 + (tmpSELF/rhoValue)*weight[id][ic] ;}

                                         } // !end over ipria
                                      } // !end over iprid
                                   } // !end over ipric

			         // ----- a'1=b'1       c2=d2    -----//
			         //        diagonal   x diagonal     // 
                                 for(unsigned int ipric=0; ipric<reducednbPrimInB; ++ipric)
                                  {  unsigned int cpri = reducedFragBPrim[ipric]; // cpri returned in the range[0:nbPrim-1]  
                                     unsigned int c    = reducedFragBIndexToFragPrimIndex[ipric];
                                     for(unsigned int ipria=0; ipria<reducednbPrimInA; ++ipria)
                                     {  unsigned int apri = reducedFragAPrim[ipria];
                                        unsigned int a    = reducedFragAIndexToFragPrimIndex[ipria];
                                        tmpSELF =     (D[a][a]*D[c][c]-D[a][c]*D[a][c]) * DOV[a][a]*OV[c][c];
                                        SELF = SELF + tmpSELF;
                                       
                                        // store atomic pair decomposition for each fragment
                                        //---- Fragment 1 ----//
                                        ia = icenter[apri]-1;
                                        pauli[threadID][ia][ia] = pauli[threadID][ia][ia] 
                                              + (tmpSELF/rhoValue);

                                        //---- Fragment 2 ----//
                                        ic = icenter[cpri]-1;
                                        pauli[threadID][ic][ic] = pauli[threadID][ic][ic] 
                                              + (tmpSELF/rhoValue);

                                      } // !end over ipria
                                  } // !end over ipric
//
//				 // ########################  I N T R A ##########################################
//				 // ........................   4/0 partition ........................
//
//				 //                    a'1b'1c1d1
//				 //            for(unsigned int ipria=0; ipria<reducednbPrimInA; ++ipria)
//				 //               {  unsigned int a = reducedFragAPrim[ipria]; // a returned in the range[0:reducednbPrimInA-1] 
//				 //                  for(unsigned int iprib=0; iprib<reducednbPrimInA; ++iprib)
//				 //                     {  unsigned int b = reducedFragAPrim[iprib];
//				 //                             for(unsigned int ipric=0; ipric<reducednbPrimInA; ++ipric)
//				 //                              {  unsigned int c = reducedFragAPrim[ipric];
//				 //                                 for(unsigned int iprid=0; iprid<reducednbPrimInA; ++iprid)
//				 //                                    {  unsigned int d = reducedFragAPrim[iprid];
//				 //                                        SELF = SELF + (D[a][b]*D[c][d]-
//				 //                                                       D[a][c]*D[b][d]) *
//				 //                                                       DOV[a][b]*OV[c][d];
//				 //                                    } // !end over iprid
//				 //			      } // !end over ipric
//				 //                     } // !end over iprib
//				 //               } // !end over ipria
//				 //
//				 //
//				 //            //                    a'2b'2c2d2
//				 //            for(unsigned int ipria=0; ipria<reducednbPrimInB; ++ipria)
//				 //               {  unsigned int a = reducedFragBPrim[ipria]; // a returned in the range[0:reducednbPrimInA-1] 
//				 //                  for(unsigned int iprib=0; iprib<reducednbPrimInB; ++iprib)
//				 //                     {  unsigned int b = reducedFragBPrim[iprib];
//				 //                             for(unsigned int ipric=0; ipric<reducednbPrimInB; ++ipric)
//				 //                           {  unsigned int c = reducedFragBPrim[ipric];
//				 //                              for(unsigned int iprid=0; iprid<reducednbPrimInB; ++iprid)
//				 //                                 {  unsigned int d = reducedFragBPrim[iprid];
//				 //
//				 //                                     SELF = SELF + (D[a][b]*D[c][d]-
//				 //                                                    D[a][c]*D[b][d]) *
//				 //                                                    DOV[a][b]*OV[c][d];
//				 //                                 } // !end over iprid
//				 //                           } // !end over ipric
//				 //                     } // !end over iprib
//				 //               } // !end over ipria
//				 // ########################  END of I N T R A ##########################################
//
//				 //                    every thing  
//				 //SELF = 0.0;
//				 //            for(unsigned int ipria=0; ipria<fragNbPrim; ++ipria)
//				 //               {  unsigned int a = fragPrim[ipria]; // a returned in the range[0:reducednbPrimInA-1] 
//				 //                  for(unsigned int iprib=0; iprib<fragNbPrim; ++iprib)
//				 //                     {       unsigned int b = fragPrim[iprib];
//				 //                             for(unsigned int ipric=0; ipric<fragNbPrim; ++ipric)
//				 //                           {      unsigned int c = fragPrim[ipric];
//				 //                                  for(unsigned int iprid=0; iprid<fragNbPrim; ++iprid)
//				 //                                     {  unsigned int d = fragPrim[iprid];
//				 //
//				 //                                         SELF = SELF + (D[a][b]*D[c][d]-
//				 //                                                        D[a][c]*D[b][d]) *
//				 //                                                        DOV[a][b]*OV[c][d];
//				 //                                 } // !end over iprid
//				 //                           } // !end over ipric
//				 //                     } // !end over iprib
//				 //               } // !end over ipria


                           // D E A L L O C A T E 
                           for(unsigned int iat=0; iat<nbAtoms; ++iat)
                             {                            
                                delete[] weight[iat];                       
                             } // end over iat                                    
                           delete[] weight;

  			 } // end of else of if (not isPauliAtomic())
//
//                         // S T O R E  SELF=PAULI22 (direct way or atomic way)
//                         // rhoValue = must be the total ED (not limited to FRAG1+FRAG2 !!!)
//                         //            since G - GW = (4*Sum_abcd [DabDcd - DacDbd]a'b'cd ) / (8rho_total)
                         deltaginterFC[ip][jp][kp] = (4*SELF/(rhoValue*8))*ekin2kcal; 


			 // D E A L L O C A T E 
			 for(unsigned int ipria=0;ipria<fragNbPrim; ++ipria)
			 {
				 delete[] OV[ipria];
				 delete[] DOV[ipria];
				 for(unsigned int iprib=0;iprib<fragNbPrim; ++iprib)
				 {
					 delete[] COV[ipria][iprib]; 
				 }
				 delete[] COV[ipria];
			 }
			 delete[] OV;
			 delete[] DOV;
			 delete[] COV;

		 } // end of if (rhoValue > PauliRhoThresh)

	 } // end of isPAULI()

	 // ...................... E N D  o f      P A U L I .......................................

	 // handle timing previsions
	 if(threadID==0)
	 {
		 // get time and check if 8s have passed
		 gettimeofday(&stop,NULL);
		 time_t period = stop.tv_sec - interm.tv_sec;
		 time_t ellapsed = stop.tv_sec - start.tv_sec;
		 counter++;
		 if (period >= 8.0)
		 { // its time to update the runinfo file
			 printCurrentState(counter*nbThreads,nbSteps0*nbSteps1*nbSteps2,ellapsed);
			 runinfostate = true;
			 gettimeofday(&interm,NULL);
		 }
	 } // end of if(threadID==0)                                                                   


	      }// i = 0, n(1) -1  ............. E N D    O F  X   A X I S ...............................................

	      // deallocate some variables
	      for( unsigned int i=0 ; i < npri ; ++i )
	      {
		      delete[] chi[i];
	      }
	      delete[] chi;


	      for(std::vector<moleculeOrbital>::size_type i=0 ; i< molecularOrbitals.size() ; ++i)
	      {
		      delete[] phi[i];
	      }
	      delete[] phi;

	      delete[] dist;

	      delete[] rhoFree;
	      delete[] pregradFree;

	      //delete[] basisSet;

	      for (unsigned int iat=0; iat<nbAtoms; ++iat) {
		      delete[] gradAtom[iat];
	      }    
	      delete[] gradAtom;

	      delete[] rhoAtom;

	      // D E A L L O C A T E 
	      delete[] deltagAt;
	      delete[] dgInterAt;


	    } //! k = 0, n(3)-1 ................  E N D    O F  Z   A X I S ...............................................

	} //! j = 0, n(2)-1 ................  E N D    O F  Y   A X I S ...............................................


      // ..... E N D   O F    P R A G M A     F O R  ....................................................................

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



  // ============================================================================//
  //             C P s   S E A R C H    i n   t h e    g r i d                   //
  // ============================================================================//

  if (isCriticmodeActivated()) // no fragment scheme
  {

	  // LOCAL VARIABLES
	  std::vector<criticalpoint>  cpListTMP;  // temporary list of solutions
	  std::vector<point> seedPosList; // a vector containing positions of seeds in the grid
	  // assumed to be good guess for CP search 

	  double** dxcurr = NULL;  // common to IGMPRO (IGM seeding) and IGMQ calculations (in Newton-Raphson)
	  double** dycurr = NULL;
	  double** dzcurr = NULL;

	  dxcurr = new double*[nbThreads];
	  dycurr = new double*[nbThreads];
	  dzcurr = new double*[nbThreads];

	  for(unsigned int i=0;i<nbThreads;++i)
	  {
		  dxcurr[i] = new double[nbAtoms];
		  dycurr[i] = new double[nbAtoms];
		  dzcurr[i] = new double[nbAtoms];
	  }


	  // xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx //
	  //  -A1-   G E O M E T R I C A L    S E E D I N G                    
	  // xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx //

	  double a1[3], a2[3], a3[3]; 

	  int nmidp = 0;
	  for(unsigned int iat=0;iat<(nbAtoms-1);++iat)
	  {
		  a1[0] = atomCoordinates.xValues[iat];
		  a1[1] = atomCoordinates.yValues[iat];
		  a1[2] = atomCoordinates.zValues[iat];

		  for(unsigned int jat=iat+1;jat<nbAtoms;++jat)
		  {  
			  a2[0] = atomCoordinates.xValues[jat];
			  a2[1] = atomCoordinates.yValues[jat];
			  a2[2] = atomCoordinates.zValues[jat];

			  if (distance(a1,a2) < 15.0)    // each pair of atoms less than 15 bohr apart are considered)
			  {
				  // deals with pairs of atoms
				  point midPoint; // = A1...A2 midpoint 
				  midPoint.x = (a1[0]+a2[0])/2;
				  midPoint.y = (a1[1]+a2[1])/2; 
				  midPoint.z = (a1[2]+a2[2])/2; 
				  seedPosList.push_back(midPoint);
				  nmidp = nmidp+1;

				  if (isCriticAddSeedsmodeActivated())   
				  {
					  // deal also with triangles (triplets of atoms)
					  if (iat < nbAtoms-2)
					  {
						  for (unsigned int kat=jat+1;kat<nbAtoms;++kat)
						  {
							  a3[0] = atomCoordinates.xValues[kat];
							  a3[1] = atomCoordinates.yValues[kat];
							  a3[2] = atomCoordinates.zValues[kat];

							  if ( (distance(a1,a3) < 15.0)  and 
									  (distance(a2,a3) < 15.0)
							     )
							  {
								  point trianglecenter; // = A1...A2...A3 center   
								  trianglecenter.x = (midPoint.x+a3[0])/2;
								  trianglecenter.y = (midPoint.y+a3[1])/2;
								  trianglecenter.z = (midPoint.z+a3[2])/2;

								  seedPosList.push_back(trianglecenter);
								  nmidp = nmidp+1;
							  } // end of if ( (distance(a1,a3) < 15.0)  and  (distance(a2,a3) < 15.0))

						  } // end over kat

					  } // end of if (iat < nbAtoms-2)
				  }// end of if (isCriticAddSeedsmodeActivated())  

			  } // end of if (distance(a1,a2) < 15.0)


		  } // end over jat
	  } // end over iat

	  // ................ E N D    o f    G E O M E T R I C A L    S E E D I N G ...............................//
	  // .......................................................................................................//




	  // xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx //
	  //  -A2-   P R O M O L E C U L A R    S E A R C H   O F   S E E D S 
	  // xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx //


	  if (IGMSeeds)
	  {

		  // =================================================================== //
		  //   s e a r c h   f o r    d g       m a x i m a        = s e e d s 
		  // =================================================================== //

		  // Setting the steps for PROMOLECULAR grid (retrieved from params.increments variable)
		  double       incrPRO[3]  = {params.increments[0], params.increments[1], params.increments[2]}; // in bohr
		  unsigned int nbSteps0PRO = data->getNbSteps(0);
		  unsigned int nbSteps1PRO = data->getNbSteps(1);
		  unsigned int nbSteps2PRO = data->getNbSteps(2);

		  double*** qgPRO=NULL;
		  try
		  {
			  qgPRO = new double**[nbSteps0PRO];
			  for(unsigned int ip=0;ip<nbSteps0PRO;++ip)
			  {
				  qgPRO[ip] = new double*[nbSteps1PRO];
				  for(unsigned int jp=0;jp<nbSteps1PRO;++jp)
				  {
					  qgPRO[ip][jp] = new double[nbSteps2PRO];
					  for(unsigned int kp=0;kp<nbSteps2PRO;++kp)
					  {
						  qgPRO[ip][jp][kp] = 0.0;
					  }// end over X
				  }// end over Y
			  }// end over Z
		  } // end of  try allocating global arrays
		  catch (const std::bad_alloc& bad_alloc_error)
		  {
			  std::cerr << std::endl
				  << "[ERROR] Allocation failed: bad_alloc error catched in routine calcprops_wfn in critical point search" << std::endl
				  << "[ERROR] The chosen increments have generated a too high amount of data for the shared data structures" << std::endl
				  << "[ERROR] that the current version of IGMPLOT is not able to manage, sorry." << std::endl
				  << "[ERROR] The program will now exit." << std::endl << std::endl;
			  exit(EXIT_FAILURE);
		  }
		  // ....... END of LOCAL VARIABLES .....................................................


#ifndef No_OpenMP         
#pragma omp parallel for /*default(none)*/ shared(nbSteps0PRO, nbSteps1PRO, nbSteps2PRO, incrPRO, nbAtoms, qgPRO, dxcurr, dycurr, dzcurr) schedule(dynamic)
#endif

		  // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //
		  //    1 )   c a l c u l a t e    q g P r o m o l               
		  // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //

		  // loop over Y dimension
		  for ( unsigned int jp=0; jp < nbSteps1PRO; ++jp )
		  {
#ifndef No_OpenMP
			  unsigned int threadID = omp_get_thread_num();
#else
			  unsigned int threadID = 0;
#endif 

			  //loop over Z     dimension 
			  for ( unsigned int kp=0; kp < nbSteps2PRO; ++kp )
			  {  
				  //loop over X     dimension ======================================== X  axis ===============
				  for( unsigned int ip=0 ; ip < nbSteps0PRO ; ++ip )
				  { 
					  double rcurr[3]; // current position
					  rcurr[0] = data->getMinCoord(0) + ip * incrPRO[0];
					  rcurr[1] = data->getMinCoord(1) + jp * incrPRO[1];
					  rcurr[2] = data->getMinCoord(2) + kp * incrPRO[2];

					  for(unsigned int iat=0;iat<nbAtoms;++iat)
					  {
						  dxcurr[threadID][iat] = rcurr[0]   - atomCoordinates.xValues[iat];
						  dycurr[threadID][iat] = rcurr[1]   - atomCoordinates.yValues[iat];
						  dzcurr[threadID][iat] = rcurr[2]   - atomCoordinates.zValues[iat];
					  }


					  // compute dg at the promolecular level
					  double deltagPRO; // ineffective, just to receive unused deltagPRO
					  this-> IGMPRO(dxcurr[threadID], dycurr[threadID], dzcurr[threadID], deltagPRO, qgPRO[ip][jp][kp]); 
				  } // end over X
			  } // end over Z
		  } // end over Y

		  // ....... E N D   o f   P R A G M A   p a r a l l e l   f o r ............................... //



		  // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //
		  //    2 )    f i n d   o u t   q g    m a x    v a l u e s    i n   t h e    g r i d 
		  // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //

#ifndef No_OpenMP
#pragma omp parallel shared(seedPosList, nbSteps0PRO, nbSteps1PRO, nbSteps2PRO, qgPRO) 
		  {
#endif

			  // a vector containing properties of all qgmax positions found by current thread
			  std::vector<point> qgmaxPosListSLAVE; // one by thread


#ifndef No_OpenMP
#pragma omp for nowait schedule(dynamic)
#endif

			  // loop over Y dimension
			  for ( unsigned int jp=1; jp < (nbSteps1PRO-1); ++jp )
			  { // exceptionnaly starts from second index and ends before last 
				  // CP are not expected close to the grid boundary 

				  //loop over Z     dimension 
				  for ( unsigned int kp=1; kp < (nbSteps2PRO-1); ++kp )
				  {  // exceptionnaly starts from second index and ends before last 
					  // CP are not expected close to the grid boundary

					  //loop over X     dimension ======================================== X  axis ===============
					  for( unsigned int ip=1 ; ip < (nbSteps0PRO-1) ; ++ip )
					  { // exceptionnaly starts from second index and ends before last 
						  // CP are not expected close to the grid boundary 

						  // examine the current point only if Qg > threshold (close to a dg max value with ED gradient close to zero)

						  // ................................................. //
						  //   l o o k    a r o u n d                          
						  // ................................................. //
						  // explore all positions around current point ! assuming qgmax is always a maximum
						  // (no saddle point in the qg topology)
						  // 26 comparisons to perform:
						  int nlower=0;
						  // current Y slice
						  if (qgPRO[ip][jp][kp] <= qgPRO[ip][jp][kp+1]) {nlower=nlower+1;} // go to the next grid point
						  if (qgPRO[ip][jp][kp] <= qgPRO[ip][jp][kp-1]) {nlower=nlower+1;} 
						  if (qgPRO[ip][jp][kp] <= qgPRO[ip+1][jp][kp]) {nlower=nlower+1;} 
						  if (qgPRO[ip][jp][kp] <= qgPRO[ip-1][jp][kp]) {nlower=nlower+1;} 
						  if (qgPRO[ip][jp][kp] <= qgPRO[ip+1][jp][kp+1]) {nlower=nlower+1;} 
						  if (qgPRO[ip][jp][kp] <= qgPRO[ip-1][jp][kp-1]) {nlower=nlower+1;} 
						  if (qgPRO[ip][jp][kp] <= qgPRO[ip-1][jp][kp+1]) {nlower=nlower+1;} 
						  if (qgPRO[ip][jp][kp] <= qgPRO[ip+1][jp][kp-1]) {nlower=nlower+1;} 

						  // next Y slice
						  if (qgPRO[ip][jp][kp] <= qgPRO[ip][jp+1][kp]) {nlower=nlower+1;} // go to the next grid point

						  if (qgPRO[ip][jp][kp] <= qgPRO[ip][jp+1][kp+1]) {nlower=nlower+1;} 
						  if (qgPRO[ip][jp][kp] <= qgPRO[ip][jp+1][kp-1]) {nlower=nlower+1;} 
						  if (qgPRO[ip][jp][kp] <= qgPRO[ip+1][jp+1][kp]) {nlower=nlower+1;} 
						  if (qgPRO[ip][jp][kp] <= qgPRO[ip-1][jp+1][kp]) {nlower=nlower+1;} 
						  if (qgPRO[ip][jp][kp] <= qgPRO[ip+1][jp+1][kp+1]) {nlower=nlower+1;} 
						  if (qgPRO[ip][jp][kp] <= qgPRO[ip-1][jp+1][kp-1]) {nlower=nlower+1;} 
						  if (qgPRO[ip][jp][kp] <= qgPRO[ip-1][jp+1][kp+1]) {nlower=nlower+1;} 
						  if (qgPRO[ip][jp][kp] <= qgPRO[ip+1][jp+1][kp-1]) {nlower=nlower+1;} 

						  // previous slice
						  if (qgPRO[ip][jp][kp] <= qgPRO[ip][jp-1][kp]) {nlower=nlower+1;} // go to the next grid point


						  if (qgPRO[ip][jp][kp] <= qgPRO[ip][jp-1][kp+1]) {nlower=nlower+1;} 
						  if (qgPRO[ip][jp][kp] <= qgPRO[ip][jp-1][kp-1]) {nlower=nlower+1;} 
						  if (qgPRO[ip][jp][kp] <= qgPRO[ip+1][jp-1][kp]) {nlower=nlower+1;} 
						  if (qgPRO[ip][jp][kp] <= qgPRO[ip-1][jp-1][kp]) {nlower=nlower+1;} 
						  if (qgPRO[ip][jp][kp] <= qgPRO[ip+1][jp-1][kp+1]) {nlower=nlower+1;} 
						  if (qgPRO[ip][jp][kp] <= qgPRO[ip-1][jp-1][kp-1]) {nlower=nlower+1;} 
						  if (qgPRO[ip][jp][kp] <= qgPRO[ip-1][jp-1][kp+1]) {nlower=nlower+1;} 
						  if (qgPRO[ip][jp][kp] <= qgPRO[ip+1][jp-1][kp-1]) {nlower=nlower+1;} 

						  // if the current point passed all (or all -1) previous tests
						  // ==> qgPRO[ip][jp][kp] is a local maximum in the grid:
						  // a) re-generate its position from [ip][jp][kp] 
						  // b) store its position in a vector
						  if (nlower <=1) {
							  point currPoint;
							  currPoint.x = data->getMinCoord(0) + ip * incrPRO[0];             
							  currPoint.y = data->getMinCoord(1) + jp * incrPRO[1];             
							  currPoint.z = data->getMinCoord(2) + kp * incrPRO[2];             

							  qgmaxPosListSLAVE.push_back(currPoint);
						  }

					  } // end over ip
				  } // end over kp
			  } // end over jp
			  // .............. E N D    o f    t h e    p r a g ma    o m p    f o r ...................... //


#ifndef No_OpenMP
#pragma omp critical // Slaves that perform their part of the job
			  // will perform this following part thread by thread ! (reduction operation)
			  seedPosList.insert(seedPosList.end(),
					  qgmaxPosListSLAVE.begin(),
					  qgmaxPosListSLAVE.end()
					  );
			  /* ERIC ADDENTUM *******************************************************
			     has to be done also without parallelism activation */
#else
			  seedPosList.insert(seedPosList.end(),
					  qgmaxPosListSLAVE.begin(),
					  qgmaxPosListSLAVE.end()
					  );
#endif

#ifndef No_OpenMP
		  } // .............. E N D    o f    t h e    p r a g ma    o m p    s e c t i o n .......... //
#endif


		  /* ==== D E A L L O C A T I N G   M E M O R Y   ==== */
		  for( unsigned int ip=0 ; ip < nbSteps0PRO ; ++ip )
		  {
			  for(unsigned int jp=0;jp < nbSteps1PRO;++jp)
			  {
				  delete[] qgPRO[ip][jp];
			  }
			  delete[] qgPRO[ip];
		  }
		  delete[] qgPRO;

	  } // end of if (IGMSeeds)    

	  // ................ E N D    o f    I G M     S E E D I N G ..........................................//
	  // .......................................................................................................//


	  // xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx //
	  //  -B-  N e w t o n - R a p h s o n   f r o m   e a c h    s e e d 
	  // xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx //

	  //   Newton-Raphson procedure to localize a possible BCP
	  //   The GUESS=SEED xp,yp,zp with maximum dg value found in the grid

#ifndef No_OpenMP
#pragma omp parallel shared(seedPosList, dxcurr, dycurr, dzcurr, nbAtoms, cpListTMP) 
	  {
#endif
		  // ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: // 
		  //     V A R I A B L E S  L O C A L    t o    t h e    T H R E A D
		  // ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: //

		  // LOCAL VARIABLES for Newton-Raphson
		  //
		  // a vector containing properties of all found critical points
		  std::vector<criticalpoint>  cpListSLAVE; // one by thread

		  double **Rcp, **L123; // array containing coordinates of found critical points and ED hessian eigenval.
		  double *Gcp, *RHOcp, *GRADRHOcp;  // and kinetic energy density and ED and ED gradient magnitude

		  Gcp   = new double[seedPosList.size()];
		  RHOcp = new double[seedPosList.size()];
		  GRADRHOcp = new double[seedPosList.size()];
		  Rcp   = new double*[seedPosList.size()]; 
		  L123  = new double*[seedPosList.size()]; 

		  for(unsigned int iguess = 0; iguess < seedPosList.size(); iguess++)
		  {
			  Rcp[iguess]  = new double[3];
			  L123[iguess] = new double[3];
		  }

		  // LOCAL VARIABLES for QM CALCULATION
		  double** chi  = new double*[npri];
		  double** gradPrimcurr = new double*[npri];
		  double** phi  = new double*[molecularOrbitals.size()];
		  for(unsigned int i=0;i<npri;++i)
		  {
			  chi[i]    = new double[10];
		  }
		  for(unsigned int ipri=0;ipri<npri;++ipri)
		  {
			  gradPrimcurr[ipri] = new double[3];
			  for(unsigned int ix=0;ix<3;++ix)
			  {gradPrimcurr[ipri][ix]=0.0;}
		  }

		  for(std::vector<moleculeOrbital>::size_type i=0 ; i< molecularOrbitals.size() ; ++i)
		  {
			  phi[i]        = new double[10];
		  }
		  double rhocurr;
		  double gradcurr[3];
		  gradcurr[0] = 0.0;
		  gradcurr[1] = 0.0;
		  gradcurr[2] = 0.0;
		  double** hesscurr      = new double*[3];
		  double** eigenVectcurr = new double*[3];
		  double* heigscurr      = new double[3];
		  for (unsigned int i=0; i<3; ++i)
		  {
			  heigscurr[i]     = 0.0;
			  hesscurr[i]      = new double[3];
			  eigenVectcurr[i] = new double[3];
		  }

		  double* d2curr = NULL;
		  d2curr = new double[nbAtoms];

		  double deltagIntraCURRENT, qgIntraCURRENT;
		  double deltagInterCURRENT, qgInterCURRENT;

		  // :::::::::: E N D    O F    L O C A L    V A R I A B L E S ::::::::::::: //




		  // ::::::::::::::::::::::::::::::::::::::::::::::::::::::: // 
		  // S E A R C H    f o r     C r i t i c a l    P o i n t
		  // ::::::::::::::::::::::::::::::::::::::::::::::::::::::: //
#ifndef No_OpenMP
#pragma omp for nowait schedule(dynamic)
#endif
		  for(unsigned int iguess = 0; iguess < seedPosList.size(); iguess++)
		  {

#ifndef No_OpenMP
			  unsigned int threadID = omp_get_thread_num();
#else
			  unsigned int threadID = 0;
#endif
			  // Initializations
			  Rcp[iguess][0] = seedPosList[iguess].x;
			  Rcp[iguess][1] = seedPosList[iguess].y;
			  Rcp[iguess][2] = seedPosList[iguess].z;



			  L123[iguess][0] = 0.0;
			  L123[iguess][1] = 0.0;
			  L123[iguess][2] = 0.0;

			  RHOcp[iguess]     = 0.0;
			  GRADRHOcp[iguess] = 0.0;
			  Gcp[iguess]       = 0.0;

			  // Newton-Raphson procedure (CRITIC KEYWORD)
			  bool okCP = NewtonRaphson(fragNbPrim, fragPrim, Rcp[iguess], L123[iguess], Gcp[iguess], RHOcp[iguess], GRADRHOcp[iguess]);

			  // C H E C K S 
			  if (okCP) 
			  {
				  int signature = cptype(L123[iguess]);
				  // rule out nucleus critical points and rank different from 3
				  if ( (signature != -3) and (signature != 0) )
				  {
					  // prepare the QM calculation to get dg by computing dx,dy,dz,d2 for each atom:
					  for(unsigned int iat=0;iat<nbAtoms;++iat)
					  {
						  dxcurr[threadID][iat] = Rcp[iguess][0]   - atomCoordinates.xValues[iat];
						  dycurr[threadID][iat] = Rcp[iguess][1]   - atomCoordinates.yValues[iat];
						  dzcurr[threadID][iat] = Rcp[iguess][2]   - atomCoordinates.zValues[iat];
						  d2curr[iat] = dxcurr[threadID][iat]*dxcurr[threadID][iat] + 
							  dycurr[threadID][iat]*dycurr[threadID][iat] + 
							  dzcurr[threadID][iat]*dzcurr[threadID][iat];
					  }

					  // compute the wave function features at this point for QTAIM critic points earch
                                          // note that in this case allprimitives are considered (CP search not compatible
                                          // with FRAG1,2 and then not on systeme with FRAG1+2 < ALL)
					  calcQMAO(fragNbPrim, fragPrim, d2curr, dxcurr[threadID], dycurr[threadID], dzcurr[threadID], chi);
                                          calcQMPROP(fragNbPrim, fragPrim, chi, phi, rhocurr, gradcurr, hesscurr);

					  // basis change for chi, phi, dx,y,z, gradcurr        
					  double matrixHessianEigVec[3][3];
					  // we need ED hessian eigenvectors
					  this->getLambdaOfHessian(hesscurr,heigscurr,eigenVectcurr);

					  // define the change-of-basis matrix to go from the WFN reference frame to the Curvature reference frame 
					  // assuming that the 3 eigenvectors of the ED hessian are set in columns in eigenVect structure and
					  // all eigenvectors are orthonormal ==> the change-of-basis matrix to go from the WFN reference frame to the Curvature reference frame
					  // is the transposed matrix (i.e. the inverse here) of the matrix formed with the 3 eigenVect and is called now: matrixHessianEigVec
					  getTransposed(eigenVectcurr, matrixHessianEigVec);


					  // xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
					  // ==> basis change for the primitive gradient chi (atomic orbital gradient)
					  //                          Molecular orbitals derivatives
					  //                          position vectors dx,y,z
					  //                          ED gradient
					  // xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
					  basischange(fragNbPrim, fragPrim, matrixHessianEigVec, chi,
							  dxcurr[threadID], dycurr[threadID], dzcurr[threadID], phi, gradcurr);

					  //  ...................................... end of B A S I S   C H A N G E            




					  // xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
					  // ==> Test battery and storage      
					  //                    
					  // xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

					  // calculate deltag, qg
					  this->IGM(chi, phi, dxcurr[threadID], dycurr[threadID], dzcurr[threadID], gradPrimcurr,
							  deltagIntraCURRENT, deltagInterCURRENT, qgIntraCURRENT, qgInterCURRENT);


					  // check if it is not a point drifted towards the vacuum 
					  // dg must be larger than a given threshold
					  if (qgIntraCURRENT > qgThreshNR)
					  {
						  // check whether this point has already been encountered or not
						  bool newCp = true;
						  for(unsigned int icp = 0; icp < cpListSLAVE.size(); icp++) 
						  {
							  // calculate distance between current found critical point and each critical point already found
							  if (distance(Rcp[iguess],cpListSLAVE[icp].pos) < cpSeparationThresh)
							  {
								  newCp = false; break;
							  } // end of if distance
						  } // end over list of cp

						  // if it is a new critical point, add it to the list
						  if (newCp) 
						  { criticalpoint cp; 
							  cp.pos[0]    = Rcp[iguess][0];
							  cp.pos[1]    = Rcp[iguess][1];
							  cp.pos[2]    = Rcp[iguess][2];
							  cp.L123[0]   = L123[iguess][0];
							  cp.L123[1]   = L123[iguess][1];
							  cp.L123[2]   = L123[iguess][2];
							  cp.type      = cptype(cp.L123); 
							  cp.G         = Gcp[iguess];
							  cp.RHO       = RHOcp[iguess];
							  cp.GRADRHO   = GRADRHOcp[iguess];

							  cp.laplac = cp.L123[0]+cp.L123[1]+cp.L123[2];

							  //    L O C A L     E N E R G Y     D E N S I T Y   (C R E M E R definition)
							  // H(r) = G(r) + V(r), and, 2G(r) + V(r) = 1/4 delta rho(r)
							  // ==> H(r) = 1/4 delta rho(r) - G(r) 
							  cp.H = 0.25*cp.laplac - cp.G;

							  // V = H - G
							  cp.V = cp.H - cp.G;

							  // qg and dg
							  cp.dg = deltagIntraCURRENT;
							  cp.qg = qgIntraCURRENT; 

							  // save the current critical point
							  cpListSLAVE.push_back(cp);
						  } // end of if (newCp)
					  } // end of if (qgIntraCURRENT > threshold)
				  } // end of if ( (signature != -3) and (signature != 0) )
			  } // end of if (okCP)
		  }  // end of loop over guess
		  // .............. E N D    o f    t h e    p r a g ma    o m p    f o r ...................... //

		  /*for(unsigned int i=0;i<(unsigned int)cpListSLAVE.size();i++) std::cout << cpListSLAVE[i].pos[0] << " "
		    << cpListSLAVE[i].pos[1] << " "
		    << cpListSLAVE[i].pos[2] << std::endl;*/


#ifndef No_OpenMP
#pragma omp critical // Slaves that perform their part of the job
		  // will perform this following part thread by thread ! (reduction operation)
		  cpListTMP.insert(cpListTMP.end(), 
				  cpListSLAVE.begin(),
				  cpListSLAVE.end()
				  );
		  /* JC ADDENTUM *******************************************************
		     has to be done also without parallelism activation */
#else
		  cpListTMP.insert(cpListTMP.end(), 
				  cpListSLAVE.begin(),
				  cpListSLAVE.end()
				  );
		  ///////////////////////////////////////////////////////////////////////
#endif


		  /* ================== D E A L L O C A T I N G   M E M O R Y  f o r     c u r r e n t    t h r e a d  ==== */
		  for(unsigned int iguess = 0; iguess < seedPosList.size(); iguess++)
		  {
			  delete[] Rcp[iguess];
			  delete[] L123[iguess];
		  }
		  delete[] Rcp;
		  delete[] L123;

		  delete[] Gcp;
		  delete[] RHOcp;
		  delete[] GRADRHOcp;


		  delete[] d2curr;

		  for(unsigned int i=0;i<npri;++i)
		  { delete[] chi[i];
			  delete[] gradPrimcurr[i];}
		  delete[] chi;
		  delete[] gradPrimcurr;



		  for(std::vector<moleculeOrbital>::size_type i=0 ; i< molecularOrbitals.size() ; ++i)
		  { delete[] phi[i];}
		  delete[] phi;

		  for (unsigned int i=0; i<3; ++i)
		  {
			  delete[] hesscurr[i];
			  delete[] eigenVectcurr[i];
		  }
		  delete[] hesscurr;
		  delete[] eigenVectcurr;
		  delete[] heigscurr;

#ifndef No_OpenMP
	  } // .............. E N D    o f    t h e    p r a g m a    o m p    s e c t i o n .......... //
#endif
	  // xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
	  // ==> Remove possible redundant solution present in cpListTMP
	  // xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
	  if (cpListTMP.size() > 0)
	  {
		  bool redundant;
		  cpList.clear();
		  cpList.push_back(cpListTMP[0]);
		  for(unsigned int icpTMP = 1; icpTMP < cpListTMP.size(); icpTMP++)
		  {
			  redundant = false;
			  for(unsigned int icp = 0; icp < cpList.size(); icp++)
			  {
				  // calculate distance between current critical point and each critical point already found
				  if (distance(cpList[icp].pos, cpListTMP[icpTMP].pos) < cpSeparationThresh )
				  {
					  redundant = true; // icpTMP is already part of cpList
					  break;  // no need to keep running over cpList
				  }
			  } // end over cpList
			  if (not redundant) {cpList.push_back(cpListTMP[icpTMP]);}
		  } // end over cpListTMP
	  } // end of if (cpListTMP.size() > 0)


	  /* ==== D E A L L O C A T I N G   M E M O R Y   ==== */
	  for(unsigned int i=0;i<nbThreads;++i)
	  {
		  delete[] dxcurr[i];
		  delete[] dycurr[i];
		  delete[] dzcurr[i];
	  }
	  delete[] dxcurr;
	  delete[] dycurr;
	  delete[] dzcurr;

	  // ::::::::::::::::::::::::::::::::::: // 
	  //  D I S P L A Y    R E S U L T S   
	  // ::::::::::::::::::::::::::::::::::: //
	  std::cout << "   *                                                                   *" << std::endl;
	  std::cout << "   * ################################################################  *" << std::endl;
	  std::cout << "   * ################## C R I T I C A L    P O I N T ################  *" << std::endl;
	  std::cout << "   * ##################      A N A L Y S I S        #################  *" << std::endl;
	  std::cout << "   * ################################################################  *" << std::endl;
	  std::cout << "   * ----------------------------------------------------------------  *" << std::endl;
	  std::string emptyLine("   *                                                                   *");
	  std::cout << "   *   Seeding algorithm:                                              *" << std::endl;
	  if (IGMSeeds)
	  {
		  std::cout << "   *                      - IGM Promolecular Qgmax points              *" << std::endl;
		  if (isCriticMEDIUMmodeActivated()) 
		  {
			  std::cout << "   *                        with medium grid    (default)              *" << std::endl; 
		  }
		  else if (isCriticFINEmodeActivated())
		  {
			  std::cout << "   *                        with fine grid      (CRITICFINE)           *" << std::endl;
		  }
		  else if (isCriticULTRAFINEmodeActivated())
		  {
			  std::cout << "   *                        with ultrafine grid (CRITICULTRAFINE)      *" << std::endl;
		  }
	  }
	  std::cout << "   *                      - midpoint between atom pairs                *" << std::endl;
	  if (isCriticAddSeedsmodeActivated())
	  {
		  std::cout << "   *                      - center of atomic triplets  (CRITICADDSEEDS)*" << std::endl;
	  }
	  std::cout << emptyLine << std::endl;
	  std::cout << "   *   BCP + RCP + CCP                 :";
	  std::cout << std::setw(8) << std::right << cpList.size();
	  std::cout << "                       *" << std::endl;

	  // bond, ring, cage
	  unsigned int nbcp = 0;
	  unsigned int nrcp = 0;
	  unsigned int nccp = 0;
	  int poincarehopf = 0;
	  for(unsigned int icp = 0; icp < cpList.size(); icp++)
	  {
		  if (cpList[icp].type == -1) {nbcp=nbcp+1;}
		  if (cpList[icp].type ==  1) {nrcp=nrcp+1;}
		  if (cpList[icp].type ==  3) {nccp=nccp+1;}
	  }
	  poincarehopf = nbAtoms-nbcp+nrcp-nccp;

	  std::cout << emptyLine << std::endl;
	  std::cout << "   *   Number of critical points of each type            Signature     *" << std::endl;
	  std::cout << emptyLine << std::endl;
	  std::cout << "   *       Bond critical points (BCP)  :";
	  std::cout << std::setw(8) << std::right << nbcp;
	  std::cout << "          (3,-1)       *" << std::endl;
	  std::cout << "   *       Ring critical points (RCP)  :";
	  std::cout << std::setw(8) << std::right << nrcp;
	  std::cout << "          (3,+1)       *" << std::endl;
	  std::cout << "   *       Cage critical points (CCP)  :";
	  std::cout << std::setw(8) << std::right << nccp;
	  std::cout << "          (3,+3)       *" << std::endl;
	  std::cout << emptyLine << std::endl;
	  std::cout << "   *   Poincare-Hopf relationship  NCP  -  BCP  +  RCP  -  CCP  = 1    *" << std::endl;
	  std::cout << "   *   Verification              "; 
	  std::cout << std::setw(4) << std::right << nbAtoms << "    ";
	  std::cout << std::setw(4) << std::right << nbcp    << "    ";
	  std::cout << std::setw(4) << std::right << nrcp    << "    ";
	  std::cout << std::setw(4) << std::right << nccp    << "   =";
	  std::cout << std::setw(2) << std::right << poincarehopf << "    *" << std::endl;
	  if (poincarehopf == 1)
	  {std::cout << "   *       relationship satisfied                                      *" << std::endl;}
	  else
	  {  std::cout << "   *       relationship not satisfied                                  *" << std::endl;
		  if (IGMSeeds) {
			  if (params.paramFound[CRITIC]) {
				  std::cout << "   *          --> try CRITICFINE keyword instead of CRITIC to          *" << std::endl;
				  std::cout << "   *              uncover missing critical points                      *" << std::endl;
			  } // end of if (params->paramFound[CRITIC])
			  else if (params.paramFound[CRITICFINE]) {
				  std::cout << "   *          --> try CRITICULTRAFINE keyword instead of CRITICFINE    *" << std::endl;
				  std::cout << "   *              to uncover missing critical points                   *" << std::endl;
			  } // end of if (params->paramFound[CRITIC])
		  } // end of if (IGMSeeds)
		  std::cout << "   *          --> try CRITICADDSEEDS in addition to CRITIC (or to      *" << std::endl;
		  std::cout << "   *              CRITICFINE or CRITICULTRAFINE) to uncover            *" << std::endl; 
		  std::cout << "   *              missing critical points                              *" << std::endl; 
                  std::cout << emptyLine << std::endl;
                  std::cout << "   *          --> additionally, ensure that no effective core          *" << std::endl;
                  std::cout << "   *              potentials (ECPs) are employed: they may not         *" << std::endl;
                  std::cout << "   *              adequately represent the electronic density close to *" << std::endl;
                  std::cout << "   *              the nucleus.                                         *" << std::endl;
                  std::cout << emptyLine << std::endl;
                  std::cout << "   *          --> note that certain computational levels can poorly    *" << std::endl;
                  std::cout << "   *              describe electron density, leading to false critical *" << std::endl;
                  std::cout << "   *              points (e.g., M062X/6-31G** on C2H2)                 *" << std::endl;


	  } // end of if (poincarehopf == 1)

	  std::cout << emptyLine << std::endl;
	  std::cout << emptyLine << std::endl;
	  std::cout << "   ---------------------------------------------------------------------" << std::endl;

  } // end of if (isCriticmodeActivated())
  //...... E N D    O F    C R I T I C A L     P O I N T S    T R E A T M E N T S..........//



  if ( (not isCriticmodeActivated() and (not isELFmodeActivated()) ) )
  {

	  /* ============================================================================================================== */
	  /*                              T E S T I N G   I N T E R A C T I O N S                                           */
	  /*                                      Q M   C U B I C   G R I D                                                 */
	  /* ============================================================================================================== */

	  // sums
	  double   dgInterSum   = 0.0; // sum of dgInter scores
  double   dgInterSumPos= 0.0; // sum of dgInter scores for L2 > 0
  double   dgInterSumNeg= 0.0; // sum of dgIntra scores for L2 < 0

  double   dgInterSumW  = 0.0; // sum of dgInter scores for low   ED (weak   interactions)
  double   dgInterSumS  = 0.0; // sum of dgInter scores for large ED (strong interactions)

  double   dgIntraSum   = 0.0; // sum of dgIntra scores
  double   dgIntraSumPos= 0.0; // sum of dgIntra scores for L2 > 0
  double   dgIntraSumNeg= 0.0; // sum of dgIntra scores for L2 < 0

  double   dgIntraSumW  = 0.0; // sum of dgIntra scores for low   ED (weak   interactions)
  double   dgIntraSumS  = 0.0; // sum of dgIntra scores for large ED (strong interactions)

  double   dgInterSumPeakFocus = 0.0; // sum of dgInter scores in the peak defined by user
  double   dgIntraSumPeakFocus = 0.0; // sum of dgIntra scores in the peak defined by user
   

  //  A T O M I C    D e c o m p o s i t i o n (G E N E R A L   a n d    I N T E R)
  double* dgAtSum     = new double[nbAtoms]; // total atomic contribution
//double* dgAtSumNB   = new double[nbAtoms]; // non-bonding contribution for atomic decomposition
  double* dgAtSumW    = new double[nbAtoms];  // 20220120 CL. weak contribution for atomic decomposition
  double* dgAtSumW2   = new double[nbAtoms];  // The same as dgAtSumW but to computed for another ED threshold
                                              // to estimate the variability of the atomic contribut to NCC

//double* dgAtSumS    = new double[nbAtoms];  // 20220120 CL. strong contribution for atomic decomposition 
  double* dgInterAtSum = new double[nbAtoms];  // Contribution of each atom to the inter interactions between fragments A and B
  
  for (unsigned int iat=0; iat<nbAtoms; ++iat)
    {
       dgAtSum[iat]      = 0.0;
//     dgAtSumNB[iat]    = 0.0;
       dgAtSumW[iat]     = 0.0;
       dgAtSumW2[iat]    = 0.0;
//     dgAtSumS[iat]     = 0.0;
       dgInterAtSum[iat] = 0.0;
    }

// A T O M I C   S E L F   D e c o m p o s i t i o n 
  double* pauliAT = new double[nbAtoms];  // Contribution of each atom to the Pauli repulsion (SELF analysis)
  for (unsigned int iat=0; iat<nbAtoms; ++iat)
    {
       pauliAT[iat]        = 0.0;
    }
  double pauliMinimumValue = 0.0;
  double pauliMaximumValue = 0.0;
  double pauliATMin        = 0.0;
  double pauliATMax        = 0.0;

// ===== MODIF ERIC 29/07/2020 =========================================================================
  double scoreInteract1 = 0.0; // to ultimately estimate deltaEInter in kcal/mol, but at least
                               // to estimate interations solely based on and focusing on the inter peak
// =====================================================================================================
  // detecting max Intra
  double maxAttractDgIntra = -1.0;
  double maxRepulsiDgIntra = -1.0;
  // detecting max Inter
  double maxAttractDgInter = -1.0;
  double maxRepulsiDgInter = -1.0;

  // the elemental volume (constant over the cubic grid)
  double dv = params.increments[0] * params.increments[1] * params.increments[2];

         // ================================================================================================
         // I   N    T    E    G    R    A    T    I    O    N    S             
         // ================================================================================================

  // compute all scores (outside the THREAD section)
  for(unsigned int jp = 0;jp < nbSteps1 ; ++jp) {
     for(unsigned int kp = 0;kp < nbSteps2 ; ++kp) {
        for(unsigned int ip = 0;ip < nbSteps0 ; ++ip) {

         if (not isPauli())
         {
             double tmprho     = rho[ip][jp][kp]; 
             double tmpdgInter = deltaginterFC[ip][jp][kp];
             double tmpdgIntra = std::max<double>(deltagFC[ip][jp][kp], 1e-30);; // <-> dgIntra 
             double gradrho    = RDG[ip][jp][kp] * (constValue * std::pow(std::abs(tmprho), FOUR_THIRD));//retrieve gradrho
                                                                                                              // from RDG( grad[ijk]here) 
             double gradrhoIGMIntra = tmpdgIntra + gradrho;
                                                            // since we cannot directly reach gradrhoIGMIntra
                                                            // where only intramol interact. have been disabled
                                                            // while intermol. interaction are active
                                                            // we consider that: (1) dgIntra = gradrhoIGM   - gradrhoInter
                                                            //                   (2) dgIntra = gradrhoIntra - gradnorm
                                                            // The first definition is the one used practically
                                                            // in IGMplot since gradrhoIGM cancel every interations
                                                            // and gradrhoInter cancels only intermol. inter., 
                                                            // then gradrhoIGM   - gradrhoInter represents the drop
                                                            // in gradient due to intramol. interactions !
                                                            // But, if we could access directly to gradrhoIntra
                                                            // where only intramol. interactions were cancelled (2) would
                                                            // also represent dgIntra ! Unfortunately, practically, cancelling
                                                            // every intramol. interactions also cancels every intermol.
                                                            // interactions ==> we can reach gradrhoIntra by :
                                                            // gradrhoIntra = dgIntra + gradnorm from (2) (each of them
                                                            // strored in arrays) , and dgIntra was obtained from (1) 
                                                            // previously and stored in array !!!
    
             double gradrhoIGMInter = tmpdgInter + gradrho;// (1) dgInter = gradrhoIGMInter - gradnorm
                                                            // gradnorm contains every interaction while gradrhoIGMInter cancels 
                                                            // intermo. interaction but contains intramol. inter. 
                                                            // ==> dgInter describes only intermol. interaction
                                                            // ==> gradrhoIGMInter can be recovered from gradnorm and dgInter
                                                            //     which have been stored in arrays 
    
            gradrhoIGMInter = std::max<double>(gradrhoIGMInter, 1e-30); // to avoid possible problem with the following calcul. of qgInter
            gradrho = std::max<double>(gradrho, 1e-30);
    
            // Our definition of ratios qg:
            // a) QgINTRA = gradRHOIGMINTRA / gradRHO
            // b) QgINTER = gradRHOIGMINTER / gradRHO
            //
            // 
            //                                     deltagINTRA
            //                   <...........................................>
            //     -|------------|------------------------------|------------|---------------->
            //   gradRHO  gradRHOIGMINTER               gradRHOIGMINTRA   gradRHOIGM
            //     real   inter cancelled               intra cancelled   every interaction cancelled
            //     known        known                    unreachable        known
            //                                           with absolute
            //                                           values
            //      <...........................................>                                        
            //                     deltagINTRA
            // qgIntra and qgInter are calculated below (although returned by IGM, since not yet returned by IGMHirshfeld !)
            double qgIntra    = gradrhoIGMIntra/gradrho; // an important criterion to filter dgIntra peaks !
                                                         // in the intra signature
            double qgInter    = gradrhoIGMInter/gradrho; // an important criterion to filter dgInter peaks !
                                                         // in the inter signature
    
            //O P T I O N 2 :  entire grid is used
            dgInterSum = dgInterSum + tmpdgInter; //  will serve to detect if there is no interaction between frag
            dgIntraSum = dgIntraSum + tmpdgIntra; //will serve to monitor for instance
                                                  // the total interactions in a single species in reaction
    
            // contributions of the "repulsive" part              
            if (tmprho>0) {
                 dgIntraSumPos = dgIntraSumPos + tmpdgIntra;
                 dgInterSumPos = dgInterSumPos + tmpdgInter;
              }
              // contributions of the "attractive" part    
            if (tmprho<0) {
                 dgIntraSumNeg = dgIntraSumNeg + tmpdgIntra;
                 dgInterSumNeg = dgInterSumNeg + tmpdgInter;
              }
    
              // contributions of the weak attractive interactions Intra
            if ( (tmprho>-0.09) and ( (tmprho<0) and (qgIntra > qgThresh) ) ) {
                 // will serve to monitor for instance weak attractive interactions
                 // only peaks of weak ED regions are used from the intra signature
                 dgIntraSumW  = dgIntraSumW  + tmpdgIntra;
            }
    
              // contributions of the weak attractive interactions Inter
            if ( (tmprho>-0.09) and (tmprho<0) and (qgInter > qgThresh) ) {
                 // will serve to monitor for instance weak attractive interactions
                 // only peaks of weak ED regions are used from the inter signature
                 dgInterSumW  = dgInterSumW  + tmpdgInter;
            }
    
             // contributions of the strong attractive interactions Intra
            if ( (tmprho<=-0.09) and (qgIntra > qgThresh) ) {
                 // will serve to monitor for instance strong attractive interactions
                 // only peaks of strong ED regions are used from the intra signature
                 dgIntraSumS = dgIntraSumS + tmpdgIntra;
            }
    
             // contributions of the strong attractive interactions Inter
            if ( (tmprho<=-0.09) and (qgInter > qgThresh) ) {
                 // will serve to monitor for instance strong attractive interactions
                 // only peaks of strong ED regions are used from the inter signature
                 dgInterSumS = dgInterSumS + tmpdgInter;
            }
    
             // contributions of the peak defined by the user 
             if (isPeakFocusIntraActivated()) {
                 if ((tmprho>=params.cutpeakIntra[0]) and (tmprho<=params.cutpeakIntra[1])) {
                    if (qgIntra > qgThresh) {
                       dgIntraSumPeakFocus = dgIntraSumPeakFocus + tmpdgIntra;
                    }
                 }
             } // end of peakFocusIntra
    
             // contributions of the peak defined by the user 
             if (isPeakFocusInterActivated()) {
                 if ((tmprho>=params.cutpeakInter[0]) and (tmprho<=params.cutpeakInter[1])) {
                    if (qgInter > qgThresh) {
                       dgInterSumPeakFocus = dgInterSumPeakFocus + tmpdgInter;
                    }
                 }
             } // end of peakFocusInter
    
    
             // dgInter scoreInteract1 only dedicated to conversion in kcal/mol
            if ( (qgInter > 1.2) and (tmprho<0) ) // qgInter>1.2 <-> only  peak regions are used from the dgInter
                                             // rho<0  <-> only attractive regions are considered
    
              {
                 scoreInteract1  = scoreInteract1  - tmpdgInter; // will lead to the final score in kcal/mol
              }
    
            //== identify maxima in the 2D plot signature ==========
           // search for the maximum value of dgIntra and dgInter in the grid
           // will serve to detect the type of strongest interaction present in the system
             if ((tmprho<0) and  (tmpdgInter > maxAttractDgInter) )
                {maxAttractDgInter = tmpdgInter;}
    
             if ((tmprho>0) and  (tmpdgInter > maxRepulsiDgInter) )
                {maxRepulsiDgInter = tmpdgInter;}
    
             if ((tmprho<0) and  (tmpdgIntra > maxAttractDgIntra) )
                {maxAttractDgIntra = tmpdgIntra;}
    
             if ((tmprho>0) and  (tmpdgIntra > maxRepulsiDgIntra) )
                {maxRepulsiDgIntra = tmpdgIntra;}
         } // end of not isPauli()

         // ########################## P A U L I ##########################
         else // Pauli integration
         {
            dgInterSum = dgInterSum + deltaginterFC[ip][jp][kp]; // PAULI: sum D = G - W only for interFRAGMENTS interaction
         }
         // ............. E N D     P A U L I .............................


    // ................. E N D  O F   I N T E R  A T O M I C  D E C O M P O S I T I O N ..........

        }// end ip
     } // end kp
  }// end jp

  // FINALIZE INTEGRATIONS  (threads reduction)
  for(unsigned int ithread=0;ithread<nbThreads;++ithread)
        {
           // General and Inter Atomic Decomposition
            for (unsigned int iat=0; iat<nbAtoms; ++iat)
               {
                 dgAtSum[iat]      = dgAtSum[iat]      + dgAtSum_proc[ithread][iat];
                 dgAtSumW[iat]     = dgAtSumW[iat]     + dgAtSumW_proc[ithread][iat];
                 dgAtSumW2[iat]    = dgAtSumW2[iat]    + dgAtSumW2_proc[ithread][iat];
                 dgInterAtSum[iat] = dgInterAtSum[iat] + dgInterAtSum_proc[ithread][iat];
               }

        } // end of for(unsigned int ithread=0;ithread<nbThreads;++ithread)

//............................... E N D   I N T E G R A T I O N S    ......................................

  // search for the maximum value of dgInterAtSum
 double dgInterAtSumMax = 0.0;
 double total = 0.0;
 for (unsigned int iat=0; iat<nbAtoms; ++iat)
 {
    dgInterAtSum[iat] = dgInterAtSum[iat] * dv;
    total = total + dgInterAtSum[iat];
    if (dgInterAtSum[iat] > dgInterAtSumMax)
       {dgInterAtSumMax = dgInterAtSum[iat];}
 }
 dgInterAtSumMax = dgInterAtSumMax*100.0/total;

  // FINALIZE INTEGRATIONS 
  dgInterSum    = dgInterSum    * dv;
  dgInterSumPos = dgInterSumPos * dv;
  dgInterSumNeg = dgInterSumNeg * dv;
  dgInterSumW   = dgInterSumW   * dv;
  dgInterSumS   = dgInterSumS   * dv;

  dgIntraSum    = dgIntraSum    * dv;
  dgIntraSumPos = dgIntraSumPos * dv;
  dgIntraSumNeg = dgIntraSumNeg * dv;
  dgIntraSumW   = dgIntraSumW   * dv;
  dgIntraSumS   = dgIntraSumS   * dv;

  dgInterSumPeakFocus = dgInterSumPeakFocus * dv;
  dgIntraSumPeakFocus = dgIntraSumPeakFocus * dv;

  std::string emptyLine("   *                                                                   *");
  std::cout << emptyLine << std::endl;

  scoreInteract1 = scoreInteract1* dv; 

  // ################# S T A N D A R D     J O B S     d g ##################################################
  if (not isPauli()) // if standard job (dgIntra, dgInter, or dgIntraS, dgInterS)
  {
     /* IF SUM dgInter is NULL ===> no intermolecular situation detected ... */
     if(dgInterSum < interacThreshold)
       {
         std::cout << emptyLine << std::endl;
         std::cout << "   * ################################################################  *" << std::endl;
         std::cout << "   * ################## I N T E R - F R A G M E N T #################  *" << std::endl;
         std::cout << "   * ##################      A N A L Y S I S        #################  *" << std::endl;
         std::cout << "   * ################################################################  *" << std::endl;
         std::cout << "   * ----------------------------------------------------------------  *" << std::endl;
         std::cout << "   *   No interaction detected between fragments.                      *" << std::endl;
         std::cout << "   *   Possible causes : only one fragment defined, or fragments too   *" << std::endl;
         std::cout << "   *                     distant or inappropriate gridBox definition   *" << std::endl;
         std::cout << "   *                     (check CUBE or RADIUS definition)             *" << std::endl;
         std::cout << "   *                     or wfn/wfx incorrectly read (check format)    *" << std::endl;
         std::cout << emptyLine << std::endl;
         if ((data->getNbAtomMolA()+data->getNbAtomMolB()) == 1 )
            {
              std::cout << emptyLine << std::endl;
              std::cout << "   *   Only one atom considered here ...                               *" << std::endl;
            }
         std::cout << "   ---------------------------------------------------------------------" << std::endl;
     } // end of if(dgInterSum < interacThreshold) 
   
     else if ((data->getNbAtomMolA()+data->getNbAtomMolB())  == 2 )
      {
   
         std::cout << "   *                                                                   *" << std::endl;
         std::cout << "   * ----------------------------------------------------------------  *" << std::endl;
         std::cout << "   *                  QUANTIFICATION of INTERACTIONS                   *" << std::endl;
         std::cout << "   *                        between FRAGMENT(S)                        *" << std::endl;
         std::cout << "   *                        -------                                    *" << std::endl;
   
         std::cout << emptyLine << std::endl;
         std::cout << "   *   Only 2 atoms were considered here.                              *" << std::endl;
         std::cout << "   *   Please use the IBSI keyword to quantify the interaction         *" << std::endl; 
         std::cout << "   *   between only two atoms.                                         *" << std::endl;
         std::cout << "   *   Cube files have however been generated to plot bond             *" << std::endl;
         std::cout << "   *   dg isosurfaces.                                                 *" << std::endl;
         std::cout << "   ---------------------------------------------------------------------" << std::endl;
      }
   
     else if ((data->getNbAtomMolA()+data->getNbAtomMolB()) >2 ) // display the integration score of dgInter
      {
   
   /* ===============   I N T E R A C T I O N   F A M I L Y    I N T E R                   ========= */
         std::cout << "   *                                                                   *" << std::endl;
         std::cout << "   * ################################################################  *" << std::endl;
         std::cout << "   * ################## I N T E R - F R A G M E N T #################  *" << std::endl;
         std::cout << "   * ##################      A N A L Y S I S        #################  *" << std::endl;
         std::cout << "   * ################################################################  *" << std::endl;
         std::cout << "   * ----------------------------------------------------------------  *" << std::endl;
         std::cout << "   *               T Y P E   o f   t h e   S T R O N G E S T           *" << std::endl;
         std::cout << "   *          I N T E R A C T I O N   i n    t h e    S Y S T E M      *" << std::endl;
         std::cout << emptyLine << std::endl;
         std::cout << "   *   MAXIMUM of the dg peak(s)  = ";
         std::cout << std::scientific << std::setprecision(4) << std::setw(8);
         std::cout << maxAttractDgInter << " a.u. (attractive part)  *" << std::endl;
         if (maxAttractDgInter > 2.5)
           {
             std::cout << "   *   ! Warning: uncommonly strong interaction                        *" << std::endl;
             std::cout << emptyLine << std::endl;
           }
   
         // if the GBP is used
         if (not isHirshmodeActivated())
              {
              // calculate the cursor position on the dg SCALE:
              unsigned int dgcursor = 1;
              if ( (maxAttractDgInter > 0.001) and (maxAttractDgInter <= 0.0083333333) )
               {
                  dgcursor = 1;
               }
              else if ( (maxAttractDgInter > 0.0083333333) and (maxAttractDgInter <=0.1) )
               {
                  dgcursor =  (maxAttractDgInter / 0.1) * 12;
               }
              else if ( (maxAttractDgInter > 0.1) and (maxAttractDgInter <=0.6) )
               {
                  dgcursor =  12 + ( (maxAttractDgInter-0.1) / (0.6-0.1) ) * 17;
               }
              else if ( (maxAttractDgInter > 0.6) and (maxAttractDgInter <=2.5) )
               {
                  dgcursor =  29 + ( (maxAttractDgInter-0.6) / (2.5-0.6) ) * 31;
               }
              else if (maxAttractDgInter > 2.5) 
               {
                  dgcursor =  62;
               }
              int rest = 64 - dgcursor;
        
              std::cout << "   *   dg SCALE:                                                       *" << std::endl;
              std::cout << "   *    " << std::setw(dgcursor) << "|" << std::setw(rest) << "*" << std::endl;
              std::cout << "   *    " << std::setw(dgcursor) << "|" << std::setw(rest) << "*" << std::endl;
              std::cout << "   *    " << std::setw(dgcursor) << "v" << std::setw(rest) << "*" << std::endl;
              std::cout << "   *   |___________|________________|______________________________|   *" << std::endl;
              std::cout << "   *   0          0.1              0.6                            2.5  *" << std::endl;
              std::cout << "   *    < non-cov >  < metal coord >                                   *" << std::endl;
              std::cout << "   *                 < ................... covalent ............. >    *" << std::endl;
          } // end of 
   
         // warning is  delivered to notify repulsive interactions 
         if ( maxRepulsiDgInter >= maxAttractDgInter)
            {
              std::cout << emptyLine << std::endl;
              std::cout << "   *   ! Warning: non-bonding dg peak > attractive dg peak             *" << std::endl;
              std::cout << "   *              possibly resulting from steric repulsion forces      *" << std::endl;
            } // end of the first warning (repsulsive case detected)
   
   
   
   /* ===============   I N T E R   S C O R E                                               ========= */
         std::cout << "   *                                                                   *" << std::endl;
         std::cout << "   * ----------------------------------------------------------------  *" << std::endl;
         std::cout << "   *     Q U A N T I F I C A T I O N   o f   I N T E R A C T I O N     *" << std::endl;
         std::cout << "   *                        between FRAGMENT(S)                        *" << std::endl;
         std::cout << "   *                        -------                                    *" << std::endl;
         std::cout << emptyLine << std::endl;
         std::cout << "   *   Grid Integration scores:                                        *" << std::endl;
         std::cout << emptyLine << std::endl;
   
         std::cout << "   *   [1 ] sum     dg[Inter]        x dv =";
         std::cout << std::scientific << std::setprecision(3) << std::setw(11);
         std::cout << dgInterSum << " a.u. Total      *" << std::endl;
         std::cout << "   *   [1a] sum     dg[Inter]        x dv =";
         std::cout << std::scientific << std::setprecision(3) << std::setw(11);
         std::cout << dgInterSumPos << " a.u. L2 > 0     *" << std::endl;
         std::cout << "   *   [1b] sum     dg[Inter]        x dv =";
         std::cout << std::scientific << std::setprecision(3) << std::setw(11);
         std::cout << dgInterSumNeg << " a.u. L2 < 0     *" << std::endl;
         std::cout << emptyLine << std::endl;
   
         std::cout << "   *   [2]  sum_peak dg[Inter_weak]  x dv =";
         std::cout << std::scientific << std::setprecision(3) << std::setw(11);
         std::cout << dgInterSumW << " a.u. L2 < 0     *" << std::endl;
         std::cout << "   *   [3]  sum_peak dg[Inter_strong]x dv =";
         std::cout << std::scientific << std::setprecision(3) << std::setw(11);
         std::cout << dgInterSumS << " a.u. L2 < 0     *" << std::endl;
         std::cout << emptyLine << std::endl;
   
         if (isPeakFocusInterActivated()) {
             std::cout << "   *   [4]  sum_peakfocus dg[Inter]  x dv =";
             std::cout << std::scientific << std::setprecision(3) << std::setw(11);
             std::cout << dgInterSumPeakFocus << " a.u.            *" << std::endl;
             std::cout << emptyLine << std::endl;
         }
   
         std::cout << "   *   Integration region:                                             *" << std::endl;
         std::cout << "   *             [1] Entire grid                                       *" << std::endl;
         std::cout << "   *             [2] dg peaks in         -0.09  < ED <  0.00           *" << std::endl;
         std::cout << "   *             [3] dg peaks in   ED <= -0.09                         *" << std::endl;
   
         if (isPeakFocusInterActivated()) {
             std::cout << "   *             [4] dg peaks in         " << std::fixed << std::setprecision(2) << std::setw(5);
             std::cout << params.cutpeakInter[0] << "  < ED < ";
             std::cout << std::setw(5) << std::right << params.cutpeakInter[1] << "           *" << std::endl;;
             std::cout << emptyLine << std::endl;
         }
   
         std::cout << emptyLine << std::endl;
         if (isPeakFocusInterActivated()) {
             std::cout << "   *   Note: [2,3,4] ignore the flat parts of the dg signal, focusing  *" << std::endl;
         } else {
             std::cout << "   *   Note: [2,3] ignore the flat parts of the dg signal, focusing    *" << std::endl;
         }
         std::cout << "   *         on peaks.                                                 *" << std::endl;
         std::cout << "   ---------------------------------------------------------------------" << std::endl;
                                                                                             
   
      } // if(results->getsumdgInter() < interacThreshold)
   
   
   /* ===============   I N T R A   S C O R E    i s    a l w a y s    d i s p l a y e d    ========= */
     if (nbAtoms == 2)
      {
         std::cout << "   *                                                                   *" << std::endl;
         std::cout << "   * ################################################################  *" << std::endl;
         std::cout << "   * ################## I N T R A - F R A G M E N T #################  *" << std::endl;
         std::cout << "   * ##################      A N A L Y S I S        #################  *" << std::endl;
         std::cout << "   * ################################################################  *" << std::endl;
         std::cout << "   * ----------------------------------------------------------------  *" << std::endl;
         std::cout << emptyLine << std::endl;
         std::cout << "   *   Only 2 atoms were considered here.                              *" << std::endl;
         std::cout << "   *   Please use the IBSI keyword to quantify the interaction         *" << std::endl;
         std::cout << "   *   between only two atoms.                                         *" << std::endl;
         std::cout << "   *   Cube files have however been generated to plot bond             *" << std::endl;
         std::cout << "   *   dg isosurfaces.                                                 *" << std::endl;
         std::cout << "   ---------------------------------------------------------------------" << std::endl;
      }
   
     else if ( (data->getNbAtomMolA() + data->getNbAtomMolB() ) > 2 )
      {
         
   /* ===============   I N T E R A C T I O N   F A M I L Y    I N T R A                   ========= */
         std::cout << "   *                                                                   *" << std::endl;
         std::cout << "   * ################################################################  *" << std::endl;
         std::cout << "   * ################## I N T R A - F R A G M E N T #################  *" << std::endl;
         std::cout << "   * ##################      A N A L Y S I S        #################  *" << std::endl;
         std::cout << "   * ################################################################  *" << std::endl;
         std::cout << "   * ----------------------------------------------------------------  *" << std::endl;
         std::cout << "   *               T Y P E   o f   t h e   S T R O N G E S T           *" << std::endl;
         std::cout << "   *          I N T E R A C T I O N   i n    t h e    S Y S T E M      *" << std::endl;
   
         std::cout << emptyLine << std::endl;
         std::cout << "   *   MAXIMUM of the dg peak(s)  = ";
         std::cout << std::scientific << std::setprecision(4) << std::setw(8);
         std::cout << maxAttractDgIntra << " a.u. (attractive part)  *" << std::endl;
         if (maxAttractDgIntra > 2.5)
           {
             std::cout << "   *   ! Warning: uncommonly strong interaction                        *" << std::endl;
             std::cout << emptyLine << std::endl;
           }
   
         // if GBP is used
         if (not isHirshmodeActivated())
              {
               // calculate the cursor position on the dg SCALE:
               unsigned int dgcursor = 1;
               if ( (maxAttractDgIntra > 0.001) and (maxAttractDgIntra <= 0.0083333333) )
                {
                   dgcursor = 1;
                }
               else if ( (maxAttractDgIntra > 0.0083333333) and (maxAttractDgIntra <=0.1) )
                {
                   dgcursor =  (maxAttractDgIntra / 0.1) * 12;
                }
               else if ( (maxAttractDgIntra > 0.1) and (maxAttractDgIntra <=0.6) )
                {
                   dgcursor =  12 + ( (maxAttractDgIntra-0.1) / (0.6-0.1) ) * 17;
                }
               else if ( (maxAttractDgIntra > 0.6) and (maxAttractDgIntra <=2.5) )
                {
                   dgcursor =  29 + ( (maxAttractDgIntra-0.6) / (2.5-0.6) ) * 31;
                }
               else if (maxAttractDgIntra > 2.5)
                {
                   dgcursor =  62;
                }
               int rest = 64 - dgcursor;
         
               std::cout << "   *   dg SCALE:                                                       *" << std::endl;
               std::cout << "   *    " << std::setw(dgcursor) << "|" << std::setw(rest) << "*" << std::endl;
               std::cout << "   *    " << std::setw(dgcursor) << "|" << std::setw(rest) << "*" << std::endl;
               std::cout << "   *    " << std::setw(dgcursor) << "v" << std::setw(rest) << "*" << std::endl;
               std::cout << "   *   |___________|________________|______________________________|   *" << std::endl;
               std::cout << "   *   0          0.1              0.6                            2.5  *" << std::endl;
               std::cout << "   *    < non-cov >  < metal coord >                                   *" << std::endl;
               std::cout << "   *                 < ................... covalent ............. >    *" << std::endl;
              } // end of if (not isHirshmodeActivated())
   
        // warning is  delivered to notify repulsive interactions 
         if ( maxRepulsiDgIntra >= maxAttractDgIntra)
            {
              std::cout << emptyLine << std::endl;
              std::cout << "   *   ! Warning: non-bonding dg peak > attractive dg peak             *" << std::endl;
              std::cout << "   *              possibly resulting from steric repulsion forces      *" << std::endl;
            } // end of the first warning (repsulsive case detected)
   
         std::cout << "   *                                                                   *" << std::endl;
         std::cout << "   * ----------------------------------------------------------------  *" << std::endl;
         std::cout << "   *     Q U A N T I F I C A T I O N   o f   I N T E R A C T I O N     *" << std::endl;
         std::cout << "   *                        within  FRAGMENT(S)                        *" << std::endl;
         std::cout << "   *                        ------                                     *" << std::endl;
         std::cout << emptyLine << std::endl;
         std::cout << "   *   Grid Integration scores:                                        *" << std::endl;
         std::cout << emptyLine << std::endl;
         std::cout << "   *   [1 ] sum     dg[Intra]        x dv =";
         std::cout << std::scientific << std::setprecision(3) << std::setw(11);
         std::cout << dgIntraSum << " a.u. Total      *" << std::endl;
         std::cout << "   *   [1a] sum     dg[Intra]        x dv =";
         std::cout << std::scientific << std::setprecision(3) << std::setw(11);
         std::cout << dgIntraSumPos << " a.u. L2 > 0     *" << std::endl;
         std::cout << "   *   [1b] sum     dg[Intra]        x dv =";
         std::cout << std::scientific << std::setprecision(3) << std::setw(11);
         std::cout << dgIntraSumNeg << " a.u. L2 < 0     *" << std::endl;
         std::cout << emptyLine << std::endl;
   
         std::cout << "   *   [2]  sum_peak dg[Intra_weak]  x dv =";
         std::cout << std::scientific << std::setprecision(3) << std::setw(11);
         std::cout << dgIntraSumW << " a.u. L2 < 0     *" << std::endl;
         std::cout << "   *   [3]  sum_peak dg[Intra_strong]x dv =";
         std::cout << std::scientific << std::setprecision(3) << std::setw(11);
         std::cout << dgIntraSumS << " a.u. L2 < 0     *" << std::endl;
         std::cout << emptyLine << std::endl;
   
         if (isPeakFocusIntraActivated()) {
             std::cout << "   *   [4]  sum_peakfocus dg[Intra]  x dv =";
             std::cout << std::scientific << std::setprecision(3) << std::setw(11);
             std::cout << dgIntraSumPeakFocus << " a.u.            *" << std::endl;
             std::cout << emptyLine << std::endl;
         }
   
         std::cout << "   *   Integration region:                                             *" << std::endl;
         std::cout << "   *                  [1] Entire grid                                  *" << std::endl;
         std::cout << "   *                  [2] dg peaks in         -0.09  < ED <  0.00      *" << std::endl;
         std::cout << "   *                  [3] dg peaks in   ED <= -0.09                    *" << std::endl;
   
         if (isPeakFocusIntraActivated()) { 
             std::cout << "   *                  [4] dg peaks in         " << std::fixed;
             std::cout << std::setprecision(2) << std::setw(5);
             std::cout << params.cutpeakIntra[0] << "  < ED < ";
             std::cout << std::setw(5) << std::right << params.cutpeakIntra[1] << "      *" << std::endl;;
             std::cout << emptyLine << std::endl;
         }
   
         if (isPeakFocusIntraActivated()) {
             std::cout << "   *   Note: [2,3,4] ignore the flat parts of the dg signal, focusing  *" << std::endl;
         } else {
             std::cout << "   *   Note: [2,3] ignore the flat parts of the dg signal, focusing    *" << std::endl;
         }
   
         std::cout << "   *         on peaks.                                                 *" << std::endl;
         std::cout << "   ---------------------------------------------------------------------" << std::endl;
       }

   } // end of not isPauli()
     // ................................ E N D    O F    S T A N D A R D     d g i n t r a   d g i n t e r ..........

   else // of if (not isPauli())
   // ############################ P A U L I ########################################################################
   {
         std::cout << "   *                                                                   *" << std::endl;
         std::cout << "   * ################################################################  *" << std::endl;
         std::cout << "   * ################## I N T E R - F R A G M E N T #################  *" << std::endl;
         std::cout << "   * ##################      A N A L Y S I S        #################  *" << std::endl;
         std::cout << "   * ################################################################  *" << std::endl;
         std::cout << "   * ----------------------------------------------------------------  *" << std::endl;
         std::cout << "   *               F A S T   E S T I M A T I O N                       *" << std::endl;
         std::cout << "   *             O F   S T E R I C    E F F E C T                      *" << std::endl;
         std::cout << "   *         via Steric Exclusion Localization Function                *" << std::endl;
         std::cout << "   *                 S E L F    A N A L Y S I S                        *" << std::endl;
         std::cout << emptyLine << std::endl;

         pauliMaximumValue=getMaxFromPrec3DMatrix(deltaginterFC,nbSteps0,nbSteps1,nbSteps2,
                                                                     rho, -100000, 100000);

         pauliMinimumValue=getMinFromPrec3DMatrix(deltaginterFC,nbSteps0,nbSteps1,nbSteps2,
                                                                     rho, -100000, 100000);
         std::cout << "   *   Local SELF values found in the range: [ ";
          std::cout << std::fixed << std::setprecision(2) << std::setw(6);
         std::cout << pauliMinimumValue << " : ";
         std::cout << std::fixed << std::setprecision(2) << std::setw(6) << pauliMaximumValue << " ]       *" << std::endl;
         std::cout << emptyLine << std::endl;
         std::cout << "   *   Grid Integrated score iSELF:                                    *" << std::endl;
         std::cout << emptyLine << std::endl;
         std::cout << "   *   [1 ] sum    (G - GW)_22       x dv =";
         std::cout << std::scientific << std::setprecision(4) << std::setw(12);
         std::cout << dgInterSum << "   kcal/mol     *" << std::endl;
         std::cout << "   *                                         ";
         std::cout << std::scientific << std::setprecision(6) << std::setw(12);
         std::cout << dgInterSum/627.51 << " a.u.         *" << std::endl;
         std::cout << emptyLine << std::endl;


         std::cout << "   *       G  = Kinetic Energy Density (1/2 SUM_i n_i |grad phi_i|^2)  *" << std::endl;
         std::cout << "   *       GW = Weizsacker KED (1/8 |grad rho|^2/rho)                  *" << std::endl;
         std::cout << "   *      _22 = InterFragment terms are considered                     *" << std::endl;
        std::cout << emptyLine << std::endl;
         if (isPauliAtomic()) {
            std::cout << emptyLine << std::endl;
            std::cout << "   *   ATOMIC Contributions [1]:                                       *" << std::endl;
            std::cout << "   *       Atom      Contrib.(kcal/mol)  Fragment                      *" << std::endl;
   
   
            //  ATOMIC CONTRIBUTIONS estimation is based on the pauli[ithread][iat][jat] array, which stores
            //  the local descriptor value (sum on every grid nodes): [Sum_(a,b,c,d) [Dab.Dcd - Dac.Dbd] x a'.b'.c.d]/[rho]
            //  instead of the desired descriptor: [4xSum_(a,b,c,d)[Dab.Dcd-Dac.Dbd]xa'.b'.c.d]/[8xrho] corresponding
            //  to the formula : [8xrhoxG - grad(rho)^2]/[8xrho]. ==> we have now to take into account the 4/8 factor.
            //  And since the pauli[ithtread][iat][jat] array contains 2 x SELF ==> we will also have to divide by 2 again.
          
            // Compute ATOMIC CONTRIBUTIONS from ATOM PAIR CONTRIBUTIONS
            // pauli[i][j] = contribution of atom i in the atom pair (i,j), which is different from
            // pauli[j][i] = contribution of atom j in the atom pair (j,i)
            // ==> kinetic excess for atom i = sum_j pauli[i][j]
               for (unsigned iatUser=1; iatUser<=nbAtoms; ++iatUser)
               {
                 // First, convert the atomic user numering to the WF atomic numbering
                 unsigned int iat;
                 if (isRKFmodeActivated()) { // ADF requires a specific treatment
                   // user2ADF takes a user_0_based atomic index and returns a WF_1_based atomic index 
                   unsigned int* user2ADF = wf_user2ADF_atomOrder();
                   iat = user2ADF[iatUser-1]-1;   // iat in the range[0:nbAtoms]
                 } else {iat = iatUser-1;} // in WFX and WFN, user atom numbering is preserved in WF data

                 for (unsigned jat=0; jat<nbAtoms; ++jat)
                    {
                       for(unsigned int ithread=0;ithread<nbThreads;++ithread)
                       { 
                          pauliAT[iat] = pauliAT[iat] + pauli[ithread][iat][jat]*ekin2kcal*dv/4.0;
                       }
                    } // end of loop over jat
                 std::cout << "   *   "  << std::fixed << std::setw(5) <<  std::right;
                 std::cout << iatUser << std::setw(3) << std::right;
                 std::string  atomName = getAtomName(data->atomTypes[iat]); // atomTypes returns Z-1, iat refers to internal WF atom numbering
                                                                            // in the range[0:nbAtoms]
                 std::cout << atomName <<  "          ";
                 std::cout << std::fixed << std::setw(10) << std::setprecision(2) << std::right << pauliAT[iat];
                 if (isInMoleculeA(iat+1))
                   { 
                    std::cout << std::fixed << std::setw(10) << std::right;
                    std::cout << "1";
                    std::cout << "                          *" << std::endl;
                   } else if (isInMoleculeB(iat+1))
                   { 
                    std::cout << std::fixed << std::setw(10) << std::right;
                    std::cout << "2";
                    std::cout << "                          *" << std::endl;
                   }
                   else {std::cout << "                                    *" << std::endl;}

               } // end of loop over iat
         } // end of if (isPauliAtomic()) 

         std::cout << emptyLine << std::endl;
         std::cout << "   *   Integration region:                                             *" << std::endl;
         std::cout << std::scientific << std::setprecision(1) <<  std::setw(7);
         std::cout << "   *                  [1] rho         > " << PauliRhoThresh;
         std::cout << "                        *" << std::endl;
         std::cout << "   * ----------------------------------------------------------------  *" << std::endl;



   // ........................... E N D    O F   P A U L I  .......................................................
   

   
// ............. E N D   O F   T E S T I N G   A N D   I N T E G R A T I N G   I N T E R A C T I O N S  ....
//                                      Q M   T R E A T M E N T                                    
   }

  /* ============== O U T P U T    R E S U L T S   (d a t,  c u b e, ...)  ======================================= */
  gettimeofday(&stop, NULL);
  std::cout << "CPU time            : " << stop.tv_sec - start.tv_sec << "s" << std::endl;
  std::cout << "Writing files ..." << std::flush << "\n";


  std::ostringstream os;


  // ............................ OUTPUTS  ............................ //

  // ################## S T A N D A R D   O U T P U T    d g ###########################
  if (not isPauli()) // and not ELF, and not isCRITIC
  {
     if(params.outputType==1 || params.outputType==5 )
       {
      // =================  NCI.dat ===================================== //
         os << params.outputName << "-nci.dat";
         std::ofstream datWriter(os.str().c_str());
   
         // Cleaning ostringstream :-)
         os.str("");
   
   
         /* Setting stream precision to 6 numbers */
         datWriter << std::fixed << std::setprecision(6);
   			      
         /* Writing next values with scientific convention */
         datWriter << std::scientific;
   
         /* Writing header */
         datWriter << "    rho             RDG\n";
   
         for(unsigned int i=0;i<nbSteps0;++i)
   	{
   	  for(unsigned int j=0;j<nbSteps1;++j)
   	    {
   	      for(unsigned int k=0;k<nbSteps2;++k)
   		{
   		  if ( std::abs(rho[i][j][k]) < params.cutoffs[0] && RDG[i][j][k] < params.cutoffs[1] )
   		    {
   		      datWriter << rho[i][j][k] << "\t" << RDG[i][j][k] << "\n";
   		    }
   		} // end for k
   	    }// end for j
   	} // end for i
   		      
         datWriter.close();
      } // end of if(params.outputType==1 || params.outputType==5 )
   
    if(params.outputType==1 || params.outputType==5 )
       {
      // =================  AtomDOI.dat ==================================== //
         writeAtomDOI(params, data, dgAtSum, dgAtSumW, dgAtSumW2, dv, nbAtoms);
       }
   
   if(params.outputType==1 || params.outputType==4 || params.outputType==5)
       {
      // =================  AtContribInter.dat ============================= //
         //  call the routine to generate appropriate dat file to see atomic contributions to the INTER interactions
        if ( (data->getNbAtomMolA() != 0)  and  (data->getNbAtomMolB() != 0) ) // fragmentation scheme enabled
           // built AtContribInter.dat
           {outPercentQM(params, data, dgInterAtSum, dv, nbAtoms);}  
       }
   
   
   
     if(params.outputType==1 || params.outputType==5)
       {
     // =================  igm.dat  ==================================== //
         // Cleaning ostringstream :-)
         os.str("");
   
         os << params.outputName << "-igm.dat";
         std::ofstream datWriter(os.str().c_str());
   
         /* Setting stream precision to 6 numbers */
         datWriter << std::fixed << std::setprecision(6);
   			      
         /* Writing next values with scientific convention */
         datWriter << std::scientific;
   
         /* Writing header */
         if (not isdgSCALED())
            {
               datWriter << "      rho             RDG          deltagIntra    deltagInter        qgIntra         qgInter\n";
            }
         else 
            {
               datWriter << "      rho             RDG          deltagIntraS   deltagInterS       qgIntra         qgInter\n";
            }
   
         for(unsigned int i=0;i<nbSteps0;++i)
   	{
   	  for(unsigned int j=0;j<nbSteps1;++j)
   	    {
   	      for(unsigned int k=0;k<nbSteps2;++k)
   		{
   		  if ( std::abs(rho[i][j][k]) < params.cutoffs[0] && RDG[i][j][k] < params.cutoffs[1] )
   		    {
                          // 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     = rho[i][j][k];
                          double tmpdgInter = deltaginter[i][j][k];
                          double tmpdgIntra = std::max<double>(deltag[i][j][k], 1e-30); // <-> dgIntra
                          double gradnorm    = RDG[i][j][k] * (constValue * std::pow(std::abs(tmprho), FOUR_THIRD));//retrieve gradnorm
                          double gradrhoIGMIntra;
                          double gradrhoIGMInter;
                          
   
                          if (not isdgSCALED())
                          {          
                             gradrhoIGMIntra = tmpdgIntra + gradnorm;
                             gradrhoIGMInter = tmpdgInter + gradnorm; 
                          } 
                          else // dgSCALED (alone, without PAULI)
                               // take care in that case deltaginter contains actually deltaginter/rho !
                               // and  deltag contains actually deltag/rho !
                          {  
                             gradrhoIGMIntra = tmpdgIntra*std::abs(tmprho) + gradnorm;
                             gradrhoIGMInter = tmpdgInter*std::abs(tmprho) + gradnorm;        
                          }  
                          gradrhoIGMInter = std::max<double>(gradrhoIGMInter, 1e-30);
                          gradnorm = std::max<double>(gradnorm, 1e-30);                        
                          
                          double qgIntra         = gradrhoIGMIntra/gradnorm; 
                          double qgInter         = gradrhoIGMInter/gradnorm; 
                        
                          datWriter << std::right << std::scientific << std::setprecision(6) << std::setw(14);
   		          datWriter << rho[i][j][k] << "    " << RDG[i][j][k];
                          datWriter << "    " << deltag[i][j][k] << "    " << deltaginter[i][j][k];
                          datWriter << "    " << qgIntra << "    " << qgInter << std::endl;
   
   		    } // end if 
   		} //end k
   	    } //end j
   	} //end i
   	  
         datWriter.close();


          // =================  GNU ============================================ //   

         // 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 \"%.3f\""<< 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;
         datWritergnuInter << "-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 if(params.outputType==1 || params.outputType==5)
   
    // =================  CUBES    ==================================== //
     const char* dgIntraName;
     if (not isdgSCALED())
     {dgIntraName = "dgIntra";}
     else {dgIntraName = "dgIntraS";}

     const char* dgInterName;
     if (not isdgSCALED())
     {dgInterName = "dgInter";}
     else {dgInterName = "dgInterS";}

     if(params.outputType==2 || params.outputType==5)
       {
         writeCube(&params, data, "RDG", rho, RDG, true, -params.cutplot[0],params.cutplot[0],100.0);
       }
   
     if(params.outputType==2 || params.outputType==3 || params.outputType==5)
       {
         writeCube(&params, data, "dens", rho, rho);
       }
   
     if(params.outputType==3 || params.outputType==5)
       {
         if (isPeakFocusIntraActivated()) { 
             writeCube(&params, data, dgIntraName, rho, deltag, true, params.cutpeakIntra[0],params.cutpeakIntra[1],0.0);
         }
         else {
             writeCube(&params, data, dgIntraName, rho, deltag, true, -params.cutplotIGM[0],params.cutplotIGM[0],0.0);
         }
       }
         
     if(params.outputType==3 || params.outputType==5)
       {
         if (isPeakFocusInterActivated()) {
             writeCube(&params, data, dgInterName, rho, deltaginter, true, params.cutpeakInter[0],params.cutpeakInter[1],0.0);
         }
          else {
             writeCube(&params, data, dgInterName, rho, deltaginter, true, -params.cutplotIGM[1],params.cutplotIGM[1],0.0);
         }
       }
   
   
   
    // =================  VMD+XYZ  ==================================== //
     if(params.outputType==4 || params.outputType==5)
       {
         // write the coord.xyz file : needed in the vmd sess file weakAtomContrib.vmd to represent non-covalent contacts
         writeXYZ(params, data, nbAtoms, atomCoordinates);
   
       }
        
   
     if(params.outputType==5)
       {
         // prepare the VMD files (several) writing: 
   
         // 1) retrieve the maximum value of the atomic contributions to weak non-covalent contacts
         // (in view of generating the VMD file)
         double dgAtWeakMaxValue = 0.0;
         for (unsigned int iat=0; iat<nbAtoms; ++iat)
           {
               if (dgAtSumW[iat] > dgAtWeakMaxValue) {
                 dgAtWeakMaxValue = dgAtSumW[iat];
               } // end of if
           } // end of loop over atoms
   
           // 2) Next, prepare  the VMD session file parameters
           double deltagIntraMaximumValue=0.0;
           double deltagInterMaximumValue=0.0;
           if (not isdgSCALED())  
             {
               if (isPeakFocusIntraActivated()) {
                      deltagIntraMaximumValue=getMaxFromPrec3DMatrix(deltag,nbSteps0,nbSteps1,nbSteps2,
                                                                     rho, params.cutpeakIntra[0], params.cutpeakIntra[1]);
               } else {
                      deltagIntraMaximumValue=getMaxFromPrec3DMatrix(deltag,nbSteps0,nbSteps1,nbSteps2,
                                                                     rho, -params.cutplotIGM[0], params.cutplotIGM[0]);
               }
             
               if (isPeakFocusInterActivated()) {
                      deltagInterMaximumValue=getMaxFromPrec3DMatrix(deltaginter,nbSteps0,nbSteps1,nbSteps2,
                                                                     rho, params.cutpeakInter[0], params.cutpeakInter[1]);
               }
               else {
                      deltagInterMaximumValue=getMaxFromPrec3DMatrix(deltaginter,nbSteps0,nbSteps1,nbSteps2,
                                                                     rho, -params.cutplotIGM[1], params.cutplotIGM[1]);
               }
             } // end of if (not isdgSCALED())

           else { // isdgSCALED()
               deltagIntraMaximumValue = dgSCALEDisovalue/ratioPeak; // maxim value = dgSCALEDisovalue / ratioPeak 
               deltagInterMaximumValue = dgSCALEDisovalue/ratioPeak; // see include/general.h constants
                                                                     // since in writeVMDfiles maxim value will be
                                                                     // multiplied by ratioPeak

              deltagInterMaximumValue=getMaxFromPrec3DMatrix(deltaginter,nbSteps0,nbSteps1,nbSteps2,
                                                             rho, -params.cutplotIGM[1], params.cutplotIGM[1])/(2.5*ratioPeak);

           } 
   
         // 3) call the routine to generate appropriate dat file to see atomic contributions to the INTER interactions
         // ==> already done in OUTPUT = 1 or 5
           //if ( (data->getNbAtomMolA() != 0)  and  (data->getNbAtomMolB() != 0) ) // fragmentation scheme enabled
           // built AtContribInter.dat
           //{outPercentQM(params, dgInterAtSum, dv, nbAtoms);}  // required by the VMD file to color atom by their contribution
   
         // 4) call the routine to generate appropriate vmd session files devoted to isosurface representations
         // or coloring atoms by contrib
         writeVMDfiles(&params, data, deltagIntraMaximumValue, deltagInterMaximumValue, dgAtWeakMaxValue, 
                       cpList, dgInterAtSumMax,0.0);
   
       } // end of if(params.outputType==5)

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

   else 
       // ###############    P A U L I       O U T P U T    ######################################
       //                    (not  dgSCALED alone) 
   {
            // =================  PAULI Cube ================================ //
            // writing PAULI function to cube file INTRA + INTER
            // ============================================================== //
             writeCube(&params, data, "SELF", rho, deltaginterFC, false, 0.0, 0.0, 0.0); // SELFinter value

            // =================  SCALEDdgInter ============================= //
            // SCALEDdg Inter isosurfaces will be colored with PAULI cube   
            // ============================================================== //
             writeCube(&params, data, "dgInterS", rho, deltaginter, false, 0.0, 0.0, 0.0); // dgInter/rho stored in deltaginter

         
            // ================== selfatom.dat ============================== //
             if (isPauliAtomic()) {
                writeSelfAtomDat(params, data, pauliAT, nbAtoms);
             }
            // ============================================================== //


            // =================  PAULI vmd =================================== //
            // writing the vmd script                                  
            // ============================================================== //
             // for dgScaled isosurface colored by pauli, dgSCALEDisovalue = dgscaledmaxvalue/2.5


              // SELF RANGE: in the range[0:max] with max found in the self cube where dg/rho cube >=0.1 to focus on inter situations
              //double selfmaxrange=getMaxFrom2Matrices(deltaginterFC,deltaginter,0.1,nbSteps0,nbSteps1,nbSteps2);
              // function which get the 98.5e percentile of the cube array
              // using a mask cube2
              double selfmaxrange=getSelfMaxRange(deltaginterFC,deltaginter,0.1,nbSteps0,nbSteps1,nbSteps2);

              // GET the maximum value of atomic contributions to Pauli 
              for (unsigned iat=0; iat<nbAtoms; ++iat) {
                if (pauliAT[iat] > pauliATMax) {
                   pauliATMax = pauliAT[iat]; 
                }   
                if (pauliAT[iat] < pauliATMin) {
                   pauliATMin = pauliAT[iat];
                }

              }

              // GET the maximum value of DGINTER/rho (stored in deltaginter) to design the DGINTER/rho isovalue
              double dgscaledisovalue=getMaxFromPrec3DMatrix(deltaginter,nbSteps0,nbSteps1,nbSteps2,
                                                    rho, -100000, 100000)/2.5;

              // we will use the last parameter of the writeVMDfiles function to say if self(0.0) or selfatomic(1.0) has been performed
              double atomic = 0.0; // default = SELF
              if (isPauliAtomic()) {atomic = 1.0;}
              writeVMDfiles(&params, data, pauliATMin, pauliATMax, dgscaledisovalue, cpList, atomic,selfmaxrange); // cpList is empty here
               


     // =================  igm.dat  ==================================== //
         // Cleaning ostringstream :-)
//         os.str("");
//   
//         os << params.outputName << "-igm.dat";
//         std::ofstream datWriter(os.str().c_str());
//   
//         /* Setting stream precision to 6 numbers */
//         datWriter << std::fixed << std::setprecision(6);
//   			      
//         /* Writing next values with scientific convention */
//         datWriter << std::scientific;
//   
//         /* Writing header */
//         datWriter << "      rho             RDG          deltagIntra    deltagInter        qgIntra         qgInter\n";
//   
//         for(unsigned int i=0;i<nbSteps0;++i)
//   	{
//   	  for(unsigned int j=0;j<nbSteps1;++j)
//   	    {
//   	      for(unsigned int k=0;k<nbSteps2;++k)
//   		{
//   		  if ( std::abs(rho[i][j][k]) < params.cutoffs[0] && RDG[i][j][k] < params.cutoffs[1] )
//   		    {
//                          // 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     = rho[i][j][k];
//                          double tmpdgInter = deltaginter[i][j][k];
//                          double tmpdgIntra = std::max<double>(deltag[i][j][k], 1e-30); // <-> dgIntra
//                          double gradnorm    = RDG[i][j][k] * (constValue * std::pow(std::abs(tmprho), FOUR_THIRD));//retrieve gradnorm
//                          double gradrhoIGMIntra;
//                          double gradrhoIGMInter;
//                          
//   
//                          if (not isdgSCALED())
//                          {
//                             gradrhoIGMIntra = tmpdgIntra + gradnorm;
//                             gradrhoIGMInter = tmpdgInter + gradnorm; 
//                          } 
//                          else // dgSCALED (alone, without PAULI)
//                               // take care in that case deltaginter contains actually deltaginter/rho !
//                               // and  deltag contains actually deltag/rho !
//                          {  
//                             gradrhoIGMIntra = tmpdgIntra*std::abs(tmprho) + gradnorm;
//                             gradrhoIGMInter = tmpdgInter*std::abs(tmprho) + gradnorm;        
//                          }  
//                          gradrhoIGMInter = std::max<double>(gradrhoIGMInter, 1e-30);
//                          gradnorm = std::max<double>(gradnorm, 1e-30);                        
//                          
//                          double qgIntra         = gradrhoIGMIntra/gradnorm; 
//                          double qgInter         = gradrhoIGMInter/gradnorm; 
//                        
//                          datWriter << std::right << std::scientific << std::setprecision(6) << std::setw(14);
//   		          datWriter << rho[i][j][k] << "    " << RDG[i][j][k];
//                          datWriter << "    " << deltag[i][j][k] << "    " << deltaginter[i][j][k];
//                          datWriter << "    " << qgIntra << "    " << qgInter << std::endl;
//   
//   		    } // end if 
//   		} //end k
//   	    } //end j
//   	} //end i
//   	  
//         datWriter.close();


// .........................  E N D   P A U L I   O U T P U T .....................................

     // D E A L L O C A T I G     M E M O R Y
     if (isPauliAtomic()) {
         for(unsigned int ithread=0;ithread<nbThreads;++ithread)
            {
                for (unsigned iat=0; iat<nbAtoms; ++iat)
                   {
                      delete[] pauli[ithread][iat]; 
                   } // end of for (unsigned iat=0; 
                delete[] pauli[ithread];
            } // end over threads
         delete[] pauli;
     } // end of if (isPauliAtomic())


   } // end of else if (not isPauli())

   

// ............................. E N D   O F   Q M    O U T P U T  ...............................


/* ==================== D E A L L O C A T I N G   M E M O R Y  ==================== */
  delete[] dgAtSum;
//delete[] dgAtSumNB;
  delete[] dgAtSumW;
  delete[] dgAtSumW2;
//delete[] dgAtSumS;
  delete[] dgInterAtSum;

  delete[] pauliAT;


} // end of if ( (not isCriticmodeActivated() and (not isELFmodeActivated()) ) )
// ....................................................................................... //

else if ( isCriticmodeActivated() )
{
 // =================  CRITICAL POINTS  ============================ //
 //        mol-cp.txt      +   cp.vmd     files created              //
 // ================================================================ //  

  gettimeofday(&stop, NULL);
  std::cout << "CPU time            : " << stop.tv_sec - start.tv_sec << "s" << std::endl;
  std::cout << "Writing files ..." << std::flush << "\n";

    
            // =================  cp.vmd ==================================== //
            // coord.xyz + vmd file session with all critical points           
            // ============================================================== //
             writeVMDfiles(&params, data, 0.0, 0.0, 0.0, cpList,0.0,0.0);
             writeXYZ(params, data, nbAtoms, atomCoordinates);


            // =================  cp.txt ==================================== //
            // writing all information about critical points found
            // ============================================================== //
            writeCPTXT(params, cpList);
 

} // end of if (isCriticmodeActivated())

else if (isELFmodeActivated())
{
 // =================  ELF vmd   =================================== //
 //        elf.vmd                                                   //
 // ================================Props================================ //  

            // ::::::::::::::::::::::::::::::::::: // 
            //  D I S P L A Y    R E S U L T S   
            // ::::::::::::::::::::::::::::::::::: //
            std::cout << "   *                                                                   *" << std::endl;
            std::cout << "   * ################################################################  *" << std::endl;
            std::cout << "   * ##################       E L F & I G M          ################  *" << std::endl;
            std::cout << "   * ##################      A N A L Y S I S        #################  *" << std::endl;
            std::cout << "   * ################################################################  *" << std::endl;
            std::cout << "   * ----------------------------------------------------------------  *" << std::endl;
            std::string emptyLine("   *                                                                   *");
            std::cout << "   * ELF&IGM offers an alternative to intensive ELF topol. analysis.   *" << std::endl;
            std::cout << "   * It assigns colors to grid points based on 1/qg^2 (ED contragrad.) *" << std::endl;
            std::cout << "   * (qg descript. = grad Rho^RIGM/grad Rho) with BGR color scheme     *" << std::endl;
            std::cout << "   * blue regions indicating strong electron sharing                   *" << std::endl;
            std::cout << "   *                                                                   *" << std::endl;
            std::cout << "   *                 ELF&IGM Basin Identification Protocol             *" << std::endl;
            std::cout << "   *                 -------------------------------------             *" << std::endl;
            std::cout << emptyLine << std::endl;
            std::cout << "   * MONOSYNAPTIC basin (lone pair)     : appears as uniform red       *" << std::endl;
            std::cout << "   * isosurfaces with no ED contragradience                            *" << std::endl;
            std::cout << emptyLine << std::endl; 
            std::cout << "   * DISYNAPTIC   basin (2-center bonds): blue-to-green     gradient   *" << std::endl;
            std::cout << "   * perpendicular to the internuc. axis, with blue localized between  *" << std::endl;
            std::cout << "   * the two interacting atoms, often with no ‘zero contragrad.’ area  *" << std::endl;
            std::cout << emptyLine << std::endl;
            std::cout << "   * DISYNAPTIC   basin (donor-acceptor): exhibits a distinctive dual- *" << std::endl;
            std::cout << "   * character; one portion (blue) displays electronic contragradience *" << std::endl;
            std::cout << "   * the other shows no contragradience (red)                          *" << std::endl;
            std::cout << emptyLine << std::endl;
            std::cout << "   * TRISYNAPTIC  basin (3-center bonds): exhibits a polarized color   *" << std::endl;
            std::cout << "   * pattern with three blue regions pointing toward each interacting  *" << std::endl;
            std::cout << "   * nucleus, often disrupting the symmetry seen in disynaptic basins  *" << std::endl;
            std::cout << emptyLine << std::endl;
            std::cout << "   * CHARGED SHIFT bond (mono or disyn.): exhibits an electron-sharing *" << std::endl;
            std::cout << "   * character, reduced in volume due to Pauli pressure                *" << std::endl;
            std::cout << emptyLine << std::endl;
            std::cout << "   * PROTONATED   basin (C-H, O-H ...)  : exhibits asymmetric bicolor. *" << std::endl;
            std::cout << "   * ovoid isosurface, with blue polarized towards the heavy atom,     *" << std::endl;
            std::cout << "   * and red near the proton                                           *" << std::endl;
            std::cout << emptyLine << std::endl;
            std::cout << "   * CORE electron pair                 : appears red like lone pairs  *" << std::endl;
            std::cout << "   * but small basins, lacking ED contragradience                      *" << std::endl;
            std::cout << "   ---------------------------------------------------------------------" << std::endl;

            gettimeofday(&stop, NULL);
            std::cout << "CPU time            : " << stop.tv_sec - start.tv_sec << "s" << std::endl;
            std::cout << "Writing files ..." << std::flush << "\n";

            // =================  ELF cube ================================== //
            // writing ELF function to cube file                    
            // ============================================================== //
             writeCube(&params, data, "ELF", rho, ELFarray, false, 0.0, 0.0, 0.0); // elf value stored in deltaginter

            // =================  ELF_IGMcolor ============================== //
            // writing (1/qg^2) to cube file to color ELF isosurfaces
            // ============================================================== //
             writeCube(&params, data, "ELF_IGMcolor", rho, ELFColor, false, 0.0, 0.0, 0.0); // elf color stored in deltag

            // =================  ELF vmd =================================== //
            // writing the vmd script                                  
            // ============================================================== //
             writeVMDfiles(&params, data, 0.0, 0.0, 0.0, cpList,0.0,0.0); // cpList is empty here

} // end of else if (isELFmodeActivated())


/* ==================== D E A L L O C A T I N G   M E M O R Y  ==================== */

  // D E A L L O C A T E 
  // density matrix:
  for(unsigned int ipria=0;ipria<fragNbPrim; ++ipria)
      {
        delete[] D[ipria];
      }
  delete[] D;

      
  //JC Thread shared structures
  for(unsigned int i=0;i<nbThreads;++i)
    {
      for( unsigned int j=0 ; j < npri ; ++j )
	{
	  delete[] gradPrim[i][j];
	}
	  
      for( unsigned int j=0 ; j < nbSteps0 ; ++j )
	{
	  for(unsigned int k=0;k<3;++k)
	    {
	      delete[] hess[i][j][k];
              delete[] eigenVect[i][j][k];
	    }
	      
	  delete[] hess[i][j];
          delete[] eigenVect[i][j];
	}

      for( unsigned int j=0 ; j < nbSteps0 ; ++j )
	{
	  delete[] dx[i][j];
	  delete[] dy[i][j];
	  delete[] dz[i][j];
	  delete[] d2[i][j];
	}


      delete[] hess[i];
      delete[] eigenVect[i];

      delete[] gradPrim[i];

      delete[] heigs[i];
	  
      delete[] dx[i];
      delete[] dy[i];
      delete[] dz[i];
      delete[] d2[i];
	  
    } // end over threads

  // atomic decomposition
  for(unsigned int ithread=0;ithread<nbThreads;++ithread)
    {
       for (unsigned int iat=0; iat<nbAtoms; ++iat)
            {
             delete [] ggIGMAt[ithread][iat];
            }

       delete []  ggIGMAt[ithread];
    }// end over threads
  delete [] ggIGMAt;
      
      
  delete[] heigs;
  delete[] gradPrim;
  delete[] hess;
  delete[] eigenVect;
  delete[] dx;
  delete[] dy;
  delete[] dz;
  delete[] d2;



      
  ///////////////////////////////////////
  // delete NCISolver properties    
  delete[] fragPrim;
  delete[] allPrim;
  delete[] fragAPrim;
  delete[] fragBPrim;
  delete[] fragAIndexToFragPrimIndex;
  delete[] fragBIndexToFragPrimIndex;  
  delete[] primRmin;
 
  
  delete[] inMoleculeA;
  delete[] inMoleculeB;
      
  for( unsigned int i=0 ; i < nbSteps0 ; ++i )
    {
      for(unsigned int j=0;j<nbSteps1;++j)
	{
	  delete[] rho[i][j];
	  delete[] RDG[i][j];
	  delete[] deltag[i][j];
	  delete[] deltaginter[i][j];
          delete[] deltagFC[i][j];
          delete[] deltaginterFC[i][j];

	}// end of for over nbSteps1

      delete[] rho[i];
      delete[] RDG[i];
      delete[] deltag[i];
      delete[] deltaginter[i];
      delete[] deltagFC[i];
      delete[] deltaginterFC[i];

    } // end of for over nbSteps0

  delete[] rho;
  delete[] RDG;
  delete[] deltag;
  delete[] deltaginter;
  delete[] deltagFC;
  delete[] deltaginterFC;


  for(unsigned int ithread=0;ithread<nbThreads;++ithread)
    {
      delete[] dgAtSum_proc[ithread];
      delete[] dgAtSumW_proc[ithread];
      delete[] dgAtSumW2_proc[ithread];
      delete[] dgInterAtSum_proc[ithread];
    }
  delete[] dgAtSum_proc;
  delete[] dgAtSumW_proc;
  delete[] dgAtSumW2_proc;
  delete[] dgInterAtSum_proc;

  delete[] maxc;

} // end of calcpropfs method (cubic grid)



/* ===================  C A L C P R O P S _ W F N _ C Y L    M E T H O D ====================================*/
void
NCISolver::calcprops_wfn_cyl() {

    unsigned int counter = 0;

    // get the starting time
    gettimeofday(&start,NULL);
    gettimeofday(&interm,NULL);

    //printCurrentState();

    //std::cout << std::endl << "WFN CYL" << std::endl;
    unsigned int nbAtoms = data->getNbAtom();
    unsigned int nbSteps0 = data->getNbSteps(0);
    unsigned int nbSteps1 = data->getNbSteps(1);
    unsigned int nbSteps2 = data->getNbSteps(2);
    positions_t atomCoordinates = data->atomPositions;

    unsigned int* icenter = wf_primitive_centers(); // within WF atom ordering

    // Variables to generate cylinder around the A-B bond =============================================================
    // increment for the cylinder grid               
    double dr, dtheta, dzcyl;
    // theta will be a local variable in the parallel part
    // r will be a local variable in the parallel part
    // z will be a local variable in the parallel part

    //! vector associated with the AB bond (bond currently examined)
    double AB[3];
    //  A coordinates (the first atom of currently AB bond examined) = cylinder basis
    double A_atom[3];
    // norm of vector AB
    double normAB;
    // the z axis in the WFN coord system
    double wfn_z[3] = {0, 0, 1};
    // the angle between the wfn z axis and the AB bond currently examined
    double alpha;
    // rotation matrix to rotate the wfn z axis
    double rot_matrixAlpha[3][3], rot_matrixBeta[3][3];
    // a point in 3D space to the currently examined bond AB
    // point is a local variable of the parallel part
    //double point[3];
    // END Variables to generate cylinder around the A-B bond =============================================================

    // get choosen atom pairs (user definition) and store them in a vector of pairs of two integers
    std::vector<std::pair<unsigned int, unsigned int> > &chosenBonds = getChosenBonds();

    // size of chosenBonds
    unsigned int nbond = chosenBonds.size();

    // number of points per cylindrical dimension  
    unsigned int Pz, Pr, Ptheta;

    // eric: IGM Model integration
    double * deltag_ij_sum     = new double[nbond]; // dg integration over the grid
    double * pda               = new double[nbond]; // pda value from Hirshfeld approxim. for the current bond


    //eric: IGM model
    //      i and j : the two indexes of the two atoms forming the chemical bond studied
    double     ****deltag_ij = NULL; // dg value for a given bond at a given grid point (4 dimensions)
                                     // deltag_ij[ibond][ip=iz][jp=ir][kp=itheta]
    double          ****dvol = NULL; // elemntary volume at a given grid point of the cylinder (not constant like in a cubic grid ...)
                                     // for a given bond (4 dimensions)

    double ****localpda        = NULL; // pda    value calculated from the GBP at a given grid node for a given bond
    

    //integer :: ati, atj, ibond
    // ati is a local variable in for parallel step
    // atj is a local variable in for parallel step
    // ibond is a local variable in for parallel step

    //JC shared variable
    double ***dx = NULL; // x distance between current grid point ip and a given atom iat
    double ***dy = NULL; // y
    double ***dz = NULL; // z
    double ***d2 = NULL; // square distance between current grid point ip and a given atom iat

    //integer :: nmcent, istat
    //no need here, nmcent is equivalent to nbAtoms;
    // istat for checking Fortran allocation ==> try catch here

    //real*8, allocatable :: chi(:,:), phi(:,:), hess(:,:)
    // chi is a local variable of the parallel part
    // phi is a local variable of the parallel part
    //double **phi;

    // JC for global allocation
    double**** hess = NULL; // ED hessien 

    // eric: IGM model
    //real*8, allocatable, dimension(:,:) :: ggIGMa, ggIGMb
    //JC shared variable
    double ***ggIGMa   = NULL; // real total gradient
    double ***ggIGMb   = NULL; // IGM  total gradient

    //JC shared variable
    double ***gradPrim= NULL; //  first derivative of a given primitive

    double **ggLeftBond   = NULL; //  same as ggLeft but limited to the primitives of atoms A and B of the current bond
    double **ggRighBond   = NULL; //  same as ggRigh

    //eric: IGM model
    // XYZ position of current grid point (cylinder grid)
    // JC shared variable
    double ***xp = NULL;

    // JC Initial values
    Pz = nbSteps0;
    Pr = nbSteps1;
    Ptheta = nbSteps2;


    // It is the more critical part in memory allocation
    try
    {
                 dvol = new double ***[nbond];
            deltag_ij = new double ***[nbond];
        localpda      = new double ***[nbond];


        for (unsigned int b = 0; b < nbond; ++b) {
                     dvol[b] = new double **[nbSteps0]; //iz
                deltag_ij[b] = new double **[nbSteps0];
            localpda[b]      = new double **[nbSteps0];

            for (unsigned int i = 0; i < nbSteps0; ++i) {
                         dvol[b][i] = new double *[nbSteps1]; //ir
                    deltag_ij[b][i] = new double *[nbSteps1];
                localpda[b][i]      = new double *[nbSteps1];

                for (unsigned int j = 0; j < nbSteps1; ++j) {
                             dvol[b][i][j] = new double[nbSteps2]; //itheta
                        deltag_ij[b][i][j] = new double[nbSteps2];
                    localpda[b][i][j]      = new double[nbSteps2];

                    for (unsigned int k = 0; k < nbSteps2; ++k) {
                                 dvol[b][i][j][k] = 0.0;
                            deltag_ij[b][i][j][k] = 0.0;
                        localpda[b][i][j][k]      = 0.0;
                    } // end over nbSteps2
                } // end over nbSteps1
            } // end over nbSteps0
        } // end over bonds

        dx = new double**[nbThreads];
        dy = new double**[nbThreads];
        dz = new double**[nbThreads];
        d2 = new double**[nbThreads];

        ggIGMa = new double**[nbThreads];
        ggIGMb = new double**[nbThreads];

        ggLeftBond   = new double*[nbThreads];
        ggRighBond   = new double*[nbThreads];

        hess = new double***[nbThreads];

        xp = new double**[nbThreads];

        gradPrim     = new double**[nbThreads];

        for(unsigned int i=0;i<nbThreads;++i) {

            ggLeftBond[i]   = new double[3];
            ggRighBond[i]   = new double[3];

            dx[i]   = new double *[nbSteps0];
            dy[i]   = new double *[nbSteps0];
            dz[i]   = new double *[nbSteps0];
            d2[i]   = new double *[nbSteps0];



            ggIGMa[i] = new double *[nbSteps0];
            ggIGMb[i] = new double *[nbSteps0];

            hess[i] = new double**[nbSteps0];

            xp[i] = new double*[3];

            xp[i][0] = new double[nbSteps0];
            xp[i][1] = new double[nbSteps0];
            xp[i][2] = new double[nbSteps0];


            gradPrim[i]     = new double*[npri];

            for(unsigned int j=0;j<npri;++j)
            {
                gradPrim[i][j]     = new double[3];
            }

            for (unsigned int j = 0; j < nbSteps0; ++j) {
                dx[i][j] = new double[nbAtoms];
                dy[i][j] = new double[nbAtoms];
                dz[i][j] = new double[nbAtoms];
                d2[i][j] = new double[nbAtoms];


                ggIGMa[i][j] = new double[3];
                ggIGMb[i][j] = new double[3];


                hess[i][j]=new double*[3];
	        for(unsigned int k=0;k<3;++k) {
                    hess[i][j][k]= new double[3];
	          }

            }
        }
    }

    catch (const std::bad_alloc &bad_alloc_error) {
        std::cerr << std::endl
                  << "[ERROR] Allocation failed: bad_alloc error catched" << std::endl
                  << "[ERROR] The chosen increments have generated a too high amount of data for the shared data structures"
                  << std::endl
                  << "[ERROR] that the current version of IGMplot is not able to manage, sorry." << std::endl
                  << "[ERROR] The program will now exit." << std::endl << std::endl;
        exit(EXIT_FAILURE);
    }


 /***************************************************
 ! **************************************************
 !             jklein+ehenon : Cylinder grid
 ! **************************************************
 ! **************************************************/


    dr     = R_MAX/Pr;
    dtheta = THETA_MAX/Ptheta;

    // Loop over the selected bonds======================================== B O N D S ===============
    unsigned int* user2ADF = wf_user2ADF_atomOrder();
    for(unsigned int ibond=0;ibond<nbond;++ibond){

        // The indexes are natural (defined by the user) but begin at 0 in the C++ arrays
        unsigned int ibondA; // atom WF_0_based index that will be used to access atom coordinates (from WF data)
        unsigned int ibondB; // atom WF_0_based index that will be used to access atom coordinates (from WF data)
        unsigned int iatBond;
        if (isRKFmodeActivated()) { // ADF requires a specific treatment
            iatBond = chosenBonds[ibond].first-1; // user_0_based atomic index
            ibondA  = user2ADF[iatBond]-1;     // the user ibond index is going to be mapped
                                               // to the internal ADF numbering of atoms
                                               // user2ADF array takes a 0_based number as input 
                                               // (within user ordering) !
                                               // and returns a 1_based number as output = the index of the atom 
                                               // within the ADF numbering
                                               // ==> ibondA is WF_0_based and within the ADF ordering
            iatBond = chosenBonds[ibond].second-1;  
            ibondB  = user2ADF[iatBond]-1; 
        }
        else { // in WFX and WFN, user atom numbering is preserved in WF data
            ibondA=chosenBonds[ibond].first-1;
            ibondB=chosenBonds[ibond].second-1;
        }


        // ====================================================================== //
        //      G E O M E T R I C A L    S T R A T E G Y                          //
        // =======================================================================//
        // ! 1) In a cylindrical reference frame r,theta,z (cylinder 1) 
        //      we generate (r1, theta1, z1) cylindrical grid points
        // !    ==> the cylindrical z axis is assumed to be the wfn z axis 
        //      ==> r1,theta1,z1 points are converted to x1,y1,z1 points in the wfn reference frame
        //          by simple cylindrical --> rectangular grid transformation (sin, cos ...)
        //          but points remains at their position, and the cylindrical z is still the wfn z axis 
        //
        //      zcyl1=zwfn   
        //           ^
        //           |         B
        //         ..|..      /
        //         ..|..     /    
        //         ..|..    /
        //         ..|..   A
        //
        //
        // !
        // ! 2) This set of (x1,y1,z1) wfn points are reoriented in the wfn reference frame 
        //      such that to be around a new z  axis parallel to the selected AB bond 
        //      ==> requires a matric transformation (x1,y1,z1) --> (x2,y2,z2) using the rotation matrix called alpha hereafter
        // !    ==> (x2,y2,z2) are not at the same position as (x1,y1,z1), but still expressed in the wfn ref. frame.
        //      ==> the new cylinder 2 has a z axis parallel to the AB bond.

        //         zwfn  zcyl2
        //           ^    ^
        //           |  ./     B
        //           | ./.    /
        //           |./.    /    
        //           ./.    /
        //           /.    A
        //
        //
        //   3) The set (x2,y2,z2) is translated such that the cylinder 2 origin is merged with the atom A of the AB bond
        //      leading to the cylinder 3:
        //      ==> requires a translation of (x2,y2,z2) --> (x3,y3,z3) using the translation operation
        //      ==> (x3,y3,z3) are not at the same position as (x2,y2,z2), but still expressed in the wfn ref. frame.
        //
        //                       zcyl3
        //                        ^
        //         zwfn          /
        //           ^          /
        //           |         B
        //           |       ./.
        //           |      ./.   
        //           |     ./.
        //           |    ./.
        //                A
        //
        //   4) This last set of points (x3,y3,z3) expressed in the wfn ref. frame will be used to compute:
        //      a) the electron density
        //      b) the gradient vectors of primitives gradChi3 and Molecular Orbitals gradMO3
        //      c) dx3,dy3,dz3 : vectors defined between current grid points (x3,y3,z3) and atoms
        //
        //   5) We finally wish to consider every vector in the cylinder 3 reference frame !
        //      ==> will require a basis change to transform the set of vectors gradChi3, gradMO3 and (dx3,dy3,dz3)
        //          from the wfn ref. frame (where they are first obtained) to the cylinder 3 reference frame:
        //      ==> will require a matrix transformation (vx3,vy3,vz3) --> (vcylx, vcyly, vcylz) (using the rotation matrix called beta hereafter)
        //      ==> ED is not impacted by this transormation since it is a scalar, but any vector expressed in the wfn ref. frame
        //          must be transformed
        //
        //
        //                   zcyl3=new zwfn
        //                        ^
        //     old zwfn          /
        //           ^          /
        //           |         B
        //           |       ./.
        //           |      ./.   
        //           |     ./.
        //           |    ./.
        //                A
        //
        //
        // ! AB stands for current ibond  (between atoms A and B)
        // ! For a given link A-B,
        AB[0]=atomCoordinates.xValues[ibondB]-atomCoordinates.xValues[ibondA];
        AB[1]=atomCoordinates.yValues[ibondB]-atomCoordinates.yValues[ibondA];
        AB[2]=atomCoordinates.zValues[ibondB]-atomCoordinates.zValues[ibondA];

        // store this bondVector in ProgData Object
        data->storeBondVector(ibond, AB[0], AB[1], AB[2]);

        A_atom[0]=atomCoordinates.xValues[ibondA];
        A_atom[1]=atomCoordinates.yValues[ibondA];
        A_atom[2]=atomCoordinates.zValues[ibondA];

        normAB=sqrt(pow(AB[0],2)+pow(AB[1],2)+pow(AB[2],2));

        // store this bondlength in ProgData Object
        data->storeBondLength(ibond,normAB);

   //   dzcyl = normAB / Pz;
        dzcyl = (normAB-2*epsCyl)/(Pz-1); // to cover almost all the cylinder axis
                                          // but avoiding x-xat = 0

        // ! Determining the rotation matrix to rotate the z axis (in wfn coord system) to the cylinder z axis (AB bond)
        // ! First determine the angle of rotation between AB and the z axis in the wfn system
        alpha = acos(getScalarProduct(wfn_z,AB)/normAB);

        // ! Compute the rotation matrix around the axis : z ^ AB  (vectorial product
        // ! between z and AB) to go from z --> AB (alpha) or to go back from AB --> z (beta)
        computeRotationMatrix(AB,  alpha, rot_matrixAlpha);
        computeRotationMatrix(AB, -alpha, rot_matrixBeta);

        // Parallel loop not for the moment :-)

        // run over y and z
        // eric: IGM model

        // ------  for C U R R E N T   B O N D --!
        // these indexes are in the natural atom range [1: nb atoms]
        // MODIF ERIC
        //unsigned int ati = chosenBonds[ibond].first;
        //unsigned int atj = chosenBonds[ibond].second;
        unsigned int ati = ibondA + 1; // ati = atom WF 1_based index; 
                                       // will be compared to icenter (WF data) and used to store gradAtom (for DOI)
                                       // ibondA = atom WF 0_based index; 
        unsigned int atj = ibondB + 1; // atj = atom WF 1_based index
                                       // will be compared to icenter (WF data) and used to store gradAtom (for DOI)
                                       // ibondB = atom WF 0_based index; 

        #ifndef No_OpenMP
        //reminder: . "variables" stated as constant are, by default, shareable, so that it is not 
        //             compulsory to put them in the shared clause
        //          . variables belonging to an object, should not be shared, but rather the object should !
        //          . the "default(none)" ensures that every "non-private" variable will be shared

        // JC 2.6.22 update : PGI warning for nprimbond, primbond, coreMONumber, stop, start, 
        //                    interm, nbThreads, runinfostate, FOUR_THIRD not to be shared
        //                  : for older compilers, it is better to disable default(none). These variables 
        //                    (nprimbond, primbond, coreMONumber, stop, start, interm, nbThreads, 
        //                    runinfostate, FOUR_THIRD)
        //                  : does not need to be explicitely shared without the default(none)
#pragma omp parallel for /*default(none)*/ shared(std::cout, Ptheta,nbond,counter,dtheta,Pr,dr, nbSteps0,Pz,dzcyl,dvol,ibond,rot_matrixAlpha,A_atom,nbAtoms,atomCoordinates,icenter,rot_matrixBeta,ati, atj,deltag_ij, /*nprimbond, primbond, coreMONumber, stop, start, interm, nbThreads, runinfostate, FOUR_THIRD, */ /*threadID  extension*/ggIGMa,ggIGMb,xp,dx,dy,dz,d2,hess, gradPrim,ggLeftBond,ggRighBond,localpda   ) schedule(dynamic) 
# endif

        //loop over theta dimension ======================================== T H E T A ===============
        for(unsigned int itheta=0;itheta<Ptheta;++itheta){
             //std::stringstream debugstream;
             //debugstream << itheta;
            #ifndef No_OpenMP
                unsigned int threadID = omp_get_thread_num();
            #else
                unsigned int threadID = 0;
            #endif

  
            unsigned int kp=itheta; // ! to use the old cubic grid notation
            // 0 + (itheta-1)*dtheta in Fortran code but no need here
            double theta= 0 + itheta*dtheta;
            // loop over dimension r of the cylinder coord system ===========    r     ===============
            for(unsigned int ir=0;ir<Pr;++ir){
                unsigned int jp=ir;

                // 0 + (ir-1)*dr in Fortran code but no need here
                double r = 0 + ir*dr;
                //std::cout  << r << std::endl;

               // ===========================================================================
               // I N I T I A L I Z A T I O N S  O F   A R R A Y S                            
               // ==========================================================================
                for(unsigned int iInit=0;iInit<nbSteps0;++iInit)
                {
                    for(unsigned int jInit=0;jInit<3;++jInit)
                    {
                        ggIGMa[threadID][iInit][jInit]=0.0;
                        ggIGMb[threadID][iInit][jInit]=0.0;
                    }

                } // end of for(unsigned int iInit=0;iInit<nbSteps0;++iInit)


               // ===========================================================================
               // L O C A L   V A R I A B L E S   f o r   t h e   C U R R E N T   T H R E A D
               // ==========================================================================

                double** chi  = new double*[npri];
                for(unsigned int iInit=0;iInit<npri;++iInit)
                {
                    chi[iInit]    = new double[10];
                }

                double** phi = new double*[molecularOrbitals.size()];
                for(std::vector<moleculeOrbital>::size_type i=0 ; i< molecularOrbitals.size() ; ++i)
                {
                    phi[i] = new double[10];
                }

                // distance between the current node and the current atom needed for the calculation of the promol ED
                double* dist = new double[nbAtoms];

               // quantities needed to compute the Hirschfeld ED Gradients
               double* rhoFree = new double[nbAtoms]; // Atomic contrib to the total promol ED
               double* pregradFree = new double[nbAtoms]; // Scalar quantity common to the 3 promol ED Grad components 

                // Hirshfeld Atomic Electron density
                double* rhoAtom = new double[nbAtoms];

                // Hirschfeld or GBP atomic ED gradient or promolecular ED gradient
                double** gradAtom    = new double*[nbAtoms];
                for (unsigned int iat=0; iat<nbAtoms; ++iat) {
                    gradAtom[iat]    = new double[3]; //for x,y,z components
                }
                // ............. end of LOCAL VARIABLES .......................................................





               // ==========================================================================
               // C A L C U L A T E  D I S T A N C E S   a n d   X,Y,Z  P O S I T I O N S     
               // ==========================================================================

                // calculate distances between every point of the cyl grid and atoms
                //loop over Z     dimension ========================================     Z     ===============
                for(unsigned int iz=0;iz<Pz;++iz)
                {
		  //not used
		  //unsigned int ip=iz; //to use the old grid notation
                  //double z=(dzcyl/2) + iz*dzcyl; // z in cylinder coord system (but z = z WFN)
                   double z = epsCyl + iz*dzcyl; // z in cylinder coord system (but z = z WFN) 
                                                  // avoiding x-xatomA = 0 and x-xatomB = 0 


                    // Determining the elemental volume of the cyl grid according to
                    // the parameter sets choosen; only depends on r (dr, dtheta, dz are
                    // ctes)
                    dvol[ibond][iz][ir][itheta] = r*dr*dzcyl*dtheta;

                    // define the current point in the xyz coord system (Z axis cyl = z axis WFN)
                    // Z height = AB length)
                    xp[threadID][0][iz]=r*cos(theta);
                    xp[threadID][1][iz]=r*sin(theta);
                    xp[threadID][2][iz]=z;

                    // rotate this point : the z axis (XYZ_wfn) is now parallel to the AB bond) 
                    //                      but the origin is not yet superposed with A atom
                    double point[3]={xp[threadID][0][iz], xp[threadID][1][iz], xp[threadID][2][iz]};
                    double VR[3];
                    fortranMatMul33Matrix3Vector(rot_matrixAlpha,point,VR);
                    xp[threadID][0][iz]=VR[0];
                    xp[threadID][1][iz]=VR[1];
                    xp[threadID][2][iz]=VR[2];

                    //Translation  : to move the cyl grid point to the cylinder around AB bond expressed in WFN xyz sys
                    xp[threadID][0][iz]+=A_atom[0];
                    xp[threadID][1][iz]+=A_atom[1];
                    xp[threadID][2][iz]+=A_atom[2];

                    // calculate the vector between the current point and each atom in
                    // WFN XYZ original coord syt
                    for(unsigned int iat=0;iat<nbAtoms;++iat)
                    {
                        dx[threadID][iz][iat] = xp[threadID][0][iz]   - atomCoordinates.xValues[iat];
                        dy[threadID][iz][iat] = xp[threadID][1][iz]   - atomCoordinates.yValues[iat];
                        dz[threadID][iz][iat] = xp[threadID][2][iz]   - atomCoordinates.zValues[iat];
                        d2[threadID][iz][iat] = dx[threadID][iz][iat] * dx[threadID][iz][iat] + 
                                                dy[threadID][iz][iat] * dy[threadID][iz][iat] + 
                                                dz[threadID][iz][iat] * dz[threadID][iz][iat];

                    }

                } // end iz loop (cylinder) -------------------------------------   Z    ----------------------

                // calculate P R I M I T I V E S  at the points
                //loop over Z     dimension ========================================     Z     ===============
                for( unsigned int iz=0 ; iz < Pz ; ++iz ) {
                    // JC for reading compatibility
                    unsigned int ip = iz;

                    // ** INITIALIZATIONS for CURRENT grid point **//
                    //
                    // initializing ED Hessian
                    for (unsigned int iInit = 0; iInit < 3; ++iInit) {
                        for (unsigned int jInit = 0; jInit < 3; ++jInit) {
                            hess[threadID][iz][iInit][jInit] = 0.0;
                        }
                    }

                    // initializing properties for Hirshfeld calculations 
                    for (unsigned int iat=0; iat<nbAtoms; ++iat)
                        {
                           dist[iat]        = 0.0;
                           rhoFree[iat]     = 0.0;
                           pregradFree[iat] = 0.0;
                        }

                    // initializing GBP gradPrim
                    for(unsigned int ipri=0;ipri<npri;++ipri)
                      {
                        gradPrim[threadID][ipri][0]=0.0;
                        gradPrim[threadID][ipri][1]=0.0;
                        gradPrim[threadID][ipri][2]=0.0;
                      }

                    // ============================================================================================
                    //         C O M P U T E      M O s,    p r i m i t i v e s,    r h o,    h e s s
                    // ============================================================================================
                    double gglocal[3]={0.0}; // won't be used in this IBSI procedure (ED grad will be next recomputed on the fly from gradPrim)
                    double rholocal=0.0; // won't be used in this IBSI procedure
                    // chi and phi are calculated to next compute gradprim required to perform the IGM analysis:
                    calcQMAO(nprimbond[ibond], primbond[ibond], d2[threadID][iz], dx[threadID][iz], dy[threadID][iz], dz[threadID][iz],chi);
                    calcQMPROP(nprimbond[ibond], primbond[ibond], chi, phi, rholocal, gglocal, hess[threadID][iz]);
                    
    
                    /*  .................... END calculating MO, rho and hessian .......................................... */



                    // ============================================================================================
                    //         C O M P U T E      V A L E N C E       E D     a n d      G R A D 
                    // ============================================================================================

                    // 1) VALENCE MO ........................
                    // we remove the core orbitals from the set of original MOs !
                    // --> assume that MO are ordered by ascending order, with core MO coming first !
/* DEPRECATED       std::vector<unsigned int> reducValMo;
 
                    // using the coreMOWFN index determined in routine setCOREMOandELEC
                    // we leave out core MO in the set of MOs
                    for( unsigned int imo=coreMOWFN ; imo< molecularOrbitals.size() ; ++imo)
                                {
                                   reducValMo.push_back(imo);  // imo now in the range [coreMOWFN: TOTnmo - coreMONumber -1]
                                }

                    // 2) Valence ED and GRAD calculation
                    // compute the valence ED ========================================

                    double rhoval = 0.0;
                    double ggval[3] = {0.0};
                    for( unsigned int imo2=0 ; imo2< reducValMo.size() ; ++imo2)
                        {
                          unsigned int imo = reducValMo[imo2]; // returns an index in the range [coreMOWFN: TOTnmo - coreMONumber -1]
                          double occImo = molecularOrbitals[imo].occupancy;

                          rhoval  += occImo *  phi[imo][0]*phi[imo][0];
                          ggval[0] = ggval[0] + 2 * occImo *  phi[imo][1]*phi[imo][0];
                          ggval[1] = ggval[1] + 2 * occImo *  phi[imo][2]*phi[imo][0];
                          ggval[2] = ggval[2] + 2 * occImo *  phi[imo][3]*phi[imo][0];

                        } // ! end of loop over imo
 */           

                    // ............... END OF VALENCE CONSTRUCTION .....................................




                    // ======================================================
                    //         B A S I S   C H A N G E (CYLINDRICAL)                            
                    // ======================================================

                    // for VECTORS PROPERTIES ONLY !!
                    // eric: IGM model, if dg integration scheme is carried out, then
                    // transformation of primitive gradients AND displacements dx from current axis system (wfn)
                    // to the UNIQUE axis system relying on the cylinder coord system for current bond


                    // before to go on, gradient components chi(ipri,2),
                    // chi(ipri,3),chi(ipri,4) , that have been computed in
                    // the wfn coord system, must now be converted to the
                    // cart. cyl system (for current bond) (in order to be able to compare bonds)
                    // basis change for the density gradient vector

              // xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
              // ==> basis change for the primitive gradient chi (atomic orbital gradient)
              // xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
                    double vec1[3];
                    double vec2[3];
                      // note that only primitives in the set primbond are considered : default = all primitives
                      // except when an IBSI cutoff radius is supplied by the user
                      for (unsigned int ipria = 0; ipria < nprimbond[ibond]; ++ipria)
                        {
                         unsigned int ipri = primbond[ibond][ipria]; // returns an index in the range[0:nbpri-1]
 
                        vec1[0] = chi[ipri][1];
                        vec1[1] = chi[ipri][2];
                        vec1[2] = chi[ipri][3];

                        //change the coord system from WFN to local cylinder = AB bond (=z)
                        fortranMatMul33Matrix3Vector(rot_matrixBeta,vec1,vec2);

                        chi[ipri][1]=vec2[0];
                        chi[ipri][2]=vec2[1];
                        chi[ipri][3]=vec2[2];

                    } //over primitives gradients!


              // xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
              // ==> basis change for the position vector of every atoms                   
              // xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

                    for(unsigned int iat=0;iat<nbAtoms;++iat)
                    {
                        vec1[0]=dx[threadID][ip][iat]; //atomCoordinates.xValues[iat];
                        vec1[1]=dy[threadID][ip][iat]; //atomCoordinates.yValues[iat];
                        vec1[2]=dz[threadID][ip][iat]; //atomCoordinates.zValues[iat];

                        //change the coord system from WFN to local cylinder = AB bond (=z)
                        fortranMatMul33Matrix3Vector(rot_matrixBeta,vec1,vec2);

                        dx[threadID][ip][iat] = vec2[0]; //atomCoordinates.xValues[iat]
                        dy[threadID][ip][iat] = vec2[1]; //atomCoordinates.yValues[iat]
                        dz[threadID][ip][iat] = vec2[2]; //atomCoordinates.zValues[iat]

                        dist[iat] = std::sqrt(d2[threadID][ip][iat]); // distance between the current node and the current atom
                                                                       // does not depend on the basis change
                                                                       // but it is needed by the current thread to compute the promol ED
                        dist[iat] = (dist[iat] < R_TRESHOLD ? R_TRESHOLD : dist[iat]);

                    } // basis change for the position vector of every atoms


              // xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
              // ==> basis change for all MO first derivatives
              // xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

                    for( unsigned int imo=0 ; imo< molecularOrbitals.size() ; ++imo)
                      {
                        vec1[0]= phi[imo][1]; // first derivative with respect to x of MO imo
                        vec1[1]= phi[imo][2]; // first derivative with respect to y of MO imo
                        vec1[2]= phi[imo][3]; // first derivative with respect to z of MO imo

                        //change the coord system from WFN to local cylinder = AB bond (=z)
                        fortranMatMul33Matrix3Vector(rot_matrixBeta,vec1,vec2);

                        phi[imo][1] = vec2[0];                               
                        phi[imo][2] = vec2[1];                               
                        phi[imo][3] = vec2[2];                               

                    } // end of loop over all MOs

              // xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
              // ==> basis change for the ED Gradient                                      
              // xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
              // gglocal variable unused UNTIL NOW ...
                
              /*    vec1[0]= gglocal[0]; // first derivative with respect to x of ED 
                    vec1[1]= gglocal[1]; // first derivative with respect to y of ED     
                    vec1[2]= gglocal[2]; // first derivative with respect to z of ED     
      
                    //change the coord system from WFN to local ED curvatures           
                    fortranMatMul33Matrix3Vector(rot_matrixBeta,vec1,vec2);
      
                    gglocal[0] = vec2[0];
                    gglocal[1] = vec2[1];
                    gglocal[2] = vec2[2];
              */  

              // xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
              // ==> basis change for the V A L E N C E  ED Gradient
              // xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
              // unused till now
    /*              vec1[0]= ggval[0]; // first derivative with respect to x of MO imo
                    vec1[1]= ggval[1]; // first derivative with respect to y of MO imo
                    vec1[2]= ggval[2]; // first derivative with respect to z of MO imo
      
                    //change the coord system from WFN to local ED curvatures           
                    fortranMatMul33Matrix3Vector(rot_matrixBeta,vec1,vec2);
      
                    ggval[0] = vec2[0];
                    ggval[1] = vec2[1];
                    ggval[2] = vec2[2];
    */

                    /* ===============    Equation (11) of our paper in Chem. Phys. Chem. 2018 page 4 : primitive gradient =========== */
                    /*                                    G R A D I E N T    B A S E D    P A R T I T I O N                           */
                    // 
                    //       IGM IBSI model computes grad contrib of primitive ipri: PRIMITIVE GRAD PARTITION
                    //       --> requires looping over all MO ! (simplified Equation (11) of Chem. Phys. Chem. 2018 page 4)
                    //================================================================================================================

// ===============    I  B  S  I    T  R  E  A  T  M  E  N  T  ====================================================
//                    using ALL filtered MOs including CORE MOs !! but limited to ATOM1 and ATOM2 

              // !       --> requires looping over all MO pre-calculated above (meaning every MO or every MO within a cutoff radius (see IBSI keyword))
              for(unsigned int ipria=0; ipria<nprimbond[ibond]; ++ipria)
                {  unsigned int ipri = primbond[ibond][ipria]; // returns an index in the range[0:nbpri-1]
                   double cpsi=0.0;
                   // all MOs are considered (if the IBSI cutoff radus is used, 
                   // these MO are based on a reduced set of primitives, default = all AOs)

                   // according to eq. (11) of IBSI paper, cpsi has to be calculated only if 
                   // one of the derivatives is non-nul
                   if ( ( ( std::abs(chi[ipri][1]) > cutoff_gradpri ) or  
                          ( std::abs(chi[ipri][2]) > cutoff_gradpri ) 
                        ) or
                          ( std::abs(chi[ipri][3]) > cutoff_gradpri )
                      )
                      {
                         for( unsigned int imo=0 ; imo< molecularOrbitals.size() ; ++imo)
                           {
                             cpsi+= molecularOrbitals[imo].occupancy*molecularOrbitals[imo].coefficients[ipri] * phi[imo][0];
                           } // ! end of loop over imo
                      } // end of test derivatives
                         cpsi*=2; // comes from the ED derivative  (for instance, [(xi)^2]' = 2 xi  xi')
                         gradPrim[threadID][ipri][0]= cpsi*chi[ipri][1];
                         gradPrim[threadID][ipri][1]= cpsi*chi[ipri][2];
                         gradPrim[threadID][ipri][2]= cpsi*chi[ipri][3];

                } // !end over ipria
/*  .................... END   I B S I  w i t h  a l l   M O s  ......................................... */


/*====================================== I G M     M O D E L =============================================*/
                    // eric: IGM model, gathering primitive gradients lying on the left
                    //       of the current examined node, and separately, gathering
                    //       primitive gradients lying on the right of current  node
                    //       Since the location of the atom relative to the atom is used
                    //       --> all primitives belonging to a given atom will be put
                    //           in the same set, whether they have a positive or negative ED slope here !
                    //          and then, no internshell contragradience will be taken into account


                    // Initializations
                    for(unsigned int iInit=0;iInit<3;++iInit)
                    {
                        ggLeftBond[threadID][iInit]   = 0.0;// with ALL  MOs !! 
                        ggRighBond[threadID][iInit]   = 0.0;// with ALL  MOs !! 
                    }

                
                    // LOOP over primitives .................................................................
                    for (unsigned int jpria = 0; jpria < nprimbond[ibond]; ++jpria)
                     {
                        unsigned int jpri = primbond[ibond][jpria]; // returns an index in the range[0:nbpri-1]
                        // BEWARE to natural index: jpriCenter must be in [0:nat-1] range
                        unsigned int jpriCenter = icenter[jpri]-1; // ipricenter and then jpricenter within WF atom ordering

                        //x component ---------------------------------------------
                        if(dx[threadID][ip][jpriCenter] >= 0.0)
                        {
                            // To prepare Eq. 12 of the Paper in CPC 2018
                            // Using direct icenter(jpri) because ati/atj are also natural indexes
                            // icenter = WF_1_based atom index 
                            // ati     = WF_1_based atom index 
                            if( (icenter[jpri]==ati) || (icenter[jpri]==atj) )
                            {
                                // current bond is concerned (To prepare Eq. 17 of the Paper in CPC 2018)
                                ggLeftBond[threadID][0]   += gradPrim[threadID][jpri][0]; // with ALL  MOs !! 
                            }
                        }
                        else
                        {
                                // To prepare Eq. 12 of the Paper in CPC 2018
                                // Using direct icenter(jpri) because ati/atj are also natural indexes
                                if( (icenter[jpri]==ati) || (icenter[jpri]==atj) )
                                {
                                    //! current bond is concerned (To prepare Eq. 17 of the Paper in CPC 2018)
                                    ggRighBond[threadID][0]   += gradPrim[threadID][jpri][0]; // with ALL  MOs !! 
                                }
                        } // end of dx


                        //y component ---------------------------------------------
                        if(dy[threadID][ip][jpriCenter] >= 0.0)
                        {
                            // To prepare Eq. 12 of the Paper in CPC 2018
                            // Using direct icenter(jpri) because ati/atj are also natural indexes
                            if( (icenter[jpri]==ati) || (icenter[jpri]==atj) )
                            {
                                // current bond is concerned (To prepare Eq. 17 of the Paper in CPC 2018)
                                ggLeftBond[threadID][1]   += gradPrim[threadID][jpri][1];// with ALL  MOs !! 
                            }
                        }
                        else
                        {
                            // To prepare Eq. 12 of the Paper in CPC 2018
                            // Using direct icenter(jpri) because ati/atj are also natural indexes
                            if( (icenter[jpri]==ati) || (icenter[jpri]==atj) )
                            {
                                //! current bond is concerned (To prepare Eq. 17 of the Paper in CPC 2018)
                                ggRighBond[threadID][1]   += gradPrim[threadID][jpri][1]; // with ALL  MOs !! 
                            }
                        } // end of dy

                        //z component ---------------------------------------------
                        if(dz[threadID][ip][jpriCenter] >= 0.0)
                        {
                            // To prepare Eq. 12 of the Paper in CPC 2018
                            // Using direct icenter(jpri) because ati/atj are also natural indexes
                            if( (icenter[jpri]==ati) || (icenter[jpri]==atj) )
                            {
                                // current bond is concerned (To prepare Eq. 17 of the Paper in CPC 2018)
                                ggLeftBond[threadID][2]   += gradPrim[threadID][jpri][2]; // with ALL  MOs !! 
                            }
                        }
                        else
                        {
                            // To prepare Eq. 12 of the Paper in CPC 2018
                            // Using direct icenter(jpri) because ati/atj are also natural indexes
                            if( (icenter[jpri]==ati) || (icenter[jpri]==atj) )
                            {
                                //! current bond is concerned (To prepare Eq. 17 of the Paper in CPC 2018)
                                ggRighBond[threadID][2]   += gradPrim[threadID][jpri][2]; // with ALL  MOs !! 
                            }
                        } // end of dz

                    }  //end jpria  over primitives within pricut cutoff of the bond atoms 1 ,2 ..................

                    // compute grad and gradIGM components (IGM = no atom interacting with others atoms)
                    // with ALL  MOs !! // in the proper cylindrical ref frame ! (since from gradPrim, from chi)

                    //------  for  C U R R E N T   B O N D --

                    // compute a IGM gradient in which the interaction
                    // between atoms i and j cancels

                    // with ALL  MOs !! 
                    ggIGMa[threadID][ip][0] = fabs(ggLeftBond[threadID][0]) + fabs(ggRighBond[threadID][0]);
                    ggIGMb[threadID][ip][0] = fabs(ggLeftBond[threadID][0]  +         ggRighBond[threadID][0]);


                    // with ALL  MOs !! 
                    ggIGMa[threadID][ip][1] = fabs(ggLeftBond[threadID][1]) + fabs(ggRighBond[threadID][1]);
                    ggIGMb[threadID][ip][1] = fabs(ggLeftBond[threadID][1]  +         ggRighBond[threadID][1]);


                    // with ALL  MOs !! 
                    ggIGMa[threadID][ip][2] = fabs(ggLeftBond[threadID][2]) + fabs(ggRighBond[threadID][2]);
                    ggIGMb[threadID][ip][2] = fabs(ggLeftBond[threadID][2]  +         ggRighBond[threadID][2]);


                    /* ========================================================= */
                    /*       B o n d    D e n s i t y    A s y m m e tr y        */
                    /*                   P     D     A                           */
                    /* ========================================================= */

                    // Compute the Bond Density Asymmetry based on the GBP partition method (more sensitive to inductive effect)
                    // take care: the MO must be expressed in the proper coordinate frame (cylindrical here !)
                    // because the gradrho will be calculated, which depends on the frame orientation

                    // get the atom gradients for next IGM pda  analysis (prerequisite: gradPrim, calculated above)
                    gradAtomGBP(nprimbond[ibond], primbond[ibond], gradPrim[threadID], gradAtom); // gradAtom = 0_based index


                    // compute the pda between the two current atoms from previously calculated gradAtom
                    this ->IGMBDA(gradAtom[ati-1], gradAtom[atj-1], localpda[ibond][ip][jp][kp]);
                                                                                             // take care: ati/atj are 
                                                                                             // expressed in the range[1-nbAtoms]
                                                                                             // WF_1_based atom index (bond selection) 

                  // handle timing previsions
                  // in the cylindrical treatment, this time handling has been located here
                  // inside the primitive iz loop/ipria, since the GBP requires an additional loop,
                  // very time consumming ... compared to the WFN mode where time handling
                  // has been put inside the iz loop, but outside the iz loop/ipria for primitive calclation
                  //
                   if(threadID==0)
                   {
                     // get time and check if 8s have passed
                     gettimeofday(&stop,NULL);
                     time_t period = stop.tv_sec - interm.tv_sec;
                     time_t ellapsed = stop.tv_sec - start.tv_sec;
                     counter++;
                     if (period >= 8.0)
                         { // its time to update the runinfo file
                           printCurrentState(counter*nbThreads,nbond*Pz*Pr*Ptheta,ellapsed);
                           runinfostate = true;
                           gettimeofday(&interm,NULL);
                         }
                   } // end of if(threadID==0)                                                                   


                } //end loop iz ........................................................................  Z   ...............


                 for(std::vector<moleculeOrbital>::size_type iInit=0 ; iInit< molecularOrbitals.size() ; ++iInit)
                {
                    delete[] phi[iInit];
                }

                delete[] phi;

                for(unsigned int iInit=0;iInit<npri;++iInit)
                {
                    delete[] chi[iInit];
                }
                delete[] chi;

                delete[] dist;

                delete[] rhoFree;
                delete[] pregradFree;

                for (unsigned int iat=0; iat<nbAtoms; ++iat) {
                   delete[] gradAtom[iat];
                }
                delete[] gradAtom;

                delete[] rhoAtom;

                // compute final cube results (dg)
                // ==================================== L O O P   O V E R    Z   ===========================================
                for(unsigned int iz=0;iz<Pz;++iz)
                {
                    unsigned int ip=iz;  // to use the old grid notation

                   // IGM model

                   double grad2IGMa = pow(ggIGMa[threadID][ip][0],2);
                   grad2IGMa       += pow(ggIGMa[threadID][ip][1],2);
                   grad2IGMa       += pow(ggIGMa[threadID][ip][2],2);

                   double grad2IGMb = pow(ggIGMb[threadID][ip][0],2);
                   grad2IGMb       += pow(ggIGMb[threadID][ip][1],2);
                   grad2IGMb       += pow(ggIGMb[threadID][ip][2],2);

                   double normIGMa = sqrt(grad2IGMa);
                   double normIGMb = sqrt(grad2IGMb);
                   deltag_ij[ibond][ip][jp][kp] = normIGMa - normIGMb;               

                } // end loop iz ........................................................................  Z   ...............
           

            } //  end loop ir ........................................................................  r   ...............


        } // end loop itheta .................................................................... T H E T A ..........

    } // End of the bond loop .................................................................... B O N D S ..........


   /* ================================ O U T P U T   W F N      M O D E     C Y L I N D    G R I D ====================== */


      /* Information box printing */
    std::string emptyLine("   *                                                                   *");

    // writing dz increment for each bond
    for(unsigned int ibond=0;ibond<nbond;++ibond){
         std::cout << "   *     bond " << std::setw(7) << std::right << data->bonds[ibond].atomPair.first << " - ";
         std::cout << std::setw(7) << std::left  << data->bonds[ibond].atomPair.second << " dz =  ";
         std::cout << std::setprecision(3) << std::fixed << std::setw(7) << std::right;
         std::cout << data->bonds[ibond].length/data->getNbSteps(0) << "     (bohr)";
         std::cout << "               *" << std::endl;

    }// end of for ibond

    std::cout << "   *                  nz,nr,ntheta =   ";
    std::cout << FOUR << data->getNbSteps(0) << FOUR << data->getNbSteps(1) << FOUR << data->getNbSteps(2);
    std::cout << "                    *" << std::endl;

    // specify the primcutoff used throughout the calculation here
    if (params.bondcut > 0.0) 
      {
        std::cout << "   *       bond environment cutoff = "; 
        std::cout << std::setw(7) << std::setprecision(2) << std::fixed; 
        std::cout << SEVEN << params.bondcut << "      (Angs)"; 
        std::cout << "               *" << std::endl;
      }
    std::cout << emptyLine << std::endl; 




    std::cout << "   * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  *" << std::endl;
    std::cout << "   *                B O N D   S T R E N G T H   I N D E X              *" << std::endl;
    std::cout << "   *                B O N D   D E N S I T Y     A S Y M M E T R Y      *" << std::endl;
    std::cout << "   * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  *" << std::endl;
    std::cout << emptyLine << std::endl;
    std::cout << "   *                  QUANTIFICATION of INTERACTIONS                   *" << std::endl;
    std::cout << "   *               J. Phys. Chem. A 2020, 124, 1850-1860               *" << std::endl;
    std::cout << emptyLine << std::endl;
    std::cout << "   *     The IBSI integration scheme has been developed to relate      *" << std::endl;
    std::cout << "   *     the sum of dg[pair] to the intrinsic bond strength            *" << std::endl;
    std::cout << emptyLine << std::endl;
    std::cout << "   *                I n t e g r a t i o n    S c h e m e               *" << std::endl;
    std::cout << "   *                        (cylindrical grid)                         *" << std::endl;
    std::cout << emptyLine << std::endl;

    // ============================================================== //
    //     C O M P U T E    I B S I     a n d     B O D E G A         //
    //     a n d     S E A R C H        f o r     B C P               //
    // ============================================================== //
    std::stringstream  BDAIBSIWarning; 
    bool flagBDAIBSIWarning=false;

    // L O C A L    V A R I A B L E S 
    double *Gbcp, *Hbcp, *Vbcp, *RHObcp, *GRADRHObcp, *ellip, *laplac;
    double **L123, **Rbcp;
    Gbcp       = new double[nbond];
    Hbcp       = new double[nbond];
    Vbcp       = new double[nbond];
    RHObcp     = new double[nbond];
    GRADRHObcp = new double[nbond];
    ellip      = new double[nbond];
    laplac     = new double[nbond];
    
    L123       = new double*[nbond];
    Rbcp       = new double*[nbond];

    bool *okBCP = new bool[nbond];


    for(unsigned int ibond=0;ibond<nbond;++ibond)
     {
      Gbcp[ibond]   = 0.0;
      Hbcp[ibond]   = 0.0;
      Vbcp[ibond]   = 0.0;
      RHObcp[ibond] = 0.0;
      GRADRHObcp[ibond] = 0.0;
      ellip[ibond]  = 0.0;
      laplac[ibond] = 0.0;

      L123[ibond] = new double[3];
      Rbcp[ibond] = new double[3];
      for (unsigned int i=0; i<3; ++i) 
         {
           L123[ibond][i] = 0.0;
           Rbcp[ibond][i] = 0.0;
         }

      okBCP[ibond] = false;          

     } // end over bonds
    //.................................................................

    for(unsigned int ibond=0;ibond<nbond;++ibond)
    {   

       // ========================================================//
       //             I B S I        P D A                        //
       // ========================================================//

        deltag_ij_sum[ibond]     = 0.0;
        pda[ibond]            = 0.0;
        for(unsigned int iz=0;iz<Pz;++iz)
        {
            for(unsigned int ir=0;ir<Pr;++ir)
            {
                for(unsigned int itheta=0;itheta<Ptheta;++itheta)
                {   double dv = dvol[ibond][iz][ir][itheta];
                    deltag_ij_sum[ibond]     = deltag_ij_sum[ibond]     + 
                                               deltag_ij[ibond][iz][ir][itheta]    *dv;                           
                    pda[ibond]               = pda[ibond] + localpda[ibond][iz][ir][itheta]*dv;

                } // end of loop over itheta
            }// end of loop over ir     
        }// end of loop over iz      

        if (!isRKFmodeActivated()) { // GTO primitive family
           data->setIBSI(ibond, deltag_ij_sum[ibond] / (pow(data->bonds[ibond].length*BOHRTOA,2) * NormCoeff_deltagd2GTO )  );
        }
        else
        { // STO primitive family
           data->setIBSI(ibond, deltag_ij_sum[ibond] / (pow(data->bonds[ibond].length*BOHRTOA,2) * NormCoeff_deltagd2STO )  );
        }
         
        // test if pda    > pda    detection threshold
        if (std::abs(pda[ibond])> pdaThreshold ) { // we divide by the bond length to make different bonds comparable
          data->setBAF(ibond, pda[ibond]*100.0/data->bonds[ibond].length); 
                                                          
        }
        else { // detection limit reached
          data->setBAF(ibond, 0.0);
        }
       // ....................... END IBSI  PDA   ...................................... //



       // ============================================================================//
       //             B C P   S E A R C H   f o r    c u r r e n t   B O N D          //
       // ============================================================================//
       // the following atom user_1_based indices ibondA and ibondB have to be converted in WF_0_based indices
       // conversion
       unsigned int ibondA; // atom WF_0_based index that will be used to access atom coordinates (from WF data)
       unsigned int ibondB; // atom WF_0_based index that will be used to access atom coordinates (from WF data)
       unsigned int iatBond;
        if (isRKFmodeActivated()) { // ADF requires a specific treatment compared to WFN or WFX
            iatBond = chosenBonds[ibond].first-1; // user_0_based atomic index
            ibondA  = user2ADF[iatBond]-1;     // the user iatBond index is going to be mapped
                                               // to the internal ADF numbering of atoms
                                               // user2ADF array takes a 0_based number as input 
                                               // (within user ordering) !
                                               // and returns a 1_based number as output = the index of the atom 
                                               // within the ADF numbering
                                               // ==> ibondA is WF_0_based and within the ADF ordering
            iatBond = chosenBonds[ibond].second-1;  
            ibondB  = user2ADF[iatBond]-1; 
        }    
        else { // in WFX and WFN, user atom numbering is preserved in WF data
            ibondA=chosenBonds[ibond].first-1;
            ibondB=chosenBonds[ibond].second-1;
        }    




       double A[3];
       double B[3];
       A[0] = atomCoordinates.xValues[ibondA];
       A[1] = atomCoordinates.yValues[ibondA];
       A[2] = atomCoordinates.zValues[ibondA];
       B[0] = atomCoordinates.xValues[ibondB];
       B[1] = atomCoordinates.yValues[ibondB];
       B[2] = atomCoordinates.zValues[ibondB];

            // ================================================= //
            //   s e a r c h   f o r    dgpair    m a x i m a        
            // ================================================= //

      // single seed = midpoint of the atom pair 
      // = A1...A2 midpoint 
         
      // +++++++++++++++++++++++++++++++++++++++++++++++++ //
      //   N e w t o n - R a p h s o n                     
      // +++++++++++++++++++++++++++++++++++++++++++++++++ //
  
      //   Newton-Raphson procedure to localize a possible BCP
      //   The GUESS= xp,yp,zp with maximum dgpair value found in the grid
      //   Note: don't forget that dgpair is calculated from gradprim
      //   derived from two atoms only --> a dgpair maximum in between two atoms
      //   does not ensure that there exists a BCP obtained based on all primitives
      //   prerequisite to obtain the wave function = d2,dx,dy,dz:
      Rbcp[ibond][0] = (A[0]+B[0])/2;           
      Rbcp[ibond][1] = (A[1]+B[1])/2;            
      Rbcp[ibond][2] = (A[2]+B[2])/2;            
      // within IBSI calculation
      bool okCP = NewtonRaphson(nprimbond[ibond], primbond[ibond], Rbcp[ibond], L123[ibond], Gbcp[ibond], RHObcp[ibond], GRADRHObcp[ibond]);
      if (okCP)
        {  
           // test if it is really a BCP (L2 and L1 < 0 and L3>0)
           if ( not (
                      ( (L123[ibond][0]<0) and (L123[ibond][1]<0) )
                                    and
                        (L123[ibond][2]>0)
                    )
              )
            {       okBCP[ibond] = false;}
           else {  // a true bcp has been found, set results
                    okBCP[ibond] = true;}

        } // end of if (okCP)
        else {okBCP[ibond] = false;}

      if (okBCP[ibond]) {

     // ===============================================================
     //          ED    L A P L A C I A N                                
     // ===============================================================
               laplac[ibond] = L123[ibond][0]+L123[ibond][1]+L123[ibond][2];

     // ===============================================================
     //          E L L I P T I C I T Y                                  
     // ===============================================================
               ellip[ibond] = L123[ibond][0]/L123[ibond][1] - 1.0;

     // ===============================================================
     //         L O C A L     E N E R G Y     D E N S I T Y   (C R E M E R definition)
     // ===============================================================
     // H(r) = G(r) + V(r), and, 2G(r) + V(r) = 1/4 delta rho(r)
     // ==> H(r) = 1/4 delta rho(r) - G(r) 
               Hbcp[ibond] = 0.25*laplac[ibond] - Gbcp[ibond];

     // ===============================================================
     //          P O T E N T I A L   E N E R G Y   D E N S I T Y        
     // ===============================================================
               Vbcp[ibond] = (Hbcp[ibond]-Gbcp[ibond])       ;

       } // end of if (okBCP[ibond]) 
  //    else {std::cout << "pb determining the BCP" << std::endl;}
     }// end of loop over ibond   ...........................................................

     // .............. E N D   O F   IBSI   PDA      and  BCP  A N A L Y S I S .................



    // Bond Features          
    std::cout << "   *                       FINAL BONDING FEATURES                      *" << std::endl;
    std::cout << "   * ================================================================  *" << std::endl;
    std::cout << "   *   x - y   |d(Angs)|   Dg   | IBSI  |    PDA(E-01)  Asymmetry Dir. *" << std::endl;
       
    for(unsigned int ibond=0;ibond<nbond;++ibond)
       {
          std::cout << "   * ----------------------------------------------------------------  *" << std::endl;
          unsigned int at1 = data->bonds[ibond].atomPair.first; // returns a User_1_based atomic index
          unsigned int at2 = data->bonds[ibond].atomPair.second;//    "
          unsigned int at1WF; // in the range[1:nbAtoms]
          unsigned int at2WF; // in the range[1:nbAtoms]
          if (isRKFmodeActivated()) { // ADF requires a specific treatment
             // convert them to WF_1_based atomic indices to get their atom label fromw WF data
             at1WF = user2ADF[at1-1];
             at2WF = user2ADF[at2-1];
          }    
         // else in WFX and WFN, user atom numbering is preserved in WF data
          else {
             at1WF = at1;
             at2WF = at2;
          }

          std::string  atomName1 = getAtomName(data->atomTypes[at1WF-1]); // take care to decrease 
                                                                          // by 1 the index passed to array atomTypes
          std::string  atomName2 = getAtomName(data->atomTypes[at2WF-1]);
          std::stringstream ossa;
          std::stringstream ossb;
          std::stringstream ossc;
          ossa << atomName1 << std::left << at1;
          ossb << FIVE << std::right<< ossa.str();
          std::cout << "   *" << ossb.str(); ossa.str("");
          ossa << atomName2 << std::left << at2;
          ossc <<  FIVE << std::left << ossa.str(); ossa.str("");
          std::cout << "-" << ossc.str() <<  "|";
          std::cout << std::setprecision(3) << std::fixed << std::setw(6) << std::right;
          std::cout << data->bonds[ibond].length*BOHRTOA  << " |";
          std::cout << SIX << std::right  << std::setprecision(3) <<deltag_ij_sum[ibond] << "  |";
          std::cout << SIX << std::right  << data->getIBSI(ibond) << " |  ";

          std::cout << std::fixed << std::setprecision(1);
          std::cout << HEIGHT << std::right << std::abs(data->getBAF(ibond)) << "    "; 
          if (data->getBAF(ibond)<0) 
             {
               std::cout << " " << ossb.str() << "<--" << ossc.str() ;
             }
          else if (data->getBAF(ibond)>0)
             {
               std::cout << " " << ossb.str() << "-->" << ossc.str() ;
             }
          else 
             {
               std::cout << " " << ossb.str() << " - " << ossc.str() ;
             }

          // warn the user for possible degree of uncertainty
          if (data->getIBSI(ibond) < IBSILimitForBDA)  
            {
               std::cout <<" !*" << std::endl;
            }
          else
               {  std::cout <<"  *" << std::endl;}

           // check BDA validity according to IBSI scale (uncertainty degrees of interpretation of BDA)

          if (data->getIBSI(ibond) < IBSILimitForBDA)
               {
                    if (data->getBAF(ibond)<0)
                     {
                       BDAIBSIWarning  << "   *                         " << ossb.str() << "<--" << ossc.str() ;
                       BDAIBSIWarning  << "                             *";
                     }
                    else if (data->getBAF(ibond)>0)
                     {
                       BDAIBSIWarning  << "   *                         " << ossb.str() << "-->" << ossc.str() ;
                       BDAIBSIWarning  << "                             *";
                     }
                    else 
                     {
                       BDAIBSIWarning  << "   *                         " << ossb.str() << " - " << ossc.str() ;
                       BDAIBSIWarning  << "                             *";
                     }
    
                    BDAIBSIWarning << std::endl; 
                    flagBDAIBSIWarning=true;
              } // end of if (data->getIBSI(ibond) < IBSILimitForBDA)
    
    
       } // end over ibond

       std::cout << "   * ================================================================  *" << std::endl;
       if (flagBDAIBSIWarning)
         {
           std::cout << "   * PDA warning: uncertain interpretation for the following bonds     *" << std::endl;
           std::cout << "   * because of a too small covalent character of the bond             *" << std::endl;
           std::cout << "   * Bond(s) potentially affected for PDA prediction:                  *" << std::endl;
           std::cout << BDAIBSIWarning.str();
         }
       std::cout << emptyLine << std::endl; 
       std::cout << emptyLine << std::endl;


    // Bond Features          
    std::cout << "   *                    at Bond Critical Point                         *" << std::endl;
    std::cout << "   * ================================================================  *" << std::endl;
    std::cout << "   *   x - y   |   rho  |   Lapl  |    G   |    V   |    H   | Ellipt. *" << std::endl;

    for(unsigned int ibond=0;ibond<nbond;++ibond)
       {
          std::cout << "   * ----------------------------------------------------------------  *" << std::endl;
      // x - y 
          unsigned int at1 = data->bonds[ibond].atomPair.first; // returns a User_1_based atomic index
          unsigned int at2 = data->bonds[ibond].atomPair.second;//    "
          unsigned int at1WF;
          unsigned int at2WF;
          if (isRKFmodeActivated()) { // ADF requires a specific treatment
             // convert them to WF_1_based atomic indices to get their atom label fromw WF data
             at1WF = user2ADF[at1-1];
             at2WF = user2ADF[at2-1];
          }   
         // else in WFX and WFN, user atom numbering is preserved in WF data
          else {
             at1WF = at1;
             at2WF = at2;
          }
          std::string  atomName1 = getAtomName(data->atomTypes[at1WF-1]); // take care to decrease 
                                                                        // by 1 the index passed to the function
          std::string  atomName2 = getAtomName(data->atomTypes[at2WF-1]);
          std::stringstream ossa;
          std::stringstream ossb;
          std::stringstream ossc;
          ossa << atomName1 << std::left << at1;
          ossb << FIVE << std::right<< ossa.str();
          std::cout << "   *" << ossb.str(); ossa.str("");
          ossa << atomName2 << std::left << at2;
          ossc <<  FIVE << std::left << ossa.str(); ossa.str("");
          std::cout << "-" << ossc.str() <<  "|";

          if (okBCP[ibond]) {
      // rho,laplacian,Kinetic energy density G, Energy density, Ellipticity
          std::cout << std::setprecision(3) << std::fixed << std::setw(7) << std::right;
          std::cout << RHObcp[ibond]  << " |";
          std::cout << HEIGHT << std::right  << laplac[ibond] << " |";
          std::cout << SEVEN << std::right  << Gbcp[ibond] << " |";
          std::cout << SEVEN << std::right  << Vbcp[ibond] << " |";
          std::cout << SEVEN << std::right  << Hbcp[ibond] << " |";
          std::cout << SEVEN << std::right  << ellip[ibond];
          std::cout << "  *" << std::endl;
          } // end of if (okBCP[ibond]) 
          else {
             std::cout << "             No bond critical point found              *" << std::endl;
          } // end of if (okBCP[ibond])

       } // end over bonds
    std::cout << "   * ----------------------------------------------------------------  *" << std::endl;
    std::cout << "   *  Note: only (3,-1) cp found in between the atom pair are reported *" << std::endl;
    std::cout << emptyLine << std::endl;
    std::cout << "   * ================================================================  *" << std::endl;
    std::cout << "   *   x - y   | bcp_x  | bcp_y  | bcp_z  |   L1   |   L2   |   L3     *" << std::endl;

    for(unsigned int ibond=0;ibond<nbond;++ibond)
       {
          std::cout << "   * ----------------------------------------------------------------  *" << std::endl;
      // x - y 
          unsigned int at1 = data->bonds[ibond].atomPair.first; // returns a User_1_based atomic index
          unsigned int at2 = data->bonds[ibond].atomPair.second;//    "
          unsigned int at1WF;
          unsigned int at2WF;
          if (isRKFmodeActivated()) { // ADF requires a specific treatment
             // convert them to WF_1_based atomic indices to get their atom label fromw WF data
             at1WF = user2ADF[at1-1];
             at2WF = user2ADF[at2-1];
          }   
         // else in WFX and WFN, user atom numbering is preserved in WF data
          else {
             at1WF = at1;
             at2WF = at2;
          }

          std::string  atomName1 = getAtomName(data->atomTypes[at1WF-1]); // take care to decrease 
                                                                        // by 1 the index passed to the function
          std::string  atomName2 = getAtomName(data->atomTypes[at2WF-1]);
          std::stringstream ossa;
          std::stringstream ossb;
          std::stringstream ossc;
          ossa << atomName1 << std::left << at1;
          ossb << FIVE << std::right<< ossa.str();
          std::cout << "   *" << ossb.str(); ossa.str("");
          ossa << atomName2 << std::left << at2;
          ossc <<  FIVE << std::left << ossa.str(); ossa.str("");
          std::cout << "-" << ossc.str() <<  "|";

          if (okBCP[ibond]) {
      // BCP position and L1,L2,L3
          std::cout << std::setprecision(3) << std::fixed;
          std::cout << SEVEN << std::right << Rbcp[ibond][0]*BOHRTOA  << " |";
          std::cout << SEVEN << std::right << Rbcp[ibond][1]*BOHRTOA  << " |";
          std::cout << SEVEN << std::right << Rbcp[ibond][2]*BOHRTOA  << " |";
          std::cout << SEVEN << std::right << L123[ibond][0]          << " |";
          std::cout << SEVEN << std::right << L123[ibond][1]          << " |";
          std::cout << SEVEN << std::right << L123[ibond][2];
          std::cout << "   *" << std::endl;
          } // end of if (okBCP[ibond]) 
          else {
             std::cout << "             No bond critical point found              *" << std::endl;
          } // end of if (okBCP[ibond])  

       } // end over bonds
       std::cout << "   * ================================================================  *" << std::endl;


  std::string field;
  std::stringstream ssrest;
  if ( flagBDAIBSIWarning)
     {
       std::cout << "   * ................................................................  *" << std::endl;
     }

   std::cout << emptyLine << std::endl;
   std::cout << "   *   Units: rho (e-/bohr^3); Laplacian, L1,L2,L3 (e-/bohr^5)         *" << std::endl;
   std::cout << "   *          G,V,H (Hartree/bohr^3)                                   *" << std::endl;
   std::cout << "   *          bcp_xyz (angstrom)                                       *" << std::endl;
   std::cout << emptyLine << std::endl;
   std::cout << "   *   IBSI = 'Intrinsic Bond Strength Index'                          *" << std::endl;
   std::cout << emptyLine << std::endl;
   if (!isRKFmodeActivated()) { // GTO primitive family
      std::cout << "   *     .  IBSI[H2] (M06-2X/6-31G**) = 1.0  (GTO primitive family)    *" << std::endl;
   }
   else // STO primitive family
   {
      std::cout << "   *     .  IBSI[H2] (M06-2X/TZP)     = 1.0  (STO primitive family)    *" << std::endl;
   }
   std::cout << "   *     .  Closely related to local bond stretching force cte         *" << std::endl;
   std::cout << "   *        (but is not affected by neg. TS curv. nor ring constraint) *" << std::endl;
   std::cout << "   *     .  IBSI is not a bond order (not a nb of shared e- pair)      *" << std::endl;
   std::cout << "   *     .  Can be used for minima, reaction path point, rings, ...    *" << std::endl;
   std::cout << "   *     .  Indicative scale:                                          *" << std::endl;
   std::cout << emptyLine << std::endl;
   std::cout << "   *                        Covalent          Bonding                  *" << std::endl;
   std::cout << "   *                  <----------------------------------------->      *" << std::endl;
   std::cout << "   *   0         ...0.15...         ...0.60                 ...4.0     *" << std::endl;       
   std::cout << "   *   <-------------><----------------->                              *" << std::endl;
   std::cout << "   *    Non-Covalent   Transition-Metal                                *" << std::endl;
   std::cout << "   *                     Coordination                                  *" << std::endl;
   std::cout << emptyLine << std::endl;
   std::cout << "   *   PDA = 'Pair Density Asymmetry'                                  *" << std::endl;
   std::cout << emptyLine << std::endl;
   std::cout << "   *     .  Detects density gradient asymmetry in the region of the    *" << std::endl;
   std::cout << "   *     .  AB atom pair, based on atomic ED gradients (GBP) along AB  *" << std::endl;
   std::cout << "   *     .  PDA = 0 for purely symmetrical bonds                       *" << std::endl;
   std::cout << "   *     .  Dir. of asymmetry = Dir. of largest atomic ED gradient     *" << std::endl;
   std::cout << "   *     .  Is very sensitive to the bonding environment               *" << std::endl;
   std::cout << "   ---------------------------------------------------------------------" << std::endl;
   std::cout <<  std::endl; 

 // write CPU time information
    gettimeofday(&stop, NULL);
    std::cout << "CPU time            : " << stop.tv_sec - start.tv_sec << "s" << std::endl;
    std::cout << std::flush << "\n";


 // Liberating memory
    delete [] deltag_ij_sum; 
    delete [] pda;
    //delete [] user2ADF;


    for(unsigned int b=0;b<nbond;++b) {
        for (unsigned int i = 0; i < nbSteps0; ++i) {
            for (unsigned int j = 0; j < nbSteps1; ++j) {
                delete[]      dvol[b][i][j];
                delete []     deltag_ij[b][i][j];
                delete []     localpda[b][i][j];

            } // end of loop over nbSteps1

            delete[]          dvol[b][i];
            delete[]     deltag_ij[b][i];
            delete[] localpda[b][i];

        } // end of loop over nbSteps0

        delete[]          dvol[b];
        delete[]     deltag_ij[b];
        delete[]     localpda[b];
    } // end of loop over bonds

    delete[]          dvol;
    delete[]     deltag_ij;
    delete[]     localpda;
    

    //JC Thread shared structures
    for(unsigned int i=0;i<nbThreads;++i)
    {

        for(unsigned int j=0;j<npri;++j)
        {
            delete[] gradPrim[i][j];
        }

        delete[] gradPrim[i];

        for( unsigned int j=0 ; j < nbSteps0 ; ++j )
        {
            delete[] dx[i][j];
            delete[] dy[i][j];
            delete[] dz[i][j];
            delete[] d2[i][j];

            delete[] ggIGMa[i][j];
            delete[] ggIGMb[i][j];

            for(unsigned int k=0;k<3;++k) {
	            delete[] hess[i][j][k];
	        }

            delete[] hess[i][j];
        } // end of j loop over nbStep0

        delete[] dx[i];
        delete[] dy[i];
        delete[] dz[i];
        delete[] d2[i];

        delete[] ggIGMa[i];
        delete[] ggIGMb[i];

        delete[] ggLeftBond[i];
        delete[] ggRighBond[i];



        delete[] hess[i];

        delete[] xp[i][0];
        delete[] xp[i][1];
        delete[] xp[i][2];
        delete[] xp[i];
    }// end of i loop over threads

    delete[] ggLeftBond;
    delete[] ggRighBond;

    delete[] gradPrim;

    delete[] dx;
    delete[] dy;
    delete[] dz;
    delete[] d2;

    delete[] ggIGMa;
    delete[] ggIGMb;
   

    delete[] hess;

    delete[] xp;

    delete[] nprimbond;
    for(unsigned int ibond=0;ibond<nbond;++ibond)
    {
      delete[] primbond[ibond];
    }
    delete[] primbond;
    delete[] primRmin;

    delete[] maxc;

    delete[] Gbcp; 
    delete[] Hbcp;
    delete[] Vbcp;
    delete[] RHObcp;
    delete[] GRADRHObcp;
    delete[] ellip;
    delete[] laplac;

    for(unsigned int ibond=0;ibond<nbond;++ibond)
     {
      delete[] L123[ibond];
      delete[] Rbcp[ibond];
     }
    delete[] L123;
    delete[] Rbcp;

    delete[] okBCP;


   // end of calcprops_cyl
} // end of method C A L C P R O P S _ C Y L   .......................................................



/* ===================  S O L V E    M E T H O D  ===================================================*/
void NCISolver::solve()
{

/* ============ QM ED TREATMENT (from a WFN/WFX input file already read)  ============================== */
  if(isWFmodeActivated())
    {
      // IBSI treatment: cylindrical grid, no dat nor cube outputfiles generated, only IBSI and BDA scores are computed
      if(isIBSImodeActivated())
	{
	  calcprops_wfn_cyl();
	}
      else // dat and cube generation: cubic grid
	{
	  calcprops_wfn();
	}

      return; // !! ==> END of the program      
    } // end of if(isWFNmodeActivated())



// E L S E :
/* ============ PROMOLECULAR TREATMENT ============================================================ */

  
  unsigned int counter=0;

  //unsigned int totalNbSteps= data->getNbSteps(0) * data->getNbSteps(1) * data->getNbSteps(2);
  unsigned int totOper = data->getNbSteps(1) * data->getNbSteps(2);
  

  /* Processing full size (without last axis for percentage) */
  zyAxisSize = data->getNbSteps(1) * data->getNbSteps(2);
  currentPercentage = 0;

  // get the starting time
  gettimeofday(&start,NULL);
  gettimeofday(&interm,NULL);

  //std::cout << "0%" << std::flush;
  //printCurrentState();
			
  //std::cout << "[" << std::flush;
            
  /*  Z axis */
  int gridZAxis(0);

#ifndef No_OpenMP
#pragma omp parallel for private(gridZAxis)
#endif
  /* ============= L O O P   O V E R   Z    A X I S    =================================*/
  for ( gridZAxis=0; gridZAxis < data->getNbSteps(2); ++gridZAxis )
    {
	  
#ifndef No_OpenMP
      unsigned int threadID = omp_get_thread_num();
#else
      unsigned int threadID = 0;
#endif
      /* Y axis */
      /* ============= L O O P   O V E R   Y    A X I S    =================================*/
      int gridYAxis(0);
      for ( gridYAxis=0; gridYAxis < data->getNbSteps(1); ++gridYAxis )
	{
	  /* Actuel process */
          // index : the total number of grid nodes already processed before starting new X loop hereafter
	  unsigned int index=(gridYAxis*data->getNbSteps(0) + gridZAxis*data->getNbSteps(0) * data->getNbSteps(1));


          /* ============= L O O P   O V E R   X    A X I S    inside lineProcessing routine  ==*/
	  lineProcessing( gridYAxis, gridZAxis,  index );

          //handling timing info
          if(threadID==0)
          {
            // get time and check if 8s have passed
            gettimeofday(&stop,NULL);
            time_t ellapsed = stop.tv_sec - start.tv_sec;
            time_t period = stop.tv_sec - interm.tv_sec;
            counter++;
            if (period >= 8.0)
                { // its time to update the runinfo file
                  printCurrentState(counter*nbThreads,totOper,ellapsed);
                  runinfostate = true;
                  gettimeofday(&interm,NULL);
                }
          } // end of if(threadID==0)                                                                   


	} // end of for ( gridYAxis
    } // end of for ( gridZAxis

  /* A T O M  C O N T R I B U T I O N */	
  /* Results' sums' reduction */
  /* --> the deltagInterAtom is summed over the grid and put in variable DGSIE */ 
  results->sum(); // reduction over threads
                  // the sum is stored in element 0 of the array : sumDeltaGInterAtom[i][0] for each atom i
			
  total = 0.0;
  max = 0.0;
			
  for( int i(0) ; i < data->getNbAtom() ; ++i )
    {
      total += results->get(i, Results::DGSIE); // get(i, Results::DGSIE) returns the summed score for atom i
      if(max < results->get(i, Results::DGSIE) ) max = results->get(i, Results::DGSIE);
    }

  if (total !=0.0 )
    {	
      max = (max * 100.0)/total;
    }
  else
    {
      max = 0.0;
    }

/* ============================ T E S T I N G   I N T E R A C T I O N S  ======================================== */
/*                                 P R O M O L E C U L A R    C A S E                                            */



  /* Gathering pointers */
  double * cubeRho      = results->get(TYPE_RHO);            // get the pointer to the rho cube results
  double * cubeRDG      = results->get(TYPE_RDG);            // get the pointer to the RDG cube results
  double * cubedgInterFC= results->get(TYPE_DELTA_G_INTERFC);  // get the pointer to the dgInterFC cube results
                                                             // rotational independent
  double * cubedgIntraFC= results->get(TYPE_DELTA_G_INTRAFC);  // get the pointer to the dgIntraFC cube results
                                                             // rotational independent

  // sums
  double   dgInterSum   = 0.0; // sum of dgInter scores
  double   dgInterSumPos= 0.0; // sum of dgInter scores for L2 > 0
  double   dgInterSumNeg= 0.0; // sum of dgIntra scores for L2 < 0

  double   dgInterSumW  = 0.0; // sum of dgInter scores for low   ED (weak   interactions)
  double   dgInterSumS  = 0.0; // sum of dgInter scores for large ED (strong interactions)

  double   dgIntraSum   = 0.0; // sum of dgIntra scores
  double   dgIntraSumPos= 0.0; // sum of dgIntra scores for L2 > 0
  double   dgIntraSumNeg= 0.0; // sum of dgIntra scores for L2 < 0

  double   dgIntraSumW  = 0.0; // sum of dgIntra scores for low   ED (weak   interactions)
  double   dgIntraSumS  = 0.0; // sum of dgIntra scores for large ED (strong interactions)
  double scoreInteract1 = 0.0; // to ultimately estimate deltaEInter in kcal/mol

  double   dgInterSumPeakFocus = 0.0; // sum of dgInter scores in the peak defined by user
  double   dgIntraSumPeakFocus = 0.0; // sum of dgIntra scores in the peak defined by user



  // detecting max
  double maxAttractDgInter = -1.0;
  double maxRepulsiDgInter = -1.0;
  
  unsigned int ijkIndex = 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 )
          {   // first, retrieve the appropriate index value in the one dimension array
              // corrresponding to the current gridpoint
              ijkIndex = k*data->getNbSteps(0)*data->getNbSteps(1)+j*data->getNbSteps(0)+i;
              double tmprho = cubeRho[ijkIndex];
              double tmpRDG = cubeRDG[ijkIndex];
              double tmpdgIntra = std::max<double>(cubedgIntraFC[ijkIndex], 1e-30); //FC<-> rotational independent score
              double tmpdgInter = cubedgInterFC[ijkIndex]; //FC<-> rotational independent score

/* ============================ IGM gradient Partition ========================================================= */
              double gradrho =  tmpRDG * (std::pow(std::abs(tmprho), FOUR_THIRD)) / ALPHA;//recover gradrho from RDG
              double gradrhoIGMIntra = tmpdgIntra + gradrho;
             
                                                            // since we cannot directly reach gradrhoIGMIntra
                                                             // where only intramol interact. have been disabled
                                                             // while intermol. interaction are active
                                                             // we consider that: (1) dgIntra = gradrhoIGM   - gradrhoInter
                                                             //                   (2) dgIntra = gradrhoIntra - gradrho
                                                             // The first definition is the one used practically
                                                             // in IGMplot since gradrhoIGM cancel every interations
                                                             // and gradrhoInter cancels only intermol. inter., 
                                                             // then gradrhoIGM   - gradrhoInter represents the drop
                                                             // in gradient due to intramol. interactions !
                                                             // But, if we could access directly to gradrhoIntra
                                                             // where only intramol. inter/ were cancel (2) would
                                                             // also represent dgIntra ! Unfortunately, cancelling
                                                             // every intramol. interactions also cancel every intermol.
                                                             // interactions ==> we can reach gradrhoIntra by :
                                                             // gradrhoIntra = dgIntra + gradrho from (2) (each of them
                                                             // strored in arrays) , and dgIntra was obtained from (1) 
                                                             // previously and stored in array !!!

              double gradrhoIGMInter = tmpdgInter + gradrho; // (1) dgInter = gradrhoIGMInter - gradrho
                                                             // gradrho contains every interaction while gradrhoIGMInter cancels 
                                                             // intermo. interaction but contains intramol. inter. 
                                                             // ==> dgInter describes only intermol. interaction
                                                             // ==> gradrhoIGMInter can be recovered from gradrho and dgInter
                                                             //     which have been stored in arrays 

              double qgIntra = gradrhoIGMIntra/gradrho; // an important criterion to filter dgIntra peaks !
              double qgInter = gradrhoIGMInter/gradrho; // an important criterion to filter dgInter peaks !

              //O P T I O N 2 :  entire grid is used
              //             // integration of dgInter and intra
              //              
               dgInterSum = dgInterSum + tmpdgInter; // will serve to detect if there is no interaction 
                                                     // between fragments
               dgIntraSum = dgIntraSum + tmpdgIntra;  // will serve to monitor for instance
                                                      // the total interactions in a single species in reaction
                              
               // contributions of the "repulsive" part              
               if (tmprho>0) { 
                  dgIntraSumPos = dgIntraSumPos + tmpdgIntra;
                  dgInterSumPos = dgInterSumPos + tmpdgInter;
               }
               // contributions of the "attractive" part    
               if (tmprho<0) {
                  dgIntraSumNeg = dgIntraSumNeg + tmpdgIntra;
                  dgInterSumNeg = dgInterSumNeg + tmpdgInter;
               }                                      

                // contributions of the weak attractive interactions Intra
               if ( (tmprho>-0.09) and (tmprho<0) and (qgIntra > qgThresh) ) {
                  // will serve to monitor for instance weak attractive interactions
                  // only peaks of weak ED regions are used
                  dgIntraSumW = dgIntraSumW + tmpdgIntra;  
               }                                          

                // contributions of the weak attractive interactions Inter
               if ( (tmprho>-0.09) and (tmprho<0) and (qgInter > qgThresh) ) {
                  // will serve to monitor for instance weak attractive interactions
                  // only peaks of weak ED regions are used
                  dgInterSumW = dgInterSumW + tmpdgInter;
               }

                // contributions of the strong attractive interactions Intra
               if ( (tmprho<=-0.09) and (qgIntra > qgThresh) ) {
                  // will serve to monitor for instance strong attractive interactions
                  // only peaks of strong ED regions are used
                  dgIntraSumS = dgIntraSumS + tmpdgIntra; 
               }  

                // contributions of the strong attractive interactions Inter
               if ( (tmprho<=-0.09) and (qgInter > qgThresh) ) {
                  // will serve to monitor for instance strong attractive interactions
                  // only peaks of strong ED regions are used
                  dgInterSumS = dgInterSumS + tmpdgInter;
               } 

              // contributions of the peak defined by the user 
              if (isPeakFocusIntraActivated()) {
                  if ((tmprho>=params.cutpeakIntra[0]) and (tmprho<=params.cutpeakIntra[1])) {
                     if (qgIntra > qgThresh) {
                        dgIntraSumPeakFocus = dgIntraSumPeakFocus + tmpdgIntra;
                     }
                  }
              } // end of peakFocusIntra

              // contributions of the peak defined by the user 
              if (isPeakFocusInterActivated()) {
                  if ((tmprho>=params.cutpeakInter[0]) and (tmprho<=params.cutpeakInter[1])) {
                     if (qgInter > qgThresh) {
                        dgInterSumPeakFocus = dgInterSumPeakFocus + tmpdgInter;
                     }
                  }
              } // end of peakFocusInter



               // dgInter scoreInteract1 only dedicated to conversion in kcal/mol
               if ( (qgInter > 1.2) and (tmprho<0) ) // qgInter>1.2 <-> only  peak regions are used from the dgInter
                                                // rho<0  <-> only attractive regions are considered

                 {
                    scoreInteract1  = scoreInteract1  - tmpdgInter; // will lead to the final score in kcal/mol
                 }

              // search for the maximum value of dgInter in the grid
              // only maxima for the INTER analysis are performed in promol. mode
              if ((tmprho<0) and  (tmpdgInter > maxAttractDgInter) )
                 {maxAttractDgInter = tmpdgInter;}

              if ((tmprho>0) and  (tmpdgInter > maxRepulsiDgInter) )
                 {maxRepulsiDgInter = tmpdgInter;}
            

          } // end k
        } // end j
    } // end i

  // multiply the dgInter sum by the elemental volume (constant over the cubic grid)
  double dv = params.increments[0] * params.increments[1] * params.increments[2];
  dgInterSum    = dgInterSum    * dv;
  dgInterSumPos = dgInterSumPos * dv;
  dgInterSumNeg = dgInterSumNeg * dv;
  dgInterSumW   = dgInterSumW   * dv;
  dgInterSumS   = dgInterSumS   * dv;

  dgIntraSum    = dgIntraSum    * dv;
  dgIntraSumPos = dgIntraSumPos * dv;
  dgIntraSumNeg = dgIntraSumNeg * dv;
  dgIntraSumW   = dgIntraSumW   * dv;
  dgIntraSumS   = dgIntraSumS   * dv;
  scoreInteract1 = scoreInteract1 * dv;

  dgInterSumPeakFocus = dgInterSumPeakFocus * dv;
  dgIntraSumPeakFocus = dgIntraSumPeakFocus * dv;



  results->setsumdgInter(&dgInterSum);

  std::string emptyLine("   *                                                                   *");

  /* IF SUM dgInter is NULL : no interaction detected ... */
  if(results->getsumdgInter() < interacThreshold)
   {

      std::cout << "   *                                                                   *" << std::endl;
      std::cout << "   * ################################################################  *" << std::endl;
      std::cout << "   * ################## I N T E R - F R A G M E N T #################  *" << std::endl;
      std::cout << "   * ##################      A N A L Y S I S        #################  *" << std::endl;
      std::cout << "   * ################################################################  *" << std::endl;
      std::cout << "   * ----------------------------------------------------------------  *" << std::endl;

      std::cout << emptyLine << std::endl;
      std::cout << "   *   No interaction detected between fragments.                      *" << std::endl;
      std::cout << "   *   Possible causes : only one fragment defined, or fragments too   *" << std::endl;
      std::cout << "   *                     distant or inappropriate gridBox definition   *" << std::endl;
      std::cout << "   *                     (check CUBE or RADIUS definition)             *" << std::endl;
      std::cout << emptyLine << std::endl;
      if ((data->getNbAtomMolA()+data->getNbAtomMolB()) == 1 )
         {
           std::cout << emptyLine << std::endl;
           std::cout << "   *   Only one atom considered here ...                               *" << std::endl;
           std::cout << "   ---------------------------------------------------------------------" << std::endl;
         }


  } // end of if(sumdgInter < interacThreshold) 

  else if ((data->getNbAtomMolA()+data->getNbAtomMolB()) >=2 ) // display the integration score of dgInter
                                                               // here, the interaction between 2 atoms is allowed
                                                               // (for instance Ne....Ar)

    { double scoreinkcal= 20.2620*std::pow(scoreInteract1,3) +    // this formula has been deduced from
                          48.3744*std::pow(scoreInteract1,2) +    // relationship studies between scoreInteract1
                          64.0711*scoreInteract1;                  // and PM7 deltaE energies mesured along MD traj.
                                                                   // with R^2 = 0.987
//      double scoreinkcal= 16.4375*scoreInteract1*scoreInteract1 +  // this formula has been deduced from
 //                         53.0784*scoreInteract1;                  // relationship studies between scoreInteract1
                                                                   //
      results->setscoreInterac(scoreinkcal);

/* ===============   I N T E R A C T I O N   F A M I L Y    I N T E R                   ========= */
      std::cout << "   *                                                                   *" << std::endl;
      std::cout << "   * ################################################################  *" << std::endl;
      std::cout << "   * ################## I N T E R - F R A G M E N T #################  *" << std::endl;
      std::cout << "   * ##################      A N A L Y S I S        #################  *" << std::endl;
      std::cout << "   * ################################################################  *" << std::endl;
      std::cout << "   * ----------------------------------------------------------------  *" << std::endl;
      std::cout << "   *               T Y P E   o f   t h e   S T R O N G E S T           *" << std::endl;
      std::cout << "   *          I N T E R A C T I O N   i n    t h e    S Y S T E M      *" << std::endl;
      std::cout << emptyLine << std::endl;
      std::cout << "   *   MAXIMUM of the dg peak(s)  = ";
      std::cout << std::scientific << std::setprecision(4) << std::setw(8);
      std::cout << maxAttractDgInter << " a.u. (attractive part)  *" << std::endl;
      if (maxAttractDgInter > 2.5)
        {
          std::cout << "   *   ! Warning: uncommonly strong interaction                        *" << std::endl;
          std::cout << emptyLine << std::endl;
        }

      // calculate the cursor position on the dg SCALE:
      unsigned int dgcursor = 1;
      if ( (maxAttractDgInter > 0.001) and (maxAttractDgInter <= 0.0083333333) )
       {
          dgcursor = 1;
       }
      else if ( (maxAttractDgInter > 0.0083333333) and (maxAttractDgInter <=0.1) )
       {
          dgcursor =  (maxAttractDgInter / 0.1) * 12;
       }
      else if ( (maxAttractDgInter > 0.1) and (maxAttractDgInter <=0.6) )
       {
          dgcursor =  12 + ( (maxAttractDgInter-0.1) / (0.6-0.1) ) * 17;
       }
      else if ( (maxAttractDgInter > 0.6) and (maxAttractDgInter <=2.5) )
       {
          dgcursor =  29 + ( (maxAttractDgInter-0.6) / (2.5-0.6) ) * 31;
       }
      else if (maxAttractDgInter > 2.5)
       {
          dgcursor =  62;
       }
      int rest = 64 - dgcursor;


      std::cout << "   *   dg SCALE:                                                       *" << std::endl;
      std::cout << "   *    " << std::setw(dgcursor) << "|" << std::setw(rest) << "*" << std::endl;
      std::cout << "   *    " << std::setw(dgcursor) << "|" << std::setw(rest) << "*" << std::endl;
      std::cout << "   *    " << std::setw(dgcursor) << "v" << std::setw(rest) << "*" << std::endl;
      std::cout << "   *   |___________|________________|______________________________|   *" << std::endl;
      std::cout << "   *   0          0.1              0.6                            2.5  *" << std::endl;
      std::cout << "   *    < non-cov >  < metal coord >                                   *" << std::endl;
      std::cout << "   *                 < ................... covalent ............. >    *" << std::endl;

      // warning is  delivered to notify repulsive interactions 
      if ( maxRepulsiDgInter >= maxAttractDgInter)
         {
           std::cout << emptyLine << std::endl;
           std::cout << "   *   ! Warning: non-bonding dg peak > attractive dg peak             *" << std::endl;
           std::cout << "   *              possibly resulting from steric repulsion forces      *" << std::endl;
         } // end of the first warning (repsulsive case detected)

      // warning are delivered to notify strong interactions 
      if ( maxAttractDgInter >= 0.1 )
         {
           std::cout << emptyLine << std::endl;
           std::cout << "   *   ! Warning: attractive dg[Inter] peak > 0.1 a.u. threshold       *" << std::endl;
           std::cout << "   *              possibly revealing covalent features; promolecular   *" << std::endl;
           std::cout << "   *              ED should only be used to describe weak interactions *" << std::endl;
         } // end of the first warning (covalent domaine detected)


      /* ===============   I N T E R   S C O R E                                               ========= */
      std::cout << "   *                                                                   *" << std::endl;
      std::cout << "   * ----------------------------------------------------------------  *" << std::endl;
      std::cout << "   *     Q U A N T I F I C A T I O N   o f   I N T E R A C T I O N     *" << std::endl;
      std::cout << "   *                        between FRAGMENT(S)                        *" << std::endl;
      std::cout << "   *                        -------                                    *" << std::endl;
      std::cout << emptyLine << std::endl;
      std::cout << "   *   Grid Integration scores:                                        *" << std::endl;
      std::cout << emptyLine << std::endl;

      std::cout << "   *   [1 ] sum     dg[Inter]        x dv =";
      std::cout << std::scientific << std::setprecision(3) << std::setw(11);
      std::cout << dgInterSum << " a.u. Total      *" << std::endl;
      std::cout << "   *   [1a] sum     dg[Inter]        x dv =";
      std::cout << std::scientific << std::setprecision(3) << std::setw(11);
      std::cout << dgInterSumPos << " a.u. L2 > 0     *" << std::endl;
      std::cout << "   *   [1b] sum     dg[Inter]        x dv =";
      std::cout << std::scientific << std::setprecision(3) << std::setw(11);
      std::cout << dgInterSumNeg << " a.u. L2 < 0     *" << std::endl;
      std::cout << emptyLine << std::endl;

      std::cout << "   *   [2]  sum_peak dg[Inter_weak]  x dv =";
      std::cout << std::scientific << std::setprecision(3) << std::setw(11);
      std::cout << dgInterSumW << " a.u. L2 < 0     *" << std::endl;
      std::cout << "   *   [3]  sum_peak dg[Inter_strong]x dv =";
      std::cout << std::scientific << std::setprecision(3) << std::setw(11);
      std::cout << dgInterSumS << " a.u. L2 < 0     *" << std::endl;
      std::cout << emptyLine << std::endl;
      std::cout << "   *   [4]  Inter. Energy crude Estimate = " << std::setw(10);
      std::cout << results->getscoreInterac();
      std::cout << " kcal/mol         *" << std::endl;
      std::cout << emptyLine << std::endl;
      std::cout << emptyLine << std::endl;

      if (isPeakFocusInterActivated()) {
          std::cout << "   *   [5]  sum_peakfocus dg[Inter]  x dv =";
          std::cout << std::scientific << std::setprecision(3) << std::setw(11);
          std::cout << dgInterSumPeakFocus << " a.u.            *" << std::endl;
          std::cout << emptyLine << std::endl;
      }


      std::cout << "   *   Integration region:                                             *" << std::endl;
      std::cout << "   *             [1] Entire grid                                       *" << std::endl;
      std::cout << "   *             [2] dg peaks in         -0.09  < ED <  0.00           *" << std::endl;
      std::cout << "   *             [3] dg peaks in   ED <= -0.09                         *" << std::endl;
      std::cout << "   *             [4] Estimated from [2,3](see doc for appropriate use) *" << std::endl;

      if (isPeakFocusInterActivated()) {
          std::cout << "   *             [5] dg peaks in         " << std::fixed << std::setprecision(2) << std::setw(5);
          std::cout << params.cutpeakInter[0] << "  < ED < ";
          std::cout << std::setw(5) << std::right << params.cutpeakInter[1] << "           *" << std::endl;;
          std::cout << emptyLine << std::endl;
      }


      std::cout << emptyLine << std::endl;
      std::cout << "   *   Notes: - Promolecular Electron Density (ED) should only be      *" << std::endl;
      std::cout << "   *            used to adress weak interactions.                      *" << std::endl;

      if (isPeakFocusInterActivated()) {
          std::cout << "   *          - [2,3,5] ignore the flat parts of the dg signal,        *" << std::endl;
      } else {
          std::cout << "   *          - [2,3] ignore the flat parts of the dg signal,          *" << std::endl;
      }
      std::cout << "   *            focusing on peaks.                                     *" << std::endl;
      std::cout << "   ---------------------------------------------------------------------" << std::endl;

      // warning are delivered to notify an interaction energy out of fit range 
      if ( results->getscoreInterac() < -40.0 )  
         {
           std::cout << emptyLine << std::endl;
           std::cout << "   * ! Warning: Inter. Energy estimate outside the calibration range   *" << std::endl;
         } // end of the first warning (repsulsive case detected)


      if ((data->getNbAtomMolA()+data->getNbAtomMolB()) == 2 )
         {
           std::cout << "   ---------------------------------------------------------------------" << std::endl; 
         }



    } // end of if(results->getsumdgInter() < interacThreshold)

/* ===============   I N T R A   S C O R E    i s    a l w a y s    d i s p l a y e d    ========= */
      if ((data->getNbAtomMolA()+data->getNbAtomMolB()) >2)
   {
      std::cout << "   *                                                                   *" << std::endl;
      std::cout << "   * ################################################################  *" << std::endl;
      std::cout << "   * ################## I N T R A - F R A G M E N T #################  *" << std::endl;
      std::cout << "   * ##################      A N A L Y S I S        #################  *" << std::endl;
      std::cout << "   * ################################################################  *" << std::endl;
      std::cout << "   * ----------------------------------------------------------------  *" << std::endl;
      std::cout << "   *     Q U A N T I F I C A T I O N   o f   I N T E R A C T I O N     *" << std::endl;
      std::cout << "   *                        within  FRAGMENT(S)                        *" << std::endl;
      std::cout << "   *                        ------                                     *" << std::endl;
      std::cout << emptyLine << std::endl;
      std::cout << "   *   Grid Integration scores:                                        *" << std::endl;
      std::cout << emptyLine << std::endl;
      std::cout << "   *   [1 ] sum     dg[Intra]        x dv =";
      std::cout << std::scientific << std::setprecision(3) << std::setw(11);
      std::cout << dgIntraSum << " a.u. Total      *" << std::endl;
      std::cout << "   *   [1a] sum     dg[Intra]        x dv =";
      std::cout << std::scientific << std::setprecision(3) << std::setw(11);
      std::cout << dgIntraSumPos << " a.u. L2 > 0     *" << std::endl;
      std::cout << "   *   [1b] sum     dg[Intra]        x dv =";
      std::cout << std::scientific << std::setprecision(3) << std::setw(11);
      std::cout << dgIntraSumNeg << " a.u. L2 < 0     *" << std::endl;
      std::cout << emptyLine << std::endl;


      std::cout << "   *   [2]  sum_peak dg[Intra_weak]  x dv =";
      std::cout << std::scientific << std::setprecision(3) << std::setw(11);
      std::cout << dgIntraSumW << " a.u. L2 < 0     *" << std::endl;
      std::cout << "   *   [3]  sum_peak dg[Intra_strong]x dv =";       
      std::cout << std::scientific << std::setprecision(3) << std::setw(11);
      std::cout << dgIntraSumS << " a.u. L2 < 0     *" << std::endl;
      std::cout << emptyLine << std::endl;

      if (isPeakFocusIntraActivated()) {
          std::cout << "   *   [4]  sum_peakfocus dg[Intra]  x dv =";
          std::cout << std::scientific << std::setprecision(3) << std::setw(11);
          std::cout << dgIntraSumPeakFocus << " a.u.            *" << std::endl;
          std::cout << emptyLine << std::endl;
      }


      std::cout << "   *   Integration region:                                             *" << std::endl;
      std::cout << "   *                  [1] Entire grid                                  *" << std::endl;
      std::cout << "   *                  [2] dg peaks in         -0.09  < ED <  0.00      *" << std::endl; 
      std::cout << "   *                  [3] dg peaks in   ED <= -0.09                    *" << std::endl; 

      if (isPeakFocusIntraActivated()) {
          std::cout << "   *                  [4] dg peaks in         " << std::fixed << std::setprecision(2) << std::setw(5);
          std::cout << params.cutpeakIntra[0] << "  < ED < ";
          std::cout << std::setw(5) << std::right << params.cutpeakIntra[1] << "      *" << std::endl;;
          std::cout << emptyLine << std::endl;
      }

      std::cout << emptyLine << std::endl;
      std::cout << "   *   Notes: - Promolecular Electron Density (ED) should only be      *" << std::endl;
      std::cout << "   *            used to adress weak interactions.                      *" << std::endl;

      if (isPeakFocusIntraActivated()) {
         std::cout << "   *          - [2,3,4] ignore the flat parts of the dg signal,        *" << std::endl;
      } else {
         std::cout << "   *          - [2,3] ignore the flat parts of the dg signal,          *" << std::endl;
      }
      std::cout << "   *            focusing on peaks.                                     *" << std::endl;
      std::cout << "   ---------------------------------------------------------------------" << std::endl;
   } // end of  if ((data->getNbAtomMolA()+data->getNbAtomMolB()) >2)



// ....................... E N D   O F   T E S T I N G   I N T E R A C T I O N S  ................................
// .......................   F O R   P R O M O L E C U L A R   C A S E   .........................................

  //printCurrentState(false,true);		      
  
} // end of solve function
  
void
NCISolver::lineProcessing(int posY, int posZ, int cubePosYZ)
{

// cubePosYZ : input --> the total number of gride nodes already processed before the new X loop hereafter

   // Promolecular treatement of a grid line
  /* Looping across X axis */
  int i(0);
		  
#ifndef No_OpenMP
  int index(omp_get_thread_num());
#else
  int index(0);
#endif
			
  for( i = 0 ; i < data->getNbSteps(0) ; ++i )
    {
      /* Updating axis grid positions */
      posGrid[index].x	= i 	* params.increments[0] + data->getMinCoord(0);
      posGrid[index].y 	= posY 	* params.increments[1] + data->getMinCoord(1);
      posGrid[index].z 	= posZ 	* params.increments[2] + data->getMinCoord(2);
		      
      /* Processing */
      // i + cubePosYZ: represents the current grid node index (in absolute value)
      nodes[index]->process(posGrid[index], i + cubePosYZ, *results, params);
    } 
} // end of lineProcessing
		
void
NCISolver::output()
{ // ===========  output for the PROMOLECULAR MODE ==================== //

  double deltagIntraMaximumValue;
  double deltagInterMaximumValue;
  double dgAtWeakMaxValue = 0.0; // still unused, but in anticipation of generating the ncc.vmd


  switch( params.outputType )
    {
	
      /* OUTPUT NOTHING */
    case OUTPUT_NONE :  // outputType = 0
				
      // DOES NOTHING
				
      break;
	
      /* OUTPUT .DAT Ostd::endlY */
    case OUTPUT_DAT :  // outputType = 1
					
      /* Outputing rho and RDG's cubes as a .dat file */
      outDat(&params, data, *results);

      /* Outputing percents if and only if a fragmentation scheme is enabled */
      if ( not ( (data->getNbAtomMolA() == 0)  or  (data->getNbAtomMolB() == 0) ) )
        {outPercent(&params, *results, data);}
 
				
      break;
				
      /* OUTPUT RHO AND RDG'S CUBES Ostd::endlY */
    case OUTPUT_RHO_RDG : // outputType = 2
				
      /* Outputing rho's cube as a .cube file */
      outCubeRho(&params, data, results->get(TYPE_RHO));
					
      /* Outputing RDG's cube as a .cube file */
      outCubeRDG(&params, data, results->get(TYPE_RDG), results->get(TYPE_RHO), results->getShouldPrint());
					
      break;

    // D E P R E C A T E D				
    case OUTPUT_PERCENT : // outputType = 4 // fragmentation scheme has been supplied (tested before)
				
      /* Outputing percents */
      outPercent(&params, *results, data);
      deltagIntraMaximumValue=getMaxFromPrecVectorized3DMatrix(results->get(TYPE_DELTA_G_INTRA),results->get(TYPE_RHO),
                              -params.cutplotIGM[0],params.cutplotIGM[0],data->getNbSteps(0),data->getNbSteps(1),data->getNbSteps(2));
      deltagInterMaximumValue=getMaxFromPrecVectorized3DMatrix(results->get(TYPE_DELTA_G_INTER),results->get(TYPE_RHO),
                              -params.cutplotIGM[1],params.cutplotIGM[1],data->getNbSteps(0),data->getNbSteps(1),data->getNbSteps(2));
      
      /* Outputing VMD script */
      outVMD(&params, data, max, deltagIntraMaximumValue, deltagInterMaximumValue, dgAtWeakMaxValue);
				
      break;
				
      /* OUTPUT EVERYTHING */
    case OUTPUT_ALL : 
				
      /* Outputing rho and RDG's cubes as a .dat file*/
      outDat(&params, data, *results);
					
      /* Outputing rho's cube as a .cube file */
      outCubeRho(&params, data, results->get(TYPE_RHO));
					
      /* Outputing RDG's cube as a .cube file */
      outCubeRDG(&params, data, results->get(TYPE_RDG), results->get(TYPE_RHO), results->getShouldPrint());

      /* Outputing IGM */
      /* Outputing all cube's types as .cube files according to the appropriate ED cutoff*/
      for( int i(0) ; i < SIZE_CUBE_DELTA ; ++i ){
						
	outCube( i, &params, data, results->get(TYPE_RHO), results->get(i) );
						
      }
      
      /* PROMOL: Outputing percents if and only if a fragmentation scheme is enabled */
      if ( not ( (data->getNbAtomMolA() == 0)  or  (data->getNbAtomMolB() == 0) ) ) 
        {outPercent(&params, *results, data);}


      /* preparing vmd output */
      if (not isdgSCALED())
      {
            if (isPeakFocusIntraActivated()) {
     
               deltagIntraMaximumValue=getMaxFromPrecVectorized3DMatrix(results->get(TYPE_DELTA_G_INTRA),results->get(TYPE_RHO),
                                       params.cutpeakIntra[0], params.cutpeakIntra[1], data->getNbSteps(0),data->getNbSteps(1),data->getNbSteps(2));
     
           }
            else {
               deltagIntraMaximumValue=getMaxFromPrecVectorized3DMatrix(results->get(TYPE_DELTA_G_INTRA),results->get(TYPE_RHO),
                                       -params.cutplotIGM[0], params.cutplotIGM[0], data->getNbSteps(0),data->getNbSteps(1),data->getNbSteps(2));
           }
     
     
           if (isPeakFocusInterActivated()) {
     
               deltagInterMaximumValue=getMaxFromPrecVectorized3DMatrix(results->get(TYPE_DELTA_G_INTER),results->get(TYPE_RHO),
                                       params.cutpeakInter[0], params.cutpeakInter[1], data->getNbSteps(0),data->getNbSteps(1),data->getNbSteps(2));
           }
            else {
               deltagInterMaximumValue=getMaxFromPrecVectorized3DMatrix(results->get(TYPE_DELTA_G_INTER),results->get(TYPE_RHO),
                                       -params.cutplotIGM[1], params.cutplotIGM[1], data->getNbSteps(0),data->getNbSteps(1),data->getNbSteps(2));
           }  
           
      } // end of if (not isdgSCALED())
   else { //isdgSCALED()
               deltagIntraMaximumValue = dgSCALEDisovalue/ratioPeak; // maxim value = dgSCALEDisovalue / ratioPeak 
               deltagInterMaximumValue = dgSCALEDisovalue/ratioPeak; // see include/general.h constants
                                                                     // since in writeVMDfiles maxim value will be
                                                                     // multiplied by ratioPeak
        }




      /* Outputing VMD script */
      outVMD(&params, data, max, deltagIntraMaximumValue, deltagInterMaximumValue, dgAtWeakMaxValue);
      
      break;
				
      /* OUTPUT DELTA'S CUBE Ostd::endlY */
    case OUTPUT_DELTA : // outputType = 3
				
      /* Outputing all cube's types as .cube files */
      for( int i(0) ; i < SIZE_CUBE_DELTA ; ++i )
	{
	  outCube( i, &params, data, results->get(TYPE_RHO), results->get(i) );
	}
					
      /* Outputing rho's cube as a .cube file */
      outCubeRho(&params, data, results->get(TYPE_RHO));
				
      break;
				
    default :
				
      if(log)
	{
	  std::cout<<"[NCISOLVER::LOG]\t" << " [WARNING] : Output type not understood : " 
                   << params.outputType << std::endl;
	}
				
    }			
			
}// end of output method for PROMOLECULAR .........................................
		
std::string
NCISolver::score()
{
  return "Not implemented yet";
}


unsigned int*
NCISolver::index0(const unsigned int i)
{
  if(i>=35)
    {
      std::cerr <<  std::endl;
      std::cerr << "[ERROR] Primitive types higher than G are not yet supported." << std::endl;
      std::cerr << "[ERROR] Maximum allowed primitive type in WFN/WFX file = 35" << std::endl;
      std::cerr << "[ERROR] Current primitive type = " << i+1 << std::endl;
      std::cerr << "[ERROR] In routine index0." << std::endl;
      std::cerr << "[ERROR] The program will now exit." << std::endl;
      exit(EXIT_FAILURE);
    }
  //std::cout << "\ti " << i << " gives " << LI[i][0] << " " << LI[i][1] << " " << LI[i][2] << std::endl;

  return LI[i];
} // end of index0

unsigned int
NCISolver::index1(const unsigned int i)
{                                                    
// returns 0,1,2,3, or 4 corresponding to s,p,d,f,g AO type function
  if(i>=35)                                          
    {
      std::cerr <<  std::endl;
      std::cerr << "[ERROR] Primitive types higher than G are not yet supported." << std::endl;
      std::cerr << "[ERROR] Maximum allowed primitive type in WFN/WFX file = 35" << std::endl;
      std::cerr << "[ERROR] Current primitive type = " << i+1 << std::endl;
      std::cerr << "[ERROR] In routine index0." << std::endl;
      std::cerr << "[ERROR] The program will now exit." << std::endl;
      exit(EXIT_FAILURE);
    }

  return L3[i];
        
} // end of index1




void
NCISolver::initializeLI()
{
  LI[0][0]= 0; LI[0][1]= 0; LI[0][2]= 0;

  LI[1][0]= 1; LI[1][1]= 0; LI[1][2]= 0;

  LI[2][0]= 0; LI[2][1]= 1; LI[2][2]= 0;

  LI[3][0]= 0; LI[3][1]= 0; LI[3][2]= 1;

  LI[4][0]= 2; LI[4][1]= 0; LI[4][2]= 0;

  LI[5][0]= 0; LI[5][1]= 2; LI[5][2]= 0;

  LI[6][0]= 0; LI[6][1]= 0; LI[6][2]= 2;

  LI[7][0]= 1; LI[7][1]= 1; LI[7][2]= 0;

  LI[8][0]= 1; LI[8][1]= 0; LI[8][2]= 1;

  LI[9][0]= 0; LI[9][1]= 1; LI[9][2]= 1;

  LI[10][0]=3; LI[10][1]=0; LI[10][2]=0;

  LI[11][0]=0; LI[11][1]=3; LI[11][2]=0;

  LI[12][0]=0; LI[12][1]=0; LI[12][2]=3;

  LI[13][0]=2; LI[13][1]=1; LI[13][2]=0;

  LI[14][0]=2; LI[14][1]=0; LI[14][2]=1;

  LI[15][0]=0; LI[15][1]=2; LI[15][2]=1;

  LI[16][0]=1; LI[16][1]=2; LI[16][2]=0;

  LI[17][0]=1; LI[17][1]=0; LI[17][2]=2;

  LI[18][0]=0; LI[18][1]=1; LI[18][2]=2;

  LI[19][0]=1; LI[19][1]=1; LI[19][2]=1;

  LI[20][0]=0; LI[20][1]=0; LI[20][2]=4;

  LI[21][0]=0; LI[21][1]=1; LI[21][2]=3;

  LI[22][0]=0; LI[22][1]=2; LI[22][2]=2;

  LI[23][0]=0; LI[23][1]=3; LI[23][2]=1;

  LI[24][0]=0; LI[24][1]=4; LI[24][2]=0;

  LI[25][0]=1; LI[25][1]=0; LI[25][2]=3;

  LI[26][0]=1; LI[26][1]=1; LI[26][2]=2;

  LI[27][0]=1; LI[27][1]=2; LI[27][2]=1;

  LI[28][0]=1; LI[28][1]=3; LI[28][2]=0;
		  
  LI[29][0]=2; LI[29][1]=0; LI[29][2]=2;

  LI[30][0]=2; LI[30][1]=1; LI[30][2]=1;

  LI[31][0]=2; LI[31][1]=2; LI[31][2]=0;

  LI[32][0]=3; LI[32][1]=0; LI[32][2]=1;

  LI[33][0]=3; LI[33][1]=1; LI[33][2]=0;

  LI[34][0]=4; LI[34][1]=0; LI[34][2]=0;
}

void
NCISolver::initializeL3()
{
// L3 contains the sum i+j+k of integers used in the expression of the GTO
// x^i * y^j * z^k * exp(-alpha r^2) coded by a number in the range[0:35]
L3[0] = 0;

L3[1] = 1;
L3[2] = 1;
L3[3] = 1;

L3[4] = 2;
L3[5] = 2;
L3[6] = 2;
L3[7] = 2;
L3[8] = 2;
L3[9] = 2;

L3[10] = 3;
L3[11] = 3;
L3[12] = 3;
L3[13] = 3;
L3[14] = 3;
L3[15] = 3;
L3[16] = 3;
L3[17] = 3;
L3[18] = 3;
L3[19] = 3;

L3[20] = 4;
L3[21] = 4;
L3[22] = 4;
L3[23] = 4;
L3[24] = 4;
L3[25] = 4;
L3[26] = 4;
L3[27] = 4;
L3[28] = 4;
L3[29] = 4;
L3[30] = 4;
L3[31] = 4;
L3[32] = 4;
L3[33] = 4;
L3[34] = 4;
} // end of initializeL3

void
NCISolver::getLambdaOfHessian(double** hessian,double* eigenValues, double** eigenVect)
{

// hessian     : ED hessian
// eigenvalues : the three eigenvalues of the ED hessian
// eigenVect   : the three eigenvectors of the ED hessian
//
  
  // // matrix for the eigenvalues/vectors
  // not using pointers since the eigen_decomposition function takes:returns V which is not a pointer ...
  // but, curiously, eigen_decomposition takes the 'hessian' as being a pointer of pointers
  double V[3][3]={{0.0}}; // a static ARRAY

  // diagonalize the ED Hessian
  eigen_decomposition(hessian, V, eigenValues);

  // prepare the eigenVect results
  eigenVect[0][0] = V[0][0];
  eigenVect[0][1] = V[0][1];
  eigenVect[0][2] = V[0][2];
  eigenVect[1][0] = V[1][0];
  eigenVect[1][1] = V[1][1];
  eigenVect[1][2] = V[1][2];
  eigenVect[2][0] = V[2][0];
  eigenVect[2][1] = V[2][1];
  eigenVect[2][2] = V[2][2];

} // end of getLambdaOfHessian

double
NCISolver::getPredictedSize()
{
  return predictedSize;
}

bool NCISolver::getRuninfostate()
{
  return runinfostate;
}


void NCISolver::setDensityMatrixForPauli(double** D)
{
// this method compute the density matrix element which will be used in SELF (Pauli) approach
// limited to AOs of FRAG1+FRAG2, which is allPrim if ELF treatment is performed for instance.
    try      
       {               
         for(unsigned int ipria=0;ipria<fragNbPrim; ++ipria)
            {
              D[ipria]   = new double[fragNbPrim];
              for(unsigned int iprib=0;iprib<fragNbPrim; ++iprib)
               {
                D[ipria][iprib]    = 0.0;
               } // end of loop over a
            } // end of loop over b
       } // end of trying allocating local arrays
     catch (const std::bad_alloc& bad_alloc_error)
       {
         std::cerr << std::endl
                   << "[ERROR] Allocation of density matrix failed: bad_alloc error catched" << std::endl
                   << "[ERROR] The program will now exit." << std::endl << std::endl;
         exit(EXIT_FAILURE);
       }

     // compute the elements of the density matrix D
     // with the MAPPING:  Dxy in the xy original chi numbering ==> Dab in the ab fragPrim numbering hereafter
     // i.e. in the original numbering: D[apri][bpri] = now D[ipria][jpria]:
     for(unsigned int ipria=0; ipria<fragNbPrim; ++ipria) // ipria in the range [0:nA+nB] (nA+nB <= n)
       {  unsigned int apri = fragPrim[ipria]; // original apri returned in the range[0:nbprim-1] 
           for(unsigned int jpria=0; jpria<fragNbPrim; ++jpria) // jpria in the range [0:nA+nB] (nA+nB <= n)
             {  unsigned int bpri = fragPrim[jpria]; // original bpri returned in the range[0:nbprim-1] 
                 for( unsigned int imo=0 ; imo< molecularOrbitals.size() ; ++imo)
                     {
                         D[ipria][jpria] =  D[ipria][jpria]
                                        + molecularOrbitals[imo].occupancy *
                                          molecularOrbitals[imo].coefficients[apri] *
                                          molecularOrbitals[imo].coefficients[bpri];
                     } // end over imo
              } // !end over jpria
       } // !end over ipria


} // end of setDensityMatrixForPauli


void NCISolver::setPrimList()
{
 // this method has three aims:
 // a) set the list of primitives to be used in D[a][b] (PAULI SELF treatment)
 //    --> nbPrimInA, nbPrimInB
 // b) set the list of primitives to be used in  IGM FRAG1/FRAG2 mode
 //    --> fragNbPrim
 // c) set the list of not-null primitives to be used in calcQMAO (speedup test, scalprod test)
 //    --> 

 // !!!! if dgscaled option enabled : the whole real system has to be considered to compute rho and ED grad
 // !!!! else only ED and ED grad coming from FRAG1+FRAG2 will be considered
 // then:
 // !!!! : if option dgscaled is used, then :
 // !!!!   a) the true ED, true ED gradient will be calculated through calcQM
 // !!!!   b) within IGM, in: gradPrim[ipri][0]= cpsi*chi[ipri][1] ==> cpsi accounts for ALL primitives
 // !!!! : if option dgscaled is NOT used in IGM calculations, then :
 // !!!!   a) the ED,ED gradient of FRAG1+FRAG2 will be calculated through calcQM
 // !!!!   b) within IGM, in: gradPrim[ipri][0]= cpsi*chi[ipri][1] ==> cpsi accounts for FRAG1+FRAG2 primitives

   //                    F R A G 1     -     F R A G 2      
  /* ===============  P R I M I T I V E    S O R T I N G  ======================*/
  /*                                                                            */
  // here, two tasks are achieved:
  // 1) classify primitives of FRAG1 (moleculeA) or FRAG2 (moleculeB)
  // 2) build an array (fragprim) of the primitives to be used in all calculations
  //    (these selected primitives do not necessarily represent the all set of primitives)
  //
  inMoleculeA = new bool[npri]; // field of NCISolver
  inMoleculeB = new bool[npri]; // field of NCISolver
  fragNbPrim  = 0; // field of NCISolver
  fragPrim  = new unsigned int[npri]; // to store the primitives indexes  // field of NCISolver
                                      // in the natural range [0:nA+nB-1] (limited to FRAG1+2 ) !! nA+nB <= n
                                      // but, since nA,nB are not yet known, fragPrim is finally sized to npri
  allPrim   = new unsigned int[npri]; // to store ALL the primitives indexes  // field of NCISolver
                                      // <-> allPrim[x] = x, simply
                                      // in the natural range [0:npri   ] (FULL, not limited to FRAG1+2 )
  fragAPrim = new unsigned int[npri]; // to store the primitives indexes  of FRAG1 // field of NCISolver
                                      // in the natural range [0:nA-1] (of FRAG1+2 )
                                      // but, since nA,nB are not yet known, fragAPrim is finally sized to npri
  fragBPrim = new unsigned int[npri]; // to store the primitives indexes  of FRAG2 // field of NCISolver
                                     // in the natural range [0:nB-1] (of FRAG1+2 )
                                      // but, since nA,nB are not yet known, fragBPrim is finally sized to npri

  // An array converting indexes of fragAPrim to keys of  fragPrim 
  fragAIndexToFragPrimIndex = new unsigned int[npri];  // input = integer in the range [0:nA-1]
                                                       // returns an index in the range[0:nA+nB-1]

  // An array converting indexes of fragBPrim to keys of fragPrim 
  fragBIndexToFragPrimIndex = new unsigned int[npri];  // input = integer in the range [0:nB-1]
                                                       // returns an index in the range[0:nA+nB-1]


  nbPrimInA = 0; // number of primitives in frag A, field of NCISolver
  nbPrimInB = 0; // number of primitives in frag B, field of NCISolver

  // ========================= AO PRUNING ===============================================================//
  // compute the minimum radius below which every primitive has to be computed (scalProd speedup test) 
  // for now, implemented only for GTOs
  // ====================================================================================================//
  findRminAllPrim();
  

  unsigned int* icenter  = wf_primitive_centers(); // WFN or WFX or RKF

// ============== FILTERING AOs of FRAGMENTS A and B ONLY =============================
//                    A + B can be the whole system
// ====================================================================================
                                 
  for(unsigned int jpri=0;jpri<npri;++jpri) //  jpri in the range [0:npri -1]
    {
      // built a boolean array for each fragment
      if (isInMoleculeA(icenter[jpri])) // <-> FRAG1 // isInMoleculeA takes a 1_based WFN/WFX/RKF
                                                     // atom index as input and returns true or false
        {
            inMoleculeA[jpri]=true; // takes as input jpri within WFN/WFX/RKF numbering 
            nbPrimInA = nbPrimInA + 1;
            fragAPrim[nbPrimInA-1] = jpri;  // fragAprim index in the range [0:nbPrimInA-1]
                                            // fragAprim returns an index in the range [0:npri -1]     

            // store the number of primitive by fragment
            // and build a list of primitive belonging to FRAG1 + FRAG2 to be studied 
            fragNbPrim = fragNbPrim + 1;    // if FRAGA + FRAGB = the whole molecule --> fragNbPrim-1 = jpri
                    
            fragPrim[fragNbPrim-1] = jpri;  // jpri in the range [0:npri -1]
                                            // fragprim index must be in the range [0:nbprimFRAG-1] 

            // connect fragAprim indexes to fragPrim indexes
            fragAIndexToFragPrimIndex[nbPrimInA-1] = fragNbPrim-1;

        }
      else
       {
            inMoleculeA[jpri]=false;
       }

      if (isInMoleculeB(icenter[jpri])) // <-> FRAG2 // isInMoleculeB takes a 1_based WFN/WFX/RKF
                                                     // atom index as input and returns true or false
        {
            inMoleculeB[jpri]=true; // takes as input jpri within WFN/WFX/RKF numbering  
            nbPrimInB = nbPrimInB + 1;
            fragBPrim[nbPrimInB-1] = jpri;  // fragBprim index in the range [0:nbPrimInB-1]
                                            // fragBprim returns an index in the range [0:npri -1]     

            // store the number of primitive by fragment
            // and build a list of primitive belonging to FRAG1 + FRAG2 to be studied 
            fragNbPrim = fragNbPrim + 1;
            fragPrim[fragNbPrim-1] = jpri;  // jpri in the range [0:npri -1]
                                            // fragprim index must be in the range [0:nbprimFRAG-1] 

            // connect fragBprim indexes to fragPrim indexes
            fragBIndexToFragPrimIndex[nbPrimInB-1] = fragNbPrim-1;
        }
      else
       {
            inMoleculeB[jpri]=false;
       }

      //in any case, build the allPrim array, very simple (will be used in case of dgscaled IGM option and 
      // all other IGMPLOT calculations: ELF, SELF)
      allPrim[jpri] = jpri;

    }// end of for(jpri=0;jpri<npri;++jpri

// ............ E X A M P L E .............................. //
// ipri                        1  2  3  4  5  6  7  8  9  10
// FRAG                        B  A  B  x  A  A  B  B  A  B  (two fragments are considered here, with FRAG1+2 < ALL)
// fragPrim                    1  2  3  5  6  7  8  9  10
// fragAPrim                   2  5  6  9
// fragBPrim                   1  3  7  8  10
// allPrim                     1  2  3  4  5  6  7  8  9  10
// fragAIndexToFragPrimIndex   2  4  5  8
// fragAIndexToFragPrimIndex   1  3  6  7  9
// ......................................................... //

} // end of setPrimList ..........................



void NCISolver::setIBSIPrimList()
{

  // ========================= AO PRUNING ===============================================================//
  // compute the minimum radius below which every primitive has to be computed (scalProd speedup test) 
  // for now, implemented only for GTOs
  // ====================================================================================================//
  findRminAllPrim();

/*========== S P E E D  U P  T H E   C O D E   W I T H   C U T O F F   o n    P R I M I T I V E S ========*
 ! limiting calculations to those primitives in a radius of r angstroms
 ! around both atoms of the considered bond  
 ! Build the list of atoms to be considered for each bond */

   // get choosen atom pairs and store them in a vector of pairs of two integers
   std::vector<std::pair<unsigned int, unsigned int> > &chosenBonds = getChosenBonds();

   //  get the centers information already read before
   //  all the center are recorded (no concept of fragments here)
   centerData* wfnReadCenters=wf_center_data();

   // get the primitive centers
   unsigned int* icenter  = wf_primitive_centers();   // WFN or WFX or RKF

   // number  of chosenBonds
   unsigned int nbond = chosenBonds.size();   

   // total number of primitives in WFN (WFX)

   double primcut   = params.bondcut/BOHRTOA; // converted to Bohr from Angstroms

   // vector associated with the AB bond (bond currently examined) and its norm
   double AB[3];

   nprimbond = new unsigned int[nbond];
   primbond  = new unsigned int*[nbond];
   for(unsigned int ibond=0;ibond<nbond;++ibond)
    {
      primbond[ibond] = new unsigned int[npri];
    }


   // check if a bond cutoff has been given or not
   unsigned int* user2ADF = wf_user2ADF_atomOrder();
   if (primcut == 0.0)  // full calculation involving every primitives
                        // no bond environment cutoff used
     {
       for(unsigned int ibond=0;ibond<nbond;++ibond)
          {
           nprimbond[ibond]=npri;
           // loop over all primitives
           for(unsigned int ipri=0;ipri<npri;++ipri)
              { primbond[ibond][ipri] = ipri;}
          } // end of ibond
     } // end of if (primcut == 0.0) 

   else 
     { // find the atoms around atoms A and B of each bond within the primcut cutoff
       for(unsigned int ibond=0;ibond<nbond;++ibond)
        {
          nprimbond[ibond]=0;

          // The indexes are natural (defined by the user) but begin at 0 in the C++ arrays
          unsigned int ibondA; // atom WF_0_based index that will be used to access atom coordinates (from WF data)
          unsigned int ibondB; // atom WF_0_based index that will be used to access atom coordinates (from WF data)
          unsigned int iatBond;
          if (isRKFmodeActivated()) { // ADF requires a specific treatment
            iatBond = chosenBonds[ibond].first-1; // 0_based USER defined
            ibondA  = user2ADF[iatBond]-1;        // the user ibond index is going to be mapped
                                                  // to the internal ADF numbering of atoms
                                                  // user2ADF array takes a 0_based number as input 
                                                  // (within user ordering) !
                                                  // and returns a 1_based number as output = the index of the atom 
                                                  // within the ADF numbering
                                                  // ==> ibondA is WF_0_based and within the ADF ordering
            iatBond = chosenBonds[ibond].second-1;    
            ibondB  = user2ADF[iatBond]-1; 
           } // end of  if (isRKFmodeActivated()) 
           else { // in WFX and WFN, user atom numbering is preserved in WF data
               ibondA=chosenBonds[ibond].first-1;
               ibondB=chosenBonds[ibond].second-1;
           }    

        double       normAB= 0;
    
        // loop over primitives
        for(unsigned int ipri=0;ipri<npri;++ipri)
                { 
                   // check if atom of current prim is in the cutoff radius from atom1 of the current bond
                   // BEWARE icenter returns => natural count ==> decrease of one
                   AB[0] = wfnReadCenters[icenter[ipri]-1].coordinates[0] - 
                           wfnReadCenters[ibondA].coordinates[0];

                   AB[1] = wfnReadCenters[icenter[ipri]-1].coordinates[1] - 
                           wfnReadCenters[ibondA].coordinates[1];

                   AB[2] = wfnReadCenters[icenter[ipri]-1].coordinates[2] - 
                           wfnReadCenters[ibondA].coordinates[2];

                   normAB=std::sqrt(AB[0]*AB[0] + AB[1]*AB[1] + AB[2]*AB[2]);
                   if (normAB < primcut) 
                    { // the current primitive is selected
                      nprimbond[ibond] = nprimbond[ibond]+1;
                      primbond[ibond][nprimbond[ibond]-1] = ipri;
                    }
    
                   else  // check if atom of current prim is in the cutoff radius from atom2 of the bond
                    {
                      AB[0] = wfnReadCenters[icenter[ipri]-1].coordinates[0] -
                              wfnReadCenters[ibondB].coordinates[0];
    
                      AB[1] = wfnReadCenters[icenter[ipri]-1].coordinates[1] - 
                              wfnReadCenters[ibondB].coordinates[1];
    
                      AB[2] = wfnReadCenters[icenter[ipri]-1].coordinates[2] - 
                              wfnReadCenters[ibondB].coordinates[2];

                      normAB=std::sqrt(AB[0]*AB[0] + AB[1]*AB[1] + AB[2]*AB[2]);
                      if (normAB < primcut)
                         { // the current primitive is selected
                           nprimbond[ibond] = nprimbond[ibond]+1;
                           primbond[ibond][nprimbond[ibond]-1] = ipri;
                         }
                    } // end of if (normAB < primcut)
                } // end of loop ipri
         } // end of ibond

  } // end of else of if (primcut == 0.0)
  // free memory
  //delete[] user2ADF;


/*.......... E N D   O F    P R I M I T I V E    S E L E C T I O N     W I T H I N    C U T O F F ....*/


} // end of setIBSIPrimList ..........................

void NCISolver::setCOREMOandELEC()
{
// now, only counts the number of electrons and occupied MOs
//
// before:
// reorganize the MO in ascending order of energy
// set the number of core molecular orbitals needed for BDA calculations
// set the number of core electrons needed to check if it is > 0 (else this is probaby a WFN with pseudopotential)
// detecting if an atom is too heavy (ATOM_COREORB and ATOM_ELECTRONS arrays not appropriate to derive core electrons in IGMPLot)
//

  COREHeavyAtomWarning = false; // atomic number upper limit for counting core electrons

// first, check that the CHARGE parameter potentially read from the param.igm input file
// and the netCharge read from the WFX input file (if WFX and not WFN)
// are the same (and different from NOCHARGE) when QM data were read from WFX and the user supplied a CHARGE keyword ... !
// (though it is not necessary in that case since the WFX contains the net charge information !)

/* deprecated
     if (  isWFXmodeActivated() ) // data were taken from a WFX input file, not from a WFN one
     {
      if (    params.paramFound[CHARGE]
         and (params.CHARGE != wf_netCharge() )
         )
            {
               std::cerr << std::endl;
               std::cerr << "[ERROR] A WFX input file was supplied with the "  << std::endl;
               std::cerr << "[ERROR] net Charge set to: " << wf_netCharge()  << ", different from " << std::endl;
               std::cerr << "[ERROR] the CHARGE keyword provided in the param.igm input file: " << params.CHARGE << std::endl;
               std::cerr << "[ERROR] Check the CHARGE keyword in the param.igm input file, or" << std::endl;
               std::cerr << "[ERROR] remove it (the net charge is directly read from the WFX file)." << std::endl;
               std::cerr << "[ERROR] in reading parameters" << std::endl;
               std::cerr << "The program will now exit." << std::endl;
               exit(EXIT_FAILURE);
            } // end of if (    params.paramFound[CHARGE] ...
     } // end of if (  isWFXmodeActivated()
*/
   // else, if WFN mode ==> the required CHARGE keyword has already been checked 

//
/* ================  O R G A N I Z E    M O s   i n   A S C E N D I N G    ==================*/
/*                            O R D E R    o f    E N E R G Y                               */
// this is especially important to make sure that coreMO are located at the beginning
// of the list of MOs 

  // Reorganize the MO by ascending order of energy if necessary
  // ! For Natural orbitals in WFX: impossible to sort them by energy
  //   since no energy is provided for Natural Orbitals ...
  //   and this might cause troubles for BDA calculations
  //   since in that case the only way to identify core MOs
  //   is by vizualising them -> user must achieve this work ...
  //   (the strategy of determining how much atoms contributes
  //   to the MO does not work because some core MO combines
  //   2 or more core atomic orbitals ... 
// DEPRECATED 
// MOSorted = wf_molecular_orbitals_sort();

//
/* ================  C O U N T    A T O M I C   C O R E    O R B I T A L S ==================*/
/*                                d o u b l y    o c c u p i e d                            */
/*                               s t o r e d    i n    I G M P l o t                       */
// loop over all atoms of the system and add atomic core orbitals (saved in array ATOM_COREORB)
/*  DEPRECATED
        coreAONumber = 0; // NCISolver variable (see NCISolver.h)  
        unsigned int nbAtoms = data->getNbAtom();
        for (unsigned int iatom=0; iatom<nbAtoms; iatom++)
          {
            coreAONumber = coreAONumber +  ATOM_COREORB[data->atomTypes[iatom]]; // ATOM_COREORB index in the range [0:SIZE_ATOM-1]
                                                                                 // atomTypes    index in the range [0:NbAtoms-1]

            // Add a TEST
            if (data->atomTypes[iatom] > COREAtomLimit) // detecting if one atom is beyond the atomic number upper limit
                                                       // for finding core electrons
               {
                 COREHeavyAtomWarning = true; 
               }

          } // end of for (unsigned int iatom=0; iatom<nbAtoms; iatom++)
*/

/*========== C o u n t   t h e   n u m b e r   o f   e l e c t r o n s */
// Both the theoretical total number and real number of electrons read from WFN are calculated


/*  =============  THEORETICAL TOTAL e-  for a NEUTRAL system =========== */
// loop over all atoms of the system and add atomic electron nb (saved in array ATOM_ELECTRONS)
    elecNumb=0; // NCISolver variable (see NCISolver.h)  
    unsigned int nbAtoms = data->getNbAtom();
    for (unsigned int iatom=0; iatom<nbAtoms; iatom++)
      {
        elecNumb = elecNumb +  ATOM_ELECTRONS[data->atomTypes[iatom]]; // ATOM_COREORB index in the range [0:SIZE_ATOM-1]
                                                                       // atomTypes    index in the range [0:NbAtoms-1]
      }

/*  =============  REAL e- NUMBER  FROM  WFN  ======= */
    WFNelecNumb=0; // NCISolver variable (see NCISolver.h) 

    // LOOP over all MO read from WFN
    for( unsigned int imo=0 ; imo< molecularOrbitals.size() ; ++imo)
      {
        WFNelecNumb = WFNelecNumb + molecularOrbitals[imo].occupancy;  // ! occupancy can be a double for excited states WFN
      }


// ================   D E T E R M I N E    t h e   N U M B E R   o f  =======================*/
/*                         W F X ( W F N )    C O R E    e l e c t r o n s                  */
// (accounting simultaneously for potential CHARGES and PSEUDO
      coreElecWFN = 0 ;// deprecated 
    //coreElecWFN = 2 * coreAONumber - elecNumb + WFNelecNumb + wf_netCharge();


/* =========    TEST of COMPATIBILITY with CHARGE specified by the user   == */
// if and only if the WFN is activated and then when the user
// supplied manually in the param.igm file the value of the net CHARGE:

/* D E P R E C A T E D
      if (isWFNmodeActivated())
      {
          // nbTHEOR e- - CHARGE - PSEUDO = WFN e-
          // --> CHARGE = [nbTHEOR e- - WFN e-] - PSEUDO, with PSEUDO >= 0
          // --> CHARGE <= [nbTHEOR e- - WFN e-]
          if (params.CHARGE > (elecNumb - WFNelecNumb) )
            {
            std::cerr << std::endl;
            std::cerr << "[ERROR] The specified CHARGE should be less than or equal to: " << (elecNumb - WFNelecNumb)  << std::endl;
            std::cerr << "[ERROR] The program will now exit." << std::endl;
            exit(EXIT_FAILURE);
            }
       
          double chargeMax = (WFNelecNumb + params.CHARGE) /2;
          if (fabs(params.CHARGE) > chargeMax)  // !! the user specified a too large positive/negative CHARGE 
          {
            std::cerr << std::endl;
            std::cerr << "[ERROR] specified CHARGE too large (in absolute values): " << params.CHARGE  << std::endl;
            std::cerr << "[ERROR] The program will now exit." << std::endl;
            exit(EXIT_FAILURE);
          } // end of if fabs(params.CHARGE) > chargeMax)
       
       // =========    TEST of COMPATIBILITY PSEUDO with CHARGE specified by the user   == //
       // Remind that PSEUDO = f(CHARGE) = elecNumb - WFNelecNumb - params.CHARGE
       // --> if the CHARGE is correct, the PSEUDO should not be an odd number !
          if ( int(elecNumb - WFNelecNumb - params.CHARGE)%2 == 1)  // !! the user specified a WRONG positive/negative CHARGE 
          {
            std::cerr << std::endl;
            std::cerr << "[ERROR] The specified CHARGE: " << params.CHARGE << ", leads to a suspicious PSEUDOPOTENTIAL" << std::endl;
            std::cerr << "[ERROR] described by an odd number of electrons ..." << std::endl;
            std::cerr << "[ERROR] The program will now exit. Check the CHARGE in param.igm." << std::endl;
            std::cerr << "[ERROR] Contact the authors for more details." << std::endl;
            exit(EXIT_FAILURE);
          } // end of if ( int(elecNumb - WFNelecNumb - params.CHARGE)%2 == 1)

        // =========    TEST of COMPATIBILITY with CHARGE specified by the user   == //
        // Remind that: coreElecWFN = CORE_Total - Nb elec Neutre + WFN elec + CHARGE 
        //              must be positive or zero  !!                               
        // --> if the CHARGE is correct, the coreElecWFN must be >=0                
           
          if ( coreElecWFN < 0)  // !! the user specified a WRONG positive/negative CHARGE 
          {
            std::cerr << std::endl;
            std::cerr << "[ERROR] The specified CHARGE: " << params.CHARGE << ", leads to a negative number of CORE electrons !" << std::endl;
            std::cerr << "[ERROR] The program will now exit. Check the CHARGE in param.igm." << std::endl;
            exit(EXIT_FAILURE);
          } // end of if ( coreElecWFN < 0)   
      } // end of if (isWFNmodeActivated)

*/


/* ================  C O U N T    W F X / W F N   C O R E     M O    a n d    E L E C T R O N S    ==================*/
/*                             L E F T    i n     B A F   c a l c u l a t  i o n s                   */
// (let's recall that MO in WFX(WFN) can be either doubly, or singly, or partially occupied, we don't know in advance)

/* DEPRECATED
   double  coreElecToRead = coreElecWFN;       
   // loop  over the WFN MO until this number is reached,
   // be the WFN obtained by a restricted or an unrestricted calculation !
 
   coreMOWFN        = 0; // // NCISolver unsigned int variable (see NCISolver.h)

   // JC_2_6_22 update for avoiding sign-compare warning
   unsigned int imo = 0;
   while ( (coreElecToRead > 0) and (imo<molecularOrbitals.size()) )
     {
        coreElecToRead   = coreElecToRead - round(molecularOrbitals[imo].occupancy);
        imo              = imo + 1;
     } // end of  while (elecNumberToRead > 0)
    coreMOWFN  = imo; // obtained in the range [0: ] (0 for H2 for instance)
                      // singly or doubly occupied
*/

/*  =============  MO number with occupancy > 0.1  == */
//  for display
    MOoccupied=0; // NCISolver variable (see NCISolver.h) 

    // LOOP over all MO read from WFN
    for( unsigned int imo=0 ; imo< molecularOrbitals.size() ; ++imo)
      {
        if (molecularOrbitals[imo].occupancy>=0.1) 
           {        
             MOoccupied  = MOoccupied  + 1;                                
             
           }
    } // end of for( unsigned int imo=0 ; imo< molecularOrbitals.size()


} // end of setCOREMOandELEC() ...........................................................................................

void NCISolver::setChemFormula()
{

/*========== G e t   t h e   c h e m i c a l   f o r m u l a  */

// loop over all atoms of the system 

    // Iterator used to store the position 
    // of searched element
    std::vector<int>::iterator it;
    int index;

    unsigned int nbAtoms = data->getNbAtom();
    for (unsigned int iatom=0; iatom<nbAtoms; iatom++)
      {
        it = std::find (atomTypeList.begin(), atomTypeList.end(), data->atomTypes[iatom]); 
        // if this atom type is not already present in the atomType List : 
        if (it == atomTypeList.end())
           { // complete the atomType list: its a new atomtype !
             atomTypeList.push_back(data->atomTypes[iatom]);
             atomTypeNb.push_back(1); 
           }
        else 
           {
             index = it - atomTypeList.begin(); // convert the iterator to an integer  (distance between begin and it)
             atomTypeNb[index] = atomTypeNb[index] + 1;
           }


      }

} // end of setChemFormula() ......................................................................................



void NCISolver::IGM(double** chi, double** phi,
                    double* dx, double* dy, double* dz, double** gradPrim,
                    double &deltagIntraCURRENT, double &deltagInterCURRENT, double &qgIntraCURRENT, double &qgInterCURRENT)
// chi : input --> primitive array
// phi : input --> double array of MO (first index = MO index, second index = 0 - 10 = MO, first derivative, ...)
// dx,dy,dz input --> array of positions of current grid node relative to every atom
// gradPrim : output --> Gradient based partition : contribution of each primitive to the ED gradient 2 Dimensions [iprimit][0,1,2]
// deltagIntraCURphi[imo]RENT : output --> the deltagIntra value for the current node
// deltagInterCURRENT : output --> the deltagInter value for the current node
// qgIntraCURRENT : output --> the qgIntra value for the current node
// qgInterCURRENT : output --> the qgInter value for the current node

// !! this routine does not directly depend on dgscaled option (already taken into account
// !! through phi[imo] which accounts for every primitives if dgscaled option is enabled
// !! even though FRAG1+FRAG2 < whole system in that case)
// !! otherwise, if dgscaled option is disabled, if FRAG1+FRAG2 < whole system then
// !! phi[imo] only accounts for primitives of FRAG1+FRAG2
// !! For PAULI SELF calculations, the whole system is considered

{
// LOCAL VARIABLE
double gg[3]         = {0.0}; // real ED gradient of the (FRAG1+FRAG2) system or of the whole system
                              // depending of the dgscaled option
                              // !! if dgscaled option enabled: total rho and ED grad and phi[imo] have been considered before
                              // !! even if FRAG1 + FRAG2 <  whole system
                              // thus, dgscaled option only impacts the calculation of cpsi through phi[imo]


/**/
/* ===============    Equation (11) of our paper in Chem. Phys. Chem. 2018 page 4 : primitive gradient =========== */
/*                                    G R A D I E N T    B A S E D    P A R T I T I O N                           */


                  // ! eric: IGM model compute grad contrib of primitive ipri
                  // !       --> requires looping over all MO pre-calculated above and LIMITED to fragments 1 and 2 ! 
                  //             since the IGM approach is based on fragment definitions
                  // !           even though dgscaled option is enabled 
                  //             (dgscaled option only impacts total rho and total ED grad and total phi[imo])
              for(unsigned int ipria=0; ipria<fragNbPrim; ++ipria)  // Note that fragNbPrim is defined in setPrimList()
                                                                    // = fragment primitives only if FRAG1 / FRAG2 defined
                                                                    // or allPrimitives if no fragment defined
                {  unsigned int ipri = fragPrim[ipria]; // ipri returned in the range[0:nbprim-1] 
                   double cpsi=0.0;

                  //eric : IGM model, x,y,z comp. of each primitive gradient
                  // INITIALIZATIONS
                  gradPrim[ipri][0]=0.0;
                  gradPrim[ipri][1]=0.0;
                  gradPrim[ipri][2]=0.0;


                   // the following quantity cpsi has to be computed
                   // only when one of the primitive gradient is different from 0
                   if ( ( ( std::abs(chi[ipri][1]) > cutoff_gradpri ) or
                          ( std::abs(chi[ipri][2]) > cutoff_gradpri )
                        ) or
                          ( std::abs(chi[ipri][3]) > cutoff_gradpri )
                      )
                     {   
                      // loop over ALL MOs calculated with primitives limited to FRAG1 and FRAG2 if dgscaled option in disabled
                      // else if dgscaled option is enabled, MOs habe been calculated with all primitives
                      for( unsigned int imo=0 ; imo< molecularOrbitals.size() ; ++imo)
                         {
                           cpsi+= molecularOrbitals[imo].occupancy*molecularOrbitals[imo].coefficients[ipri] * phi[imo][0];
                         } // ! end of loop over imo
                     } // end of the test of chi[ipri][1], chi[ipri][2],chi[ipri][3]
                   cpsi*=2; // comes from the ED derivative  (for instance, [(xi)^2]' = 2 xi  xi')
                   gradPrim[ipri][0]= cpsi*chi[ipri][1];
                   gradPrim[ipri][1]= cpsi*chi[ipri][2];
                   gradPrim[ipri][2]= cpsi*chi[ipri][3];

                } // !end over ipria

/*======================  I G M    M O D E L   ==========================================================*/

                  //! eric: IGM model, gathering primitive gradients lying on the left
                  //! gradPrim      of the current examined node, and separately, gathering
                  //!       primitive gradients lying on the right of current  node
                  //!       --> all primitives belonging to a given atom will be put
                  //!           in the same set, whether they have a positive or negative ED slope here !

              double ggLeft[3]     ={0.0};
              double ggLeftA[3]    ={0.0};
              double ggLeftB[3]    ={0.0};
              double ggRigh[3]     ={0.0};
              double ggRighA[3]    ={0.0};
              double ggRighB[3]    ={0.0};

              double ggIGM[3]      ={0.0};
              double ggIGMInter[3] ={0.0};
              double ggAB[3]       ={0.0};



              // get primitive centers
              unsigned int* icenter = wf_primitive_centers();

              // =====================================================
              // LEFT   RIGHT   tests
              // =====================================================
              for(unsigned int ipria=0; ipria<fragNbPrim; ++ipria)
                {  unsigned int jpri = fragPrim[ipria]; // jpri returned in the range[0:nbprim-1]
                  // ! only electron density of fragment1 AND fragment 2 are considered
                  // if FRAG1+FRAG2 = the total system, then, all the ED is considered
                  // BEWARE : dx[jpriCenter] expressed with jpriCenter in the range [0 - nb Atoms - 1]
                  // but icenter[jpri] returns an index in the range [1 - nb Atoms]
                  unsigned int jpriCenter = icenter[jpri]-1;


                  // ! x component ---------------------------------------------
                  if(dx[jpriCenter] >= 0.0)
                    {
                      if(inMoleculeA[jpri])
                        {
                          //  ! FRAGMENT A
                          ggLeft[0] += gradPrim[jpri][0];
                          ggLeftA[0]+= gradPrim[jpri][0];
                        }
                      else if (inMoleculeB[jpri])
                        {
                          //  ! FRAGMENT B
                          ggLeft[0] += gradPrim[jpri][0];
                          ggLeftB[0]+= gradPrim[jpri][0];
                        }
                    } // end if dx >=0
                  else
                    {
                      if(inMoleculeA[jpri])
                        { 
                          //  ! FRAGMENT A
                          ggRigh[0] += gradPrim[jpri][0];
                          ggRighA[0]+= gradPrim[jpri][0];
                        }
                      else if (inMoleculeB[jpri])
                        { 
                          //  ! FRAGMENT B
                          ggRigh[0] += gradPrim[jpri][0];
                          ggRighB[0]+= gradPrim[jpri][0];
                        }
                    } // ! if dx
                     
                      // ! y component ---------------------------------------------
                  if(dy[jpriCenter] >= 0.0)
                    {
                      if(inMoleculeA[jpri])
                        {
                          //  ! FRAGMENT A
                          ggLeft[1] += gradPrim[jpri][1];
                          ggLeftA[1]+= gradPrim[jpri][1];
                        }
                      else if (inMoleculeB[jpri])
                        {
                          //  ! FRAGMENT B
                          ggLeft[1] += gradPrim[jpri][1];
                          ggLeftB[1]+= gradPrim[jpri][1];
                        }
                    } // end if dy >=0
                  else
                    {
                      if(inMoleculeA[jpri])
                        {
                          //  ! FRAGMENT A
                          ggRigh[1] += gradPrim[jpri][1];
                          ggRighA[1]+= gradPrim[jpri][1];
                        }
                      else if (inMoleculeB[jpri])
                        {
                          //  ! FRAGMENT B
                          ggRigh[1] += gradPrim[jpri][1];
                          ggRighB[1]+= gradPrim[jpri][1];
                        }
                    } // ! if dy
              
                      // ! z component ---------------------------------------------
                  if(dz[jpriCenter] >= 0.0)
                    {
                      if(inMoleculeA[jpri])
                        {
                          //  ! FRAGMENT A
                          ggLeft[2] += gradPrim[jpri][2];
                          ggLeftA[2]+= gradPrim[jpri][2];
                        }
                      else if (inMoleculeB[jpri])
                        {
                          //  ! FRAGMENT B
                          ggLeft[2] += gradPrim[jpri][2];
                          ggLeftB[2]+= gradPrim[jpri][2];
                        }
                    } // end if dz >=0
                  else
                    {
                      if(inMoleculeA[jpri])
                        {
                          //  ! FRAGMENT A
                          ggRigh[2] += gradPrim[jpri][2];
                          ggRighA[2]+= gradPrim[jpri][2];
                        }
                      else if (inMoleculeB[jpri])
                        {
                          //  ! FRAGMENT B
                          ggRigh[2] += gradPrim[jpri][2];  
                          ggRighB[2]+= gradPrim[jpri][2];
                        }
                    } // ! if dz

	        } // ! end  over primitives of FRAG1 + FRAG2

	      /* ======  IGM model applied solely to FRAG1+FRAG2 (may be a subset of the whole system!) ===== */
	      //
	      // every interaction within (FRAG1+FRAG2) system is cancelled (atoms outside FRAG1-2 are not considered)
              // ==> INTRA and INTER interaction of FRAG1+FRAG2 system are cancelled
              ggIGM[0] = std::abs(ggLeft[0]) + std::abs(ggRigh[0]);
              ggIGM[1] = std::abs(ggLeft[1]) + std::abs(ggRigh[1]);
              ggIGM[2] = std::abs(ggLeft[2]) + std::abs(ggRigh[2]);
	      
              // every interactions within  the system represented by the 2 fragments FRAG1+FRAG2 are allowed 
              // = the true gradient, but limited to FRAG1+FRAG2
              gg[0] = ggLeftA[0] + ggRighA[0] + ggLeftB[0] + ggRighB[0];
              gg[1] = ggLeftA[1] + ggRighA[1] + ggLeftB[1] + ggRighB[1];
              gg[2] = ggLeftA[2] + ggRighA[2] + ggLeftB[2] + ggRighB[2];
              ggAB[0] = std::abs(gg[0]);
              ggAB[1] = std::abs(gg[1]);
              ggAB[2] = std::abs(gg[2]); 

              // only interactions between the 2 fragments FRAG1...FRAG2 are cancelled
	      ggIGMInter[0] =
		std::abs( ggLeftA[0] + ggRighA[0] ) +
		std::abs( ggLeftB[0] + ggRighB[0] );
		  
	      ggIGMInter[1] =
		std::abs( ggLeftA[1] + ggRighA[1] ) +
		std::abs( ggLeftB[1] + ggRighB[1] );

	      ggIGMInter[2] =
		std::abs( ggLeftA[2] + ggRighA[2] ) +
		std::abs( ggLeftB[2] + ggRighB[2] );

              //! eric: IGM model applied solely to FRAG1+FRAG2 (may be a subset of the whole system!)
     /*       double grad2IGM      = // every interaction inside (FRAG1+FRAG2) system is cancelled 
                                    // (atoms outside FRAG1-2 are not considered)
                ggIGM[0]*ggIGM[0] +
                ggIGM[1]*ggIGM[1] +
                ggIGM[2]*ggIGM[2];
     */
              double gradIGM = getNormOfVector(ggIGM);

     /*       double grad2    = // every interactions inside the 2 fragments FRAG1...FRAG2 are allowed   
                ggAB[0]*ggAB[0] +
                ggAB[1]*ggAB[1] +
                ggAB[2]*ggAB[2];
     */
              double grad = getNormOfVector(ggAB);

/*            double grad2IGMInter = // only interactions between the 2 fragments FRAG1...FRAG2 are cancelled
                                      // if only one fragment is defined: no interaction are canceleed => real gradient
                ggIGMInter[0]*ggIGMInter[0] +
                ggIGMInter[1]*ggIGMInter[1] +
                ggIGMInter[2]*ggIGMInter[2];
*/
              double gradIGMInter = getNormOfVector(ggIGMInter);

               // ! eric: IGM model
	      //! the output will now be the intra and inter contributions:
              deltagIntraCURRENT      = gradIGM                  - gradIGMInter; // INTRA

              if (deltagIntraCURRENT < 0) // case of INTERMOLECULAR interaction outside the interaction corridor
                                          // this may exceptionnaly occur at a point, outside the interaction corridor
                                          // where FRAG2 and FRAG1 are not too far (for instance near 
                                          // an atom of FRAG1 bound to FRAG2)
                                          // and this is due to the penetration of an ED of FRAG2 with an ED of FRAG1 
                                          // described with a p,d,f,... (not a s); for instance : FRAG1 = atom A, 
                                          // FRAG2 = atom B, x at left
                                          //         
                                          //             -------x-------A-----------------B------------------>
                                          // ==> at point x located  close to A in the region where a p orbital of A describes 
                                          //     a decreasing ED toward right direction (negative slope in the core region), 
                                          //     a "s" orbital of B
                                          //     at x will have a positive slope and then generate A/B contragradience ...
                                          //     that will be detected by deltagIntra ... this is the only case, and in that
                                          //     case, deltagIntra will take a negative value, otherwise it is always positive !
                {
                      deltagIntraCURRENT = 0.0; // since it has nothing to do with INTRA FRAGMENT contragradience 
                }


 
              deltagInterCURRENT      = gradIGMInter - grad;
                                                                    // INTER, mechanically, by construction, positive, and only
                                                                   // corresponds to inter fragment interaction
                                                                   // It may correspond to inter fragment interaction outside the 
                                                                   // interaction corridor ..., very close to an atom,
                                                                   // for the same reason explained above, but this time (in 
                                                                   // contrast with above),
                                                                   // deltaginter will be positive and this case cannot be detected
                                                                   // from the normal case (intermolecular interaction in 
                                                                   // the interaction corridor)
                                                                   // by testing the sign; this will only occur in core region 
                                                                   // for a small order of magnitude because it requires 
                                                                   // to consider, outside the interaction corridor,
                                                                   // the ED of the two atoms, one of which will be very small
                                                                   // because far from its atom nucleus !


                 qgIntraCURRENT      = (deltagIntraCURRENT+grad) / grad;  // INTRA
                 qgInterCURRENT      =  gradIGMInter             / grad;  // INTER

                                               // Our definition of differences dg:
                                               // a) dgINTRA = gradRHOIGM      - gradRHOIGMINTER = gradRHOIGMINTRA - gradRHO
                                               // b) dgINTER = gradRHOIGMINTER - gradRHO
                                               // Our definition of ratios qg:
                                               // a) QgINTRA = gradRHOIGMINTRA / gradRHO
                                               // b) QgINTER = gradRHOIGMINTER / gradRHO
                                               //
                                               // 
                                               //       deltagINTER 
                                               //      <............>                                              
                                               //     -|------------|------------------------------|------------|---------------->
                                               //   gradRHO  gradRHOIGMINTER               gradRHOIGMINTRA   gradRHOIGM
                                               //     real   inter cancelled               intra cancelled   every interaction cancelled
                                               //     known        known                    unreachable        known
                                               //                                           with absolute
                                               //                                           values
                                               //                    <..........................................>                                        
                                               //                                    deltagINTRA
                                               //      <...........................................>
                                               //                        deltagINTRA as well
                                               //

} // end of IGM() ......................................................................................


// gradAtomHirsh: requires rhoFree and pregradFree already calculated:
void NCISolver::gradAtomHirsh(bool fullRHOGRAD, unsigned int threadID, 
                    double rho, double gradrho[3],
                    double*** dx, double*** dy, double*** dz, int ip, 
                    double* rhoFree, double *pregradFree, double** gradAtom, double* rhoAtom) {
// fullRHOGRAD : input --> true: rho and gradrho are for the whole system, else limited to fragments
// threadID    : input --> the current index of the thread : needed to access the dx,dy,dz quantities
// rho         : input --> FRAG1+FRAG2 ED or real rho (depend on isdgscaled option or isPauli option ) at the current grid point
// gradrho     : input -->  FRAG1+FRAG2 ED gradient or real gradient (depend on isdgscaled option) used in the Hirschfeld IGM method
// dx,dy,dz    : input --> array of positions of current grid node relative to every atom: needed to 
//                      calculate promolecular rho and gradrho
// ip          : input --> current position in the third nested dimension of the grid (currently crossed)
// rhoFree     : input --> the free promol ED for every atom (calculated for every atom)
// pregradFree : input --> SumOverBRho: a quantity common to the 3 components of the free promol ED grad for each atom
//                                     (calculated for every atom)
// gradAtom    : output --> Hirschfeld atomic Gradient partition : contribution of each ATOM to the ED gradient of FRAG1+FRAG2
//                                                              or of the whole system (depends on fullRHOGRAD option)
// rhoAtom     : output --> Hirschfeld Atomic Electron Density   : contribution of each ATOM to the ED of FRAG1+FRAG2
//                                                              or of the whole system (depends on fullRHOGRAD option)
//                                                              not employed so far


 // !!!! : if option fullRHOGRAD is true :    
 // !!!!     the total ED, total ED gradient will be used throughout the calculations here
 // !!!! : else :                                  
 // !!!!     the ED,ED gradient of FRAG1+FRAG2 (which can be the whole system) 
 // !!!!     will be calculated throughout the calculations here
 // !!!!   This option is necessary since locally, in this function, we have no idea of whether the full AO set has been
 // !!!!   computed or only the limited set corresponding to FRAG1+FRAG2


                    /* ===============     I  G  M  H     P A R T I T I O N   ========================== =================            */
                    // IGM using the Hirschfeld partion of the ED to access the atomic partition of the ED Gradient 
                    // instead of the Gradient Based Partition, 
                    // See Equation (9) of the paper by Tian Lu in 2022: DOI: 10.1002/jcc.26812
                    // Here, we apply this equation to FRAG1+FRAG2: condidering the Hirshfeld weight Wi
                    // deduced from the promolecular approximation applied to FRAG1+FRAG2 
                    // such that sum_i Wi = 1 upon considering FRAG1+FRAG2 !
                    // =============== =============== =============== =============== =============== =============== =============== 



                    // ====================================================================== //
                    //   P R O M O L E C U L A R       Q U A N T I T I E S
                    // ====================================================================== //
                    // -A- compute the promolecular ED at the current NODE
                    // -B- compute the first derivative of the free ED for atom i
                      double rhopro = 0.0; // for FRAG1+FRAG2                     
                      unsigned int nbAtoms = data->getNbAtom();

                    // Declaration/Initialization of gradpro
                    // = promolecular gradient components needed for the Hirschfeld calculation of IGM
                      double* gradpro = new double[3]; // promol gradient for x,y,z for FRAG1+FRAG2 or for the whole system
                      gradpro[0] = 0.0;
                      gradpro[1] = 0.0;
                      gradpro[2] = 0.0;

                
                    // atomic promolecular gradient components needed for the Hirschfeld calculation of IGM
                      double** gradFree = new double*[nbAtoms];
                      for (unsigned int iat=0; iat<nbAtoms; ++iat) { // calculated for all atoms in any case
                         gradFree[iat] = new double[3]; //for x,y,z components
                         gradFree[iat][0] = 0.0;
                         gradFree[iat][1] = 0.0;
                         gradFree[iat][2] = 0.0;
                      }

                     bool atomConsidered;
                     for( unsigned int iat=0; iat < nbAtoms ; ++iat){
                         atomConsidered = false;
                         // !! if rho and grad passed as parameters are not the ED and gradrho of the full system
                         // !! (i.e., limited to two subfragments)
                         // !! then rhopromolecular and gradpromolecular must be calculated to represent the subsystem, not the full system
                         if (fullRHOGRAD)
                            { atomConsidered = true;}
                         else 
                            {  // in other cases, a basisSize = fragNbPrim calcQMAO has been required previously
                               // then only atoms of FRAG1+2 have to be considered in Hirshfeld
                               if ( isInMoleculeA(iat+1) or isInMoleculeB(iat+1) )// take care: isInMolecule A/B
                                                                                 // requires an index parameter 
                                                                                 // in the range [1:nbAtoms]
                                 { atomConsidered = true;}  
                               // end of if ( isInMoleculeA(iat+1) or isInMoleculeB(iat+1)
                            } // end of else of if (isdgSCALED() or isPAuli())) 
                        
   
                           if (atomConsidered) 
                            {
                             /* PROMOLECULAR  rho processing */
                             rhopro += rhoFree[iat];

                             /* PROMOLECULAR ED Gradient processing */                            
                             gradFree[iat][0] = -dx[threadID][ip][iat] * pregradFree[iat];// take care: dx must be expressed 
                                                                                          // in the appropriate frame
                                                                                          // (either cylindrical or cartesian)
                             gradFree[iat][1] = -dy[threadID][ip][iat] * pregradFree[iat];
                             gradFree[iat][2] = -dz[threadID][ip][iat] * pregradFree[iat];
                              /* update the total gradient components x y z */
                             gradpro[0] =  gradpro[0] + gradFree[iat][0];
                             gradpro[1] =  gradpro[1] + gradFree[iat][1];
                             gradpro[2] =  gradpro[2] + gradFree[iat][2];
                            } // end of if (atomConsidered)

                      } // end of loop over atoms
                     // ......... E N D   O F    P R O M O L E C U L A R  C A L C U L A T I O N S .............



                    // =======================================================
                    //         H I R S H F E L D      T R E A T M E N T
                    // =======================================================
                    // compute the atomic ED gradient according to the Hirschfeld partition
                    double rhoprosquared = rhopro*rhopro;
                    for(unsigned int iat=0; iat<nbAtoms; ++iat)
                      { 
                         // initialization of the Hirschfeld atomic ED gradient mixing QM and PROMOLECULAR treatments
                         gradAtom[iat][0] = 0.0;
                         gradAtom[iat][1] = 0.0;
                         gradAtom[iat][2] = 0.0;

                         // initialization of the Hirschfeld atomic ED
                         rhoAtom[iat] = 0.0; 

                         // if dgscaled option enabled : allPrim considered -> the whole real system here 
                         //                                                    has to be considered to compute rhopro and gradpro
                         // else only ED and ED grad coming from FRAG1+FRAG2 will be considered (which corresponds to FRAG1+2 <= ALL)
                         atomConsidered = false;
                         if (fullRHOGRAD)
                            { atomConsidered = true;} // we know that rho_total has been computed
                         else
                            { // in other cases, a basisSize = fragNbPrim calcQMPROP has been required previously 
                              //                               including the second calcQMPROP (to get deltag here)
                              // then only atoms of FRAG1+2 have to be considered in Hirshfeld here
                              if ( isInMoleculeA(iat+1) or isInMoleculeB(iat+1) )// take care: isInMolecule A/B
                                                                                 // requires an index parameter 
                                                                                 // in the range [1:nbAtoms]
                                 { atomConsidered = true;}
                              // end of if ( isInMoleculeA(iat+1) or isInMoleculeB(iat+1)
                            } // end of else of if (isdgSCALED()) 


                         if (atomConsidered)  // not really necessary here, since quantities calculated hereafter
                                             // gradAtom[iat][ix] will only serve to the FRAG1/FRAG2 IGM scheme
                           {
                              // =======================================================
                              //         H I R S H F E L D     E D     
                              // =======================================================
                              rhoAtom[iat] = rhoFree[iat] * rho / rhopro;  // = wi * rho



                              // =======================================================
                              //         H I R S H F E L D     E D     G R A D I E N T
                              // =======================================================
                              for (unsigned ix=0; ix<3; ++ix) 
                                {
                                   double term1, term2, term3; 
                                   term1 = rhoFree[iat] * gradrho[ix] / rhopro;
                                   term2 = gradFree[iat][ix] * rho/rhopro;
                                   term3 = rho * rhoFree[iat] * gradpro[ix] / rhoprosquared;
                                   gradAtom[iat][ix] = term1 + term2 - term3;  // correct treatment
//                                 gradAtom[iat][ix] = term1 + (-term2 + term3);    // error by Tian Lu
                                }// end of loop over x,y,z
                           } // end of if (atomConsidered)  

                      }// end of loop over atoms  

                    // Release of memory
                    delete[] gradpro; 

                    for (unsigned int iat=0; iat<nbAtoms; ++iat) {
                       delete[] gradFree[iat];
                    }
                    delete[] gradFree;


} // end of  gradAtomHirsh routine

void NCISolver::rhoFree__pregradFree_sumSquareBRho(double* dist, 
                                                   double* rhoFree, double* pregradFree, double* sumSquareBRho) {
// P R O M O L E C U L A R
// dist: input --> distance between the current node and every atom       (dist given in bohr)
// rhoFree : output --> ED for each individual atom of the WHOLE SYSTEM (not limited to FRAG1+FRAG2)
//                      at the promolecular level                                          
// pregradFree : output --> sumBRhoOverR needed for the calculation of the ED gradient for each atom of the WHOLE SYSTEM
//                          at the promolecular level , pregradFree=sumBRhoOverR is a scalar
// sumSquareBRho : output --> sumB^2Rho

                      double rho1, rho2, rho3;
                      int atType;
                      unsigned int nbAtoms = data->getNbAtom();

                      for( unsigned int iat=0; iat < nbAtoms ; ++iat){

                              /* PROMOLECULAR  rho processing */
                              /* three exponential contributions */
                              atType = data->atomTypes[iat];
                              rho1= A1[atType] * std::exp( -B1[atType] * dist[iat]); 
                              rho2= A2[atType] * std::exp( -B2[atType] * dist[iat]);
                              rho3= A3[atType] * std::exp( -B3[atType] * dist[iat]);
                              rhoFree[iat] = rho1    + rho2    + rho3;


                              /* pregradFree processing */                            
                              // this quantity is common to the 3 ED gradient components for a given atom 
                              double multInvR  = 1.0 / dist[iat];
                                   /* Summing rho * B = prefigure the promol first derivative of the atomic density */
                              double sumBRho = B1[atType] * rho1 + B2[atType] * rho2 + B3[atType] * rho3;
                                   /* Operation used three times */
                              pregradFree[iat] = multInvR * sumBRho;

                              sumSquareBRho[iat] = B1[atType]*B1[atType] * rho1 + 
                                                   B2[atType]*B2[atType] * rho2 + 
                                                   B3[atType]*B3[atType] * rho3;
                      } // end of loop over atoms
} // end of rhoFree__pregradFree_sumSquareBRho

void NCISolver::rhoFree__pregradFree(double* dist, double* rhoFree, double* pregradFree) {
// P R O M O L E C U L A R
// dist: input --> distance between the current node and every atom       (dist given in bohr)
// rhoFree : output --> ED for each individual atom of the WHOLE SYSTEM (not limited to FRAG1+FRAG2)
//                      at the promolecular level                                          
// pregradFree : output --> sumBRhoOverR needed for the calculation of the ED gradient for each atom of the WHOLE SYSTEM
//                          at the promolecular level , pregradFree=sumBRhoOverR is a scalar

                      double rho1, rho2, rho3;
                      int atType;
                      unsigned int nbAtoms = data->getNbAtom();

                      for( unsigned int iat=0; iat < nbAtoms ; ++iat){

                              /* PROMOLECULAR  rho processing */
                              /* three exponential contributions */
                              atType = data->atomTypes[iat];
                              rho1= A1[atType] * std::exp( -B1[atType] * dist[iat]); 
                              rho2= A2[atType] * std::exp( -B2[atType] * dist[iat]);
                              rho3= A3[atType] * std::exp( -B3[atType] * dist[iat]);
                              rhoFree[iat] = rho1    + rho2    + rho3;

                              /* pregradFree processing */                            
                              // this quantity is common to the 3 ED gradient components for a given atom 
                              double multInvR  = 1.0 / dist[iat];
                                   /* Summing rho * B = prefigure the promol first derivative of the atomic density */
                              double sumBRho = B1[atType] * rho1 + B2[atType] * rho2 + B3[atType] * rho3;
                                   /* Operation used three times */
                              pregradFree[iat] = multInvR * sumBRho;
                      } // end of loop over atoms

} // end of rhoFree__pregradFree  

double NCISolver::rhopromolFRAG1FRAG2(double* rhoFree) {
// rhoFree  : input --> the free promol ED for every atom (calculated previously for every atom in the WHOLE system)
// RETURN: the ED for the FRAG1+FRAG2 system.

                    // ====================================================================== //
                    //   P R O M O L E C U L A R        D E N S I T Y       
                    // ====================================================================== //
                    //  compute the promolecular ED at the current NODE
                     unsigned int nbAtoms = data->getNbAtom();
                     double rhopro = 0.0;
                     for( unsigned int iat=0; iat < nbAtoms ; ++iat){
                         if ( isInMoleculeA(iat+1) or isInMoleculeB(iat+1) )
                            {
                             //  ! FRAGMENT A and B  only (can be the whole system eventually if A+B = all)

                             /* PROMOLECULAR  rho processing */
                             rhopro += rhoFree[iat];
                            } // end of if ( isInMoleculeA(iat+1) or isInMoleculeB(iat+1) )
                      } // end of loop over atoms
                     // ......... E N D   O F    P R O M O L E C U L A R  C A L C U L A T I O N S .............
                      return rhopro;
} // end of  rhopromolFRAG1FRAG2 routine


void NCISolver::rhogradlap(unsigned int at1, unsigned int at2, double* rhoFree, double* pregradFree, double* sumSquareBRho,
                           double* dx, double* dy, double* dz, double* dist,
                           double &rhopro, double &normgradpro, double &lap) {
// ========= PROMOLECULAR MODE ONLY ================ //
// at1 : input --> atom index lowerlimit, 0-based index
// at2 : input --> atom index upperlimit, 0-based index
// rhoFree  : input --> the free promol ED for every atom (calculated previously for every atom in the WHOLE system) 
// pregradFree : input --> prefactor to later on compute the atomic gradients (promolecular)
// sumSquareBRho : input --> factor = sum_i B_i^2 rho_i
// dx,dy,dz : input --> vector between current grid node and atoms
// dist     : input -_> distance between current grid node and atoms
// rhopro   : output --> ED
// normgradpro  : output --> norm of the gradient of ED
// lap      : output --> Laplacian of the hessien of ED

    // ====================================================================== //
    //   P R O M O L E C U L A R       Q U A N T I T I E S
    // ====================================================================== //
    // -A- compute the promolecular ED at the current NODE
    // -B- compute the first derivative of ED
    // -C- compute the hessien of ED

    // initi.
      rhopro     = 0.0;

    // Initialization of gradpro
      double gradpro[3];
      gradpro[0] = 0.0;
      gradpro[1] = 0.0;
      gradpro[2] = 0.0;

    // Initialization of ED Hessian   
      double hess[3][3]; // a static ARRAY 
      hess[0][0] = 0.0;
      hess[1][1] = 0.0;
      hess[2][2] = 0.0;
      hess[0][1] = 0.0;
      hess[0][2] = 0.0;
      hess[1][2] = 0.0;

    // Intermediate quantities
      double squareX, squareY, squareZ, d2;


     for( unsigned int iat=at1; iat <= at2 ; ++iat){

             // .............................................. //
             // ........ PROMOLECULAR  rho processing ........ //
             // .............................................. //
             rhopro += rhoFree[iat];

             // .............................................. //
             // ........ PROMOLECULAR  grad rho .............. //
             // .............................................. //
              /* update the total gradient components x y z */
             gradpro[0] =  gradpro[0] - dx[iat] * pregradFree[iat]; // pregradFree_i = 1/ri x sum_j Bjrho_j
             gradpro[1] =  gradpro[1] - dy[iat] * pregradFree[iat];
             gradpro[2] =  gradpro[2] - dz[iat] * pregradFree[iat];
      } // end of loop over atoms
      normgradpro = getNormOfVector(gradpro);


             // .............................................. //
             // ........ PROMOLECULAR  hessien ............... //
             // .............................................. //
    double multInvR;
    double squareMultInvR;
//    double opti2;
    
    for( unsigned int iat=at1; iat <= at2 ; ++iat){
       // preparing constants              //
       multInvR       = 1.0 / dist[iat];
       squareMultInvR = multInvR*multInvR;            
       squareX        = dx[iat]*dx[iat];
       squareY        = dy[iat]*dy[iat];
       squareZ        = dz[iat]*dz[iat];
       d2             = dist[iat]*dist[iat];
       // Operation used three times //
//       opti2  = pregradFree[iat] + sumSquareBRho[iat];
//       opti2 *= squareMultInvR;
 
       // Updating hessian diagonal values //
       hess[0][0]+=   squareMultInvR * ( (squareX  - d2) * pregradFree[iat] + squareX * sumSquareBRho[iat]);
       hess[1][1]+=   squareMultInvR * ( (squareY  - d2) * pregradFree[iat] + squareY * sumSquareBRho[iat]);
       hess[2][2]+=   squareMultInvR * ( (squareZ  - d2) * pregradFree[iat] + squareZ * sumSquareBRho[iat]);


       // Updating hessian off-diagonal values //
//       hess[0][1]+=   dx[iat] * dy[iat] * opti2;
//       hess[0][2]+=   dx[iat] * dz[iat] * opti2;
//       hess[1][2]+=   dy[iat] * dz[iat] * opti2;

    } // end of loop over atoms

    // complete hessien
//    hess[1][0] = hess[0][1];
//    hess[2][0] = hess[0][2];
//    hess[2][1] = hess[1][2];

    lap = hess[0][0]+hess[1][1]+hess[2][2];

// since the ED hessien is symmetrical, laplacian = trace, no need to diagonalize
//// make the matrix tridiagonal
//   double d[3]; // diagonal elements after tridiagonalization
//   double e[3]; // subdiagonal elements after tridiagonalization
//   // hessian = the matrix to be diagonalized
//   tred2(hess, d, e);
//
//// diagonalize
//// hess = eigenVectors = result
//// d = eigenvalues in ascending order = result
//// e = (subdiagonal elements, has been destroyed at the end)
//// hessian = input and output = the eigenvectors associated with eigenvalues
//   tql2(hess, d, e);
//
//  // compute Laplacian            
//    lap = d[0] + d[1] + d[2];
//
//std::cout << "DEBUG  lap = " << lap  << std::endl;


} // end of rhogradlap routine


double NCISolver::rhoHIRSHFRAG1FRAG2(double rhoQM, double* rhoFree) {
// rhoQM    : input --> the true rho obtained by means of QM for the WHOLE system 
// rhoFree  : input --> the free promol ED for every atom (calculated previously for every atom in the WHOLE system) 
// RETURN   : estimated QM ED for the FRAG1+FRAG2 system from HIRSHFELD partition
                    
                    // ====================================================================== //
                    //   T O T A L      P R O M O L E C U L A R        D E N S I T Y       
                    // ====================================================================== //
                    //  compute the total promolecular ED at the current NODE
                     unsigned int nbAtoms = data->getNbAtom();
                     double rhopro   = 0.0;
                     double rhoproAB = 0.0;
                     for( unsigned int iat=0; iat < nbAtoms ; ++iat){
                         rhopro += rhoFree[iat];

                         if ( isInMoleculeA(iat+1) or isInMoleculeB(iat+1) )
                            { 
                             rhoproAB += rhoFree[iat];
                            }
                     } // end of loop over atoms

           

                    // ====================================================================== //
                    //   F R A G 1  +   F R A G 2    e s t i m a t e d    Q M   E D        
                    // ====================================================================== //
                    //  estimates the QM ED for FRAG1+FRAG2 at the current NODE
                     return (rhoproAB/rhopro)*rhoQM;
} // end of rhoHIRSHFRAG1FRAG2



void NCISolver::gradAtomGBP(unsigned int nbPrim, unsigned int* prim, double** gradPrim, double** gradAtom) {
// nbPrim             input  --> Number of primitives forming the basis set          
// prim               input  --> Array of primitive indices (each returned index points toward the proper
// gradPrim : input --> Gradient based partition : contribution of each primitive to the ED gradient 2 Dimensions[iprimit][0,1,2]
// gradAtom : output --> GBP atomic Gradient partition : contribution of each ATOM (0_based index) to the ED gradient

    unsigned int nbAtoms = data->getNbAtom();

    // initialization for atomic decomposition
    for(unsigned int iat=0; iat<nbAtoms; ++iat)
     {
      gradAtom[iat][0] = 0.0; 
      gradAtom[iat][1] = 0.0;
      gradAtom[iat][2] = 0.0;

     } // end of for(unsigned int iat=0; iat<nbAtoms; ++iat)


    // compute the atomic gradients
    unsigned int* icenter = wf_primitive_centers();
    for(unsigned int ipria=0; ipria<nbPrim; ++ipria)
      {  unsigned int jpri = prim[ipria]; // jpri returned in the range[0:nbprim-1]
        // ! only electron density of fragment1 AND fragment 2 are considered (does not depend on dgscaled option)
        // if FRAG1+FRAG2 = the total system, then, all the ED is considered (like in the IBSI treatment)
        // BEWARE : jpriCenter in the range [0 : nb Atoms - 1]
        // but icenter[jpri] returns an index in the range [1 - nb Atoms]
        unsigned int jpriCenter = icenter[jpri]-1;
        
        // for THOSE primitives of FRAG1+FRAG2 only (depends on parameters nbPrim and prim)
       if ( isInMoleculeA(jpriCenter+1) or isInMoleculeB(jpriCenter+1) ) // take care: isInMoleculeA / B
                                                                           // requires an index parameter in the range [1:nbAtoms]
                                                                           // while jpriCenter in the range [0 : nb Atoms - 1]
                           
          {
           // compute A T O M I C    E D    G R A D I E N T       
            gradAtom[jpriCenter][0]+= gradPrim[jpri][0];
            gradAtom[jpriCenter][1]+= gradPrim[jpri][1];
            gradAtom[jpriCenter][2]+= gradPrim[jpri][2];
          } // end of if is FRAG1 or FRAG2

      } // end of for(unsigned int ipria=0; ipria<nbPrim; ++ipria)

} // end of  gradAtomGBP   routine

void NCISolver::IGMH(double** gradAtom, double &deltagIntraCURRENT, double &deltagInterCURRENT,
                                        double &qgIntraCURRENT, double &qgInterCURRENT)
// gradAtom : input --> Hirschfeld atomic ED Gradient partition : contribution of each ATOM to the ED gradient
// deltagIntraCURRENT : output --> the deltagIntra value for the current node
// deltagInterCURRENT : output --> the deltagInter value for the current node
// qgIntraCURRENT     : output --> the     qgIntra value for the current node
// qgInterCURRENT     : output --> the     qgInter value for the current node

// !! this procedure does not depend on dgscaled option
// !! if dgscaled option enabled: total rho and ED grad have been considered prior to IGMH
// !! even if FRAG1 + FRAG2 <  whole system
// !! then gradAtom already adapated to either FRAG1+FRAG2 or the whole system

{
              // DECLARATION/INITIALIZATION of local variables
              double ggA[3]        = {0.0}; // sum of atomic gradient for atoms in fragment A
              double ggB[3]        = {0.0}; // sum of atomic gradient for atoms in fragment B
              double ggIGM[3]      = {0.0}; // gradient with every interactions cancelled
              double ggAB[3]       = {0.0}; // gradient of fragment A and B with every interaction present
              double ggIGMInter[3] = {0.0}; // gradient with interaction between A and B cancelled


    
              // Start the IGM process by cancelling interactions between atoms or fragments
              unsigned int nbAtoms = data->getNbAtom();
              for(unsigned int iat=0; iat<nbAtoms; ++iat)
                { 
                  // ! x component ---------------------------------------------
                   if(isInMoleculeA(iat+1))  // take care: isInMoleculeA requires an index parameter in the range [1:nbAtoms]
                        {
                          //  ! FRAGMENT A
                          ggA[0]+= gradAtom[iat][0];
                          ggA[1]+= gradAtom[iat][1];
                          ggA[2]+= gradAtom[iat][2];
                        }
                   else if (isInMoleculeB(iat+1))
                        {
                          //  ! FRAGMENT B
                          ggB[0]+= gradAtom[iat][0];
                          ggB[1]+= gradAtom[iat][1];
                          ggB[2]+= gradAtom[iat][2];
                        }

                 // compute the gradient by cancelling every interaction between atoms of fragments A and B 
                 // take care: interactions between atoms other than those of A and B are not considered
                 ggIGM[0] = ggIGM[0] + std::abs(gradAtom[iat][0]);
                 ggIGM[1] = ggIGM[1] + std::abs(gradAtom[iat][1]);
                 ggIGM[2] = ggIGM[2] + std::abs(gradAtom[iat][2]); 

               } // end of loop over atoms

              // every interactions within  the system represented by the 2 fragments FRAG1+FRAG2 are allowed 
              // = the true gradient (like gg!) :
             
              ggAB[0] = std::abs(ggA[0] + ggB[0]);
              ggAB[1] = std::abs(ggA[1] + ggB[1]);
              ggAB[2] = std::abs(ggA[2] + ggB[2]);

              // only interactions between the 2 fragments FRAG1...FRAG2 are cancelled
	      ggIGMInter[0] = std::abs( ggA[0] ) + std::abs( ggB[0] );
              ggIGMInter[1] = std::abs( ggA[1] ) + std::abs( ggB[1] );
              ggIGMInter[2] = std::abs( ggA[2] ) + std::abs( ggB[2] );
              //! eric: IGM model applied solely to FRAG1+FRAG2 (may be a subset of the whole system!)
//              double grad2IGM      = // every interaction inside (FRAG1+FRAG2) system is cancelled 
//                                    // (atoms outside FRAG1-2 are not considered)
//                ggIGM[0]*ggIGM[0] +
//                ggIGM[1]*ggIGM[1] +
//                ggIGM[2]*ggIGM[2];

              double gradIGM = getNormOfVector(ggIGM);


//              double grad2AB       = // every interactions inside the 2 fragments FRAG1...FRAG2 are allowed   
//                ggAB[0]*ggAB[0] +
//                ggAB[1]*ggAB[1] +
//                ggAB[2]*ggAB[2];

              double gradAB = getNormOfVector(ggAB);

//              double grad2IGMInter = // only interactions between the 2 fragments FRAG1...FRAG2 are cancelled
//                                      // if only one fragment is defined: no interaction are canceleed => real gradient
//                ggIGMInter[0]*ggIGMInter[0] +
//                ggIGMInter[1]*ggIGMInter[1] +
//                ggIGMInter[2]*ggIGMInter[2];

              double gradIGMInter = getNormOfVector(ggIGMInter);

               // IGM model
	      //! the output will now be the intra and inter contributions:
              //deltagIntraCURRENT      = std::sqrt(grad2IGM)      - std::sqrt(grad2IGMInter);  // INTRA
              deltagIntraCURRENT      = gradIGM   - gradIGMInter;  // INTRA
	      deltagInterCURRENT      = gradIGMInter - gradAB;     // INTER

              qgIntraCURRENT      = (deltagIntraCURRENT+gradAB) / gradAB;  // INTRA
              qgInterCURRENT      = gradIGMInter / gradAB;                 // INTER

                                               // Our definition of differences dg:
                                               // a) dgINTRA = gradRHOIGM      - gradRHOIGMINTER = gradRHOIGMINTRA - gradRHO
                                               // b) dgINTER = gradRHOIGMINTER - gradRHO
                                               // Our definition of ratios qg:
                                               // a) QgINTRA = gradRHOIGMINTRA / gradRHO
                                               // b) QgINTER = gradRHOIGMINTER / gradRHO
                                               //    
                                               //    
                                               //       deltagINTER 
                                               //      <............>                                              
                                               //     -|------------|------------------------------|------------|---------------->
                                               //   gradRHO  gradRHOIGMINTER               gradRHOIGMINTRA   gradRHOIGM
                                               //     real   inter cancelled               intra cancelled   every interaction cancelled
                                               //     known        known                    unreachable        known
                                               //                                           with absolute
                                               //                                           values
                                               //                    <..........................................>                                        
                                               //                                    deltagINTRA
                                               //      <...........................................>
                                               //                        deltagINTRA as well
                                               //    


} // end of IGMH() ......................................................................................


void NCISolver::IGMBDA(double *gradAtom1QM, double *gradAtom2QM, double &bda)
// gradAtom : input --> Hirschfeld or GBP atomic ED Gradient partition : contribution of each ATOM 
//                      to the ED gradient; gradAtom[0:nbAtoms][0:2]; for FRAG1+FRAG2 only
// bda : output --> the difference in the current bond deltag under two different perturbations applied to each atom
{
              // BDA model
          bda = (gradAtom1QM[2] + gradAtom2QM[2]);// - (gradAtom1PRO[2] + gradAtom2PRO[2]);
         /* double k = bafepsilon+1.0;
          double dgAplus = std::abs(  gradAtom1QM[2]) + std::abs(  gradAtom2QM[2]) - std::abs(k*gradAtom1QM[2]+   gradAtom2QM[2]);
          double dgBplus = std::abs(  gradAtom1QM[2]) + std::abs(  gradAtom2QM[2]) - std::abs(  gradAtom1QM[2]+ k*gradAtom2QM[2]);


          bda = (dgAplus - dgBplus)/bafepsilon; */

} // end of IGMBDA() ......................................................................................


void NCISolver::calcQMAO(unsigned int nbPrim, unsigned int* prim, double* d2, double* dx, double* dy, double* dz, double** chi)
// nbPrim             input  --> Number of primitives forming the basis set          
// prim               input  --> Array of primitive indices (each returned index points toward the proper
//                               primitive index in the WFX/WFN data): 1 dimension [0:nbPrim-1]
// d2                 input  --> Array of squared distance between the atoms and the current space point                     
//                               1 dimension [0:nbatoms-1]
// dx,dy,dz           input  --> Arrays of x-xat,y-yat,z-zar between the atoms and the current space point 
//                               1 dimension [0:nbatoms-1] 
// chi                output --> atomic orbital (and derivatives) values at current point, 2 dimensions: [0:nbprim-1][0:9]
{
// =========== LOCAL VARIABLES ========================================
   double** xl = new double*[3];
   for(unsigned int i=0;i<3;++i)
     {
       xl[i] =  new double[3];
     }

   unsigned int* itype   = wf_primitive_types();
   unsigned int* icenter = wf_primitive_centers();
   double*       e       = wf_primitive_exponents();
// ....................................................................

// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// G T O     p r i m i t i v e s 
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 
if (!isRKFmodeActivated()) {
// =================================================================== //
// C O M P U T E   P R I M I T I V E   V A L U E S                     //
// ====================================================================//
   for(unsigned int ipria = 0;  ipria < nbPrim ; ++ipria)
      {
        unsigned int ipri = prim[ipria]; // ipri returned in the range[0:nbprim-1]
                                            // ipri = the index of on of the primitives on FRAG1+2
        unsigned int iat  = icenter[ipri]; // the atom bearing the current primitive
        // JC: BEWARE iat in the range [1:nb Atoms],natural count
        // but dx[threadID][ip][iat] with iat in the range [0:nb Atoms -1]
        // ==> decrease by 1 iat
        iat--;

       // first initialize the current primitive and its derivatives
       for(unsigned int ix=0;ix<10;++ix)
          {chi[ipri][ix]=0.0;}

       // ======================================================= //
       //         S P E E D U P   T E S T  o n   A O              //
       //            int_0^rmin AO^2 dr < 0.999                   //
       // ======================================================= 
       // (only those AO for which int_0^rmin AO^2 dr < 0.999     //
       //  will be calculated !)

       // if the distance between the atom bearing the current AO and the current grid node:  sqrt(d2[iat])
       // is larger than Rmin (the criterium telling us if the currentAO is very small or not at this distance)
       // then we skip it (AO and derivatives !) else we calculate it !
       if ( std::sqrt(d2[iat]) > primRmin[ipri]) {
          continue;
       } // this primitive is skipped ! ... saving time


        // ....... E N D   O F   S P E E D U P   T E S T ....... //
        // ..................................................... //


        double al   = e[ipri];
        double expon= -al * d2[iat];
        double ex   = std::exp(expon);
//      if (expon < exponThreshold) // if the exponent is to small ( < -40), the GTO is considered to be zero
//         {continue;}                // save about 10% of CPU Time // another possibility could be to use the 
//                                  // J. Pilme strategy (see DOI: 10.1002/jcc.27105)
        double x0[3];

        x0[0] = dx[iat];
        x0[1] = dy[iat];
        x0[2] = dz[iat];

        unsigned int* l=this->index0(itype[ipri]-1); // index0 waits for a parameter in the range[0:34]      
                                                     // while itype[ipri] returns an index read 
                                                     // from wfn in the range[1:35]

        double xl2;

        // loop over ix=components x,y,z of space
        // and compute x factor (0<->x, 1<->y, 2<-> z) in GTO function [0] and its derivatives (first) 
        // [1] and (second) [2]
        // ======================================================= //
        //        N O T A T I O N
        // ======================================================= //
        // GTO = xl[0][0]*xl[1][0]*xl[2][0]*ex
        //      d(xl[0][0])/dx   => xl[0][1]
        //      d(xl[1][0])/dy   => xl[1][1]
        //      d(xl[2][0])/dz   => xl[2][1]
        //
        //      d2(xl[0][0])/dx2 => xl[0][2]
        //      d2(xl[1][0])/dy2 => xl[1][2]
        //      d2(xl[2][0])/dz2 => xl[2][2]
        // ....................................................... //
        
        for(unsigned int ix = 0; ix < 3; ++ix)
           {
             switch(l[ix])
               {
               case 0:
                 xl[ix][0] = 1.0;
                 xl[ix][1] = 0.0;
                 xl[ix][2] = 0.0;
                 break;
           
               case 1:
                 xl[ix][0] = x0[ix];
                 xl[ix][1] = 1.0;
                 xl[ix][2] = 0.0;
                 break;
           
               case 2:
                 xl[ix][0] = x0[ix] * x0[ix];
                 xl[ix][1] = 2.0 * x0[ix];
                 xl[ix][2] = 2.0;
                 break;
           
               case 3:
                 xl[ix][0] =  x0[ix] * x0[ix] * x0[ix];
                 xl[ix][1] = 3.0 * x0[ix] * x0[ix];
                 xl[ix][2] = 6.0 * x0[ix];
                 break;
                    
               case 4:
                 xl2 = x0[ix] * x0[ix];
                 xl[ix][0] = xl2 * xl2;
                 xl[ix][1] = 4.0 * xl2 * x0[ix];
                 xl[ix][2] = 12.0 * xl2;
                 break;
           
               default:
                 //std::cout << "prio012 power of L not supported" << std::endl;
                 break;
               } // end of switch
           } // end of ix


          // the following section has been written to speed up the code
          // since some factors are used more than once ...
          double x00l0p1, x01l1p1, x02l2p1;
          double x00l0p2, x01l1p2, x02l2p2;

          if(l[0]==0)
            {
              x00l0p1=x0[0];
            }
          else
            {
              if(l[0]==1)
                {
                  x00l0p1=x0[0]*x0[0];
                }
              else
                {
                  if(l[0]==2)
             {
               x00l0p1=x0[0]*x0[0]*x0[0];
             }
                  else
             {
               if(l[0]==3)
                 {
                   x00l0p1=x0[0]*x0[0]*x0[0]*x0[0];
                 }
               else
                 {
                   x00l0p1=x0[0]*x0[0]*x0[0]*x0[0]*x0[0];
                 }
             }
                }
             }

             x00l0p2=x00l0p1*x0[0];

             if(l[1]==0)
                {
                  x01l1p1=x0[1];
                }
             else
                {
                  if(l[1]==1)
                    {
                      x01l1p1=x0[1]*x0[1];
                    }
                  else
                    {
                      if(l[1]==2)
                 {
                   x01l1p1=x0[1]*x0[1]*x0[1];
                 }
                      else
                 {
                   if(l[1]==3)
                     {
                       x01l1p1=x0[1]*x0[1]*x0[1]*x0[1];
                     }
                   else
                     {
                       x01l1p1=x0[1]*x0[1]*x0[1]*x0[1]*x0[1];
                     }
                 }
                    }
                }

             x01l1p2=x01l1p1*x0[1];

             if(l[2]==0)
              {
                x02l2p1=x0[2];
              }
             else
              {
                if(l[2]==1)
                  {
                    x02l2p1=x0[2]*x0[2];
                  }
                else
                  {
                    if(l[2]==2)
               {
                 x02l2p1=x0[2]*x0[2]*x0[2];
               }
                    else
               {
                 if(l[2]==3)
                   {
                     x02l2p1=x0[2]*x0[2]*x0[2]*x0[2];
                   }
                 else
                   {
                     x02l2p1=x0[2]*x0[2]*x0[2]*x0[2]*x0[2];
                   }
               }
                   }
               }

               x02l2p2=x02l2p1*x0[2];

        // the AIM: compute primitives (index 0, atomic orbitals values at current grid point)
        //          and their first (indexes 1,2,3) and second derivatives (indexes 4 to 9)
        chi[ipri][0] =  xl[0][0]*xl[1][0]*xl[2][0]*ex;
        chi[ipri][1] = (xl[0][1]-2*al*x00l0p1)*xl[1][0]*xl[2][0]*ex;
        chi[ipri][2] = (xl[1][1]-2*al*x01l1p1)*xl[0][0]*xl[2][0]*ex;
        chi[ipri][3] = (xl[2][1]-2*al*x02l2p1)*xl[0][0]*xl[1][0]*ex;
        chi[ipri][4] = (xl[0][2]-2*al*(2*l[0]+1)*xl[0][0]+4*al*al*x00l0p2)*xl[1][0]*xl[2][0]*ex;
        chi[ipri][5] = (xl[1][2]-2*al*(2*l[1]+1)*xl[1][0]+4*al*al*x01l1p2)*xl[2][0]*xl[0][0]*ex;
        chi[ipri][6] = (xl[2][2]-2*al*(2*l[2]+1)*xl[2][0]+4*al*al*x02l2p2)*xl[0][0]*xl[1][0]*ex;
        chi[ipri][7] = (xl[0][1]-2*al*x00l0p1)*(xl[1][1]-2*al*x01l1p1)*xl[2][0]*ex;
        chi[ipri][8] = (xl[0][1]-2*al*x00l0p1)*(xl[2][1]-2*al*x02l2p1)*xl[1][0]*ex;
        chi[ipri][9] = (xl[2][1]-2*al*x02l2p1)*(xl[1][1]-2*al*x01l1p1)*xl[0][0]*ex;
   } // end of loop ipria over primitives of FRAG1 + 2 ........................................

/*  .................... END calculating the PRIMITIVES ............................................... */

} // END of GTO primitives treatment

// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// S T O     p r i m i t i v e s 
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 
else 
{
// =================================================================== //
// C O M P U T E   P R I M I T I V E   V A L U E S                     //
// ====================================================================//

   // retrieve the k values for STOs (r^kr x^kx y^ky z^kz exp(-alf r))
   unsigned int *k=wf_primitive_krs();
  
   for(unsigned int ipria = 0;  ipria < nbPrim ; ++ipria)
      {
        unsigned int ipri = prim[ipria]; // ipri returned in the range[0:nbprim-1]
                                            // ipri = the index of on of the primitives on FRAG1+2
        unsigned int iat  = icenter[ipri];
        // JC: BEWARE iat in the range [1:nb Atoms],natural count
        // but dx[threadID][ip][iat] with iat in the range [0:nb Atoms -1]
        // ==> decrease by 1 iat
        iat--;

        double x0[3];
        x0[0] = (dx[iat]>0?std::max<double>(dx[iat], 1e-30):std::min<double>(dx[iat],-1e-30));
        x0[1] = (dy[iat]>0?std::max<double>(dy[iat], 1e-30):std::min<double>(dy[iat],-1e-30));
        x0[2] = (dz[iat]>0?std::max<double>(dz[iat], 1e-30):std::min<double>(dz[iat],-1e-30));

        // compute some constants
        double r     = std::max<double>(std::sqrt(d2[iat]), 1e-30);
        double r2    = r*r;
        double r4    = r2*r2;

        double rinv  = 1.0/r;
        double rinv2 = rinv*rinv;
        double rinv4 = rinv2*rinv2;

        double xinv  = 1.0/x0[0];
        double yinv  = 1.0/x0[1];
        double zinv  = 1.0/x0[2];

        double xinv2 = xinv*xinv;
        double yinv2 = yinv*yinv;
        double zinv2 = zinv*zinv;

        double x2    = x0[0]*x0[0];
        double x4    = x2*x2;
        double y2    = x0[1]*x0[1];
        double y4    = y2*y2;
        double z2    = x0[2]*x0[2];
        double z4    = z2*z2;
        double xy    = x0[0]*x0[1];
        double xz    = x0[0]*x0[2];
        double yz    = x0[1]*x0[2];




       // first initialize the current primitive and its derivatives
       for(unsigned int ix=0;ix<10;++ix)
          {chi[ipri][ix]=0.0;}

        double al   = e[ipri];
        double al2  = al*al;
        double expon= al * r;
   
        if (-1.0*expon < exponThreshold) // if the exponent is to small ( < -40), the GTO is considered to be zero
         {continue;}                // save about 10% of CPU Time

        double ex   = std::exp(-expon);
   
     
   
        unsigned int* l=this->index0(itype[ipri]-1); // index0 waits for a parameter in the range[0:34]      
                                                     // while itype[ipri] returns an index read 
                                                     // from wfn in the range[1:35]

        // Compute the primitive itself
        chi[ipri][0] =  std::pow(x0[0],l[0]) *
                        std::pow(x0[1],l[1]) *
                        std::pow(x0[2],l[2]) *
                        std::pow(r,k[ipri]) * ex; 


        
        double alrinv  = al*rinv;
        double kr2inv  = k[ipri]*rinv2;


        // First derivatives
        chi[ipri][1] =  chi[ipri][0] * (l[0]*xinv - alrinv*x0[0] + x0[0]*kr2inv);
        chi[ipri][2] =  chi[ipri][0] * (l[1]*yinv - alrinv*x0[1] + x0[1]*kr2inv);
        chi[ipri][3] =  chi[ipri][0] * (l[2]*zinv - alrinv*x0[2] + x0[2]*kr2inv);

        // second derivatives
        double ct1   =  expon*(expon+1) + k[ipri]*(k[ipri]-2*expon-2); 
        double ct2   =  k[ipri] - expon;
        double ct3   =  expon*(1.0-2*k[ipri]) - k[ipri]*(2.0-k[ipri]);
        double ct4   =  (k[ipri] - expon);
        

        // d/dx2
        chi[ipri][4] = chi[ipri][0]*rinv4*xinv2 * (x4*ct1 +
                                                   x2*r2*(1.0+2*l[0])*ct2  + 
                                                   r4*l[0]*(l[0]-1)
                                                  );
        // d/dy2                                  
        chi[ipri][5] = chi[ipri][0]*rinv4*yinv2 * (y4*ct1 +
                                                   y2*r2*(1.0+2*l[1])*ct2  +
                                                   r4*l[1]*(l[1]-1)
                                                  );
        // d/dz2
        chi[ipri][6] = chi[ipri][0]*rinv4*zinv2 * (z4*ct1 +
                                                   z2*r2*(1.0+2*l[2])*ct2  +
                                                   r4*l[2]*(l[2]-1)
                                                  );

        // d/dxy
        chi[ipri][7] = chi[ipri][0]*rinv4/xy * ( xy*xy*ct3 + 
                                                 y2*r2*(x2*al2+l[0]*k[ipri]-l[0]*expon) +
                                                 x2*r2*l[1]* ct4 +
                                                 l[0]*l[1]*r4
                                                );

        // d/dxz
        chi[ipri][8] = chi[ipri][0]*rinv4/xz * ( xz*xz*ct3 +
                                                 z2*r2*(x2*al2+l[0]*k[ipri]-l[0]*expon) +
                                                 x2*r2*l[2]* ct4 +
                                                 l[0]*l[2]*r4
                                                );

        // d/dyz
        chi[ipri][9] = chi[ipri][0]*rinv4/yz * ( yz*yz*ct3 +
                                                 z2*r2*(y2*al2+l[1]*k[ipri]-l[1]*expon) +
                                                 y2*r2*l[2]* ct4 +
                                                 l[1]*l[2]*r4
                                               );

   } // end of loop ipria over primitives of FRAG1 + 2 ........................................

/*  .................... END calculating the PRIMITIVES ............................................... */

} // END of STO primitives treatment

// ======= S E T    M E M O R Y   F R E E ==============================
   for( unsigned int i=0 ; i < 3 ; ++i )
     {
       delete[] xl[i];
     }
   delete[] xl;

// .....................................................................


} // end of routine calcQMAO


void NCISolver::calcQMPROP(unsigned int nbPrim, unsigned int* prim, double** chi, double** phi, double &rho, double grad[3], double** hess)
// nbPrim             input  --> Number of primitives forming the basis set (given in the second parameter)
// prim               input  --> Array of primitive indices (each returned index points toward the proper
//                               primitive index in the WFX/WFN data): 1 dimension [0:nbPrim-1]
// chi                input  --> atomic orbital (and derivatives) values at current point, 2 dimensions: [0:nbprim-1][0:9]
// phi                output --> Molecular orbital values (and derivatives), 2 dimensions: [0:nmo-1][0:9]  (can be limited to FRAG1 + FRAG2)
// rho                output --> Electron density at current point (can be limited to FRAG1 + FRAG2)
// grad               output --> Electron density gradient vector at current point (can be limited to FRAG1 + FRAG2  (or Atom1 + Atom2 for IBSI))
// hess               output --> ED hessian at current point, 2 dimensions: [0:2][0:2] (can be limited to FRAG1 + FRAG2  (or Atom1 + Atom2 for IBSI))
{

   // ==========================================================================================================
   //  C O M P U T I N G   M O,  H e s s i a n  and  r h o   a n d   g r a d   f o r   F R A G 1   +   F R A G 2
   // ==========================================================================================================
   //  !!!!!!   
   //  !!!!!! : molecularOrbitals : all MOs read from WFX/WFN (occupancy, LCAO coeffs, ...)
   //  !!!!!! : phi               : all MOs values calculated using the set of primitives passed as parameters               
   //  !!!!!!                       obtained by the calcQMAO call upstream
   //  !!!!!! : rho, grad, hess   : limited to the set of primitives passed as parameters: 
   //  !!!!!!                       governed by the parameters nbPrim and prim of the routine 

   // Note that if fragNbPrim has been passed to this routine, then 
   // rho_12 (limited to FRAG1+2) is required, not all the ED
   // else if npri has been passed to this routine, then
   // the total rho has to be returned

   // build the MO values at the point
   // INITIALIZATION
   for(std::vector<moleculeOrbital>::size_type imo=0 ; imo< molecularOrbitals.size() ; ++imo)
    {
     for(unsigned int ix=0; ix<10; ++ix)
      {
        phi[imo][ix]=0.0;
      }
    } // end over imo

   // compute all MOs using primitives passed as parameters (for IBSI, the primitives
   // in a radius of r angstroms  around both atoms of the considered bond )
   // calculations performed for those primitives whose value is not below the threshold
     for(unsigned ix=0; ix<10; ++ix)
        {
        for(unsigned int ipria=0; ipria<nbPrim; ++ipria) 
          // and only for those primitives whose value is not below the threshold
          {  
             unsigned int ipri = prim[ipria]; // ipri returned in the range[0:nbprim-1]

             // check if current primitive (and its derivatives) have to be used to compute MO
             // Note that, in previous versions, this test was saved in ldopri bool variable
             // which howver required to allocate memory for an array: waste of time
             if( (std::abs(chi[ipri][ix])*maxc[ipri]) > cutoff_pri )
               {
                 for( std::vector<moleculeOrbital>::size_type imo=0 ; imo< molecularOrbitals.size() ; ++imo)
                    {
                      // real MO (limited to FRAG1 + FRAG2)   
                      phi[imo][ix] += molecularOrbitals[imo].coefficients[ipri]*chi[ipri][ix]; 
                    } // end imo
               }// // end if ldopri
          } // end ipria
        } // end ix 

   // =====================================================================================
   //   E L E C T R O N    D E N S I T Y   f o r   F R A G 1   +   F R A G 2    or    A L L 
   //   a n d     H E S S I A N 
   // =====================================================================================


   // density, grad and hessian using MO phi truncated to FRAG1 and FRAG2  or ALL (not truncated)!! 
   // The full ED will be considered if FRAG1+2 = whole system or if isDgScaled option is true
   // initialize rho and grad
   rho     = 0.0;
   grad[0] = 0.0;
   grad[1] = 0.0;
   grad[2] = 0.0;

   // initializing ED Hessian
   for (unsigned int i = 0; i < 3; ++i) {
       for (unsigned int j = 0; j < 3; ++j) {
           hess[i][j] = 0.0;
       }
   }

   for( unsigned int imo=0 ; imo< molecularOrbitals.size() ; ++imo)
     {
       double occImo = molecularOrbitals[imo].occupancy;

       rho       +=     occImo *  phi[imo][0]*phi[imo][0];
       hess[0][0]+= 2 * occImo * (phi[imo][0]*phi[imo][4]+ phi[imo][1]*phi[imo][1]);
       hess[1][1]+= 2 * occImo * (phi[imo][0]*phi[imo][5]+ phi[imo][2]*phi[imo][2]);
       hess[2][2]+= 2 * occImo * (phi[imo][0]*phi[imo][6]+ phi[imo][3]*phi[imo][3]);
       hess[0][1]+= 2 * occImo * (phi[imo][0]*phi[imo][7]+phi[imo][1]*phi[imo][2]);
       hess[0][2]+= 2 * occImo * (phi[imo][0]*phi[imo][8]+phi[imo][1]*phi[imo][3]);
       hess[1][2]+= 2 * occImo * (phi[imo][0]*phi[imo][9]+phi[imo][2]*phi[imo][3]);


       // compute the total gradient for FRAG1+FRAG2 (or ALL if nbPrim == allPrim)
       grad[0] = grad[0] + 2 * occImo *  phi[imo][1]*phi[imo][0];
       grad[1] = grad[1] + 2 * occImo *  phi[imo][2]*phi[imo][0];
       grad[2] = grad[2] + 2 * occImo *  phi[imo][3]*phi[imo][0];

     } // ! end of loop over imo
     // complete the symmetrical Hessian
     hess[1][0] = hess[0][1];
     hess[2][0] = hess[0][2];
     hess[2][1] = hess[1][2];


} // end of calcQMPROP


// =================================================================== //
// P R I M I T I V E     C U T O F F                                   //
// ====================================================================// 
void NCISolver::findmaxc()
{

  for(unsigned int i=0;i<npri;i++)
    {
      maxc[i]=0.0;
    }
  for(std::vector<moleculeOrbital>::size_type i=0 ; i< molecularOrbitals.size() ; ++i)
    {
      for(unsigned int ipri=0; ipri<npri;++ipri)
        {
          if(maxc[ipri]<std::abs(molecularOrbitals[i].coefficients[ipri]))
            {
              maxc[ipri]=std::abs(molecularOrbitals[i].coefficients[ipri]);
            }
        }
    }// end over MOs
//.....................................................................//
} // end of routine findmaxc

bool NCISolver::NewtonRaphson(unsigned int nbPrim, unsigned int* prim, double* rcurr, double* L, double &G, double &rhocp, double &gradrhocp) 
// nbPrim             input  --> Number of primitives forming the basis set  
// prim               input  --> Array of primitive indices (each returned index points toward the proper
//                               primitive index in the WFX/WFN data): 1 dimension [0:nbPrim-1]
// rcurr:    input/output    -> current position during the Newton-Raphson search
// L    :    output          -> eigenvalues of the ED hessian 
// G    :    output          -> kinetic energy density at the cp in a.u.
// rhocp:   output           -> ED at the cp in a.u.
// gradrhocp: output         -> ED gradient magnitude in a.u.
{
      unsigned int nbAtoms = data->getNbAtom();
      positions_t atomCoordinates = data->atomPositions;

      //      * Newton-Raphson procedure to localize a possible CP
      //        guess = xp,yp,zp with maximum dgpair value found in the grid
      //        prerequisite to obtain the wave function = d2,dx,dy,dz:

      // ===============================
      // L O C A L   V A R I A B L E S   
      // ===============================

      double  displact[3] = {0.0}; // displacement  during a step of the Newton-Raphson search
      double* dxcurr = NULL;
      double* dycurr = NULL;
      double* dzcurr = NULL;
      double* d2curr = NULL;
      dxcurr = new double[nbAtoms];
      dycurr = new double[nbAtoms];
      dzcurr = new double[nbAtoms];
      d2curr = new double[nbAtoms];

      double** chi  = new double*[npri];
      double** phi  = new double*[molecularOrbitals.size()];
      for(unsigned int i=0;i<npri;++i)
      {
          chi[i]    = new double[10];
      }
      for(std::vector<moleculeOrbital>::size_type i=0 ; i< molecularOrbitals.size() ; ++i)
      {
          phi[i]        = new double[10];
      }

      // ED gradient inverse Jacobian, current ED hessian
      double Jac[3][3]={{0.0}};
      double JacInv[3][3]={{0.0}};
      double** hesscurr = new double*[3];
      for (unsigned int i=0; i<3; ++i)
        {
           hesscurr[i]    = new double[3];
        }

     // ED
     double gradcurr[3];
     gradcurr[0] = 0.0;
     gradcurr[1] = 0.0;
     gradcurr[2] = 0.0;

     // to host eigenvalues et eigenvectors:
     double*  heigs = new double[3];
     double** eigenVect = new double*[3];
     for (unsigned int ix=0; ix<3; ++ix) 
       {  heigs[ix] = 0.0;
          eigenVect[ix] = new double[3];
          for (unsigned int iy=0; iy<3; ++iy)
             {eigenVect[ix][iy] =0.0;}
       }

     // ... E N D   o f   L O C A L   V A R I A B L E S ..................
     // ..................................................................


     // Newton-Raphson search until convergence
     double normdisplact = 1000.0;
     int niter = 0;
     bool OKinversion = true; // true if the inversion of the ED gradient jacobian performs well
     do {
         niter++;
         // prepare the QM calculation by computing dx,dy,dz,d2 for each atom:
         for(unsigned int iat=0;iat<nbAtoms;++iat)
            {
                dxcurr[iat] = rcurr[0]   - atomCoordinates.xValues[iat];
                dycurr[iat] = rcurr[1]   - atomCoordinates.yValues[iat];
                dzcurr[iat] = rcurr[2]   - atomCoordinates.zValues[iat];
                d2curr[iat] = dxcurr[iat]*dxcurr[iat] + dycurr[iat]*dycurr[iat] + dzcurr[iat]*dzcurr[iat];
            }
    
//      std::cout << " current posit= " << std::endl;
//      std::cout << rcurr[0]*BOHRTOA << " " << rcurr[1]*BOHRTOA << " " <<rcurr[2]*BOHRTOA << " " << std::endl;
   
        // compute the hessian and ED gradient:
        rhocp = 0.0;
        gradcurr[0] = 0.0; // we look for a point with zero ED gradient
        gradcurr[1] = 0.0;
        gradcurr[2] = 0.0;
        for (unsigned int i=0; i<3; ++i)
        {
           for (unsigned int j=0; j<3; ++j)
              {hesscurr[i][j] =0.0;}
        }

         // QM calculations for Newton-Raphson search for QTAIM CP search 
         // all primitives are considered here
         calcQMAO(nbPrim, prim, d2curr, dxcurr, dycurr, dzcurr,chi);
         calcQMPROP(nbPrim, prim, chi, phi, rhocp, gradcurr, hesscurr); 
         

  
//  std::cout <<"DBUG: niter = " << niter << " HESSIAN" << std::endl;
//  std::cout << std::setw (10) << std::fixed << std::setprecision (8) << std::right; 
//  std::cout << hesscurr[0][0] << "  " << hesscurr[0][1] << "  " << hesscurr[0][2] << "  " << std::endl;
//  std::cout << std::setw (10) << std::fixed << std::setprecision (8) << std::right;
//  std::cout << hesscurr[1][0] << "  " << hesscurr[1][1] << "  " << hesscurr[1][2] << "  " << std::endl;
//  std::cout << std::setw (10) << std::fixed << std::setprecision (8) << std::right;
//  std::cout << hesscurr[2][0] << "  " << hesscurr[2][1] << "  " << hesscurr[2][2] << "  " << std::endl;

        // Build the Jacobian matrix inverse needed in Newton-Raphson
         // convert hesscurr (pointer of pointer) to a static array
         for (unsigned int i= 0; i<3; ++i)
           {   
             for (unsigned int j= 0; j<3; ++j)
                {Jac[i][j] = hesscurr[i][j];}
           }
         OKinversion = invSym33(Jac,JacInv);
         if (OKinversion) 
            {
                // Predict the new iterate:
                fortranMatMul33Matrix3Vector(JacInv,gradcurr,displact);
                rcurr[0] = rcurr[0] - displact[0];
                rcurr[1] = rcurr[1] - displact[1];
                rcurr[2] = rcurr[2] - displact[2];
           
                normdisplact = std::sqrt((displact[0]*displact[0]) + (displact[1]*displact[1]) +(displact[2]*displact[2]));

            } // end of if (OKinversion)
            // else  the determinant of the Jacobian is zero , singular Hessian matrix (non invertible)
            // reason: probably the search does not converge and goes far away from atoms where ED = 0 ...

           else { // OKinversion = false: singular Hessian matrix ...
                  // try to perturb a little bit the current position to go far from this singular matrix
            rcurr[0] = rcurr[0] + epsXNR;
            rcurr[1] = rcurr[1] + epsYNR;
            rcurr[2] = rcurr[2] + epsZNR;
           }
         
          // check whether the ED grad is zero !         
           gradrhocp = getNormOfVector(gradcurr);


//std::cout << "DBUG: niter = " << niter << std::endl;
//std::cout << std::fixed << std::setprecision(6) << std::setw(13);
//std::cout << rcurr[0]*0.5292 << std::setw(13) << rcurr[1]*0.5292 << std::setw(13) << rcurr[2]*0.5292 << std::endl;
//std::cout << std::scientific << std::setprecision(3);
//std::cout << "gradRho = " << gradrhocp << "  normdisplact = " << normdisplact << " OKinversion = " << OKinversion << std::endl;
// ERIC: modif 16082023
//   } while (      (  ( normdisplact > normdisplactThreshNR ) and (niter < niterThreshNR) )
     } while (      (niter < niterThreshNR) 
            //  and  OKinversion  
                and ( (gradrhocp > gradThresholdNR) or ( normdisplact > normdisplactThreshNR ) )
             ) ;
     // ===========================================
     // F I N A L       S T A T E M E N T 
     // ===========================================
     // test if convergence has not been achieved
     bool pb = false;

     if ( (niter == niterThreshNR) or (OKinversion==false) )
      {pb = true;}

     else {  // niter<niterThreshNR  AND   OKinversion==true

         // EH: modif 16082023
         // check whether the ED grad is zero !         
         //  gradrhocp = getNormOfVector(gradcurr);
         //  if (gradrhocp > gradThresholdNR)
         // now test on L1,L2,L3:

         // ===============================================================
         //         C O M P U T E      H E S S I A N  e i g e n v a l u e s 
         // ===============================================================
         this->getLambdaOfHessian(hesscurr,heigs,eigenVect);
         // Eigenvalues
         L[0] = heigs[0];
         L[1] = heigs[1];
         L[2] = heigs[2];
         // test to avoid misleading point
         if( (std::abs(L[0]) < EVthreshCP) and 
             (std::abs(L[1]) < EVthreshCP) and
             (std::abs(L[2]) < EVthreshCP)
           )
            {pb = true; } 
         else // convergence towards a cp has been achieved:
           { // MODIF ERIC: 16082023
            // ===============================================================
            //         C O M P U T E      H E S S I A N  e i g e n v a l u e s 
            // ===============================================================
           
            //   this->getLambdaOfHessian(hesscurr,heigs,eigenVect);
                 // Eigenvalues
            //        L[0] = heigs[0];
            //        L[1] = heigs[1];
            //        L[2] = heigs[2];

            // ===============================================================
            //         G(r) = K I N E T I C    E N E R G Y    D E N S I T Y    
            // ===============================================================
                 // compute kinetic energy density using all the MOs 
                 // beyond the thresholdMO cutoff and using primitives 
                 // of atoms within the IBSI cutoff (default = all)
                      G = 0.0;
                      for( unsigned int imo=0 ; imo< molecularOrbitals.size() ; ++imo)
                         {
                          G = G + molecularOrbitals[imo].occupancy*(phi[imo][1]*phi[imo][1] +  // contrib. of MO x gradient
                                                                    phi[imo][2]*phi[imo][2] +  // contrib. of MO y gradient
                                                                    phi[imo][3]*phi[imo][3]);  // contrib. of MO z gradient
            
                        } // end of Ekin calculation
                      G = G / 2.0; // +1/2 (nabla Psi).(nabla Psi)
       
           } // else of if abs(L1) >0 abs(L2) >0 abs(L3) >0
     } // end of else of if // niter<niterThreshNR  AND   OKinversion==true

      // ===========================================
      // f r e e   M E M O R Y  for local variables
      // ===========================================

      delete[] dxcurr;
      delete[] dycurr;
      delete[] dzcurr;
      delete[] d2curr;
      for(unsigned int i=0;i<npri;++i)
      { delete[] chi[i];}
      delete[] chi;

      for(std::vector<moleculeOrbital>::size_type i=0 ; i< molecularOrbitals.size() ; ++i)
      { delete[] phi[i];}
      delete[] phi;

      for (unsigned int i=0; i<3; ++i)
         {
            delete[] hesscurr[i];
         }
      delete[] hesscurr;

      for(unsigned int ivect=0;ivect<3;++ivect)
        {
          delete[] eigenVect[ivect];
        }
      delete[] eigenVect;
      delete[] heigs;

     // ... E N D   o f    f r e e    m e m o r y ........................
     // ..................................................................
     //
     if (not pb) {return true;}
     else        {return false;}

} // end of NewtonRaphson


int NCISolver::cptype(double L123[3])
// L123: an array of three double = three eigenvalues of ED hessian         
// return -3 (NCP) or -1 (BCP) or 1 (RCP) or 3 (CCP) 
// return 0 for rank different from 3
{
int signature = 0;
if ( ( (L123[0]==0.0) or (L123[1]==0.0) ) or  (L123[2]==0.0) 
   )
{ // rank of cp lower than 3 !
  //      std::cerr << std::endl;
  //      std::cerr << "[ERROR] Rank of current critical point is diffrent from 3"  << std::endl;
  //      std::cerr << "[ERROR] in NCISolver.cpp" << std::endl;
  //      std::cerr << "The program will now exit." << std::endl;
  //      exit(EXIT_FAILURE);
  return 0;  // coding for error of signature
}
if (L123[0] < 0) {signature=signature-1;} else {signature=signature+1;}
if (L123[1] < 0) {signature=signature-1;} else {signature=signature+1;}
if (L123[2] < 0) {signature=signature-1;} else {signature=signature+1;}
return signature;
} // end of cptype


// routine to chnage the basis for several vectors of the QM calculation
void NCISolver::basischange(unsigned int nbPrim, unsigned int* prim, double matrixHessianEigVec[3][3], 
                 double **chi, double* dx, double* dy, double* dz, double **phi, double gg[3])
// nbPrim             input  --> Number of primitives forming the basis set          
// prim               input  --> Array of primitive indices (each returned index points toward the proper
//                               primitive index in the WFX/WFN data): 1 dimension [0:nbPrim-1]
// matrixHessianEigVec input --> the change-of-basis 3x3 matrix
// chi          input/output --> atomic orbital (and derivatives) values at current point, 2 dimensions: [0:nbprim-1][0:9]
// dx,dy,dz     input/output --> Arrays of x-xat,y-yat,z-zar between the atoms and the current space point 
//                               1 dimension [0:nbatoms-1] 
// phi          input/output --> Molecular orbital values (and derivatives), 2 dimensions: [0:nmo-1][0:9] 
//                               limited to primitives FRAG1 + FRAG2  (or for atoms within the IBSI radius, default = all primitives)
// gg           input/output --> Electron density gradient vector at current point limited to FRAG1 + FRAG2  (or Atom1 + Atom2 for IBSI)

{
unsigned int nbAtoms        = data->getNbAtom();
double vec1[3];
double vec2[3];

// xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
// ==> basis change for the primitive gradient chi (atomic orbital gradient)
// xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
for(unsigned int ipria=0; ipria<nbPrim; ++ipria)
  { unsigned int ipri = prim[ipria]; // ipri returned in the range[0:nbprim-1] 

    vec1[0] = chi[ipri][1];
    vec1[1] = chi[ipri][2];
    vec1[2] = chi[ipri][3];

    //change the coord system from WFN to local ED curvatures
    fortranMatMul33Matrix3Vector(matrixHessianEigVec,vec1,vec2);

    chi[ipri][1]=vec2[0];
    chi[ipri][2]=vec2[1];
    chi[ipri][3]=vec2[2];

  } //over primitives gradients!

// xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
// ==> basis change for the position vector dx,dy,dz of every atoms  
// xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
for(unsigned int iat=0;iat<nbAtoms;++iat)
  {
    vec1[0]=dx[iat];
    vec1[1]=dy[iat];
    vec1[2]=dz[iat];

    //change the coord system from WFN to local ED curvatures           
    fortranMatMul33Matrix3Vector(matrixHessianEigVec,vec1,vec2);

    dx[iat] = vec2[0];
    dy[iat] = vec2[1];
    dz[iat] = vec2[2];

  } // end of basis change for the position vector of every atoms

// xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
// ==> basis change for the MO first derivatives            
// xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
for( unsigned int imo=0 ; imo< molecularOrbitals.size() ; ++imo)
 {
    vec1[0]= phi[imo][1]; // first derivative with respect to x of MO imo
    vec1[1]= phi[imo][2]; // first derivative with respect to y of MO imo
    vec1[2]= phi[imo][3]; // first derivative with respect to z of MO imo

    //change the coord system from WFN to local ED curvatures           
    fortranMatMul33Matrix3Vector(matrixHessianEigVec,vec1,vec2);

    phi[imo][1] = vec2[0];
    phi[imo][2] = vec2[1];
    phi[imo][3] = vec2[2];

 } // end of loop over all MOs

// xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
// ==> basis change for the ED gradient gg 
// xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
vec1[0]= gg[0]; // first derivative with respect to x 
vec1[1]= gg[1]; // first derivative with respect to y 
vec1[2]= gg[2]; // first derivative with respect to z 

//change the coord system from WFN to local ED curvatures           
fortranMatMul33Matrix3Vector(matrixHessianEigVec,vec1,vec2);

gg[0] = vec2[0];
gg[1] = vec2[1];
gg[2] = vec2[2];


} // end of routine basischange

void NCISolver::IGMPRO(double *dx, double *dy, double *dz, double &deltag, double &qg) {
// dxyz: input --> vector position of the current point with respect to the atoms (bohr)
// deltag: output --> the deltag descriptor accounting for all the interactions in the system
// qg    : output --> the qg     descriptor accounting for all the interactions in the system


// ======================================================================
//     L O C A L     V A R I A B L E S 
// ======================================================================

double rho1, rho2, rho3;
double dist;
int atType;
unsigned int nbAtoms = data->getNbAtom();

double gradFree[3]   = {0.0}; // atomic gradient within the promolecular approximation
double ggIGM[3]      = {0.0}; // gradient with every interactions cancelled
double gg[3]         = {0.0}; // real gradient
// .......................................................................


for( unsigned int iat=0; iat < nbAtoms ; ++iat){
        dist = std::sqrt(dx[iat]*dx[iat] + dy[iat]*dy[iat] + dz[iat]*dz[iat]);

        // PROMOLECULAR  rho processing //
        // three exponential contributions //
        atType = data->atomTypes[iat];
        rho1= A1[atType] * std::exp( -B1[atType] * dist);
        rho2= A2[atType] * std::exp( -B2[atType] * dist);
        rho3= A3[atType] * std::exp( -B3[atType] * dist);

        // pregradFree processing //
        // this quantity is common to the 3 ED gradient components for a given atom 
        // Summing B*rho = prefigure the promol first derivative of the atomic density //
        double sumBRho = B1[atType]*rho1 + B2[atType]*rho2 + B3[atType]*rho3;
        // Operation used three times //
        double pregradFree = sumBRho/dist;

        // gradient components 
        gradFree[0] = -dx[iat] * pregradFree; // with dx = x - x_atom
        gradFree[1] = -dy[iat] * pregradFree;
        gradFree[2] = -dz[iat] * pregradFree;

        // compute the gradient by cancelling every interaction between atoms of fragments A and B 
        // no fragmentation scheme used here since this promolecular routine is devised for critical point search
        ggIGM[0] = ggIGM[0] + std::abs(gradFree[0]);
        ggIGM[1] = ggIGM[1] + std::abs(gradFree[1]);
        ggIGM[2] = ggIGM[2] + std::abs(gradFree[2]);
 
        // compute the real gradient 
        gg[0] = gg[0] + gradFree[0];
        gg[1] = gg[1] + gradFree[1];
        gg[2] = gg[2] + gradFree[2];
 
} // end of loop over atoms

// proceed to the deltag calculation
double gradIGM  = getNormOfVector(ggIGM);  // every interaction inside the system is cancelled 
double grad     = getNormOfVector(gg);     // every interaction inside the system is present   

deltag = gradIGM - grad;
gradIGM = std::max<double>(gradIGM, 1e-30);
grad    = std::max<double>(grad   , 1e-30);
qg     = gradIGM / grad;

} // end of IGMPRO routine

void NCISolver::gradPRO(double *dx, double *dy, double *dz, double **gradAtom)
// dxyz: input --> vector position of the current point with respect to the atoms (bohr)
// gradAtom: output --> promolecular ED gradient of each atom, 2 dimensions: [0-nbat-1][0,1,2]

{
double rho1, rho2, rho3;
double dist;
int atType;
unsigned int nbAtoms = data->getNbAtom();
// .......................................................................


for( unsigned int iat=0; iat < nbAtoms ; ++iat){
        dist = std::sqrt(dx[iat]*dx[iat] + dy[iat]*dy[iat] + dz[iat]*dz[iat]);

        // PROMOLECULAR  rho processing //
        // three exponential contributions //
        atType = data->atomTypes[iat];
        rho1= A1[atType] * std::exp( -B1[atType] * dist);
        rho2= A2[atType] * std::exp( -B2[atType] * dist);
        rho3= A3[atType] * std::exp( -B3[atType] * dist);

        // pregradFree processing //
        // this quantity is common to the 3 ED gradient components for a given atom 
        // Summing B*rho = prefigure the promol first derivative of the atomic density //
        double sumBRho = B1[atType]*rho1 + B2[atType]*rho2 + B3[atType]*rho3;
        // Operation used three times //
        double pregradFree = sumBRho/dist;

        // gradient components 
        gradAtom[iat][0] = -dx[iat] * pregradFree; // with dx = x - x_atom
        gradAtom[iat][1] = -dy[iat] * pregradFree;
        gradAtom[iat][2] = -dz[iat] * pregradFree;

} // end of loop over atoms

} // end of gradPRO


/**
 * @fn dgdgAtPRO(double *dx, double *dy, double *dz, double &deltag, double** dgAt)
 * @brief compute the deltag descriptor at current point as well as every atomic contribution
 * @param dx input --> vector x position of the current point with respect to the atoms (bohr) 
 * @param dy input --> vector y position of the current point with respect to the atoms (bohr) 
 * @param dz input --> vector z position of the current point with respect to the atoms (bohr) 
 * @param     dg   output --> the deltag descriptor at current point accounting for all interactions 
 * @param   dgAt   output --> the atomic contributions    */ 

void NCISolver::dgdgAtPRO(double *dx, double *dy, double *dz, double &deltag, std::vector<double>& dgAt) {

double rho1, rho2, rho3;
double dist;
int atType;
unsigned int nbAtoms = data->getNbAtom();
double sumBRho = 0.0;
double pregradFree = 0.0;

std::vector<double> gradAtomX(nbAtoms, 0.0);
std::vector<double> gradAtomY(nbAtoms, 0.0);
std::vector<double> gradAtomZ(nbAtoms, 0.0);

double ggIGM[3]      = {0.0}; // gradient with every interactions cancelled
double gg[3]         = {0.0}; // real gradient
double ggIGMAt[3]    = {0.0}; // IGM Gradient for a given atom

// Make sure that dgAt has the proper size
dgAt.clear();  // if full            
dgAt.reserve(nbAtoms);  // To improve performance            



// .......................................................................

for( unsigned int iat=0; iat < nbAtoms ; ++iat){

        dist = std::sqrt(dx[iat]*dx[iat] + dy[iat]*dy[iat] + dz[iat]*dz[iat]);
        
        // PROMOLECULAR  rho processing //
        // three exponential contributions //
        atType = data->atomTypes[iat];
        rho1= A1[atType] * std::exp( -B1[atType] * dist);
        rho2= A2[atType] * std::exp( -B2[atType] * dist);
        rho3= A3[atType] * std::exp( -B3[atType] * dist);
        
        // pregradFree processing //
        // this quantity is common to the 3 ED gradient components for a given atom 
        // Summing B*rho = prefigure the promol first derivative of the atomic density //
        sumBRho = B1[atType]*rho1 + B2[atType]*rho2 + B3[atType]*rho3;
        // Operation used three times //
        pregradFree = sumBRho/dist;
        
        // gradient components 
        gradAtomX[iat] = -dx[iat] * pregradFree; // with dx = x - x_atom
        gradAtomY[iat] = -dy[iat] * pregradFree;
        gradAtomZ[iat] = -dz[iat] * pregradFree;

        // compute the gradient by cancelling every interaction between atoms of fragments A and B 
        // no fragmentation scheme used here since this promolecular routine is devised for critical point search
        ggIGM[0] += std::abs(gradAtomX[iat]);
        ggIGM[1] += std::abs(gradAtomY[iat]);
        ggIGM[2] += std::abs(gradAtomZ[iat]);

        // compute the real gradient 
        gg[0] += gradAtomX[iat];
        gg[1] += gradAtomY[iat];
        gg[2] += gradAtomZ[iat];


} // end of loop over atoms


// proceed with dgAt calculation
for(unsigned int iat=0; iat<nbAtoms; ++iat)
{
        ggIGMAt[0]  = std::abs(gradAtomX[iat]) + std::abs( gg[0] - gradAtomX[iat] );
        ggIGMAt[1]  = std::abs(gradAtomY[iat]) + std::abs( gg[1] - gradAtomY[iat] );
        ggIGMAt[2]  = std::abs(gradAtomZ[iat]) + std::abs( gg[2] - gradAtomZ[iat] );

        dgAt.push_back(getNormOfVector(ggIGMAt) - getNormOfVector(gg) );
}// end over atoms



// proceed with the deltag calculation
double gradIGM  = getNormOfVector(ggIGM);  // every interaction inside the system is cancelled 
double grad     = getNormOfVector(gg);     // every interaction inside the system is present   

deltag = gradIGM - grad;


} // end of  NCISolver::dgdgAtPRO


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

// ---------- sorting non-nul GTO according to the Julien Pilme procedure ---------
//                c.f. J. Comp. Chem. DOI: 10.1002/jcc.21903  
//   only those AOs for which the current node is closer than Rmin will considered
// --------------------------------------------------------------------------------

// Zero search procedure                              
double NCISolver::findRmin(
    int GTOType,               // integer [0:4] coding for s,p,d,f or g INPUT
    double alpha,              // alpha exponent of the GTO             INPUT
    double tolerance,          // radius convergence threshold (bohr)   INPUT
    int maxIterations,         // Maximum number of iteration           INPUT
    double initialGuess        // initial guess of rmin (bohr) for which f(r,alpha) = 0.999 INPUT
) {

// I D E N T I F Y I N G   AO type and the associated appropriate function returning Rmin

NCISolver::FunctionType f; // dynamically linked to the current GTO scalar product type function

if (GTOType == 0) {
   f = NCISolver::functions[0];
} else if (GTOType == 1)  {
   f = NCISolver::functions[1];
} else if (GTOType == 2)  {
   f = NCISolver::functions[2];
} else if (GTOType == 3)  {
   f = NCISolver::functions[3];
} else if (GTOType == 4)  {
   f = NCISolver::functions[4];
}   else {
      std::cerr << "[ERROR] GTOType function > 4 detected       "  << std::endl;
      std::cerr << "        GTOType should be 0(s), 1(p), 2(d),  " << std::endl;
      std::cerr << "        3(f) or 4(g)            " << std::endl;
      std::cerr << "[ERROR] in NCISolver.cpp.    " << std::endl;
      std::cerr << "The program will now exit." << std::endl;
      exit(EXIT_FAILURE);
}


//std::cout << "GTOType = " << GTOType << " alpha = " << alpha << " tolerance = " << tolerance << std::endl;

// .....................................................................................//
// SEARCH first point for which the function changes of sign                            //
// (at the first point (initial guess, 0.01 bohr) the function is necessarily negative) //
// .....................................................................................//
    double x_low  = initialGuess;   // lower limit
    double x_high = initialGuess;   // upper limit                           
    double f_low = f(x_low,alpha);  // initial value of the function 
    double f_high;
    const double xStep = 1.0; // in bohr
//std::cout << "proceed with detecting sign change " << std::endl;
    while (true) {
        x_high = x_low + xStep;   
        f_high = f(x_high,alpha);        

// std::cout << " x_low = " << x_low << " x_high= " << x_high<< " f_low = " << f_low  << " f_high = " << f_high << std::endl;
        if (f_low * f_high <= 0) { // detecting sign change of f 
            break;
        }

        x_low = x_high;            // update boundaries 
        f_low = f_high;

        double rmax = 1E3; // bohr
        if (x_high > rmax) {        // just in case
            return rmax;
        }
    } // end of while



// .....................................................................................//
//     D I C H O T O M Y    S E A R C H    o f   Z E R O                                //
// .....................................................................................//
//std::cout << "proceeding with Dichotomie procedure " << std::endl;

    double x_mid; // middle point;

    while ((x_high - x_low) / 2.0 > tolerance) {
        x_mid = (x_low + x_high) / 2.0; // middle point
        double f_mid = f(x_mid, alpha);

        if (f_low * f_mid < 0) { // zero is in between x_low and x_mid
            x_high = x_mid;
            f_high = f_mid;
        } else {                // zero is in between x_mid et x_high
            x_low = x_mid;
            f_low = f_mid;
        }

//std::cout << " x_low = " << x_low << " x_high= " << x_high<< " f_low = " << f_low  << " f_high = " << f_high << std::endl;
    } // end of while

//   std::cout << " R m i n = "  << (x_low + x_high) / 2.0 << std::endl;
    return  (x_low + x_high) / 2.0; 


} // end of findRmin  
// =================================================================================================




// ======================= S C A L P R O D   S P E E D U P   T E S T ===================================
// .............  s  .............
double  NCISolver::func0(double r, double alpha) {
    double result;
    double twoalpha = 2.0*alpha;
    result = std::erf(std::sqrt(twoalpha)*r);
    result = result - 2*sqrt(twoalpha/PI) * r * std::exp(-twoalpha * r*r);
    result = result - maxElectron;

    return result;  
}

// .............  p  .............
double  NCISolver::func1(double r, double alpha) {
    double result;
    double twoalpha = 2.0*alpha;
    result = std::erf(std::sqrt(twoalpha)*r);
    result = result - (2.0/3.0)*sqrt(twoalpha/PI) * (4*alpha*r*r*r + 3*r) * std::exp(-twoalpha * r*r);
    result = result - maxElectron;
    return result;  
}
    
// .............  d  .............
double  NCISolver::func2(double r, double alpha) {
    double result;
    double twoalpha = 2.0*alpha;
    result = std::erf(std::sqrt(twoalpha)*r);
    result = result - (2.0/15.0)*sqrt(twoalpha/PI) * (16*alpha*alpha*r*r*r*r*r + 20*alpha*r*r*r + 15*r) * std::exp(-twoalpha * r*r);
    result = result - maxElectron;
    return result;
}

// .............  f  .............                                                               
double  NCISolver::func3(double r, double alpha) {
    double result;   
    double twoalpha = 2.0*alpha;
    double SQalpha  = alpha*alpha;
    result = std::erf(std::sqrt(twoalpha)*r);
    result = result - (2.0/105.0)*sqrt(twoalpha/PI) * (64*SQalpha*alpha*r*r*r*r*r*r*r + 112*SQalpha*r*r*r*r*r 
                                                     + 140*alpha*r*r*r + 105*r) * std::exp(-twoalpha * r*r);
    result = result - maxElectron;                                               
    return result;
} 

// .............  g  .............                                                               
double  NCISolver::func4(double r, double alpha) {
    double result;   
    double twoalpha = 2.0*alpha;
    double SQalpha  = alpha*alpha;
    result = std::erf(std::sqrt(twoalpha)*r);
    result = result - (2.0/945.0)*sqrt(twoalpha/PI) * (256*SQalpha*SQalpha*std::pow(r,9) + 
                                                       576*SQalpha*alpha*r*r*r*r*r*r*r   + 
                                                      1008*SQalpha*r*r*r*r*r             +
                                                      1260*alpha*r*r*r                   +
                                                       945*r) *  std::exp(-twoalpha * r*r);
    result = result - maxElectron;                                               
    return result;
} 
   

// Define an array of functions used to speed-up AO calculations
NCISolver::FunctionType NCISolver::functions[] = {
    NCISolver::func0,
    NCISolver::func1,
    NCISolver::func2,
    NCISolver::func3,
    NCISolver::func4
};

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


// =============== f i n d R m i n A l l P r i m ===================================================
// find for each primitive its minimal Rmin radius beyond which
// we can avoid computing this primitive
void NCISolver::findRminAllPrim() {

 // Array to store the minimum radius below which a primitive has to be computed (scalProd speedup test) 
 primRmin = new double[npri]; // NCISolver field 

 if (!isRKFmodeActivated()) {
       std::cout << "Primitives pruning ..." << std::endl;
      
       unsigned int* itype    = wf_primitive_types();
       double*       e        = wf_primitive_exponents();
         
      for(unsigned int ipri=0;ipri<npri;++ipri) //  ipri in the range [0:npri -1]
         {
           // retrieve GTO type (still to be implemented on STO)
           unsigned int l1=this->index1(itype[ipri]-1);  // index1 waits for a parameter in the range[0:34]      
                                                          // while itype[ipri] returns an index read 
                                                          // from wfn in the range[1:35]
                                                          // returns 0 to 4 (s,p,d,f,g AO type)
     
           // compute the radius beyond which it is not useful to compute the primitive
           double al      = e[ipri]; // primitive exponent
           primRmin[ipri] = findRmin(l1, al);  // for GTO for now ... still to be implemented for STO
         } // end of loop over all primitives
  } // end of GTO
  else { // STO
    for(unsigned int ipri=0;ipri<npri;++ipri) //  ipri in the range [0:npri -1]
         {
           primRmin[ipri] = 1E5; // in bohr, to make sure that they will be necessarily computed
                                 // until the pruning procedure by J. Pilme is implemented for STO
         }
  } // end of else of if (!isRKFmodeActivated())

} // end of findRminAllPrim
// =================================================================================================

// default accuracy level for AOs calculations (according to the J. Pilme procedure)
double NCISolver::maxElectron = maxElecAccurlev1;

// Code the atom index pair              
double NCISolver::encodeBasin(int a, int b) { 
// a,b: input ->  1-based atom number
// return: a,b encoded in one single real number


       unsigned int nbAtoms = data->getNbAtom ();
       return a * (nbAtoms + 1.0) + b;
}

// decode the atom index pair              
std::vector<int> NCISolver::decodeBasin(double n) {
    int atom1, atom2; // 1-based atom number
    unsigned int nbAtoms = data->getNbAtom ();
    atom1 = static_cast<int>(n / (nbAtoms + 1.0));
    atom2 = static_cast<int>(n) % (nbAtoms + 1);
    return {atom1, atom2};
}

// Function to compute the number of electron in each ELF basin                      
std::map<std::pair<int, int>, NCISolver::basinProperties> 
NCISolver::computeBasinProperties(double*** SELFbasin, double*** KE, double*** rho, const unsigned int dimX, const unsigned int dimY, const unsigned int dimZ)
    {

    // compute the volume element dv  
    double dv = params.increments[0] * params.increments[1] * params.increments[2]; // in a.u.

    // Map to store atom pair for each basin and the associated electrons and volume
    std::map<std::pair<int, int>, NCISolver::basinProperties> basinProps;

//DEBUG
std::ofstream basinXYZ("basin.xyz"); // Ouvre ou crée le fichier
std::ofstream basinPDB("basin.pdb"); // Ouvre ou crée le fichier
    
    for(unsigned int i=0; i<dimX; ++i) {
        for(unsigned int j=0; j<dimY; ++j) {
            for(unsigned int k=0; k<dimZ; ++k) {
                // Decoding basin at current point  
                std::vector<int> basinAtoms = decodeBasin(SELFbasin[i][j][k]);
                int atom1 = basinAtoms[0]; // 1-based atom number
                int atom2 = basinAtoms[1];

                // Create a pair to indentify the basin                       
                std::pair<int, int> basinKey = std::make_pair(atom1, atom2);
                
                // Accumulate electrons for this basin  (if it does no exist yet, it is created)
                basinProps[basinKey].electrons += rho[i][j][k] * dv;

                // Accumulate volume for this basin
                basinProps[basinKey].volume += dv;

                // Acumulate Kinetic energy excess for this basin
                basinProps[basinKey].KinExcess += KE[i][j][k] * dv * ekin2kcal;

//DEBUG

      if ( (atom1==1) and (atom2==2) ) {

std::cout << atom1 << "  -   " << atom2 << std::endl;
// create a z-matrix of fictive atoms to represent basin in 3D space:
double x = data->getMinCoord(0) + i * params.increments[0];
double y = data->getMinCoord(1) + j * params.increments[1];
double z = data->getMinCoord(2) + k * params.increments[2];

// XYZ
basinXYZ << "O   " << std::fixed << std::setprecision(3) << std::setw(10) << x*0.5292;
basinXYZ << std::fixed << std::setprecision(3) << std::setw(10) << y*0.5292;
basinXYZ << std::fixed << std::setprecision(3) << std::setw(10) << z*0.5292 << std::endl;

//PDB 
double rhoValue = std::abs(rho[i][j][k]);
double Dh = ELF_const * std::pow(rhoValue, FIVE_THIRD);
basinPDB << "ATOM      1  O   UNK     1    " << std::right << std::fixed << std::setprecision(3) << std::setw(8) << x*0.5292;
basinPDB << std::fixed << std::setprecision(3) << std::setw(8) << y*0.5292;
basinPDB << std::fixed << std::setprecision(3) << std::setw(8) << z*0.5292 << "  1.00";
basinPDB << std::scientific << std::setprecision(1) << std::setw(8) << (KE[i][j][k]/Dh) << "           O" << std::endl;

       } // end of if if (atom1==1 and atom2==2)    


                // take the opportunity to color code for mono or disynaptic basins
//                if ( atom1==atom2) {
//                  SELFbasin[i][j][k] = 0; // monosynaptic
//                  } else {SELFbasin[i][j][k] = 1;}// disynaptic
            } // end of loop over k
        } // end of loop over j
    } // end of loop over i

basinXYZ.close();
basinPDB.close();

    return basinProps;
} // end of function computeBasinElectrons



// Fonction pour trouver les indices des deux atomes avec les valeurs les plus élevées
std::pair<size_t, size_t> NCISolver::find2max(const std::vector<double>& dgAt) {
// returns the two 1_based indices corresponding to the two largest values of vector dgAt
// with pair.first = the value of largest index

    // check if the vector has at least to elements     
    if (dgAt.size() < 2) 
        // Error
   {
      std::cerr << std::endl;
      std::cerr << "[ERROR] dgAt has less than 2 elements in function find2max"  << std::endl;
      std::cerr << "The program will now exit." << std::endl;
      exit(EXIT_FAILURE);
   }
    
    // Initialize 
    size_t maxIndex1 = 0;
    size_t maxIndex2 = 1;
    
    // Make sure that maxIndex1 corresponds to the index of the largest element 
    if (dgAt[maxIndex1] < dgAt[maxIndex2]) {
        std::swap(maxIndex1, maxIndex2);
    }
    
    // Loop over the vector dgAt     
    for (size_t i = 2; i < dgAt.size(); ++i) {
        if (dgAt[i] > dgAt[maxIndex1]) {
            // A new largest value has been detected for max1
            maxIndex2 = maxIndex1;
            maxIndex1 = i;
        } else if (dgAt[i] > dgAt[maxIndex2]) {
            // A new largest value has been detected for max2
            maxIndex2 = i;
        }
    } // end of for (int i = 2; i < dgAt.size(); ++i)

    // Make sure that maxIndex1 is the largest index 
    if (maxIndex1 < maxIndex2) {
        std::swap(maxIndex1, maxIndex2);
    }
    
    return {maxIndex1+1, maxIndex2+1};
} // end of find2max

#endif
