/***************************************************************************
 *cr
 *cr            (C) Copyright 1995-2016 The Board of Trustees of the
 *cr                        University of Illinois
 *cr                         All Rights Reserved
 *cr
 ***************************************************************************/

/***************************************************************************
 * RCS INFORMATION:
 *
 *      $RCSfile: gaussianplugin.h,v $
 *      $Author: johns $       $Locker:  $             $State: Exp $
 *      $Revision: 1.10 $       $Date: 2016/11/28 05:01:54 $
 *
 ***************************************************************************/
/*******************************************************************
 * 
 *  headerfile for the gaussianplugin
 *  
 ******************************************************************/

#ifndef GAUSSIANPLUGIN_H
#define GAUSSIANPLUGIN_H

#include <stdio.h>
#include "molfile_plugin.h"

/* define macros for true/false to make code 
* look somewhat nicer; the macro DONE signals
* that we're done with reading an should return
* with what we have */
#define FALSE 0
#define TRUE  1

/** macros describing the RUNTYP */
#define RUNTYP_UNKNOWN    0     /**< not set.  */
#define RUNTYP_ENERGY     1     /**< total energy, single point run  */
#define RUNTYP_OPTIMIZE   2     /**< geometry optimization  */
#define RUNTYP_SADPOINT   3     /**< transition state search  */
#define RUNTYP_HESSIAN    4     /**< frequency calculation  */
#define RUNTYP_SURFACE    5     /**< potential energy scan  */
#define RUNTYP_DYNAMICS   6     /**< molecular dynamics or monte carlo  */
#define RUNTYP_PROPERTIES 7     /**< wavefunction analysis  */

/** macros defining the SCFTYP */
#define SCFTYP_UNKNOWN 0        /**< not set. */
#define SCFTYP_RHF   1          /**< closed shell or restricted wfn.  */
#define SCFTYP_UHF   2          /**< open shell or unrestricted wfn.  */
#define SCFTYP_ROHF  3          /**< restricted open shell wfn.  */
#define SCFTYP_GVB   4          /**< generalized valence bond wfn. */
#define SCFTYP_MCSCF 5          /**< multi-configuration SCF.  */
#define SCFTYP_FF    6          /**< force field calculation.  */

/** XXX: check if this can go away. */
#define MAX_NUM_WAVE 10

typedef struct {
  float exponent;
  float contraction_coeff;
} prim_t;


typedef struct {
  int symmetry;                 /* symmetry of this shell (S, P, D, ...) */
  int numprims;                 /* number of primitive basis functions */
  prim_t *prim;                 /* array of primitives */
} shell_t;


/** Basis set definition for one atom */
typedef struct {
  char name[20];                /* atom name or type (pseudopotentialfile?) */
  int atomicnum;                /* atomic number (nuclear charge XXX) */
  int numshells;
  shell_t *shell;
} basis_atom_t;


/** structure for storing temporary values read in 
 * from the gaussian output file */
typedef struct 
{
  char type [8];                /* atom type H,N,O ..... */
  int atomicnum;                /* index in PTE. */
  float x,y,z;                  /* coordinates of atom i */
} qm_atom_t;


typedef struct {
  int   idtag;              /**< unique tag to identify this wavefunction over the trajectory */
  int   type;               /**< CANONICAL, LOCALIZED, OTHER */
  int   spin;               /**< 0 for alpha, 1 for beta */
  int   cartesian;          /**< 1 if cartesian, 0 if pure  */
  int   excitation;         /**< 0 for ground state, 1,2,3,... for excited states */
  char info[MOLFILE_BUFSIZ]; /**< string for additional type info */

  int   num_orbitals;       /**< number of orbitals that were really 
                             *   present in the output for this step */
  int   *orb_indices;       /**< list of orbital indices for wavefunction */
  float *occupancies;       /**< list of orbital occupancies for wavefunction */
  float *orb_energies;      /**< list of orbital energies for wavefunction */
  float *wave_coeffs;       /**< expansion coefficients for wavefunction in the
                             *   form {orbital1(c1),orbital1(c2),.....,orbitalM(cN)} */
} qm_wavefunction_t;


typedef struct {
  qm_wavefunction_t *wave;  /**<  */
  int numwave;              /**< number of independent wavefunctions  */
  int num_orbitals;         /**< number of orbitals per wavefunction  */
  int num_scfiter;          /**< number of SCF iterations for this step */
  double *scfenergies;      /* scfenergies per trajectory point 
                             * XXX: how about post-HF calculations?
                             *      we have the HF, MP2, CASSCF, CCS,
                             *      CCSD, CCSD(T) energies... */

  double *mulliken_charges; /* per-atom Mulliken charges */
  double *lowdin_charges;   /* per-atom Lowdin charges */
  double *esp_charges;      /* per-atom esp charges */
  double *npa_charges;      /* per-atom npa charges */

  float *gradient;          /* energy gradient for each atom */
} qm_timestep_t;


/** main gaussian plugin data structure */
typedef struct 
{
  FILE *file;
  int numatoms;
  int runtyp;        /* run type of this job as int for internal use */
  char gbasis[20];   /* canonicalized basis set string
                        for loading and external basis set. */

  char basis_string[MOLFILE_BUFSIZ]; /* basis name as "nice" string */

  char runtitle[MOLFILE_BUFSIZ];  /* title/info section of run */

  char geometry[MOLFILE_BUFSIZ];  /* either UNIQUE, CART or ZMP/ZMTMPC */
  char guess[MOLFILE_BUFSIZ];    /* type of guess method used */

  char version_string[MOLFILE_BUFSIZ]; /* full version string of binary used for run */
  int  version;  /* gaussian version code;
                  * canonicalized and set up for integer comparisons.
                  * here we keep track the exact Gaussian version,
                  * since the log file format keeps changing all
                  * the time. 
                  * Format is Year/Revision/Patchlevel: YYYYRRPP
                  * with:
                  * YYYY: g94 -> 1994, g98 -> 1998, g03 -> 2003
                  *   RR: A -> 1, B -> 2, C ->3, ...
                  *   PP: 01, 02, 03, ...
                  *
                  *   Example: G03RevB.04 -> 20030204
                  *
                  *   version = 0  => unknown/unreadable version. 
                  */

  char *file_name;

  /******************************************************
   * new API functions
   *****************************************************/

  int  scftyp;              /* UHF, RHF, ROHF, as in for 
                             * internal use*/
  char scftyp_string[MOLFILE_BUFSIZ]; /* scftyp as string. XXX: remove */
  int totalcharge;              /* Total charge of the system */
  int multiplicity;             /* Multiplicity of the system */
  int num_electrons;            /* Number of electrons */
  int nimag;                    /* Number of imaginary frequencies */
  int *nimag_modes;             /* List of imaginary modes */

  float *wavenumbers;          /* rotational and translational DoF are
                                 * included, but can be removed due to
                                 * their zero frequencies */
  float *intensities;          /* Intensities of spectral lines */

  float *normal_modes;         /* the normal modes themselves */

  int nproc;                    /* Number processors used */
  int memory;                   /* Amount of memory used in MBytes */

  int have_wavefunction; /** TRUE/FALSE flag indicating if we should
                          *  try searching for wavefunction data.
                          *  Gaussian needs IOP(6/7=3) to do this.
                          *  if it is not set, we can save time searching.
                          *  should help a lot with large log files... */

  int have_basis;        /** TRUE/FALSE flag initially indicating if we 
                          *  should try searching for basis set data.
                          *  Gaussian needs GFINPUT to print the data in a 
                          *  form that we can parse. If it is not set, we 
                          *  can save time searching. Should help a lot 
                          *  with large log files. If there is no basis in
                          *  the log, we try reading the basis set from
                          *  a local database. After initial parse it indicates
                          *  whether basis set data is available.
                          */

  int have_cart_basis;   /** flag indicating if we have a cartesian
                          *  basis set. this is additive.
                          *  0 = none (can only visualize up to P)
                          *  1 = have cartesian (6 instead of 5) d-functions)
                          *  2 = have cartesian (10 instead of 7) f-functions)
                          *  4 = have cartesian (XX instead of X) g-functions)
                          *  basis transferred to VMD is always cartesian,
                          *  so wavefunctions expanded in pure atomic orbitals
                          *  will have to be converted.
                          */

  /* arrays with atom charges */
  double *mulliken_charges; 
  int   have_mulliken; 

  int have_normal_modes; /** TRUE/FALSE flag indicating if we
			  * could properly read normal modes,
			  * wavenumbers and intensities. */

  /******************************************************
   * internal coordinate stuff
   *****************************************************/

  int have_internals;  /* TRUE/FALSE flag indicating if we
                        * could properly read the internal
                        * coordinates + internal hessian */

  int have_cart_hessian; /* TRUE/FALSE flag indicating if the
                          * cartesian Hessian matrix could
                          * be read from the output file */

  int nintcoords;    /* Number of internal coordinates */
  int nbonds;        /* Number of bonds */
  int nangles;       /* Number of angles */
  int ndiheds;       /* Number of dihedrals */
  int nimprops;      /* Number of impropers */

  int *bonds;        /* bond list (atom tuples) */
  int *angles;       /* angle list (atom triples) */
  int *dihedrals;    /* dihedral list (atom quadrupels) */
  int *impropers;    /* improper list (atom quadrupels) */

  double *internal_coordinates; /* value of internal coordinates */ 
  
  /*******************************************************
   * end internal coordinate stuff
   *******************************************************/

  double *carthessian;  /* Hessian matrix in cartesian coordinates,
                         * dimension (3*numatoms)*(3*numatoms),
                         * single array of floats 
                         * (row(1),row(2),...,row(numatoms))
                         */

  double *inthessian;  /* Hessian matrix in internal coordinates,
                        * dimension nintcoords*nintcoords,
                        * single array of floats 
                        * (row(1),row(2),...,row(nintcoords))
                        */

  /*********************************************************
   * END OF NEW API data members
   *********************************************************/

  /* this array of floats stores the contraction coefficients
   * and exponents for the basis functions:
   * { exp(1), c-coeff(1), exp(2), c-coeff(2), .... }
   * This holds also for double-zeta basis functions with
   * exp(i) = exp(j) and c-coeff(i) != c-coeff(j). */
  float *basis;

  basis_atom_t *basis_set;

  int num_basis_funcs;          /** total number of primitive basis functions */
  int num_basis_atoms;          /** total number of atoms with basis functions */

  /** atomic number per atom in basis set */
  int *atomicnum_per_basisatom;

  /** the total number of atomic shells */
  int num_shells;
  /** number of shells per atom i */
  int *num_shells_per_atom;
  /** number of primitives in shell i */
  int *num_prim_per_shell;
  /** symmetry type of each shell */
  int *shell_symmetry; 

  /** number of occupied spin alpha and beta orbitals */
  int occ_orbitals_A;
  int occ_orbitals_B;

  /** Max. rank of the wave_function matrix.
   * While the actual number of MOs present can be different
   * for each frame, this is the maximum number of 
   * possible occupied and virtual orbitals. */
  int wavef_size;
  int num_orbitals;      /* number of orbitals in file. can be less if pure WF. */

  /** Array of length 3*num_wave_f containing the exponents 
   *  describing the cartesian components of the angular momentum. 
   *  E.g. S={0 0 0}, Px={1 0 0}, Dxy={1 1 0}, or Fyyz={0 2 1}. */
  int *angular_momentum;

  /** this flag tells if the geometry search converged */
  int converged;
  int opt_status;

  /* the structure qm_atom_t was defined to read in data from
   * the Gaussian output file and store it temporarily;
   * it is then copied into the VMD specific arrays at the
   * appropriate point in time;
   * this was partially implemented since the output file does
   * not, e.g., contain the number of atoms per se. One rather
   * has to count them by hand - at that point one could as 
   * well already read in the initial coordinates, atom types ...
   * which is not really supported by the way the VMD provided
   * function are arranged....this implementation could of
   * course be changed later..... */
  qm_atom_t *initatoms;

  /** initial cell info (in cpmd notation)":
   * a, b/a, c/a, cos(alpha), cos(beta), cos(gamma) */
  float initcell[6];

  /** per timestep data like wavefunctions and scf iterations */
  qm_timestep_t *qm_timestep;

  /** number of trajectory points; single point corresponds to 1 */
  int num_frames;
  int num_frames_sent;
  int num_frames_read;

  int end_of_trajectory;

} gaussiandata;

#endif
