lammps/lib/qmmm/pwqmmm.c

363 lines
12 KiB
C

/* Copyright (C) 2013 Quantum ESPRESSO group
* This file is distributed under the terms of the
* GNU General Public License. See the file `License'
* in the root directory of the present distribution,
* or http://www.gnu.org/copyleft/gpl.txt . */
/* Toplevel wrapper code to launch QM/MM calculations via MPI */
#include <mpi.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include "libqecouple.h"
#include "libqmmm.h"
#ifndef QE_LIBCOUPLE_API_VERSION
#define QE_LIBCOUPLE_API_VERSION 1
#endif
#if QE_LIBCOUPLE_API_VERSION != 1
#error "Unsupported QE coupling API. Want API version 1."
#endif
#include "library.h"
static const char delim[] = " \t\n\r";
int main(int argc, char **argv)
{
MPI_Comm intra_comm, qm_comm, mm_comm;
int me, ncpu, nqm, key, retval;
MPI_Init(&argc, &argv);
MPI_Comm_size(MPI_COMM_WORLD,&ncpu);
MPI_Comm_rank(MPI_COMM_WORLD,&me);
/* we accept just the qm/mm file as the one argument */
if ((argc < 2) || (argc > 3)) {
if (me == 0) fprintf(stderr,"\n usage: %s <qmmm input file> "
"[<number of mm procs>]\n\n",argv[0]);
MPI_Finalize();
return -1;
}
/* parse the qm/mm file */
if (read_qmmm_config(argv[1],&qmmmcfg)) {
if (me == 0) fputs("\n Error parsing QM/MM config file\n\n",stderr);
MPI_Finalize();
return -2;
}
key = 2;
if (argc == 3) {
key = atoi(argv[2]);
}
qmmmcfg.nmm = key;
/* sanity checks */
qmmmcfg.comm_mode = QMMM_COMM_MPI;
nqm = ncpu - qmmmcfg.nmm;
retval = 0;
#if 1 // AK: temporary hack
if ( qmmmcfg.nmm != 2 ) {
if (me == 0) {
fprintf( stderr, "\n Error in the number of processors for MM code"
"\n for the time being only two processor are allowed\n");
}
MPI_Finalize();
return -1;
}
#endif
if (me == 0) {
const char *msg;
msg = check_qmmm_config(&qmmmcfg);
if ((nqm < 1) || (qmmmcfg.nmm < 2)) {
msg = "Need at least 2 MM and 1 QM processes";
}
if (msg != NULL) {
retval = 1;
fprintf(stderr,"\n%s\n\n",msg);
}
}
MPI_Bcast(&retval,1,MPI_INT,0,MPI_COMM_WORLD);
if (retval != 0) {
MPI_Finalize();
return -3;
}
if (me == 0) {
write_qmmm_config(NULL,&qmmmcfg);
printf("\nRunning QM/MM calculation for %d steps on %d procs\n\n",
qmmmcfg.steps, ncpu);
printf("QM system: procs: % 4d | directory: %-16s | input: %-16s|\n",
nqm,qmmmcfg.qmdir,qmmmcfg.qminp);
printf("MM master: procs: % 4d | directory: %-16s | input: %-16s|\n",
qmmmcfg.nmm-1,qmmmcfg.madir,qmmmcfg.mainp);
printf("MM slave: procs: % 4d | directory: %-16s | input: %-16s|\n\n",
1,qmmmcfg.sldir,qmmmcfg.slinp);
}
/* set up MPI communication */
/* process partitioning: QM | MM master | MM slave */
qmmmcfg.role = QMMM_ROLE_QM;
if (me >= nqm) qmmmcfg.role = QMMM_ROLE_MASTER;
if (me == (ncpu-1)) qmmmcfg.role = QMMM_ROLE_SLAVE;
MPI_Comm_split(MPI_COMM_WORLD, qmmmcfg.role, me, &intra_comm);
qmmmcfg.my_comm = MPI_Comm_c2f(intra_comm);
/* qm to mm-master inter communicator */
key = MPI_UNDEFINED;
if ((me == 0) || (me == nqm)) key = 1;
MPI_Comm_split(MPI_COMM_WORLD, key, ncpu-me, &qm_comm);
qmmmcfg.qm_comm = MPI_Comm_c2f(qm_comm);
/* mm-slave to mm-master inter communicator */
key = MPI_UNDEFINED;
if ((me == (ncpu-1)) || (me == nqm)) key = 1;
MPI_Comm_split(MPI_COMM_WORLD, key, me, &mm_comm);
qmmmcfg.mm_comm = MPI_Comm_c2f(mm_comm);
if (qmmmcfg.role == QMMM_ROLE_QM) {
FILE *fp;
int nimage,npots,npool,ntg,nband,ndiag;
MPI_Comm_rank(intra_comm,&me);
if (chdir(qmmmcfg.qmdir)) {
if (me == 0) fputs("failure to change into QM directory\n",stderr);
MPI_Abort(MPI_COMM_WORLD, 10);
}
/* check if input file is available and readable */
fp = fopen(qmmmcfg.qminp,"r");
if (fp == NULL) {
if (me == 0) fprintf(stderr,"failure to open QM input for "
"reading: %s\n", strerror(errno));
MPI_Abort(MPI_COMM_WORLD, 20);
} else fclose(fp);
/* redirect output to file, if requested */
if (strcmp(qmmmcfg.qmout,"NULL") != 0) {
fp = freopen(qmmmcfg.qmout,"w",stdout);
if (fp == NULL) {
if (me == 0) fprintf(stderr,"failure to open QM output for "
"writing: %s\n", strerror(errno));
MPI_Abort(MPI_COMM_WORLD, 30);
}
}
/* parse additional command line flags for pw.x */
nimage=npots=npool=ntg=nband=ndiag=1;
if (qmmmcfg.qmarg != NULL) {
char *ptr = strtok(qmmmcfg.qmarg,delim);
do {
/* -nimage parallelization is not supported with QM/MM
* -npot parallelization has been removed from QE */
if ((strncmp("-nk",ptr,3) == 0)
|| (strncmp("-npoo",ptr,5) == 0)) {
ptr=strtok(NULL,delim);
npool=atoi(ptr);
} else if (strncmp("-nt",ptr,3) == 0) {
ptr=strtok(NULL,delim);
ntg=atoi(ptr);
} else if (strncmp("-nb",ptr,3) == 0) {
ptr=strtok(NULL,delim);
nband=atoi(ptr);
} else if ((strncmp("-nd",ptr,3) == 0)
|| (strncmp("-no",ptr,3) == 0)
|| (strcmp("-nproc_diag",ptr) == 0)
|| (strcmp("-nproc_ortho",ptr) == 0)) {
ptr=strtok(NULL,delim);
ndiag=atoi(ptr);
} else {
if (me == 0) {
fprintf(stderr,"QM: ignoring flag '%s", ptr);
ptr=strtok(NULL,delim);
fprintf(stderr," %s'\n",ptr);
} else strtok(NULL,delim);
}
} while ((ptr=strtok(NULL,delim)));
}
retval = 0;
/* setup and call Q-E. */
c2qmmm_mpi_config(qmmmcfg.qmmm_mode, qmmmcfg.qm_comm,
qmmmcfg.verbose, qmmmcfg.steps);
c2libpwscf(qmmmcfg.my_comm, nimage, npots, npool, ntg, nband, ndiag,
&retval, qmmmcfg.qminp);
if (strcmp(qmmmcfg.qmout,"NULL") != 0)
fclose(fp);
} else if (qmmmcfg.role == QMMM_ROLE_MASTER) {
FILE *fp;
char *suffix;
void *lmp;
MPI_Comm_rank(intra_comm,&me);
if (chdir(qmmmcfg.madir)) {
if (me == 0)
fputs("failure to change into MM master directory\n",stderr);
MPI_Abort(MPI_COMM_WORLD, 10);
}
/* check if input file is available and readable */
fp = fopen(qmmmcfg.mainp,"r");
if (fp == NULL) {
if (me == 0) fprintf(stderr,"failure to open MM master input "
"for reading: %s\n", strerror(errno));
MPI_Abort(MPI_COMM_WORLD, 20);
} else fclose(fp);
/* redirect output to file, if requested */
if (strcmp(qmmmcfg.maout,"NULL") != 0) {
fp = freopen(qmmmcfg.maout,"w",stdout);
if (fp == NULL) {
if (me == 0) fprintf(stderr,"failure to open MM master output "
"for writing: %s\n", strerror(errno));
MPI_Abort(MPI_COMM_WORLD, 30);
}
}
/* parse additional support command line flags for LAMMPS */
if (qmmmcfg.maarg != NULL) {
char *ptr = strtok(qmmmcfg.maarg,delim);
do {
if ((strncmp("-sf",ptr,3) == 0)
|| (strncmp("-suffix",ptr,7) == 0)) {
ptr=strtok(NULL,delim);
suffix=strdup(ptr);
} else {
if (me == 0)
fprintf(stderr,"unsupported LAMMPS flag: %s\n",ptr);
MPI_Abort(MPI_COMM_WORLD, 40);
}
} while ((ptr=strtok(NULL,delim)));
}
char *lmpargs[5];
lmpargs[0] = strdup("MM Master");
lmpargs[1] = strdup("-log");
lmpargs[2] = strdup("none");
lmpargs[3] = strdup("-echo");
lmpargs[4] = strdup("screen");
lammps_open(5, lmpargs, intra_comm, &lmp);
lammps_file(lmp,qmmmcfg.mainp);
char runcmd[1024];
sprintf(runcmd,"run %d\n",qmmmcfg.steps);
lammps_command(lmp,runcmd);
if (qmmmcfg.restart != NULL) {
sprintf(runcmd,"write_restart %s\n",qmmmcfg.restart);
lammps_command(lmp,runcmd);
}
lammps_close(lmp);
fputs("MM: master done\n",stderr);
if (strcmp(qmmmcfg.maout,"NULL") != 0)
fclose(fp);
retval = 0;
} else if (qmmmcfg.role == QMMM_ROLE_SLAVE) {
FILE *fp;
char *suffix;
void *lmp;
MPI_Comm_rank(intra_comm,&me);
if (chdir(qmmmcfg.sldir)) {
if (me == 0)
fputs("failure to change into MM slave directory\n",stderr);
MPI_Abort(MPI_COMM_WORLD, 10);
}
/* check if input file is available and readable */
fp = fopen(qmmmcfg.slinp,"r");
if (fp == NULL) {
if (me == 0) fprintf(stderr,"failure to open MM slave input "
"for reading: %s\n", strerror(errno));
MPI_Abort(MPI_COMM_WORLD, 20);
} else fclose(fp);
/* redirect output to file, if requested */
if (strcmp(qmmmcfg.slout,"NULL") != 0) {
fp = freopen(qmmmcfg.slout,"w",stdout);
if (fp == NULL) {
if (me == 0) fprintf(stderr,"failure to open MM slave output "
"for writing: %s\n", strerror(errno));
MPI_Abort(MPI_COMM_WORLD, 30);
}
}
/* parse additional support command line flags for LAMMPS */
if (qmmmcfg.slarg != NULL) {
char *ptr = strtok(qmmmcfg.maarg,delim);
do {
if ((strncmp("-sf",ptr,3) == 0)
|| (strncmp("-suffix",ptr,7) == 0)) {
ptr=strtok(NULL,delim);
suffix=strdup(ptr);
} else {
if (me == 0)
fprintf(stderr,"unsupported LAMMPS flag: %s\n",ptr);
MPI_Abort(MPI_COMM_WORLD, 40);
}
} while ((ptr=strtok(NULL,delim)));
}
char *lmpargs[5];
lmpargs[0] = strdup("MM slave");
lmpargs[1] = strdup("-log");
lmpargs[2] = strdup("none");
lmpargs[3] = strdup("-echo");
lmpargs[4] = strdup("screen");
lammps_open(5, lmpargs, intra_comm, &lmp);
lammps_file(lmp,qmmmcfg.slinp);
char runcmd[64];
sprintf(runcmd,"run %d\n",qmmmcfg.steps);
lammps_command(lmp,runcmd);
lammps_close(lmp);
fputs("MM: slave done\n",stderr);
if (strcmp(qmmmcfg.slout,"NULL") != 0)
fclose(fp);
retval = 0;
} else {
fputs("\n how on earth did you end up here?\n\n",stderr);
retval = 0;
}
MPI_Barrier(MPI_COMM_WORLD);
MPI_Comm_rank(MPI_COMM_WORLD,&me);
if (me == 0) fputs("\nQM/MM run complete.\n",stderr);
MPI_Finalize();
free_qmmm_config(&qmmmcfg);
return retval;
}