diff --git a/src/GPU/pppm_gpu.cpp b/src/GPU/pppm_gpu.cpp index 4724d516c..a8138067d 100644 --- a/src/GPU/pppm_gpu.cpp +++ b/src/GPU/pppm_gpu.cpp @@ -1,984 +1,714 @@ /* ---------------------------------------------------------------------- LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator http://lammps.sandia.gov, Sandia National Laboratories Steve Plimpton, sjplimp@sandia.gov Copyright (2003) Sandia Corporation. Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains certain rights in this software. This software is distributed under the GNU General Public License. See the README file in the top-level LAMMPS directory. ------------------------------------------------------------------------- */ /* ---------------------------------------------------------------------- Contributing authors: Mike Brown (ORNL), Axel Kohlmeyer (Temple) ------------------------------------------------------------------------- */ #include "lmptype.h" #include "mpi.h" #include "string.h" #include "stdio.h" #include "stdlib.h" #include "math.h" #include "pppm_gpu.h" #include "atom.h" #include "comm.h" +#include "commgrid.h" #include "neighbor.h" #include "force.h" #include "pair.h" #include "bond.h" #include "angle.h" #include "domain.h" #include "fft3d_wrap.h" #include "remap_wrap.h" #include "gpu_extra.h" #include "math_const.h" #include "memory.h" #include "error.h" #include "update.h" #include "universe.h" using namespace LAMMPS_NS; using namespace MathConst; #define MAXORDER 7 #define OFFSET 16384 #define SMALL 0.00001 #define LARGE 10000.0 #define EPS_HOC 1.0e-7 +enum{REVERSE_RHO}; +enum{FORWARD_IK,FORWARD_AD,FORWARD_IK_PERATOM,FORWARD_AD_PERATOM}; + #ifdef FFT_SINGLE #define ZEROF 0.0f #define ONEF 1.0f #else #define ZEROF 0.0 #define ONEF 1.0 #endif -// External functions from cuda library for atom decomposition +// external functions from cuda library for atom decomposition #ifdef FFT_SINGLE #define PPPM_GPU_API(api) pppm_gpu_ ## api ## _f #else #define PPPM_GPU_API(api) pppm_gpu_ ## api ## _d #endif FFT_SCALAR* PPPM_GPU_API(init)(const int nlocal, const int nall, FILE *screen, const int order, const int nxlo_out, const int nylo_out, const int nzlo_out, const int nxhi_out, const int nyhi_out, const int nzhi_out, FFT_SCALAR **rho_coeff, FFT_SCALAR **_vd_brick, const double slab_volfactor, const int nx_pppm, const int ny_pppm, const int nz_pppm, const bool split, int &success); void PPPM_GPU_API(clear)(const double poisson_time); int PPPM_GPU_API(spread)(const int ago, const int nlocal, const int nall, double **host_x, int *host_type, bool &success, double *host_q, double *boxlo, const double delxinv, const double delyinv, const double delzinv); void PPPM_GPU_API(interp)(const FFT_SCALAR qqrd2e_scale); double PPPM_GPU_API(bytes)(); void PPPM_GPU_API(forces)(double **f); /* ---------------------------------------------------------------------- */ PPPMGPU::PPPMGPU(LAMMPS *lmp, int narg, char **arg) : PPPM(lmp, narg, arg) { if (narg != 1) error->all(FLERR,"Illegal kspace_style pppm/gpu command"); density_brick_gpu = vd_brick = NULL; kspace_split = false; im_real_space = false; GPU_EXTRA::gpu_ready(lmp->modify, lmp->error); } /* ---------------------------------------------------------------------- free all memory ------------------------------------------------------------------------- */ PPPMGPU::~PPPMGPU() { PPPM_GPU_API(clear)(poisson_time); } /* ---------------------------------------------------------------------- called once before run ------------------------------------------------------------------------- */ void PPPMGPU::init() { + // PPPM init manages all arrays except density_brick_gpu and vd_brick + // thru its deallocate(), allocate() + // NOTE: could free density_brick and vdxyz_brick after PPPM allocates them, + // before allocating db_gpu and vd_brick down below, if don't need, + // if do this, make sure to set them to NULL + // NOTE: delete/realloc of cg necessary b/c packing 4 values per grid pt, + // not 3 as PPPM does - probably a better way to account for this + // in PPPM::init() + + destroy_3d_offset(density_brick_gpu,nzlo_out,nylo_out); + destroy_3d_offset(vd_brick,nzlo_out,nylo_out); + density_brick_gpu = vd_brick = NULL; + PPPM::init(); + if (differentiation_flag == 0) { + delete cg; + int (*procneigh)[2] = comm->procneigh; + cg = new CommGrid(lmp,world,4,1, + nxlo_in,nxhi_in,nylo_in,nyhi_in,nzlo_in,nzhi_in, + nxlo_out,nxhi_out,nylo_out,nyhi_out,nzlo_out,nzhi_out, + procneigh[0][0],procneigh[0][1],procneigh[1][0], + procneigh[1][1],procneigh[2][0],procneigh[2][1]); + } + + // unsupported option + if (differentiation_flag == 1) - error->all(FLERR,"Cannot (yet) do analytic differentiation with pppm/gpu."); + error->all(FLERR,"Cannot (yet) do analytic differentiation with pppm/gpu"); if (strcmp(update->integrate_style,"verlet/split") == 0) { kspace_split=true; old_nlocal = 0; } if (kspace_split && universe->iworld == 0) { im_real_space = true; return; } // GPU precision specific init if (order>8) error->all(FLERR,"Cannot use order greater than 8 with pppm/gpu."); PPPM_GPU_API(clear)(poisson_time); int success; FFT_SCALAR *data, *h_brick; h_brick = PPPM_GPU_API(init)(atom->nlocal, atom->nlocal+atom->nghost, screen, order, nxlo_out, nylo_out, nzlo_out, nxhi_out, nyhi_out, nzhi_out, rho_coeff, &data, slab_volfactor,nx_pppm,ny_pppm,nz_pppm, kspace_split,success); GPU_EXTRA::check_flag(success,error,world); + // allocate density_brick_gpu and vd_brick + density_brick_gpu = create_3d_offset(nzlo_out,nzhi_out,nylo_out,nyhi_out, nxlo_out,nxhi_out,"pppm:density_brick_gpu",h_brick,1); vd_brick = create_3d_offset(nzlo_out,nzhi_out,nylo_out,nyhi_out, nxlo_out,nxhi_out,"pppm:vd_brick",data,4); - poisson_time=0; + poisson_time = 0.0; } /* ---------------------------------------------------------------------- compute the PPPMGPU long-range force, energy, virial ------------------------------------------------------------------------- */ void PPPMGPU::compute(int eflag, int vflag) { int nago; if (kspace_split) { if (im_real_space) return; if (atom->nlocal > old_nlocal) { nago=0; old_nlocal = atom->nlocal; - } else - nago=1; - } else - nago=neighbor->ago; + } else nago = 1; + } else nago = neighbor->ago; // set energy/virial flags // invoke allocate_peratom() if needed for first time if (eflag || vflag) ev_setup(eflag,vflag); else evflag = evflag_atom = eflag_global = vflag_global = eflag_atom = vflag_atom = 0; if (evflag_atom && !peratom_allocate_flag) { allocate_peratom(); peratom_allocate_flag = 1; } bool success = true; int flag=PPPM_GPU_API(spread)(nago, atom->nlocal, atom->nlocal + atom->nghost, atom->x, atom->type, success, atom->q, domain->boxlo, delxinv, delyinv, delzinv); if (!success) error->one(FLERR,"Insufficient memory on accelerator"); if (flag != 0) error->one(FLERR,"Out of range atoms - cannot compute PPPM"); // If need per-atom energies/virials, also do particle map on host // concurrently with GPU calculations if (evflag_atom) { memory->destroy(part2grid); nmax = atom->nmax; memory->create(part2grid,nmax,3,"pppm:part2grid"); particle_map(); } int i,j; // convert atoms from box to lamda coords if (triclinic == 0) boxlo = domain->boxlo; else { boxlo = domain->boxlo_lamda; domain->x2lamda(atom->nlocal); } - double t3=MPI_Wtime(); + double t3 = MPI_Wtime(); // all procs communicate density values from their ghost cells // to fully sum contribution in their 3d bricks // remap from 3d decomposition to FFT decomposition + cg->reverse_comm(this,REVERSE_RHO); brick2fft(); // compute potential gradient on my FFT grid and // portion of e_long on this proc's FFT grid // return gradients (electric fields) in 3d brick decomposition poisson(); - // all procs communicate E-field values to fill ghost cells - // surrounding their 3d bricks + // all procs communicate E-field values + // to fill ghost cells surrounding their 3d bricks + + if (differentiation_flag == 1) cg->forward_comm(this,FORWARD_AD); + else cg->forward_comm(this,FORWARD_IK); - fillbrick(); + // extra per-atom energy/virial communication + + if (evflag_atom) { + if (differentiation_flag == 1 && vflag_atom) + cg_peratom->forward_comm(this,FORWARD_AD_PERATOM); + else if (differentiation_flag == 0) + cg_peratom->forward_comm(this,FORWARD_IK_PERATOM); + } - poisson_time+=MPI_Wtime()-t3; + poisson_time += MPI_Wtime()-t3; // calculate the force on my particles FFT_SCALAR qscale = force->qqrd2e * scale; PPPM_GPU_API(interp)(qscale); - // Compute per-atom energy/virial on host if requested + // per-atom energy/virial + // energy includes self-energy correction if (evflag_atom) { - fillbrick_peratom(); - fieldforce_peratom(); double *q = atom->q; int nlocal = atom->nlocal; if (eflag_atom) { for (i = 0; i < nlocal; i++) { eatom[i] *= 0.5; eatom[i] -= g_ewald*q[i]*q[i]/MY_PIS + MY_PI2*q[i]*qsum / (g_ewald*g_ewald*volume); eatom[i] *= qscale; } } if (vflag_atom) { for (i = 0; i < nlocal; i++) for (j = 0; j < 6; j++) vatom[i][j] *= 0.5*qscale; } } // sum energy across procs and add in volume-dependent term if (eflag_global) { double energy_all; MPI_Allreduce(&energy,&energy_all,1,MPI_DOUBLE,MPI_SUM,world); energy = energy_all; energy *= 0.5*volume; energy -= g_ewald*qsqsum/1.772453851 + MY_PI2*qsum*qsum / (g_ewald*g_ewald*volume); energy *= qscale; } // sum virial across procs if (vflag_global) { double virial_all[6]; MPI_Allreduce(virial,virial_all,6,MPI_DOUBLE,MPI_SUM,world); for (i = 0; i < 6; i++) virial[i] = 0.5*qscale*volume*virial_all[i]; } // 2d slab correction if (slabflag) slabcorr(); // convert atoms back from lamda to box coords if (triclinic) domain->lamda2x(atom->nlocal); if (kspace_split) PPPM_GPU_API(forces)(atom->f); } /* ---------------------------------------------------------------------- - allocate memory that depends on # of K-vectors and order -------------------------------------------------------------------------- */ - -void PPPMGPU::allocate() -{ - memory->create(density_fft,nfft_both,"pppm:density_fft"); - memory->create(greensfn,nfft_both,"pppm:greensfn"); - memory->create(work1,2*nfft_both,"pppm:work1"); - memory->create(work2,2*nfft_both,"pppm:work2"); - memory->create(vg,nfft_both,6,"pppm:vg"); - - memory->create1d_offset(fkx,nxlo_fft,nxhi_fft,"pppm:fkx"); - memory->create1d_offset(fky,nylo_fft,nyhi_fft,"pppm:fky"); - memory->create1d_offset(fkz,nzlo_fft,nzhi_fft,"pppm:fkz"); - - memory->create(buf1,nbuf,"pppm:buf1"); - memory->create(buf2,nbuf,"pppm:buf2"); - - // summation coeffs - - memory->create(gf_b,order,"pppm:gf_b"); - memory->create2d_offset(rho1d,3,-order/2,order/2,"pppm:rho1d"); - memory->create2d_offset(drho1d,3,-order/2,order/2,"pppm:drho1d"); - memory->create2d_offset(rho_coeff,order,(1-order)/2,order/2,"pppm:rho_coeff"); - memory->create2d_offset(drho_coeff,order,(1-order)/2,order/2, - "pppm:drho_coeff"); - - // create 2 FFTs and a Remap - // 1st FFT keeps data in FFT decompostion - // 2nd FFT returns data in 3d brick decomposition - // remap takes data from 3d brick to FFT decomposition - - int tmp; - - fft1 = new FFT3d(lmp,world,nx_pppm,ny_pppm,nz_pppm, - nxlo_fft,nxhi_fft,nylo_fft,nyhi_fft,nzlo_fft,nzhi_fft, - nxlo_fft,nxhi_fft,nylo_fft,nyhi_fft,nzlo_fft,nzhi_fft, - 0,0,&tmp); - - fft2 = new FFT3d(lmp,world,nx_pppm,ny_pppm,nz_pppm, - nxlo_fft,nxhi_fft,nylo_fft,nyhi_fft,nzlo_fft,nzhi_fft, - nxlo_in,nxhi_in,nylo_in,nyhi_in,nzlo_in,nzhi_in, - 0,0,&tmp); - - remap = new Remap(lmp,world, - nxlo_in,nxhi_in,nylo_in,nyhi_in,nzlo_in,nzhi_in, - nxlo_fft,nxhi_fft,nylo_fft,nyhi_fft,nzlo_fft,nzhi_fft, - 1,0,0,FFT_PRECISION); -} - -/* ---------------------------------------------------------------------- - deallocate memory that depends on # of K-vectors and order -------------------------------------------------------------------------- */ - -void PPPMGPU::deallocate() -{ - destroy_3d_offset(density_brick_gpu,nzlo_out,nylo_out); - destroy_3d_offset(vd_brick,nzlo_out,nylo_out); - - memory->destroy(density_fft); - memory->destroy(greensfn); - memory->destroy(work1); - memory->destroy(work2); - memory->destroy(vg); - - memory->destroy1d_offset(fkx,nxlo_fft); - memory->destroy1d_offset(fky,nylo_fft); - memory->destroy1d_offset(fkz,nzlo_fft); - - memory->destroy(buf1); - memory->destroy(buf2); - - memory->destroy(gf_b); - memory->destroy2d_offset(rho1d,-order/2); - memory->destroy2d_offset(drho1d,-order/2); - memory->destroy2d_offset(rho_coeff,(1-order)/2); - memory->destroy2d_offset(drho_coeff,(1-order)/2); - - delete fft1; - delete fft2; - delete remap; -} - - -/* ---------------------------------------------------------------------- - ghost-swap to accumulate full density in brick decomposition remap density from 3d brick decomposition to FFT decomposition ------------------------------------------------------------------------- */ void PPPMGPU::brick2fft() { - int i,n,ix,iy,iz; - MPI_Request request; - MPI_Status status; - - // pack my ghosts for +x processor - // pass data to self or +x processor - // unpack and sum recv data into my real cells - - n = 0; - for (iz = nzlo_out; iz <= nzhi_out; iz++) - for (iy = nylo_out; iy <= nyhi_out; iy++) - for (ix = nxhi_in+1; ix <= nxhi_out; ix++) - buf1[n++] = density_brick_gpu[iz][iy][ix]; - - if (comm->procneigh[0][1] == me) - for (i = 0; i < n; i++) buf2[i] = buf1[i]; - else { - MPI_Irecv(buf2,nbuf,MPI_FFT_SCALAR,comm->procneigh[0][0],0,world,&request); - MPI_Send(buf1,n,MPI_FFT_SCALAR,comm->procneigh[0][1],0,world); - MPI_Wait(&request,&status); - } - - n = 0; - for (iz = nzlo_out; iz <= nzhi_out; iz++) - for (iy = nylo_out; iy <= nyhi_out; iy++) - for (ix = nxlo_in; ix < nxlo_in+nxlo_ghost; ix++) - density_brick_gpu[iz][iy][ix] += buf2[n++]; - - // pack my ghosts for -x processor - // pass data to self or -x processor - // unpack and sum recv data into my real cells - - n = 0; - for (iz = nzlo_out; iz <= nzhi_out; iz++) - for (iy = nylo_out; iy <= nyhi_out; iy++) - for (ix = nxlo_out; ix < nxlo_in; ix++) - buf1[n++] = density_brick_gpu[iz][iy][ix]; - - if (comm->procneigh[0][0] == me) - for (i = 0; i < n; i++) buf2[i] = buf1[i]; - else { - MPI_Irecv(buf2,nbuf,MPI_FFT_SCALAR,comm->procneigh[0][1],0,world,&request); - MPI_Send(buf1,n,MPI_FFT_SCALAR,comm->procneigh[0][0],0,world); - MPI_Wait(&request,&status); - } - - n = 0; - for (iz = nzlo_out; iz <= nzhi_out; iz++) - for (iy = nylo_out; iy <= nyhi_out; iy++) - for (ix = nxhi_in-nxhi_ghost+1; ix <= nxhi_in; ix++) - density_brick_gpu[iz][iy][ix] += buf2[n++]; - - // pack my ghosts for +y processor - // pass data to self or +y processor - // unpack and sum recv data into my real cells - - n = 0; - for (iz = nzlo_out; iz <= nzhi_out; iz++) - for (iy = nyhi_in+1; iy <= nyhi_out; iy++) - for (ix = nxlo_in; ix <= nxhi_in; ix++) - buf1[n++] = density_brick_gpu[iz][iy][ix]; - - if (comm->procneigh[1][1] == me) - for (i = 0; i < n; i++) buf2[i] = buf1[i]; - else { - MPI_Irecv(buf2,nbuf,MPI_FFT_SCALAR,comm->procneigh[1][0],0,world,&request); - MPI_Send(buf1,n,MPI_FFT_SCALAR,comm->procneigh[1][1],0,world); - MPI_Wait(&request,&status); - } - - n = 0; - for (iz = nzlo_out; iz <= nzhi_out; iz++) - for (iy = nylo_in; iy < nylo_in+nylo_ghost; iy++) - for (ix = nxlo_in; ix <= nxhi_in; ix++) - density_brick_gpu[iz][iy][ix] += buf2[n++]; - - // pack my ghosts for -y processor - // pass data to self or -y processor - // unpack and sum recv data into my real cells - - n = 0; - for (iz = nzlo_out; iz <= nzhi_out; iz++) - for (iy = nylo_out; iy < nylo_in; iy++) - for (ix = nxlo_in; ix <= nxhi_in; ix++) - buf1[n++] = density_brick_gpu[iz][iy][ix]; - - if (comm->procneigh[1][0] == me) - for (i = 0; i < n; i++) buf2[i] = buf1[i]; - else { - MPI_Irecv(buf2,nbuf,MPI_FFT_SCALAR,comm->procneigh[1][1],0,world,&request); - MPI_Send(buf1,n,MPI_FFT_SCALAR,comm->procneigh[1][0],0,world); - MPI_Wait(&request,&status); - } + int n,ix,iy,iz; - n = 0; - for (iz = nzlo_out; iz <= nzhi_out; iz++) - for (iy = nyhi_in-nyhi_ghost+1; iy <= nyhi_in; iy++) - for (ix = nxlo_in; ix <= nxhi_in; ix++) - density_brick_gpu[iz][iy][ix] += buf2[n++]; - - // pack my ghosts for +z processor - // pass data to self or +z processor - // unpack and sum recv data into my real cells - - n = 0; - for (iz = nzhi_in+1; iz <= nzhi_out; iz++) - for (iy = nylo_in; iy <= nyhi_in; iy++) - for (ix = nxlo_in; ix <= nxhi_in; ix++) - buf1[n++] = density_brick_gpu[iz][iy][ix]; - - if (comm->procneigh[2][1] == me) - for (i = 0; i < n; i++) buf2[i] = buf1[i]; - else { - MPI_Irecv(buf2,nbuf,MPI_FFT_SCALAR,comm->procneigh[2][0],0,world,&request); - MPI_Send(buf1,n,MPI_FFT_SCALAR,comm->procneigh[2][1],0,world); - MPI_Wait(&request,&status); - } - - n = 0; - for (iz = nzlo_in; iz < nzlo_in+nzlo_ghost; iz++) - for (iy = nylo_in; iy <= nyhi_in; iy++) - for (ix = nxlo_in; ix <= nxhi_in; ix++) - density_brick_gpu[iz][iy][ix] += buf2[n++]; - - // pack my ghosts for -z processor - // pass data to self or -z processor - // unpack and sum recv data into my real cells - - n = 0; - for (iz = nzlo_out; iz < nzlo_in; iz++) - for (iy = nylo_in; iy <= nyhi_in; iy++) - for (ix = nxlo_in; ix <= nxhi_in; ix++) - buf1[n++] = density_brick_gpu[iz][iy][ix]; - - if (comm->procneigh[2][0] == me) - for (i = 0; i < n; i++) buf2[i] = buf1[i]; - else { - MPI_Irecv(buf2,nbuf,MPI_FFT_SCALAR,comm->procneigh[2][1],0,world,&request); - MPI_Send(buf1,n,MPI_FFT_SCALAR,comm->procneigh[2][0],0,world); - MPI_Wait(&request,&status); - } - - n = 0; - for (iz = nzhi_in-nzhi_ghost+1; iz <= nzhi_in; iz++) - for (iy = nylo_in; iy <= nyhi_in; iy++) - for (ix = nxlo_in; ix <= nxhi_in; ix++) - density_brick_gpu[iz][iy][ix] += buf2[n++]; - - // remap from 3d brick decomposition to FFT decomposition // copy grabs inner portion of density from 3d brick // remap could be done as pre-stage of FFT, // but this works optimally on only double values, not complex values n = 0; for (iz = nzlo_in; iz <= nzhi_in; iz++) for (iy = nylo_in; iy <= nyhi_in; iy++) for (ix = nxlo_in; ix <= nxhi_in; ix++) density_fft[n++] = density_brick_gpu[iz][iy][ix]; remap->perform(density_fft,density_fft,work1); } -/* ---------------------------------------------------------------------- - Same as base class - needed to call GPU version of fillbrick_. -------------------------------------------------------------------------- */ - -void PPPMGPU::fillbrick() -{ - if (differentiation_flag == 1) fillbrick_ad(); - else fillbrick_ik(); -} - -/* ---------------------------------------------------------------------- - ghost-swap to fill ghost cells of my brick with field values -------------------------------------------------------------------------- */ - -void PPPMGPU::fillbrick_ik() -{ - int i,n,ix,iy,iz; - MPI_Request request; - MPI_Status status; - - // pack my real cells for +z processor - // pass data to self or +z processor - // unpack and sum recv data into my ghost cells - - n = 0; - int x_lo = nxlo_in * 4; - int x_hi = nxhi_in * 4 + 1; - for (iz = nzhi_in-nzhi_ghost+1; iz <= nzhi_in; iz++) - for (iy = nylo_in; iy <= nyhi_in; iy++) - for (ix = x_lo; ix < x_hi; ix+=4) { - buf1[n++] = vd_brick[iz][iy][ix]; - buf1[n++] = vd_brick[iz][iy][ix+1]; - buf1[n++] = vd_brick[iz][iy][ix+2]; - } - - if (comm->procneigh[2][1] == me) - for (i = 0; i < n; i++) buf2[i] = buf1[i]; - else { - MPI_Irecv(buf2,nbuf,MPI_FFT_SCALAR,comm->procneigh[2][0],0,world,&request); - MPI_Send(buf1,n,MPI_FFT_SCALAR,comm->procneigh[2][1],0,world); - MPI_Wait(&request,&status); - } - - n = 0; - for (iz = nzlo_out; iz < nzlo_in; iz++) - for (iy = nylo_in; iy <= nyhi_in; iy++) - for (ix = x_lo; ix < x_hi; ix+=4) { - vd_brick[iz][iy][ix] = buf2[n++]; - vd_brick[iz][iy][ix+1] = buf2[n++]; - vd_brick[iz][iy][ix+2] = buf2[n++]; - } - - // pack my real cells for -z processor - // pass data to self or -z processor - // unpack and sum recv data into my ghost cells - - n = 0; - for (iz = nzlo_in; iz < nzlo_in+nzlo_ghost; iz++) - for (iy = nylo_in; iy <= nyhi_in; iy++) - for (ix = x_lo; ix < x_hi; ix+=4) { - buf1[n++] = vd_brick[iz][iy][ix]; - buf1[n++] = vd_brick[iz][iy][ix+1]; - buf1[n++] = vd_brick[iz][iy][ix+2]; - } - - if (comm->procneigh[2][0] == me) - for (i = 0; i < n; i++) buf2[i] = buf1[i]; - else { - MPI_Irecv(buf2,nbuf,MPI_FFT_SCALAR,comm->procneigh[2][1],0,world,&request); - MPI_Send(buf1,n,MPI_FFT_SCALAR,comm->procneigh[2][0],0,world); - MPI_Wait(&request,&status); - } - - n = 0; - for (iz = nzhi_in+1; iz <= nzhi_out; iz++) - for (iy = nylo_in; iy <= nyhi_in; iy++) - for (ix = x_lo; ix < x_hi; ix+=4) { - vd_brick[iz][iy][ix] = buf2[n++]; - vd_brick[iz][iy][ix+1] = buf2[n++]; - vd_brick[iz][iy][ix+2] = buf2[n++]; - } - - // pack my real cells for +y processor - // pass data to self or +y processor - // unpack and sum recv data into my ghost cells - - n = 0; - for (iz = nzlo_out; iz <= nzhi_out; iz++) - for (iy = nyhi_in-nyhi_ghost+1; iy <= nyhi_in; iy++) - for (ix = x_lo; ix < x_hi; ix+=4) { - buf1[n++] = vd_brick[iz][iy][ix]; - buf1[n++] = vd_brick[iz][iy][ix+1]; - buf1[n++] = vd_brick[iz][iy][ix+2]; - } - - if (comm->procneigh[1][1] == me) - for (i = 0; i < n; i++) buf2[i] = buf1[i]; - else { - MPI_Irecv(buf2,nbuf,MPI_FFT_SCALAR,comm->procneigh[1][0],0,world,&request); - MPI_Send(buf1,n,MPI_FFT_SCALAR,comm->procneigh[1][1],0,world); - MPI_Wait(&request,&status); - } - - n = 0; - for (iz = nzlo_out; iz <= nzhi_out; iz++) - for (iy = nylo_out; iy < nylo_in; iy++) - for (ix = x_lo; ix < x_hi; ix+=4) { - vd_brick[iz][iy][ix] = buf2[n++]; - vd_brick[iz][iy][ix+1] = buf2[n++]; - vd_brick[iz][iy][ix+2] = buf2[n++]; - } - - // pack my real cells for -y processor - // pass data to self or -y processor - // unpack and sum recv data into my ghost cells - - n = 0; - for (iz = nzlo_out; iz <= nzhi_out; iz++) - for (iy = nylo_in; iy < nylo_in+nylo_ghost; iy++) - for (ix = x_lo; ix < x_hi; ix+=4) { - buf1[n++] = vd_brick[iz][iy][ix]; - buf1[n++] = vd_brick[iz][iy][ix+1]; - buf1[n++] = vd_brick[iz][iy][ix+2]; - } - - if (comm->procneigh[1][0] == me) - for (i = 0; i < n; i++) buf2[i] = buf1[i]; - else { - MPI_Irecv(buf2,nbuf,MPI_FFT_SCALAR,comm->procneigh[1][1],0,world,&request); - MPI_Send(buf1,n,MPI_FFT_SCALAR,comm->procneigh[1][0],0,world); - MPI_Wait(&request,&status); - } - - n = 0; - for (iz = nzlo_out; iz <= nzhi_out; iz++) - for (iy = nyhi_in+1; iy <= nyhi_out; iy++) - for (ix = x_lo; ix < x_hi; ix+=4) { - vd_brick[iz][iy][ix] = buf2[n++]; - vd_brick[iz][iy][ix+1] = buf2[n++]; - vd_brick[iz][iy][ix+2] = buf2[n++]; - } - - // pack my real cells for +x processor - // pass data to self or +x processor - // unpack and sum recv data into my ghost cells - - n = 0; - x_lo = (nxhi_in-nxhi_ghost+1) * 4; - for (iz = nzlo_out; iz <= nzhi_out; iz++) - for (iy = nylo_out; iy <= nyhi_out; iy++) - for (ix = x_lo; ix < x_hi; ix+=4) { - buf1[n++] = vd_brick[iz][iy][ix]; - buf1[n++] = vd_brick[iz][iy][ix+1]; - buf1[n++] = vd_brick[iz][iy][ix+2]; - } - - if (comm->procneigh[0][1] == me) - for (i = 0; i < n; i++) buf2[i] = buf1[i]; - else { - MPI_Irecv(buf2,nbuf,MPI_FFT_SCALAR,comm->procneigh[0][0],0,world,&request); - MPI_Send(buf1,n,MPI_FFT_SCALAR,comm->procneigh[0][1],0,world); - MPI_Wait(&request,&status); - } - - n = 0; - x_lo = nxlo_out * 4; - x_hi = nxlo_in * 4; - for (iz = nzlo_out; iz <= nzhi_out; iz++) - for (iy = nylo_out; iy <= nyhi_out; iy++) - for (ix = x_lo; ix < x_hi; ix+=4) { - vd_brick[iz][iy][ix] = buf2[n++]; - vd_brick[iz][iy][ix+1] = buf2[n++]; - vd_brick[iz][iy][ix+2] = buf2[n++]; - } - - // pack my real cells for -x processor - // pass data to self or -x processor - // unpack and sum recv data into my ghost cells - - n = 0; - x_lo = x_hi; - x_hi = (nxlo_in+nxlo_ghost) * 4; - for (iz = nzlo_out; iz <= nzhi_out; iz++) - for (iy = nylo_out; iy <= nyhi_out; iy++) - for (ix = x_lo; ix < x_hi; ix+=4) { - buf1[n++] = vd_brick[iz][iy][ix]; - buf1[n++] = vd_brick[iz][iy][ix+1]; - buf1[n++] = vd_brick[iz][iy][ix+2]; - } - - if (comm->procneigh[0][0] == me) - for (i = 0; i < n; i++) buf2[i] = buf1[i]; - else { - MPI_Irecv(buf2,nbuf,MPI_FFT_SCALAR,comm->procneigh[0][1],0,world,&request); - MPI_Send(buf1,n,MPI_FFT_SCALAR,comm->procneigh[0][0],0,world); - MPI_Wait(&request,&status); - } - - n = 0; - x_lo = (nxhi_in + 1) * 4; - x_hi = nxhi_out * 4 + 1; - for (iz = nzlo_out; iz <= nzhi_out; iz++) - for (iy = nylo_out; iy <= nyhi_out; iy++) - for (ix = x_lo; ix < x_hi; ix+=4) { - vd_brick[iz][iy][ix] = buf2[n++]; - vd_brick[iz][iy][ix+1] = buf2[n++]; - vd_brick[iz][iy][ix+2] = buf2[n++]; - } -} - /* ---------------------------------------------------------------------- Same code as base class - necessary to call GPU version of poisson_ik ------------------------------------------------------------------------- */ void PPPMGPU::poisson() { if (differentiation_flag == 1) poisson_ad(); else poisson_ik(); } /* ---------------------------------------------------------------------- FFT-based Poisson solver ------------------------------------------------------------------------- */ void PPPMGPU::poisson_ik() { int i,j,k,n; double eng; // transform charge density (r -> k) n = 0; for (i = 0; i < nfft; i++) { work1[n++] = density_fft[i]; work1[n++] = ZEROF; } fft1->compute(work1,work1,1); // if requested, compute energy and virial contribution double scaleinv = 1.0/(nx_pppm*ny_pppm*nz_pppm); double s2 = scaleinv*scaleinv; if (eflag_global || vflag_global) { if (vflag_global) { n = 0; for (i = 0; i < nfft; i++) { eng = s2 * greensfn[i] * (work1[n]*work1[n] + work1[n+1]*work1[n+1]); for (j = 0; j < 6; j++) virial[j] += eng*vg[i][j]; if (eflag_global) energy += eng; n += 2; } } else { n = 0; for (i = 0; i < nfft; i++) { energy += s2 * greensfn[i] * (work1[n]*work1[n] + work1[n+1]*work1[n+1]); n += 2; } } } // scale by 1/total-grid-pts to get rho(k) // multiply by Green's function to get V(k) n = 0; for (i = 0; i < nfft; i++) { work1[n++] *= scaleinv * greensfn[i]; work1[n++] *= scaleinv * greensfn[i]; } // extra FFTs for per-atom energy/virial if (evflag_atom) poisson_peratom(); // compute gradients of V(r) in each of 3 dims by transformimg -ik*V(k) // FFT leaves data in 3d brick decomposition // copy it into inner portion of vdx,vdy,vdz arrays // x direction gradient n = 0; for (k = nzlo_fft; k <= nzhi_fft; k++) for (j = nylo_fft; j <= nyhi_fft; j++) for (i = nxlo_fft; i <= nxhi_fft; i++) { work2[n] = fkx[i]*work1[n+1]; work2[n+1] = -fkx[i]*work1[n]; n += 2; } fft2->compute(work2,work2,-1); n = 0; int x_hi = nxhi_in * 4 + 3; for (k = nzlo_in; k <= nzhi_in; k++) for (j = nylo_in; j <= nyhi_in; j++) for (i = nxlo_in * 4; i < x_hi; i+=4) { vd_brick[k][j][i] = work2[n]; n += 2; } // y direction gradient n = 0; for (k = nzlo_fft; k <= nzhi_fft; k++) for (j = nylo_fft; j <= nyhi_fft; j++) for (i = nxlo_fft; i <= nxhi_fft; i++) { work2[n] = fky[j]*work1[n+1]; work2[n+1] = -fky[j]*work1[n]; n += 2; } fft2->compute(work2,work2,-1); n = 0; for (k = nzlo_in; k <= nzhi_in; k++) for (j = nylo_in; j <= nyhi_in; j++) for (i = nxlo_in * 4 + 1; i < x_hi; i+=4) { vd_brick[k][j][i] = work2[n]; n += 2; } // z direction gradient n = 0; for (k = nzlo_fft; k <= nzhi_fft; k++) for (j = nylo_fft; j <= nyhi_fft; j++) for (i = nxlo_fft; i <= nxhi_fft; i++) { work2[n] = fkz[k]*work1[n+1]; work2[n+1] = -fkz[k]*work1[n]; n += 2; } fft2->compute(work2,work2,-1); n = 0; for (k = nzlo_in; k <= nzhi_in; k++) for (j = nylo_in; j <= nyhi_in; j++) for (i = nxlo_in * 4 + 2; i < x_hi; i+=4) { vd_brick[k][j][i] = work2[n]; n += 2; } } /* ---------------------------------------------------------------------- - Create array using offsets from pinned memory allocation + pack own values to buf to send to another proc +------------------------------------------------------------------------- */ + +void PPPMGPU::pack_forward(int flag, FFT_SCALAR *buf, int nlist, int *list) +{ + int n = 0; + + if (flag == FORWARD_IK) { + int offset; + FFT_SCALAR *src = &vd_brick[nzlo_out][nylo_out][4*nxlo_out]; + for (int i = 0; i < nlist; i++) { + offset = 4*list[i]; + buf[n++] = src[offset++]; + buf[n++] = src[offset++]; + buf[n++] = src[offset]; + } + } else if (flag == FORWARD_AD) { + FFT_SCALAR *src = &u_brick[nzlo_out][nylo_out][nxlo_out]; + for (int i = 0; i < nlist; i++) + buf[i] = src[list[i]]; + } else if (flag == FORWARD_IK_PERATOM) { + FFT_SCALAR *esrc = &u_brick[nzlo_out][nylo_out][nxlo_out]; + FFT_SCALAR *v0src = &v0_brick[nzlo_out][nylo_out][nxlo_out]; + FFT_SCALAR *v1src = &v1_brick[nzlo_out][nylo_out][nxlo_out]; + FFT_SCALAR *v2src = &v2_brick[nzlo_out][nylo_out][nxlo_out]; + FFT_SCALAR *v3src = &v3_brick[nzlo_out][nylo_out][nxlo_out]; + FFT_SCALAR *v4src = &v4_brick[nzlo_out][nylo_out][nxlo_out]; + FFT_SCALAR *v5src = &v5_brick[nzlo_out][nylo_out][nxlo_out]; + for (int i = 0; i < nlist; i++) { + if (eflag_atom) buf[n++] = esrc[list[i]]; + if (vflag_atom) { + buf[n++] = v0src[list[i]]; + buf[n++] = v1src[list[i]]; + buf[n++] = v2src[list[i]]; + buf[n++] = v3src[list[i]]; + buf[n++] = v4src[list[i]]; + buf[n++] = v5src[list[i]]; + } + } + } else if (flag == FORWARD_AD_PERATOM) { + FFT_SCALAR *v0src = &v0_brick[nzlo_out][nylo_out][nxlo_out]; + FFT_SCALAR *v1src = &v1_brick[nzlo_out][nylo_out][nxlo_out]; + FFT_SCALAR *v2src = &v2_brick[nzlo_out][nylo_out][nxlo_out]; + FFT_SCALAR *v3src = &v3_brick[nzlo_out][nylo_out][nxlo_out]; + FFT_SCALAR *v4src = &v4_brick[nzlo_out][nylo_out][nxlo_out]; + FFT_SCALAR *v5src = &v5_brick[nzlo_out][nylo_out][nxlo_out]; + for (int i = 0; i < nlist; i++) { + buf[n++] = v0src[list[i]]; + buf[n++] = v1src[list[i]]; + buf[n++] = v2src[list[i]]; + buf[n++] = v3src[list[i]]; + buf[n++] = v4src[list[i]]; + buf[n++] = v5src[list[i]]; + } + } +} + +/* ---------------------------------------------------------------------- + unpack another proc's own values from buf and set own ghost values +------------------------------------------------------------------------- */ + +void PPPMGPU::unpack_forward(int flag, FFT_SCALAR *buf, int nlist, int *list) +{ + int n = 0; + + if (flag == FORWARD_IK) { + int offset; + FFT_SCALAR *dest = &vdx_brick[nzlo_out][nylo_out][4*nxlo_out]; + for (int i = 0; i < nlist; i++) { + offset = 4*list[i]; + dest[offset++] = buf[n++]; + dest[offset++] = buf[n++]; + dest[offset] = buf[n++]; + } + } else if (flag == FORWARD_AD) { + FFT_SCALAR *dest = &u_brick[nzlo_out][nylo_out][nxlo_out]; + for (int i = 0; i < nlist; i++) + dest[list[i]] = buf[n++]; + } else if (flag == FORWARD_IK_PERATOM) { + FFT_SCALAR *esrc = &u_brick[nzlo_out][nylo_out][nxlo_out]; + FFT_SCALAR *v0src = &v0_brick[nzlo_out][nylo_out][nxlo_out]; + FFT_SCALAR *v1src = &v1_brick[nzlo_out][nylo_out][nxlo_out]; + FFT_SCALAR *v2src = &v2_brick[nzlo_out][nylo_out][nxlo_out]; + FFT_SCALAR *v3src = &v3_brick[nzlo_out][nylo_out][nxlo_out]; + FFT_SCALAR *v4src = &v4_brick[nzlo_out][nylo_out][nxlo_out]; + FFT_SCALAR *v5src = &v5_brick[nzlo_out][nylo_out][nxlo_out]; + for (int i = 0; i < nlist; i++) { + if (eflag_atom) esrc[list[i]] = buf[n++]; + if (vflag_atom) { + v0src[list[i]] = buf[n++]; + v1src[list[i]] = buf[n++]; + v2src[list[i]] = buf[n++]; + v3src[list[i]] = buf[n++]; + v4src[list[i]] = buf[n++]; + v5src[list[i]] = buf[n++]; + } + } + } else if (flag == FORWARD_AD_PERATOM) { + FFT_SCALAR *v0src = &v0_brick[nzlo_out][nylo_out][nxlo_out]; + FFT_SCALAR *v1src = &v1_brick[nzlo_out][nylo_out][nxlo_out]; + FFT_SCALAR *v2src = &v2_brick[nzlo_out][nylo_out][nxlo_out]; + FFT_SCALAR *v3src = &v3_brick[nzlo_out][nylo_out][nxlo_out]; + FFT_SCALAR *v4src = &v4_brick[nzlo_out][nylo_out][nxlo_out]; + FFT_SCALAR *v5src = &v5_brick[nzlo_out][nylo_out][nxlo_out]; + for (int i = 0; i < nlist; i++) { + v0src[list[i]] = buf[n++]; + v1src[list[i]] = buf[n++]; + v2src[list[i]] = buf[n++]; + v3src[list[i]] = buf[n++]; + v4src[list[i]] = buf[n++]; + v5src[list[i]] = buf[n++]; + } + } +} + +/* ---------------------------------------------------------------------- + pack ghost values into buf to send to another proc +------------------------------------------------------------------------- */ + +void PPPMGPU::pack_reverse(int flag, FFT_SCALAR *buf, int nlist, int *list) +{ + if (flag == REVERSE_RHO) { + FFT_SCALAR *src = &density_brick_gpu[nzlo_out][nylo_out][nxlo_out]; + for (int i = 0; i < nlist; i++) + buf[i] = src[list[i]]; + } +} + +/* ---------------------------------------------------------------------- + unpack another proc's ghost values from buf and add to own values +------------------------------------------------------------------------- */ + +void PPPMGPU::unpack_reverse(int flag, FFT_SCALAR *buf, int nlist, int *list) +{ + if (flag == REVERSE_RHO) { + FFT_SCALAR *dest = &density_brick_gpu[nzlo_out][nylo_out][nxlo_out]; + for (int i = 0; i < nlist; i++) + dest[list[i]] += buf[i]; + } +} + +/* ---------------------------------------------------------------------- + create array using offsets from pinned memory allocation ------------------------------------------------------------------------- */ FFT_SCALAR ***PPPMGPU::create_3d_offset(int n1lo, int n1hi, int n2lo, int n2hi, - int n3lo, int n3hi, const char *name, - FFT_SCALAR *data, int vec_length) + int n3lo, int n3hi, const char *name, + FFT_SCALAR *data, int vec_length) { int i,j; int n1 = n1hi - n1lo + 1; int n2 = n2hi - n2lo + 1; int n3 = n3hi - n3lo + 1; FFT_SCALAR **plane = (FFT_SCALAR **) memory->smalloc(n1*n2*sizeof(FFT_SCALAR *),name); FFT_SCALAR ***array = (FFT_SCALAR ***) memory->smalloc(n1*sizeof(FFT_SCALAR **),name); int n = 0; for (i = 0; i < n1; i++) { array[i] = &plane[i*n2]; for (j = 0; j < n2; j++) { plane[i*n2+j] = &data[n]; n += n3*vec_length; } } for (i = 0; i < n1*n2; i++) array[0][i] -= n3lo*vec_length; for (i = 0; i < n1; i++) array[i] -= n2lo; return array-n1lo; } /* ---------------------------------------------------------------------- 3d memory offsets ------------------------------------------------------------------------- */ void PPPMGPU::destroy_3d_offset(FFT_SCALAR ***array, int n1_offset, int n2_offset) { if (array == NULL) return; memory->sfree(&array[n1_offset][n2_offset]); memory->sfree(array + n1_offset); } /* ---------------------------------------------------------------------- memory usage of local arrays ------------------------------------------------------------------------- */ double PPPMGPU::memory_usage() { - double bytes = nmax*3 * sizeof(double); - int nbrick = (nxhi_out-nxlo_out+1) * (nyhi_out-nylo_out+1) * - (nzhi_out-nzlo_out+1); - bytes += 4 * nbrick * sizeof(FFT_SCALAR); - bytes += 6 * nfft_both * sizeof(double); - bytes += nfft_both * sizeof(double); - bytes += nfft_both*5 * sizeof(FFT_SCALAR); - bytes += 2 * nbuf * sizeof(double); - - if (peratom_allocate_flag) { - bytes += 7 * nbrick * sizeof(FFT_SCALAR); - bytes += 2 * nbuf_peratom * sizeof(FFT_SCALAR); - } + double bytes = PPPM::memory_usage(); + + // NOTE: add tallying here for density_brick_gpu and vd_brick + // could subtract out density_brick and vdxyz_brick if freed them above + // it the net efffect is zero, do nothing return bytes + PPPM_GPU_API(bytes)(); } /* ---------------------------------------------------------------------- perform and time the 4 FFTs required for N timesteps ------------------------------------------------------------------------- */ int PPPMGPU::timing(int n, double &time3d, double &time1d) { if (im_real_space) { time3d = 1.0; time1d = 1.0; return 4; } PPPM::timing(n,time3d,time1d); return 4; } /* ---------------------------------------------------------------------- adjust PPPM coeffs, called initially and whenever volume has changed ------------------------------------------------------------------------- */ void PPPMGPU::setup() { if (im_real_space) return; PPPM::setup(); } diff --git a/src/GPU/pppm_gpu.h b/src/GPU/pppm_gpu.h index 4c9e8441a..98d62ffa2 100644 --- a/src/GPU/pppm_gpu.h +++ b/src/GPU/pppm_gpu.h @@ -1,100 +1,99 @@ /* ---------------------------------------------------------------------- LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator http://lammps.sandia.gov, Sandia National Laboratories Steve Plimpton, sjplimp@sandia.gov Copyright (2003) Sandia Corporation. Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains certain rights in this software. This software is distributed under the GNU General Public License. See the README file in the top-level LAMMPS directory. ------------------------------------------------------------------------- */ #ifdef KSPACE_CLASS KSpaceStyle(pppm/gpu,PPPMGPU) #else #ifndef LMP_PPPM_GPU_H #define LMP_PPPM_GPU_H #include "pppm.h" namespace LAMMPS_NS { class PPPMGPU : public PPPM { public: PPPMGPU(class LAMMPS *, int, char **); virtual ~PPPMGPU(); void init(); void setup(); void compute(int, int); int timing(int, double &, double &); double memory_usage(); protected: - FFT_SCALAR ***density_brick_gpu, ***vd_brick; bool kspace_split, im_real_space; + int old_nlocal; + double poisson_time; - void allocate(); - void deallocate(); void brick2fft(); - void fillbrick(); - void fillbrick_ik(); void poisson(); void poisson_ik(); - int old_nlocal; - double poisson_time; + void pack_forward(int, FFT_SCALAR *, int, int *); + void unpack_forward(int, FFT_SCALAR *, int, int *); + void pack_reverse(int, FFT_SCALAR *, int, int *); + void unpack_reverse(int, FFT_SCALAR *, int, int *); FFT_SCALAR ***create_3d_offset(int, int, int, int, int, int, const char *, FFT_SCALAR *, int); void destroy_3d_offset(FFT_SCALAR ***, int, int); }; } #endif #endif /* ERROR/WARNING messages: E: Illegal ... command Self-explanatory. Check the input script syntax and compare to the documentation for the command. You can use -echo screen as a command-line option when running LAMMPS to see the offending line. E: Cannot (yet) do analytic differentiation with pppm/gpu. Self-explanatory. E: Cannot use order greater than 8 with pppm/gpu. Self-explanatory. E: Insufficient memory on accelerator There is insufficient memory on one of the devices specified for the gpu package E: Out of range atoms - cannot compute PPPM One or more atoms are attempting to map their charge to a PPPM grid point that is not owned by a processor. This is likely for one of two reasons, both of them bad. First, it may mean that an atom near the boundary of a processor's sub-domain has moved more than 1/2 the "neighbor skin distance"_neighbor.html without neighbor lists being rebuilt and atoms being migrated to new processors. This also means you may be missing pairwise interactions that need to be computed. The solution is to change the re-neighboring criteria via the "neigh_modify"_neigh_modify command. The safest settings are "delay 0 every 1 check yes". Second, it may mean that an atom has moved far outside a processor's sub-domain or even the entire simulation box. This indicates bad physics, e.g. due to highly overlapping atoms, too large a timestep, etc. */ diff --git a/src/KSPACE/pppm.h b/src/KSPACE/pppm.h index 2f5dc2241..a7d0dfb2c 100644 --- a/src/KSPACE/pppm.h +++ b/src/KSPACE/pppm.h @@ -1,306 +1,306 @@ /* -*- c++ -*- ---------------------------------------------------------- LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator http://lammps.sandia.gov, Sandia National Laboratories Steve Plimpton, sjplimp@sandia.gov Copyright (2003) Sandia Corporation. Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains certain rights in this software. This software is distributed under the GNU General Public License. See the README file in the top-level LAMMPS directory. ------------------------------------------------------------------------- */ #ifdef KSPACE_CLASS KSpaceStyle(pppm,PPPM) #else #ifndef LMP_PPPM_H #define LMP_PPPM_H #include "lmptype.h" #include "mpi.h" #ifdef FFT_SINGLE typedef float FFT_SCALAR; #define MPI_FFT_SCALAR MPI_FLOAT #else typedef double FFT_SCALAR; #define MPI_FFT_SCALAR MPI_DOUBLE #endif #include "kspace.h" namespace LAMMPS_NS { class PPPM : public KSpace { public: PPPM(class LAMMPS *, int, char **); virtual ~PPPM(); virtual void init(); virtual void setup(); void setup_grid(); virtual void compute(int, int); virtual int timing_1d(int, double &); virtual int timing_3d(int, double &); virtual double memory_usage(); virtual void compute_group_group(int, int, int); protected: int me,nprocs; int nfactors; int *factors; double qsum,qsqsum,q2; double cutoff; double volume; double delxinv,delyinv,delzinv,delvolinv; double shift,shiftone; int peratom_allocate_flag; int nxlo_in,nylo_in,nzlo_in,nxhi_in,nyhi_in,nzhi_in; int nxlo_out,nylo_out,nzlo_out,nxhi_out,nyhi_out,nzhi_out; int nxlo_ghost,nxhi_ghost,nylo_ghost,nyhi_ghost,nzlo_ghost,nzhi_ghost; int nxlo_fft,nylo_fft,nzlo_fft,nxhi_fft,nyhi_fft,nzhi_fft; int nlower,nupper; int ngrid,nfft,nfft_both; FFT_SCALAR ***density_brick; FFT_SCALAR ***vdx_brick,***vdy_brick,***vdz_brick; FFT_SCALAR ***u_brick; FFT_SCALAR ***v0_brick,***v1_brick,***v2_brick; FFT_SCALAR ***v3_brick,***v4_brick,***v5_brick; double *greensfn; double **vg; double *fkx,*fky,*fkz; FFT_SCALAR *density_fft; FFT_SCALAR *work1,*work2; double *gf_b; FFT_SCALAR **rho1d,**rho_coeff,**drho1d,**drho_coeff; double *sf_precoeff1, *sf_precoeff2, *sf_precoeff3; double *sf_precoeff4, *sf_precoeff5, *sf_precoeff6; double sf_coeff[6]; // coefficients for calculating ad self-forces double **acons; // group-group interactions int group_allocate_flag; FFT_SCALAR ***density_A_brick,***density_B_brick; FFT_SCALAR *density_A_fft,*density_B_fft; class FFT3d *fft1,*fft2; class Remap *remap; class CommGrid *cg; class CommGrid *cg_peratom; int **part2grid; // storage for particle -> grid mapping int nmax; int triclinic; // domain settings, orthog or triclinic double *boxlo; // TIP4P settings int typeH,typeO; // atom types of TIP4P water H and O atoms double qdist; // distance from O site to negative charge double alpha; // geometric factor void set_grid_global(); void set_grid_local(); void adjust_gewald(); double newton_raphson_f(); double derivf(); double final_accuracy(); virtual void allocate(); virtual void allocate_peratom(); virtual void deallocate(); virtual void deallocate_peratom(); int factorable(int); double compute_df_kspace(); double estimate_ik_error(double, double, bigint); double compute_qopt(); void compute_gf_denom(); void compute_gf_ik(); void compute_gf_ad(); void compute_sf_precoeff(); virtual void particle_map(); virtual void make_rho(); virtual void brick2fft(); virtual void poisson(); void poisson_ik(); void poisson_ad(); virtual void fieldforce(); void fieldforce_ik(); void fieldforce_ad(); virtual void poisson_peratom(); virtual void fieldforce_peratom(); void procs2grid2d(int,int,int,int *, int*); void compute_rho1d(const FFT_SCALAR &, const FFT_SCALAR &, const FFT_SCALAR &); void compute_drho1d(const FFT_SCALAR &, const FFT_SCALAR &, const FFT_SCALAR &); void compute_rho_coeff(); void slabcorr(); // grid communication - void pack_forward(int, FFT_SCALAR *, int, int *); - void unpack_forward(int, FFT_SCALAR *, int, int *); - void pack_reverse(int, FFT_SCALAR *, int, int *); - void unpack_reverse(int, FFT_SCALAR *, int, int *); + virtual void pack_forward(int, FFT_SCALAR *, int, int *); + virtual void unpack_forward(int, FFT_SCALAR *, int, int *); + virtual void pack_reverse(int, FFT_SCALAR *, int, int *); + virtual void unpack_reverse(int, FFT_SCALAR *, int, int *); // group-group interactions virtual void allocate_groups(); virtual void deallocate_groups(); virtual void make_rho_groups(int, int, int); virtual void poisson_groups(int); /* ---------------------------------------------------------------------- denominator for Hockney-Eastwood Green's function of x,y,z = sin(kx*deltax/2), etc inf n-1 S(n,k) = Sum W(k+pi*j)**2 = Sum b(l)*(z*z)**l j=-inf l=0 = -(z*z)**n /(2n-1)! * (d/dx)**(2n-1) cot(x) at z = sin(x) gf_b = denominator expansion coeffs ------------------------------------------------------------------------- */ inline double gf_denom(const double &x, const double &y, const double &z) const { double sx,sy,sz; sz = sy = sx = 0.0; for (int l = order-1; l >= 0; l--) { sx = gf_b[l] + sx*x; sy = gf_b[l] + sy*y; sz = gf_b[l] + sz*z; } double s = sx*sy*sz; return s*s; }; }; } #endif #endif /* ERROR/WARNING messages: E: Illegal ... command Self-explanatory. Check the input script syntax and compare to the documentation for the command. You can use -echo screen as a command-line option when running LAMMPS to see the offending line. E: Cannot (yet) use PPPM with triclinic box This feature is not yet supported. E: Cannot use PPPM with 2d simulation The kspace style pppm cannot be used in 2d simulations. You can use 2d PPPM in a 3d simulation; see the kspace_modify command. E: Kspace style requires atom attribute q The atom style defined does not have these attributes. E: Cannot use nonperiodic boundaries with PPPM For kspace style pppm, all 3 dimensions must have periodic boundaries unless you use the kspace_modify command to define a 2d slab with a non-periodic z dimension. E: Incorrect boundaries with slab PPPM Must have periodic x,y dimensions and non-periodic z dimension to use 2d slab option with PPPM. E: PPPM order cannot be < 2 or > than %d This is a limitation of the PPPM implementation in LAMMPS. E: KSpace style is incompatible with Pair style Setting a kspace style requires that a pair style with a long-range Coulombic component be selected. E: Bond and angle potentials must be defined for TIP4P Cannot use TIP4P pair potential unless bond and angle potentials are defined. E: Bad TIP4P angle type for PPPM/TIP4P Specified angle type is not valid. E: Bad TIP4P bond type for PPPM/TIP4P Specified bond type is not valid. E: Cannot use kspace solver on system with no charge No atoms in system have a non-zero charge. W: System is not charge neutral, net charge = %g The total charge on all atoms on the system is not 0.0, which is not valid for Ewald or PPPM. W: Reducing PPPM order b/c stencil extends beyond neighbor processor LAMMPS is attempting this in order to allow the simulation to run. It should not effect the PPPM accuracy. E: PPPM grid is too large The global PPPM grid is larger than OFFSET in one or more dimensions. OFFSET is currently set to 4096. You likely need to decrease the requested accuracy. E: PPPM order has been reduced to 0 LAMMPS has attempted to reduce the PPPM order to enable the simulation to run, but can reduce the order no further. Try increasing the accuracy of PPPM by reducing the tolerance size, thus inducing a larger PPPM grid. E: KSpace accuracy must be > 0 The kspace accuracy designated in the input must be greater than zero. E: Cannot compute PPPM G LAMMPS failed to compute a valid approximation for the PPPM g_ewald factor that partitions the computation between real space and k-space. E: Out of range atoms - cannot compute PPPM One or more atoms are attempting to map their charge to a PPPM grid point that is not owned by a processor. This is likely for one of two reasons, both of them bad. First, it may mean that an atom near the boundary of a processor's sub-domain has moved more than 1/2 the "neighbor skin distance"_neighbor.html without neighbor lists being rebuilt and atoms being migrated to new processors. This also means you may be missing pairwise interactions that need to be computed. The solution is to change the re-neighboring criteria via the "neigh_modify"_neigh_modify command. The safest settings are "delay 0 every 1 check yes". Second, it may mean that an atom has moved far outside a processor's sub-domain or even the entire simulation box. This indicates bad physics, e.g. due to highly overlapping atoms, too large a timestep, etc. E: Cannot (yet) use K-space slab correction with compute group/group This option is not yet supported. */ diff --git a/src/read_data.cpp b/src/read_data.cpp index 658ac11ca..d14d5084a 100644 --- a/src/read_data.cpp +++ b/src/read_data.cpp @@ -1,1576 +1,1576 @@ /* ---------------------------------------------------------------------- LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator http://lammps.sandia.gov, Sandia National Laboratories Steve Plimpton, sjplimp@sandia.gov Copyright (2003) Sandia Corporation. Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains certain rights in this software. This software is distributed under the GNU General Public License. See the README file in the top-level LAMMPS directory. ------------------------------------------------------------------------- */ #include "lmptype.h" #include "mpi.h" #include "math.h" #include "string.h" #include "stdlib.h" #include "ctype.h" #include "read_data.h" #include "atom.h" #include "atom_vec.h" #include "atom_vec_ellipsoid.h" #include "atom_vec_line.h" #include "atom_vec_tri.h" #include "comm.h" #include "update.h" #include "modify.h" #include "fix.h" #include "force.h" #include "pair.h" #include "domain.h" #include "bond.h" #include "angle.h" #include "dihedral.h" #include "improper.h" #include "error.h" #include "memory.h" #include "special.h" using namespace LAMMPS_NS; #define MAXLINE 256 #define LB_FACTOR 1.1 #define CHUNK 1024 #define DELTA 4 // must be 2 or larger // customize for new sections #define NSECTIONS 23 // change when add to header::section_keywords /* ---------------------------------------------------------------------- */ ReadData::ReadData(LAMMPS *lmp) : Pointers(lmp) { MPI_Comm_rank(world,&me); line = new char[MAXLINE]; keyword = new char[MAXLINE]; buffer = new char[CHUNK*MAXLINE]; narg = maxarg = 0; arg = NULL; // customize for new sections // pointers to atom styles that store extra info nellipsoids = 0; avec_ellipsoid = (AtomVecEllipsoid *) atom->style_match("ellipsoid"); nlines = 0; avec_line = (AtomVecLine *) atom->style_match("line"); ntris = 0; avec_tri = (AtomVecTri *) atom->style_match("tri"); } /* ---------------------------------------------------------------------- */ ReadData::~ReadData() { delete [] line; delete [] keyword; delete [] buffer; memory->sfree(arg); } /* ---------------------------------------------------------------------- */ void ReadData::command(int narg, char **arg) { if (narg < 1) error->all(FLERR,"Illegal read_data command"); if (domain->box_exist) error->all(FLERR,"Cannot read_data after simulation box is defined"); if (domain->dimension == 2 && domain->zperiodic == 0) error->all(FLERR,"Cannot run 2d simulation with nonperiodic Z dimension"); // fixes that process data file info nfix = 0; fix_index = NULL; fix_header = NULL; fix_section = NULL; int iarg = 1; while (iarg < narg) { if (strcmp(arg[iarg],"fix") == 0) { if (iarg+4 > narg) error->all(FLERR,"Illegal read_data command"); memory->grow(fix_index,nfix+1,"read_data:fix_index"); fix_header = (char **) memory->srealloc(fix_header,(nfix+1)*sizeof(char *), "read_data:fix_header"); fix_section = (char **) memory->srealloc(fix_section,(nfix+1)*sizeof(char *), "read_data:fix_section"); fix_index[nfix] = modify->find_fix(arg[iarg+1]); if (fix_index[nfix] < 0) error->all(FLERR,"Fix ID for read_data does not exist"); int n = strlen(arg[iarg+2]) + 1; fix_header[nfix] = new char[n]; strcpy(fix_header[nfix],arg[iarg+2]); n = strlen(arg[iarg+3]) + 1; fix_section[nfix] = new char[n]; strcpy(fix_section[nfix],arg[iarg+3]); nfix++; iarg += 4; } else error->all(FLERR,"Illegal read_data command"); } // scan data file to determine max topology needed per atom // allocate initial topology arrays if (atom->molecular) { if (me == 0) { if (screen) fprintf(screen,"Scanning data file ...\n"); open(arg[0]); header(0); scan(atom->bond_per_atom,atom->angle_per_atom, atom->dihedral_per_atom,atom->improper_per_atom); if (compressed) pclose(fp); else fclose(fp); atom->bond_per_atom += atom->extra_bond_per_atom; } MPI_Bcast(&atom->bond_per_atom,1,MPI_INT,0,world); MPI_Bcast(&atom->angle_per_atom,1,MPI_INT,0,world); MPI_Bcast(&atom->dihedral_per_atom,1,MPI_INT,0,world); MPI_Bcast(&atom->improper_per_atom,1,MPI_INT,0,world); } else atom->bond_per_atom = atom->angle_per_atom = atom->dihedral_per_atom = atom->improper_per_atom = 0; // read header info if (me == 0) { if (screen) fprintf(screen,"Reading data file ...\n"); open(arg[0]); } header(1); domain->box_exist = 1; // problem setup using info from header update->ntimestep = 0; int n; if (comm->nprocs == 1) n = static_cast<int> (atom->natoms); else n = static_cast<int> (LB_FACTOR * atom->natoms / comm->nprocs); atom->allocate_type_arrays(); atom->avec->grow(n); n = atom->nmax; domain->print_box(" "); domain->set_initial_box(); domain->set_global_box(); comm->set_proc_grid(); domain->set_local_box(); // customize for new sections // read rest of file in free format int atomflag = 0; while (strlen(keyword)) { // allow special fixes first chance to match and process the section // if fix matches, continue to next section if (nfix) { for (n = 0; n < nfix; n++) if (strstr(line,fix_section[n])) { bigint nlines = modify->fix[fix_index[n]]->read_data_skip_lines(keyword); fix(n,keyword,nlines); parse_keyword(0,1); break; } if (n < nfix) continue; } if (strcmp(keyword,"Atoms") == 0) { atoms(); atomflag = 1; } else if (strcmp(keyword,"Velocities") == 0) { if (atomflag == 0) error->all(FLERR,"Must read Atoms before Velocities"); velocities(); } else if (strcmp(keyword,"Ellipsoids") == 0) { if (!avec_ellipsoid) error->all(FLERR,"Invalid data file section: Ellipsoids"); if (atomflag == 0) error->all(FLERR,"Must read Atoms before Ellipsoids"); bonus(nellipsoids,(AtomVec *) avec_ellipsoid,"ellipsoids"); } else if (strcmp(keyword,"Lines") == 0) { if (!avec_line) error->all(FLERR,"Invalid data file section: Lines"); if (atomflag == 0) error->all(FLERR,"Must read Atoms before Lines"); bonus(nlines,(AtomVec *) avec_line,"lines"); } else if (strcmp(keyword,"Triangles") == 0) { if (!avec_tri) error->all(FLERR,"Invalid data file section: Triangles"); if (atomflag == 0) error->all(FLERR,"Must read Atoms before Triangles"); bonus(ntris,(AtomVec *) avec_tri,"triangles"); } else if (strcmp(keyword,"Bonds") == 0) { if (atom->avec->bonds_allow == 0) error->all(FLERR,"Invalid data file section: Bonds"); if (atomflag == 0) error->all(FLERR,"Must read Atoms before Bonds"); bonds(); } else if (strcmp(keyword,"Angles") == 0) { if (atom->avec->angles_allow == 0) error->all(FLERR,"Invalid data file section: Angles"); if (atomflag == 0) error->all(FLERR,"Must read Atoms before Angles"); angles(); } else if (strcmp(keyword,"Dihedrals") == 0) { if (atom->avec->dihedrals_allow == 0) error->all(FLERR,"Invalid data file section: Dihedrals"); if (atomflag == 0) error->all(FLERR,"Must read Atoms before Dihedrals"); dihedrals(); } else if (strcmp(keyword,"Impropers") == 0) { if (atom->avec->impropers_allow == 0) error->all(FLERR,"Invalid data file section: Impropers"); if (atomflag == 0) error->all(FLERR,"Must read Atoms before Impropers"); impropers(); } else if (strcmp(keyword,"Masses") == 0) { mass(); } else if (strcmp(keyword,"Pair Coeffs") == 0) { if (force->pair == NULL) error->all(FLERR,"Must define pair_style before Pair Coeffs"); paircoeffs(); } else if (strcmp(keyword,"Bond Coeffs") == 0) { if (atom->avec->bonds_allow == 0) error->all(FLERR,"Invalid data file section: Bond Coeffs"); if (force->bond == NULL) error->all(FLERR,"Must define bond_style before Bond Coeffs"); bondcoeffs(); } else if (strcmp(keyword,"Angle Coeffs") == 0) { if (atom->avec->angles_allow == 0) error->all(FLERR,"Invalid data file section: Angle Coeffs"); if (force->angle == NULL) error->all(FLERR,"Must define angle_style before Angle Coeffs"); anglecoeffs(0); } else if (strcmp(keyword,"Dihedral Coeffs") == 0) { if (atom->avec->dihedrals_allow == 0) error->all(FLERR,"Invalid data file section: Dihedral Coeffs"); if (force->dihedral == NULL) error->all(FLERR,"Must define dihedral_style before Dihedral Coeffs"); dihedralcoeffs(0); } else if (strcmp(keyword,"Improper Coeffs") == 0) { if (atom->avec->impropers_allow == 0) error->all(FLERR,"Invalid data file section: Improper Coeffs"); if (force->improper == NULL) error->all(FLERR,"Must define improper_style before Improper Coeffs"); impropercoeffs(0); } else if (strcmp(keyword,"BondBond Coeffs") == 0) { if (atom->avec->angles_allow == 0) error->all(FLERR,"Invalid data file section: BondBond Coeffs"); if (force->angle == NULL) error->all(FLERR,"Must define angle_style before BondBond Coeffs"); anglecoeffs(1); } else if (strcmp(keyword,"BondAngle Coeffs") == 0) { if (atom->avec->angles_allow == 0) error->all(FLERR,"Invalid data file section: BondAngle Coeffs"); if (force->angle == NULL) error->all(FLERR,"Must define angle_style before BondAngle Coeffs"); anglecoeffs(2); } else if (strcmp(keyword,"MiddleBondTorsion Coeffs") == 0) { if (atom->avec->dihedrals_allow == 0) error->all(FLERR,"Invalid data file section: MiddleBondTorsion Coeffs"); if (force->dihedral == NULL) error->all(FLERR, "Must define dihedral_style before " "MiddleBondTorsion Coeffs"); dihedralcoeffs(1); } else if (strcmp(keyword,"EndBondTorsion Coeffs") == 0) { if (atom->avec->dihedrals_allow == 0) error->all(FLERR,"Invalid data file section: EndBondTorsion Coeffs"); if (force->dihedral == NULL) error->all(FLERR, "Must define dihedral_style before EndBondTorsion Coeffs"); dihedralcoeffs(2); } else if (strcmp(keyword,"AngleTorsion Coeffs") == 0) { if (atom->avec->dihedrals_allow == 0) error->all(FLERR,"Invalid data file section: AngleTorsion Coeffs"); if (force->dihedral == NULL) error->all(FLERR, "Must define dihedral_style before AngleTorsion Coeffs"); dihedralcoeffs(3); } else if (strcmp(keyword,"AngleAngleTorsion Coeffs") == 0) { if (atom->avec->dihedrals_allow == 0) error->all(FLERR,"Invalid data file section: AngleAngleTorsion Coeffs"); if (force->dihedral == NULL) error->all(FLERR, "Must define dihedral_style before " "AngleAngleTorsion Coeffs"); dihedralcoeffs(4); } else if (strcmp(keyword,"BondBond13 Coeffs") == 0) { if (atom->avec->dihedrals_allow == 0) error->all(FLERR,"Invalid data file section: BondBond13 Coeffs"); if (force->dihedral == NULL) error->all(FLERR,"Must define dihedral_style before BondBond13 Coeffs"); dihedralcoeffs(5); } else if (strcmp(keyword,"AngleAngle Coeffs") == 0) { if (atom->avec->impropers_allow == 0) error->all(FLERR,"Invalid data file section: AngleAngle Coeffs"); if (force->improper == NULL) error->all(FLERR,"Must define improper_style before AngleAngle Coeffs"); impropercoeffs(1); } else { char str[128]; sprintf(str,"Unknown identifier in data file: %s",keyword); error->all(FLERR,str); } parse_keyword(0,1); } // close file if (me == 0) { if (compressed) pclose(fp); else fclose(fp); } // error if natoms > 0 yet no atoms were read if (atom->natoms > 0 && atomflag == 0) error->all(FLERR,"No atoms in data file"); // create bond topology now that system is defined if (atom->molecular) { Special special(lmp); special.build(); } } /* ---------------------------------------------------------------------- read free-format header of data file if flag = 0, only called by proc 0 if flag = 1, called by all procs so bcast lines as read them 1st line and blank lines are skipped non-blank lines are checked for header keywords and leading value is read header ends with EOF or non-blank line containing no header keyword if EOF, line is set to blank line else line has first keyword line for rest of file ------------------------------------------------------------------------- */ void ReadData::header(int flag) { int n; char *ptr; // customize for new sections const char *section_keywords[NSECTIONS] = {"Atoms","Velocities","Ellipsoids","Lines","Triangles", "Bonds","Angles","Dihedrals","Impropers", "Masses","Pair Coeffs","Bond Coeffs","Angle Coeffs", "Dihedral Coeffs","Improper Coeffs", "BondBond Coeffs","BondAngle Coeffs","MiddleBondTorsion Coeffs", "EndBondTorsion Coeffs","AngleTorsion Coeffs", "AngleAngleTorsion Coeffs","BondBond13 Coeffs","AngleAngle Coeffs"}; // skip 1st line of file if (me == 0) { char *eof = fgets(line,MAXLINE,fp); if (eof == NULL) error->one(FLERR,"Unexpected end of data file"); } // customize for new header lines while (1) { // read a line and bcast length if flag is set if (me == 0) { if (fgets(line,MAXLINE,fp) == NULL) n = 0; else n = strlen(line) + 1; } if (flag) MPI_Bcast(&n,1,MPI_INT,0,world); // if n = 0 then end-of-file so return with blank line if (n == 0) { line[0] = '\0'; return; } // bcast line if flag is set if (flag) MPI_Bcast(line,n,MPI_CHAR,0,world); // trim anything from '#' onward // if line is blank, continue if (ptr = strchr(line,'#')) *ptr = '\0'; if (strspn(line," \t\n\r") == strlen(line)) continue; // allow special fixes first chance to match and process the line // if fix matches, continue to next header line if (nfix) { for (n = 0; n < nfix; n++) if (strstr(line,fix_header[n])) { modify->fix[fix_index[n]]->read_data_header(line); break; } if (n < nfix) continue; } // search line for header keyword and set corresponding variable if (strstr(line,"atoms")) sscanf(line,BIGINT_FORMAT,&atom->natoms); // check for these first // otherwise "triangles" will be matched as "angles" else if (strstr(line,"ellipsoids")) { if (!avec_ellipsoid && me == 0) error->one(FLERR,"No ellipsoids allowed with this atom style"); sscanf(line,BIGINT_FORMAT,&nellipsoids); } else if (strstr(line,"lines")) { if (!avec_line && me == 0) error->one(FLERR,"No lines allowed with this atom style"); sscanf(line,BIGINT_FORMAT,&nlines); } else if (strstr(line,"triangles")) { if (!avec_tri && me == 0) error->one(FLERR,"No triangles allowed with this atom style"); sscanf(line,BIGINT_FORMAT,&ntris); } else if (strstr(line,"bonds")) sscanf(line,BIGINT_FORMAT,&atom->nbonds); else if (strstr(line,"angles")) sscanf(line,BIGINT_FORMAT,&atom->nangles); else if (strstr(line,"dihedrals")) sscanf(line,BIGINT_FORMAT, &atom->ndihedrals); else if (strstr(line,"impropers")) sscanf(line,BIGINT_FORMAT, &atom->nimpropers); else if (strstr(line,"atom types")) sscanf(line,"%d",&atom->ntypes); else if (strstr(line,"bond types")) sscanf(line,"%d",&atom->nbondtypes); else if (strstr(line,"angle types")) sscanf(line,"%d",&atom->nangletypes); else if (strstr(line,"dihedral types")) sscanf(line,"%d",&atom->ndihedraltypes); else if (strstr(line,"improper types")) sscanf(line,"%d",&atom->nimpropertypes); else if (strstr(line,"extra bond per atom")) sscanf(line,"%d",&atom->extra_bond_per_atom); else if (strstr(line,"xlo xhi")) sscanf(line,"%lg %lg",&domain->boxlo[0],&domain->boxhi[0]); else if (strstr(line,"ylo yhi")) sscanf(line,"%lg %lg",&domain->boxlo[1],&domain->boxhi[1]); else if (strstr(line,"zlo zhi")) sscanf(line,"%lg %lg",&domain->boxlo[2],&domain->boxhi[2]); else if (strstr(line,"xy xz yz")) { domain->triclinic = 1; sscanf(line,"%lg %lg %lg",&domain->xy,&domain->xz,&domain->yz); } else break; } // error check on total system size if (atom->natoms < 0 || atom->natoms > MAXBIGINT || atom->nbonds < 0 || atom->nbonds > MAXBIGINT || atom->nangles < 0 || atom->nangles > MAXBIGINT || atom->ndihedrals < 0 || atom->ndihedrals > MAXBIGINT || atom->nimpropers < 0 || atom->nimpropers > MAXBIGINT) { if (me == 0) error->one(FLERR,"System in data file is too big"); } // check that exiting string is a valid section keyword parse_keyword(1,flag); for (n = 0; n < NSECTIONS; n++) if (strcmp(keyword,section_keywords[n]) == 0) break; if (n == NSECTIONS && me == 0) { char str[128]; sprintf(str,"Unknown identifier in data file: %s",keyword); error->one(FLERR,str); } // error check on consistency of header values if ((atom->nbonds || atom->nbondtypes) && atom->avec->bonds_allow == 0 && me == 0) error->one(FLERR,"No bonds allowed with this atom style"); if ((atom->nangles || atom->nangletypes) && atom->avec->angles_allow == 0 && me == 0) error->one(FLERR,"No angles allowed with this atom style"); if ((atom->ndihedrals || atom->ndihedraltypes) && atom->avec->dihedrals_allow == 0 && me == 0) error->one(FLERR,"No dihedrals allowed with this atom style"); if ((atom->nimpropers || atom->nimpropertypes) && atom->avec->impropers_allow == 0 && me == 0) error->one(FLERR,"No impropers allowed with this atom style"); if (atom->nbonds > 0 && atom->nbondtypes <= 0 && me == 0) error->one(FLERR,"Bonds defined but no bond types"); if (atom->nangles > 0 && atom->nangletypes <= 0 && me == 0) error->one(FLERR,"Angles defined but no angle types"); if (atom->ndihedrals > 0 && atom->ndihedraltypes <= 0 && me == 0) error->one(FLERR,"Dihedrals defined but no dihedral types"); if (atom->nimpropers > 0 && atom->nimpropertypes <= 0 && me == 0) error->one(FLERR,"Impropers defined but no improper types"); } /* ---------------------------------------------------------------------- read all atoms ------------------------------------------------------------------------- */ void ReadData::atoms() { int i,m,nchunk; bigint nread = 0; bigint natoms = atom->natoms; while (nread < natoms) { if (natoms-nread > CHUNK) nchunk = CHUNK; else nchunk = natoms-nread; if (me == 0) { char *eof; m = 0; for (i = 0; i < nchunk; i++) { eof = fgets(&buffer[m],MAXLINE,fp); if (eof == NULL) error->one(FLERR,"Unexpected end of data file"); m += strlen(&buffer[m]); } if (buffer[m-1] != '\n') strcpy(&buffer[m++],"\n"); m++; } MPI_Bcast(&m,1,MPI_INT,0,world); MPI_Bcast(buffer,m,MPI_CHAR,0,world); atom->data_atoms(nchunk,buffer); nread += nchunk; } // check that all atoms were assigned correctly bigint tmp = atom->nlocal; MPI_Allreduce(&tmp,&natoms,1,MPI_LMP_BIGINT,MPI_SUM,world); if (me == 0) { if (screen) fprintf(screen," " BIGINT_FORMAT " atoms\n",natoms); if (logfile) fprintf(logfile," " BIGINT_FORMAT " atoms\n",natoms); } if (natoms != atom->natoms) error->all(FLERR,"Did not assign all atoms correctly"); // if any atom ID < 0, error // if all atom IDs = 0, tag_enable = 0 // if any atom ID > 0, error if any atom ID == 0 // not checking if atom IDs > natoms or are unique int nlocal = atom->nlocal; int *tag = atom->tag; int flag = 0; for (int i = 0; i < nlocal; i++) if (tag[i] < 0) flag = 1; int flag_all; MPI_Allreduce(&flag,&flag_all,1,MPI_INT,MPI_SUM,world); if (flag_all) error->all(FLERR,"Invalid atom ID in Atoms section of data file"); flag = 0; for (int i = 0; i < nlocal; i++) if (tag[i] > 0) flag = 1; MPI_Allreduce(&flag,&flag_all,1,MPI_INT,MPI_MAX,world); if (flag_all == 0) atom->tag_enable = 0; if (atom->tag_enable) { flag = 0; for (int i = 0; i < nlocal; i++) if (tag[i] == 0) flag = 1; MPI_Allreduce(&flag,&flag_all,1,MPI_INT,MPI_SUM,world); if (flag_all) error->all(FLERR,"Invalid atom ID in Atoms section of data file"); } // create global mapping if (atom->map_style) { atom->map_init(); atom->map_set(); } } /* ---------------------------------------------------------------------- read all velocities to find atoms, must build atom map if not a molecular system ------------------------------------------------------------------------- */ void ReadData::velocities() { int i,m,nchunk; int mapflag = 0; if (atom->map_style == 0) { mapflag = 1; atom->map_style = 1; atom->map_init(); atom->map_set(); } bigint nread = 0; bigint natoms = atom->natoms; while (nread < natoms) { if (natoms-nread > CHUNK) nchunk = CHUNK; else nchunk = natoms-nread; if (me == 0) { char *eof; m = 0; for (i = 0; i < nchunk; i++) { eof = fgets(&buffer[m],MAXLINE,fp); if (eof == NULL) error->one(FLERR,"Unexpected end of data file"); m += strlen(&buffer[m]); } if (buffer[m-1] != '\n') strcpy(&buffer[m++],"\n"); m++; } MPI_Bcast(&m,1,MPI_INT,0,world); MPI_Bcast(buffer,m,MPI_CHAR,0,world); atom->data_vels(nchunk,buffer); nread += nchunk; } if (mapflag) { atom->map_delete(); atom->map_style = 0; } if (me == 0) { if (screen) fprintf(screen," " BIGINT_FORMAT " velocities\n",natoms); if (logfile) fprintf(logfile," " BIGINT_FORMAT " velocities\n",natoms); } } /* ---------------------------------------------------------------------- read all bonus data to find atoms, must build atom map if not a molecular system ------------------------------------------------------------------------- */ void ReadData::bonus(bigint nbonus, AtomVec *ptr, const char *type) { int i,m,nchunk; int mapflag = 0; if (atom->map_style == 0) { mapflag = 1; atom->map_style = 1; atom->map_init(); atom->map_set(); } bigint nread = 0; bigint natoms = nbonus; while (nread < natoms) { if (natoms-nread > CHUNK) nchunk = CHUNK; else nchunk = natoms-nread; if (me == 0) { char *eof; m = 0; for (i = 0; i < nchunk; i++) { eof = fgets(&buffer[m],MAXLINE,fp); if (eof == NULL) error->one(FLERR,"Unexpected end of data file"); m += strlen(&buffer[m]); } if (buffer[m-1] != '\n') strcpy(&buffer[m++],"\n"); m++; } MPI_Bcast(&m,1,MPI_INT,0,world); MPI_Bcast(buffer,m,MPI_CHAR,0,world); atom->data_bonus(nchunk,buffer,ptr); nread += nchunk; } if (mapflag) { atom->map_delete(); atom->map_style = 0; } if (me == 0) { if (screen) fprintf(screen," " BIGINT_FORMAT " %s\n",natoms,type); if (logfile) fprintf(logfile," " BIGINT_FORMAT " %s\n",natoms,type); } } /* ---------------------------------------------------------------------- */ void ReadData::bonds() { int i,m,nchunk; bigint nread = 0; bigint nbonds = atom->nbonds; while (nread < nbonds) { nchunk = MIN(nbonds-nread,CHUNK); if (me == 0) { char *eof; m = 0; for (i = 0; i < nchunk; i++) { eof = fgets(&buffer[m],MAXLINE,fp); if (eof == NULL) error->one(FLERR,"Unexpected end of data file"); m += strlen(&buffer[m]); } if (buffer[m-1] != '\n') strcpy(&buffer[m++],"\n"); m++; } MPI_Bcast(&m,1,MPI_INT,0,world); MPI_Bcast(buffer,m,MPI_CHAR,0,world); atom->data_bonds(nchunk,buffer); nread += nchunk; } // check that bonds were assigned correctly int nlocal = atom->nlocal; bigint sum; bigint n = 0; for (i = 0; i < nlocal; i++) n += atom->num_bond[i]; MPI_Allreduce(&n,&sum,1,MPI_LMP_BIGINT,MPI_SUM,world); int factor = 1; if (!force->newton_bond) factor = 2; if (me == 0) { if (screen) fprintf(screen," " BIGINT_FORMAT " bonds\n",sum/factor); if (logfile) fprintf(logfile," " BIGINT_FORMAT " bonds\n",sum/factor); } if (sum != factor*atom->nbonds) error->all(FLERR,"Bonds assigned incorrectly"); } /* ---------------------------------------------------------------------- */ void ReadData::angles() { int i,m,nchunk; bigint nread = 0; bigint nangles = atom->nangles; while (nread < nangles) { nchunk = MIN(nangles-nread,CHUNK); if (me == 0) { char *eof; m = 0; for (i = 0; i < nchunk; i++) { eof = fgets(&buffer[m],MAXLINE,fp); if (eof == NULL) error->one(FLERR,"Unexpected end of data file"); m += strlen(&buffer[m]); } if (buffer[m-1] != '\n') strcpy(&buffer[m++],"\n"); m++; } MPI_Bcast(&m,1,MPI_INT,0,world); MPI_Bcast(buffer,m,MPI_CHAR,0,world); atom->data_angles(nchunk,buffer); nread += nchunk; } // check that ang int nlocal = atom->nlocal; bigint sum; bigint n = 0; for (i = 0; i < nlocal; i++) n += atom->num_angle[i]; MPI_Allreduce(&n,&sum,1,MPI_LMP_BIGINT,MPI_SUM,world); int factor = 1; if (!force->newton_bond) factor = 3; if (me == 0) { if (screen) fprintf(screen," " BIGINT_FORMAT " angles\n",sum/factor); if (logfile) fprintf(logfile," " BIGINT_FORMAT " angles\n",sum/factor); } if (sum != factor*atom->nangles) error->all(FLERR,"Angles assigned incorrectly"); } /* ---------------------------------------------------------------------- */ void ReadData::dihedrals() { int i,m,nchunk; bigint nread = 0; bigint ndihedrals = atom->ndihedrals; while (nread < ndihedrals) { nchunk = MIN(ndihedrals-nread,CHUNK); if (me == 0) { char *eof; m = 0; for (i = 0; i < nchunk; i++) { eof = fgets(&buffer[m],MAXLINE,fp); if (eof == NULL) error->one(FLERR,"Unexpected end of data file"); m += strlen(&buffer[m]); } if (buffer[m-1] != '\n') strcpy(&buffer[m++],"\n"); m++; } MPI_Bcast(&m,1,MPI_INT,0,world); MPI_Bcast(buffer,m,MPI_CHAR,0,world); atom->data_dihedrals(nchunk,buffer); nread += nchunk; } // check that dihedrals were assigned correctly int nlocal = atom->nlocal; bigint sum; bigint n = 0; for (i = 0; i < nlocal; i++) n += atom->num_dihedral[i]; MPI_Allreduce(&n,&sum,1,MPI_LMP_BIGINT,MPI_SUM,world); int factor = 1; if (!force->newton_bond) factor = 4; if (me == 0) { if (screen) fprintf(screen," " BIGINT_FORMAT " dihedrals\n",sum/factor); if (logfile) fprintf(logfile," " BIGINT_FORMAT " dihedrals\n",sum/factor); } if (sum != factor*atom->ndihedrals) error->all(FLERR,"Dihedrals assigned incorrectly"); } /* ---------------------------------------------------------------------- */ void ReadData::impropers() { int i,m,nchunk; bigint nread = 0; bigint nimpropers = atom->nimpropers; while (nread < nimpropers) { nchunk = MIN(nimpropers-nread,CHUNK); if (me == 0) { char *eof; m = 0; for (i = 0; i < nchunk; i++) { eof = fgets(&buffer[m],MAXLINE,fp); if (eof == NULL) error->one(FLERR,"Unexpected end of data file"); m += strlen(&buffer[m]); } if (buffer[m-1] != '\n') strcpy(&buffer[m++],"\n"); m++; } MPI_Bcast(&m,1,MPI_INT,0,world); MPI_Bcast(buffer,m,MPI_CHAR,0,world); atom->data_impropers(nchunk,buffer); nread += nchunk; } // check that impropers were assigned correctly int nlocal = atom->nlocal; bigint sum; bigint n = 0; for (i = 0; i < nlocal; i++) n += atom->num_improper[i]; MPI_Allreduce(&n,&sum,1,MPI_LMP_BIGINT,MPI_SUM,world); int factor = 1; if (!force->newton_bond) factor = 4; if (me == 0) { if (screen) fprintf(screen," " BIGINT_FORMAT " impropers\n",sum/factor); if (logfile) fprintf(logfile," " BIGINT_FORMAT " impropers\n",sum/factor); } if (sum != factor*atom->nimpropers) error->all(FLERR,"Impropers assigned incorrectly"); } /* ---------------------------------------------------------------------- */ void ReadData::mass() { int i,m; char *buf = new char[atom->ntypes*MAXLINE]; char *original = buf; if (me == 0) { char *eof; m = 0; for (i = 0; i < atom->ntypes; i++) { eof = fgets(&buf[m],MAXLINE,fp); if (eof == NULL) error->one(FLERR,"Unexpected end of data file"); m += strlen(&buf[m]); - if (buffer[m-1] != '\n') strcpy(&buffer[m++],"\n"); + if (buf[m-1] != '\n') strcpy(&buf[m++],"\n"); buf[m-1] = '\0'; } } MPI_Bcast(&m,1,MPI_INT,0,world); MPI_Bcast(buf,m,MPI_CHAR,0,world); for (i = 0; i < atom->ntypes; i++) { atom->set_mass(buf); buf += strlen(buf) + 1; } delete [] original; } /* ---------------------------------------------------------------------- */ void ReadData::paircoeffs() { int i,m; char *buf = new char[atom->ntypes*MAXLINE]; char *original = buf; if (me == 0) { char *eof; m = 0; for (i = 0; i < atom->ntypes; i++) { eof = fgets(&buf[m],MAXLINE,fp); if (eof == NULL) error->one(FLERR,"Unexpected end of data file"); m += strlen(&buf[m]); - if (buffer[m-1] != '\n') strcpy(&buffer[m++],"\n"); + if (buf[m-1] != '\n') strcpy(&buf[m++],"\n"); buf[m-1] = '\0'; } } MPI_Bcast(&m,1,MPI_INT,0,world); MPI_Bcast(buf,m,MPI_CHAR,0,world); for (i = 0; i < atom->ntypes; i++) { m = strlen(buf) + 1; parse_coeffs(buf,NULL,1); force->pair->coeff(narg,arg); buf += m; } delete [] original; } /* ---------------------------------------------------------------------- */ void ReadData::bondcoeffs() { int i,m; char *buf = new char[atom->nbondtypes*MAXLINE]; char *original = buf; if (me == 0) { char *eof; m = 0; for (i = 0; i < atom->nbondtypes; i++) { eof = fgets(&buf[m],MAXLINE,fp); if (eof == NULL) error->one(FLERR,"Unexpected end of data file"); m += strlen(&buf[m]); - if (buffer[m-1] != '\n') strcpy(&buffer[m++],"\n"); + if (buf[m-1] != '\n') strcpy(&buf[m++],"\n"); buf[m-1] = '\0'; } } MPI_Bcast(&m,1,MPI_INT,0,world); MPI_Bcast(buf,m,MPI_CHAR,0,world); for (i = 0; i < atom->nbondtypes; i++) { m = strlen(buf) + 1; parse_coeffs(buf,NULL,0); force->bond->coeff(narg,arg); buf += m; } delete [] original; } /* ---------------------------------------------------------------------- */ void ReadData::anglecoeffs(int which) { int i,m; char *buf = new char[atom->nangletypes*MAXLINE]; char *original = buf; if (me == 0) { char *eof; m = 0; for (i = 0; i < atom->nangletypes; i++) { eof = fgets(&buf[m],MAXLINE,fp); if (eof == NULL) error->one(FLERR,"Unexpected end of data file"); m += strlen(&buf[m]); - if (buffer[m-1] != '\n') strcpy(&buffer[m++],"\n"); + if (buf[m-1] != '\n') strcpy(&buf[m++],"\n"); buf[m-1] = '\0'; } } MPI_Bcast(&m,1,MPI_INT,0,world); MPI_Bcast(buf,m,MPI_CHAR,0,world); for (i = 0; i < atom->nangletypes; i++) { m = strlen(buf) + 1; if (which == 0) parse_coeffs(buf,NULL,0); else if (which == 1) parse_coeffs(buf,"bb",0); else if (which == 2) parse_coeffs(buf,"ba",0); force->angle->coeff(narg,arg); buf += m; } delete [] original; } /* ---------------------------------------------------------------------- */ void ReadData::dihedralcoeffs(int which) { int i,m; char *buf = new char[atom->ndihedraltypes*MAXLINE]; char *original = buf; if (me == 0) { char *eof; m = 0; for (i = 0; i < atom->ndihedraltypes; i++) { eof = fgets(&buf[m],MAXLINE,fp); if (eof == NULL) error->one(FLERR,"Unexpected end of data file"); m += strlen(&buf[m]); - if (buffer[m-1] != '\n') strcpy(&buffer[m++],"\n"); + if (buf[m-1] != '\n') strcpy(&buf[m++],"\n"); buf[m-1] = '\0'; } } MPI_Bcast(&m,1,MPI_INT,0,world); MPI_Bcast(buf,m,MPI_CHAR,0,world); for (i = 0; i < atom->ndihedraltypes; i++) { m = strlen(buf) + 1; if (which == 0) parse_coeffs(buf,NULL,0); else if (which == 1) parse_coeffs(buf,"mbt",0); else if (which == 2) parse_coeffs(buf,"ebt",0); else if (which == 3) parse_coeffs(buf,"at",0); else if (which == 4) parse_coeffs(buf,"aat",0); else if (which == 5) parse_coeffs(buf,"bb13",0); force->dihedral->coeff(narg,arg); buf += m; } delete [] original; } /* ---------------------------------------------------------------------- */ void ReadData::impropercoeffs(int which) { int i,m; char *buf = new char[atom->nimpropertypes*MAXLINE]; char *original = buf; if (me == 0) { char *eof; m = 0; for (i = 0; i < atom->nimpropertypes; i++) { eof = fgets(&buf[m],MAXLINE,fp); if (eof == NULL) error->one(FLERR,"Unexpected end of data file"); m += strlen(&buf[m]); - if (buffer[m-1] != '\n') strcpy(&buffer[m++],"\n"); + if (buf[m-1] != '\n') strcpy(&buf[m++],"\n"); buf[m-1] = '\0'; } } MPI_Bcast(&m,1,MPI_INT,0,world); MPI_Bcast(buf,m,MPI_CHAR,0,world); for (i = 0; i < atom->nimpropertypes; i++) { m = strlen(buf) + 1; if (which == 0) parse_coeffs(buf,NULL,0); else if (which == 1) parse_coeffs(buf,"aa",0); force->improper->coeff(narg,arg); buf += m; } delete [] original; } /* ---------------------------------------------------------------------- read fix section, pass lines to fix to process n = index of fix ------------------------------------------------------------------------- */ void ReadData::fix(int ifix, char *line, bigint nlines) { int i,m,nchunk; bigint nread = 0; while (nread < nlines) { if (nlines-nread > CHUNK) nchunk = CHUNK; else nchunk = nlines-nread; if (me == 0) { char *eof; m = 0; for (i = 0; i < nchunk; i++) { eof = fgets(&buffer[m],MAXLINE,fp); if (eof == NULL) error->one(FLERR,"Unexpected end of data file"); m += strlen(&buffer[m]); } if (buffer[m-1] != '\n') strcpy(&buffer[m++],"\n"); m++; } MPI_Bcast(&m,1,MPI_INT,0,world); MPI_Bcast(buffer,m,MPI_CHAR,0,world); modify->fix[ifix]->read_data_section(line,nchunk,buffer); nread += nchunk; } } /* ---------------------------------------------------------------------- proc 0 scans the data file for topology maximums ------------------------------------------------------------------------- */ void ReadData::scan(int &bond_per_atom, int &angle_per_atom, int &dihedral_per_atom, int &improper_per_atom) { int i,tmp1,tmp2,atom1,atom2,atom3,atom4; char *eof; if (atom->natoms > MAXSMALLINT) error->one(FLERR,"Molecular data file has too many atoms"); // customize for new sections int natoms = static_cast<int> (atom->natoms); bond_per_atom = angle_per_atom = dihedral_per_atom = improper_per_atom = 0; int ellipsoid_flag = 0; int line_flag = 0; int tri_flag = 0; // customize for new sections // allocate topology counting vector // initially, array length = 1 to natoms // will grow via reallocate() if atom IDs > natoms int cmax = natoms + 1; int *count; memory->create(count,cmax,"read_data:count"); while (strlen(keyword)) { // allow special fixes first chance to match and process the section // if fix matches, continue to next section if (nfix) { for (i = 0; i < nfix; i++) { printf("LINE SECTION %s %s\n",line,fix_section[i]); if (strstr(line,fix_section[i])) { int n = modify->fix[fix_index[i]]->read_data_skip_lines(keyword); printf("NLINES SKIP %d\n",n); skip_lines(n); parse_keyword(0,0); break; } } if (i < nfix) continue; } if (strcmp(keyword,"Masses") == 0) skip_lines(atom->ntypes); else if (strcmp(keyword,"Atoms") == 0) skip_lines(natoms); else if (strcmp(keyword,"Velocities") == 0) skip_lines(natoms); else if (strcmp(keyword,"Ellipsoids") == 0) { if (!avec_ellipsoid) error->one(FLERR,"Invalid data file section: Ellipsoids"); ellipsoid_flag = 1; skip_lines(nellipsoids); } else if (strcmp(keyword,"Lines") == 0) { if (!avec_line) error->one(FLERR,"Invalid data file section: Lines"); line_flag = 1; skip_lines(nlines); } else if (strcmp(keyword,"Triangles") == 0) { if (!avec_tri) error->one(FLERR,"Invalid data file section: Triangles"); tri_flag = 1; skip_lines(ntris); } else if (strcmp(keyword,"Pair Coeffs") == 0) { if (force->pair == NULL) error->one(FLERR,"Must define pair_style before Pair Coeffs"); skip_lines(atom->ntypes); } else if (strcmp(keyword,"Bond Coeffs") == 0) { if (atom->avec->bonds_allow == 0) error->one(FLERR,"Invalid data file section: Bond Coeffs"); if (force->bond == NULL) error->one(FLERR,"Must define bond_style before Bond Coeffs"); skip_lines(atom->nbondtypes); } else if (strcmp(keyword,"Angle Coeffs") == 0) { if (atom->avec->angles_allow == 0) error->one(FLERR,"Invalid data file section: Angle Coeffs"); if (force->angle == NULL) error->one(FLERR,"Must define angle_style before Angle Coeffs"); skip_lines(atom->nangletypes); } else if (strcmp(keyword,"Dihedral Coeffs") == 0) { skip_lines(atom->ndihedraltypes); if (atom->avec->dihedrals_allow == 0) error->one(FLERR,"Invalid data file section: Dihedral Coeffs"); if (force->dihedral == NULL) error->one(FLERR,"Must define dihedral_style before Dihedral Coeffs"); } else if (strcmp(keyword,"Improper Coeffs") == 0) { if (atom->avec->impropers_allow == 0) error->one(FLERR,"Invalid data file section: Improper Coeffs"); if (force->improper == NULL) error->one(FLERR,"Must define improper_style before Improper Coeffs"); skip_lines(atom->nimpropertypes); } else if (strcmp(keyword,"BondBond Coeffs") == 0) { if (atom->avec->angles_allow == 0) error->one(FLERR,"Invalid data file section: BondBond Coeffs"); if (force->angle == NULL) error->one(FLERR,"Must define angle_style before BondBond Coeffs"); skip_lines(atom->nangletypes); } else if (strcmp(keyword,"BondAngle Coeffs") == 0) { if (atom->avec->angles_allow == 0) error->one(FLERR,"Invalid data file section: BondAngle Coeffs"); if (force->angle == NULL) error->one(FLERR,"Must define angle_style before BondAngle Coeffs"); skip_lines(atom->nangletypes); } else if (strcmp(keyword,"MiddleBondTorsion Coeffs") == 0) { if (atom->avec->dihedrals_allow == 0) error->one(FLERR,"Invalid data file section: MiddleBondTorsion Coeffs"); if (force->dihedral == NULL) error->one(FLERR, "Must define dihedral_style before " "MiddleBondTorsion Coeffs"); skip_lines(atom->ndihedraltypes); } else if (strcmp(keyword,"EndBondTorsion Coeffs") == 0) { if (atom->avec->dihedrals_allow == 0) error->one(FLERR,"Invalid data file section: EndBondTorsion Coeffs"); if (force->dihedral == NULL) error->one(FLERR, "Must define dihedral_style before EndBondTorsion Coeffs"); skip_lines(atom->ndihedraltypes); } else if (strcmp(keyword,"AngleTorsion Coeffs") == 0) { if (atom->avec->dihedrals_allow == 0) error->one(FLERR,"Invalid data file section: AngleTorsion Coeffs"); if (force->dihedral == NULL) error->one(FLERR, "Must define dihedral_style before AngleTorsion Coeffs"); skip_lines(atom->ndihedraltypes); } else if (strcmp(keyword,"AngleAngleTorsion Coeffs") == 0) { if (atom->avec->dihedrals_allow == 0) error->one(FLERR,"Invalid data file section: AngleAngleTorsion Coeffs"); if (force->dihedral == NULL) error->one(FLERR, "Must define dihedral_style before " "AngleAngleTorsion Coeffs"); skip_lines(atom->ndihedraltypes); } else if (strcmp(keyword,"BondBond13 Coeffs") == 0) { if (atom->avec->dihedrals_allow == 0) error->one(FLERR,"Invalid data file section: BondBond13 Coeffs"); if (force->dihedral == NULL) error->one(FLERR,"Must define dihedral_style before BondBond13 Coeffs"); skip_lines(atom->ndihedraltypes); } else if (strcmp(keyword,"AngleAngle Coeffs") == 0) { if (atom->avec->impropers_allow == 0) error->one(FLERR,"Invalid data file section: AngleAngle Coeffs"); if (force->improper == NULL) error->one(FLERR,"Must define improper_style before AngleAngle Coeffs"); skip_lines(atom->nimpropertypes); } else if (strcmp(keyword,"Bonds") == 0) { for (i = 1; i < cmax; i++) count[i] = 0; if (force->newton_bond) for (i = 0; i < atom->nbonds; i++) { eof = fgets(line,MAXLINE,fp); if (eof == NULL) error->one(FLERR,"Unexpected end of data file"); sscanf(line,"%d %d %d %d",&tmp1,&tmp2,&atom1,&atom2); if (atom1 >= cmax) cmax = reallocate(&count,cmax,atom1); count[atom1]++; } else for (i = 0; i < atom->nbonds; i++) { eof = fgets(line,MAXLINE,fp); if (eof == NULL) error->one(FLERR,"Unexpected end of data file"); sscanf(line,"%d %d %d %d",&tmp1,&tmp2,&atom1,&atom2); int amax = MAX(atom1,atom2); if (amax >= cmax) cmax = reallocate(&count,cmax,amax); count[atom1]++; count[atom2]++; } for (i = 1; i < cmax; i++) bond_per_atom = MAX(bond_per_atom,count[i]); if (screen) fprintf(screen," %d = max bonds/atom\n",bond_per_atom); if (logfile) fprintf(logfile," %d = max bonds/atom\n",bond_per_atom); } else if (strcmp(keyword,"Angles") == 0) { for (i = 1; i < cmax; i++) count[i] = 0; if (force->newton_bond) for (i = 0; i < atom->nangles; i++) { eof = fgets(line,MAXLINE,fp); if (eof == NULL) error->one(FLERR,"Unexpected end of data file"); sscanf(line,"%d %d %d %d %d",&tmp1,&tmp2,&atom1,&atom2,&atom3); if (atom2 >= cmax) cmax = reallocate(&count,cmax,atom2); count[atom2]++; } else for (i = 0; i < atom->nangles; i++) { eof = fgets(line,MAXLINE,fp); if (eof == NULL) error->one(FLERR,"Unexpected end of data file"); sscanf(line,"%d %d %d %d %d",&tmp1,&tmp2,&atom1,&atom2,&atom3); int amax = MAX(atom1,atom2); amax = MAX(amax,atom3); if (amax >= cmax) cmax = reallocate(&count,cmax,amax); count[atom1]++; count[atom2]++; count[atom3]++; } for (i = 1; i < cmax; i++) angle_per_atom = MAX(angle_per_atom,count[i]); if (screen) fprintf(screen," %d = max angles/atom\n",angle_per_atom); if (logfile) fprintf(logfile," %d = max angles/atom\n",angle_per_atom); } else if (strcmp(keyword,"Dihedrals") == 0) { for (i = 1; i < cmax; i++) count[i] = 0; if (force->newton_bond) for (i = 0; i < atom->ndihedrals; i++) { eof = fgets(line,MAXLINE,fp); if (eof == NULL) error->one(FLERR,"Unexpected end of data file"); sscanf(line,"%d %d %d %d %d %d", &tmp1,&tmp2,&atom1,&atom2,&atom3,&atom4); if (atom2 >= cmax) cmax = reallocate(&count,cmax,atom2); count[atom2]++; } else for (i = 0; i < atom->ndihedrals; i++) { eof = fgets(line,MAXLINE,fp); if (eof == NULL) error->one(FLERR,"Unexpected end of data file"); sscanf(line,"%d %d %d %d %d %d", &tmp1,&tmp2,&atom1,&atom2,&atom3,&atom4); int amax = MAX(atom1,atom2); amax = MAX(amax,atom3); amax = MAX(amax,atom4); if (amax >= cmax) cmax = reallocate(&count,cmax,amax); count[atom1]++; count[atom2]++; count[atom3]++; count[atom4]++; } for (i = 1; i < cmax; i++) dihedral_per_atom = MAX(dihedral_per_atom,count[i]); if (screen) fprintf(screen," %d = max dihedrals/atom\n",dihedral_per_atom); if (logfile) fprintf(logfile," %d = max dihedrals/atom\n",dihedral_per_atom); } else if (strcmp(keyword,"Impropers") == 0) { for (i = 1; i < cmax; i++) count[i] = 0; if (force->newton_bond) for (i = 0; i < atom->nimpropers; i++) { eof = fgets(line,MAXLINE,fp); if (eof == NULL) error->one(FLERR,"Unexpected end of data file"); sscanf(line,"%d %d %d %d %d %d", &tmp1,&tmp2,&atom1,&atom2,&atom3,&atom4); if (atom2 >= cmax) cmax = reallocate(&count,cmax,atom2); count[atom2]++; } else for (i = 0; i < atom->nimpropers; i++) { eof = fgets(line,MAXLINE,fp); if (eof == NULL) error->one(FLERR,"Unexpected end of data file"); sscanf(line,"%d %d %d %d %d %d", &tmp1,&tmp2,&atom1,&atom2,&atom3,&atom4); int amax = MAX(atom1,atom2); amax = MAX(amax,atom3); amax = MAX(amax,atom4); if (amax >= cmax) cmax = reallocate(&count,cmax,amax); count[atom1]++; count[atom2]++; count[atom3]++; count[atom4]++; } for (i = 1; i < cmax; i++) improper_per_atom = MAX(improper_per_atom,count[i]); if (screen) fprintf(screen," %d = max impropers/atom\n",improper_per_atom); if (logfile) fprintf(logfile," %d = max impropers/atom\n",improper_per_atom); } else { char str[128]; sprintf(str,"Unknown identifier in data file: %s",keyword); error->one(FLERR,str); } parse_keyword(0,0); } // free topology counting vector memory->destroy(count); // error check that topology was specified in file if ((atom->nbonds && !bond_per_atom) || (atom->nangles && !angle_per_atom) || (atom->ndihedrals && !dihedral_per_atom) || (atom->nimpropers && !improper_per_atom)) error->one(FLERR,"Needed topology not in data file"); // customize for new sections // error check that Bonus sections were speficied in file if (nellipsoids && !ellipsoid_flag) error->one(FLERR,"Needed bonus data not in data file"); if (nlines && !line_flag) error->one(FLERR,"Needed bonus data not in data file"); if (ntris && !tri_flag) error->one(FLERR,"Needed bonus data not in data file"); } /* ---------------------------------------------------------------------- reallocate the count vector from cmax to amax+1 and return new length zero new locations ------------------------------------------------------------------------- */ int ReadData::reallocate(int **pcount, int cmax, int amax) { int *count = *pcount; memory->grow(count,amax+1,"read_data:count"); for (int i = cmax; i <= amax; i++) count[i] = 0; *pcount = count; return amax+1; } /* ---------------------------------------------------------------------- proc 0 opens data file test if gzipped ------------------------------------------------------------------------- */ void ReadData::open(char *file) { compressed = 0; char *suffix = file + strlen(file) - 3; if (suffix > file && strcmp(suffix,".gz") == 0) compressed = 1; if (!compressed) fp = fopen(file,"r"); else { #ifdef LAMMPS_GZIP char gunzip[128]; sprintf(gunzip,"gunzip -c %s",file); fp = popen(gunzip,"r"); #else error->one(FLERR,"Cannot open gzipped file"); #endif } if (fp == NULL) { char str[128]; sprintf(str,"Cannot open file %s",file); error->one(FLERR,str); } } /* ---------------------------------------------------------------------- grab next keyword read lines until one is non-blank keyword is all text on line w/out leading & trailing white space read one additional line (assumed blank) if any read hits EOF, set keyword to empty if first = 1, line variable holds non-blank line that ended header if flag = 0, only proc 0 is calling so no bcast else flag = 1, bcast keyword line to all procs ------------------------------------------------------------------------- */ void ReadData::parse_keyword(int first, int flag) { int eof = 0; // proc 0 reads upto non-blank line plus 1 following line // eof is set to 1 if any read hits end-of-file if (me == 0) { if (!first) { if (fgets(line,MAXLINE,fp) == NULL) eof = 1; } while (eof == 0 && strspn(line," \t\n\r") == strlen(line)) { if (fgets(line,MAXLINE,fp) == NULL) eof = 1; } if (fgets(buffer,MAXLINE,fp) == NULL) eof = 1; } // if eof, set keyword empty and return if (flag) MPI_Bcast(&eof,1,MPI_INT,0,world); if (eof) { keyword[0] = '\0'; return; } // bcast keyword line to all procs if (flag) { int n; if (me == 0) n = strlen(line) + 1; MPI_Bcast(&n,1,MPI_INT,0,world); MPI_Bcast(line,n,MPI_CHAR,0,world); } // copy non-whitespace portion of line into keyword int start = strspn(line," \t\n\r"); int stop = strlen(line) - 1; while (line[stop] == ' ' || line[stop] == '\t' || line[stop] == '\n' || line[stop] == '\r') stop--; line[stop+1] = '\0'; strcpy(keyword,&line[start]); } /* ---------------------------------------------------------------------- proc 0 reads N lines from file ------------------------------------------------------------------------- */ void ReadData::skip_lines(int n) { char *eof; for (int i = 0; i < n; i++) eof = fgets(line,MAXLINE,fp); if (eof == NULL) error->one(FLERR,"Unexpected end of data file"); } /* ---------------------------------------------------------------------- parse a line of coeffs into words, storing them in narg,arg trim anything from '#' onward word strings remain in line, are not copied if addstr != NULL, add addstr as extra arg for class2 angle/dihedral/improper if 2nd word starts with letter, then is hybrid style, add addstr after it else add addstr before 2nd word if dupflag, duplicate 1st word, so pair_coeff "2" becomes "2 2" ------------------------------------------------------------------------- */ void ReadData::parse_coeffs(char *line, const char *addstr, int dupflag) { char *ptr; if (ptr = strchr(line,'#')) *ptr = '\0'; narg = 0; char *word = strtok(line," \t\n\r\f"); while (word) { if (narg == maxarg) { maxarg += DELTA; arg = (char **) memory->srealloc(arg,maxarg*sizeof(char *),"read_data:arg"); } if (addstr && narg == 1 && !islower(word[0])) arg[narg++] = (char *) addstr; arg[narg++] = word; if (addstr && narg == 2 && islower(word[0])) arg[narg++] = (char *) addstr; if (dupflag && narg == 1) arg[narg++] = word; word = strtok(NULL," \t\n\r\f"); } }