#! /bin/bash
#
# Copyright 2008 Parallel Quantum Solutions, Fayetteville, Arkansas, USA
#
# http://www.pqs-chem.com
#
# sales@pqs-chem.com
#
# Script to launch a PQS calculation.
# It can be used to run serial jobs or parallel jobs using PVM
# or various MPI flavors.
# The scripts looks for the PQS environmental variables and sets
# them to default values if they are not defined.
# It checks for the presence of the input file and
# it generates an error if it is not found.
# Before running a job, an exixting .out file will
# be appended to the corresponding .old file
#
# Default PQS_ROOT value
#
if [ -z "$PQS_ROOT" ]; then
        export PQS_ROOT=/usr/local/share/PQS
fi
#
# Default PQS_SCRDIR value
#
if [ -z "$PQS_SCRDIR" ]; then
        export PQS_SCRDIR=/scr/${USER}
fi
#
# Default PQS_BASDIR value
#
if [ -z "$PQS_BASDIR" ]; then
        export PQS_BASDIR=${PQS_ROOT}/BASDIR
fi
#
# Default parallel environment
# 
#   1  PVM
#   2  MPICH1
#   3  MPICH2
#   4  QLOGIC MPI
#   5  OPENMPI
#
p_type=1 
#
# function to print the help message
#
PrintHelp()
{
  echo
  echo "USAGE:"
  echo "Single-processor jobs:"
  echo "       pqs molecule-name"
  echo
  echo "Parallel jobs:"
  echo "       pqs molecule-name nslaves [-f machine-file] [-pvm | -mpi1 | -mpi2 | -qmpi | -ompi]"
  echo
  if [ ${p_type} -eq 1 ]; then
          echo "       the default parallel environment is PVM"
  elif [ ${p_type} -eq 2 ]; then
          echo "       the default parallel environment is MPICH1"
  elif [ ${p_type} -eq 3 ]; then
          echo "       the default parallel environment is MPICH2"
  elif [ ${p_type} -eq 4 ]; then
          echo "       the default parallel environment is QLOGIC MPI"
  elif [ ${p_type} -eq 5 ]; then
          echo "       the default parallel environment is OPENMPI"
  else
          echo "       default parallel environment unknown"
  fi
  echo
  exit 0
}
#
# function to pass an argument to the PQS executables (-v, -c)
#
RunArgument()
{
  echo
  echo "PQS serial:"
  echo
  ${PQS_ROOT}/pqs.x $1
  if [ -x "${PQS_ROOT}/pqs_pvm.x" ];then
        echo
        echo "PQS Parallel (PVM):"
        echo
        ${PQS_ROOT}/pqs_pvm.x $1
  fi
  if [ -x "${PQS_ROOT}/pqs_mpi1.x" ];then
        echo
        echo "PQS Parallel (MPICH1):"
        echo
        ${PQS_ROOT}/pqs_mpi1.x $1
  fi
  if [ -x "${PQS_ROOT}/pqs_mpi2.x" ];then
        echo
        echo "PQS Parallel (MPICH2):"
        echo
        ${PQS_ROOT}/pqs_mpi2.x $1
  fi
  if [ -x "${PQS_ROOT}/pqs_qmpi.x" ];then
        echo
        echo "PQS Parallel (QMPI):"
        echo
        ${PQS_ROOT}/pqs_qmpi.x $1
  fi
  if [ -x "${PQS_ROOT}/pqs_ompi.x" ];then
        echo
        echo "PQS Parallel (OPENMPI):"
        echo
        ${PQS_ROOT}/pqs_ompi.x $1
  fi
  if [ -x "${PQS_ROOT}/pqs_pompi.x" ];then
        echo
        echo "PQS Parallel (OPENMPI-infinipath):"
        echo
        ${PQS_ROOT}/pqs_pompi.x $1
  fi
  echo
  exit 0
}
#
# function to get the job name
#
GetJobName()
{
  jobname=${1/%.input}
  jobname=${jobname/%.inp}
  jobname=${jobname/%.com}
  jobname=${jobname/%.pqs}
}
#
# function to get the input name
#
GetInputName()
{
  if [ -r "$1" ]; then
          inpname=$1;
  elif [ -r "$1.input" ]; then
          inpname=$1.input;
  elif [ -r "$1.inp" ]; then
          inpname=$1.inp;
  elif [ -r "$1.com" ]; then
          inpname=$1.com;
  elif [ -r "$1.pqs" ]; then
          inpname=$1.pqs;
  else
          echo
          echo "Cannot find the PQS input file for '$1'."
          echo "Valid input names are:"
          echo "'$1.input'"
          echo "'$1.inp'"
          echo "'$1.com'"
          echo "'$1.pqs'"
          echo 
          echo  "Please make sure one of these files exists and is readable."
          echo 
          exit 1;
  fi
}
#
# function to run a serial job
#
RunSerial()
{
  ${PQS_ROOT}/pqs.x "$1"  > ${jobname}.messages 2>&1
}
#
# function to run a PVM job
#
RunPVM()
{
  if [ ! -x "${PQS_ROOT}/pqs_pvm.x" ];then
          echo "ERROR: PQS PVM executable not found"
          exit 1
  fi
  if [ -n "$3" ]; then
          ${PQS_ROOT}/pqs_pvm.x -f "$3" -np $2 "$1"  > ${jobname}.messages 2>&1
  else
          echo reset | pvm > /dev/null
          echo "The PVM virtual machine has been reset"
          ${PQS_ROOT}/pqs_pvm.x -np $2 "$1"  > ${jobname}.messages 2>&1
  fi
}
#
# function to run a MPICH1 job
#
RunMPI1()
{
  if [ ! -x "${PQS_ROOT}/pqs_mpi1.x" ];then
          echo "ERROR: PQS MPICH1 executable not found"
          exit 1
  fi
  if [ -n "$3" ]; then
          mpirun -np $2 -machinefile "$3" ${PQS_ROOT}/pqs_mpi1.x "$1"  > ${jobname}.messages 2>&1
  else
          mpirun -np $2 ${PQS_ROOT}/pqs_mpi1.x "$1"  > ${jobname}.messages 2>&1
  fi
}
#
# function to run a MPICH2 job
#
RunMPI2()
{
  if [ ! -x "${PQS_ROOT}/pqs_mpi2.x" ];then
          echo "ERROR: PQS MPICH2 executable not found"
          exit 1
  fi
  mpiexec -np $2 ${PQS_ROOT}/pqs_mpi2.x "$1"  > ${jobname}.messages 2>&1
}
#
# function to run a Qlogic MPI job
#
RunQMPI()
{
  if [ ! -x "${PQS_ROOT}/pqs_qmpi.x" ];then
          echo "ERROR: PQS Qlogic MPI executable not found"
          exit 1
  fi
  if [ -n "$3" ]; then
          mpirun -disable-mpi-progress-check -np $2 -machinefile "$3" ${PQS_ROOT}/pqs_qmpi.x "$1"  > ${jobname}.messages 2>&1
  else
          mpirun -disable-mpi-progress-check -np $2 ${PQS_ROOT}/pqs_qmpi.x "$1"  > ${jobname}.messages 2>&1
  fi
}
#
# function to run a OPENMPI job
#
RunOMPI()
{
  if [  -x "${PQS_ROOT}/pqs_ompi.x" ];then
          pqsexe=pqs_ompi.x
  elif [ -x "${PQS_ROOT}/pqs_pompi.x" ];then
          pqsexe=pqs_pompi.x
  else
          echo "ERROR: PQS OPENMPI executable not found"
          exit 1
  fi
  if [ -n "$3" ]; then
          mpirun -x PQS_ROOT -x PQS_SCRDIR -x PQS_BASDIR -np $2 -machinefile "$3" ${PQS_ROOT}/${pqsexe} "$1"  > ${jobname}.messages 2>&1
  else
          mpirun -x PQS_ROOT -x PQS_SCRDIR -x PQS_BASDIR -np $2 ${PQS_ROOT}/${pqsexe} "$1"  > ${jobname}.messages 2>&1
  fi
}
#
# Consistency check for the input arguments
#
if [ $# == 0 ]; then
        PrintHelp;
fi
#
# argument processing
#
while [ -n "${1}" ]; do
         case "${1}" in
            -h | -H | -help     | --help ) 
                      PrintHelp;;
            -v | -V | -version  | --version ) 
                      RunArgument -v;;
            -c | -C | -check    | --check ) 
                      RunArgument -c;;
            -l | -L | -lockcode | --lockcode ) ${PQS_ROOT}/pqs.x -l; exit 0;;
            -f | -F | -m | -M | -machine | --machine | -machinefile | --machinefile ) 
                      if [ -z "${2}" ]; then 
                              PrintHelp;
                      fi
                      MFILE="${2}"; 
                      shift; 
                      shift;;
            -pvm  | -PVM | --pvm | --PVM ) p_type=1; shift;;
            -mpi  | -MPI | --mpi | --MPI ) p_type=2; shift;;
            -mpi1 | -MPI1 | --mpi1 | --MPI1 ) p_type=2; shift;;
            -mpi2 | -MPI2 | --mpi2 | --MPI2 ) p_type=3; shift;;
            -qmpi | -QMPI | --qmpi | --QMPI ) p_type=4; shift;;
            -ompi | -OMPI | --ompi | --OMPI ) p_type=5; shift;;
            * ) 
                      if [ -z "${one}" ]; then
                          one="${1}";
                      elif [ -z "${two}" ]; then
                              two="${1}";
                      else
                          PrintHelp;
                      fi
                      shift;;
         esac
done
#
# if there are two arguments, the second must be 
# un unsigned integer number
#
if [ -n "${two}" ]; then
        if [ -n "${two//[[:digit:]]}" ]; then
                PrintHelp;
        fi
fi
#
# set number of processes
#
NPROCS=1
if [ -n "${two}" ] && [ ${two} -gt 1 ]; then
        ((NPROCS= ${two} + 1))
fi
GetJobName "${one}"
GetInputName "$jobname"
#
# store the old output file, if it exists
#
if [ -e "${jobname}.out" ]; then
        cat "${jobname}".out >> "${jobname}".old
        rm -f "${jobname}".out;
fi
#
# run the job
#
if [ ${NPROCS} -lt 2 ]; then
        echo "Running single processor job '${jobname}'"
        RunSerial "${inpname}";
else
        if [ -n "${MFILE}" ]; then 
                # test if machine file exists
                if [ ! -r "${MFILE}" ]; then
                        echo
                        echo  "The machine file '${MFILE}' does not exist or is not readable."
                        echo
                        exit 1;
                fi
        fi
        if [ ${p_type} -eq 1 ]; then
                echo "Running parallel job '${jobname}' with $((${NPROCS} -1)) slaves using PVM"
                RunPVM "${inpname}" ${NPROCS} "${MFILE}";
        elif [ ${p_type} -eq 2 ]; then
                echo "Running parallel job '${jobname}' with $((${NPROCS} -1)) slaves using MPI-1"
                RunMPI1 "${inpname}" ${NPROCS} "${MFILE}";
        elif [ ${p_type} -eq 3 ]; then
                echo "Running parallel job '${jobname}' with $((${NPROCS} -1)) slaves using MPI-2"
                RunMPI2 "${inpname}" ${NPROCS} "${MFILE}";
        elif [ ${p_type} -eq 4 ]; then
                echo "Running parallel job '${jobname}' with $((${NPROCS} -1)) slaves using Qlogic MPI"
                RunQMPI "${inpname}" ${NPROCS} "${MFILE}";
        elif [ ${p_type} -eq 5 ]; then
                echo "Running parallel job '${jobname}' with $((${NPROCS} -1)) slaves using OPENMPI"
                RunOMPI "${inpname}" ${NPROCS} "${MFILE}";
        else
                echo "ERROR: unknown parallel environment"
                exit 1
        fi
fi
#
# append the console messages to the output files
#
cat ${jobname}.messages >> "${jobname}".out
cat ${jobname}.messages >> "${jobname}".log
