Ejemplo n.º 1
0
static void write_xplor(const char *file,real *data,int *ibox,real dmin[],real dmax[])
{
  XplorMap *xm;
  int i,j,k,n;
    
  snew(xm,1);
  xm->Nx = ibox[XX];
  xm->Ny = ibox[YY];
  xm->Nz = ibox[ZZ];
  snew(xm->ed,xm->Nx*xm->Ny*xm->Nz);
  n=0;
  for(k=0; (k<xm->Nz); k++)
    for(j=0; (j<xm->Ny); j++)
      for(i=0; (i<xm->Nx); i++)
	xm->ed[n++] = data[index3(ibox,i,j,k)];
  xm->cell[0] = dmax[XX]-dmin[XX];
  xm->cell[1] = dmax[YY]-dmin[YY];
  xm->cell[2] = dmax[ZZ]-dmin[ZZ];
  xm->cell[3] = xm->cell[4] = xm->cell[5] = 90;

  clear_ivec(xm->dmin);
  xm->dmax[XX] = ibox[XX]-1;
  xm->dmax[YY] = ibox[YY]-1;
  xm->dmax[ZZ] = ibox[ZZ]-1;
  
  lo_write_xplor(xm,file);
  
  sfree(xm->ed);
  sfree(xm);
}
Ejemplo n.º 2
0
void done_grid(t_grid *grid)
{
    if (grid == nullptr)
    {
        return;
    }
    grid->nr      = 0;
    clear_ivec(grid->n);
    grid->ncells  = 0;
    sfree(grid->cell_index);
    sfree(grid->a);
    sfree(grid->index);
    sfree(grid->nra);
    grid->cells_nalloc = 0;
    sfree(grid->dcx2);
    sfree(grid->dcy2);
    sfree(grid->dcz2);
    grid->dc_nalloc = 0;

    if (debug)
    {
        fprintf(debug, "Successfully freed memory for grid pointers.");
    }
    sfree(grid);
}
Ejemplo n.º 3
0
static void get_shifts_group(
        int           npbcdim,
        const matrix  box,
        rvec         *xcoll,     /* IN:  Collective set of positions [0..nr] */
        int           nr,        /* IN:  Total number of atoms in the group */
        rvec         *xcoll_old, /* IN:  Positions from the last time step [0...nr] */
        ivec         *shifts)    /* OUT: Shifts for xcoll */
{
    int  i, m, d;
    rvec dx;


    /* Get the shifts such that each atom is within closest
     * distance to its position at the last NS time step after shifting.
     * If we start with a whole group, and always keep track of
     * shift changes, the group will stay whole this way */
    for (i = 0; i < nr; i++)
    {
        clear_ivec(shifts[i]);
    }

    for (i = 0; i < nr; i++)
    {
        /* The distance this atom moved since the last time step */
        /* If this is more than just a bit, it has changed its home pbc box */
        rvec_sub(xcoll[i], xcoll_old[i], dx);

        for (m = npbcdim-1; m >= 0; m--)
        {
            while (dx[m] < -0.5*box[m][m])
            {
                for (d = 0; d < DIM; d++)
                {
                    dx[d] += box[m][d];
                }
                shifts[i][m]++;
            }
            while (dx[m] >= 0.5*box[m][m])
            {
                for (d = 0; d < DIM; d++)
                {
                    dx[d] -= box[m][d];
                }
                shifts[i][m]--;
            }
        }
    }
}
Ejemplo n.º 4
0
/*! \brief Determine the optimal distribution of DD cells for the simulation system and number of MPI ranks */
static real optimize_ncells(FILE *fplog,
                            int nnodes_tot, int npme_only,
                            gmx_bool bDynLoadBal, real dlb_scale,
                            gmx_mtop_t *mtop, matrix box, gmx_ddbox_t *ddbox,
                            t_inputrec *ir,
                            gmx_domdec_t *dd,
                            real cellsize_limit, real cutoff,
                            gmx_bool bInterCGBondeds,
                            ivec nc)
{
    int      npp, npme, ndiv, *div, *mdiv, d, nmax;
    double   pbcdxr;
    real     limit;
    ivec     itry;

    limit  = cellsize_limit;

    dd->nc[XX] = 1;
    dd->nc[YY] = 1;
    dd->nc[ZZ] = 1;

    npp = nnodes_tot - npme_only;
    if (EEL_PME(ir->coulombtype))
    {
        npme = (npme_only > 0 ? npme_only : npp);
    }
    else
    {
        npme = 0;
    }

    if (bInterCGBondeds)
    {
        /* If we can skip PBC for distance calculations in plain-C bondeds,
         * we can save some time (e.g. 3D DD with pbc=xyz).
         * Here we ignore SIMD bondeds as they always do (fast) PBC.
         */
        count_bonded_distances(mtop, ir, &pbcdxr, NULL);
        pbcdxr /= (double)mtop->natoms;
    }
    else
    {
        /* Every molecule is a single charge group: no pbc required */
        pbcdxr = 0;
    }
    /* Add a margin for DLB and/or pressure scaling */
    if (bDynLoadBal)
    {
        if (dlb_scale >= 1.0)
        {
            gmx_fatal(FARGS, "The value for option -dds should be smaller than 1");
        }
        if (fplog)
        {
            fprintf(fplog, "Scaling the initial minimum size with 1/%g (option -dds) = %g\n", dlb_scale, 1/dlb_scale);
        }
        limit /= dlb_scale;
    }
    else if (ir->epc != epcNO)
    {
        if (fplog)
        {
            fprintf(fplog, "To account for pressure scaling, scaling the initial minimum size with %g\n", DD_GRID_MARGIN_PRES_SCALE);
            limit *= DD_GRID_MARGIN_PRES_SCALE;
        }
    }

    if (fplog)
    {
        fprintf(fplog, "Optimizing the DD grid for %d cells with a minimum initial size of %.3f nm\n", npp, limit);

        if (inhomogeneous_z(ir))
        {
            fprintf(fplog, "Ewald_geometry=%s: assuming inhomogeneous particle distribution in z, will not decompose in z.\n", eewg_names[ir->ewald_geometry]);
        }

        if (limit > 0)
        {
            fprintf(fplog, "The maximum allowed number of cells is:");
            for (d = 0; d < DIM; d++)
            {
                nmax = (int)(ddbox->box_size[d]*ddbox->skew_fac[d]/limit);
                if (d >= ddbox->npbcdim && nmax < 2)
                {
                    nmax = 2;
                }
                if (d == ZZ && inhomogeneous_z(ir))
                {
                    nmax = 1;
                }
                fprintf(fplog, " %c %d", 'X' + d, nmax);
            }
            fprintf(fplog, "\n");
        }
    }

    if (debug)
    {
        fprintf(debug, "Average nr of pbc_dx calls per atom %.2f\n", pbcdxr);
    }

    /* Decompose npp in factors */
    ndiv = factorize(npp, &div, &mdiv);

    itry[XX] = 1;
    itry[YY] = 1;
    itry[ZZ] = 1;
    clear_ivec(nc);
    assign_factors(dd, limit, cutoff, box, ddbox, mtop->natoms, ir, pbcdxr,
                   npme, ndiv, div, mdiv, itry, nc);

    sfree(div);
    sfree(mdiv);

    return limit;
}
Ejemplo n.º 5
0
static gmx_bool pme_loadbal_increase_cutoff(pme_load_balancing_t  pme_lb,
                                            int                   pme_order,
                                            const gmx_domdec_t   *dd)
{
    pme_setup_t *set;
    int          npmenodes_x, npmenodes_y;
    real         fac, sp;
    real         tmpr_coulomb, tmpr_vdw;
    int          d;
    gmx_bool     grid_ok;

    /* Try to add a new setup with next larger cut-off to the list */
    pme_lb->n++;
    srenew(pme_lb->setup, pme_lb->n);
    set          = &pme_lb->setup[pme_lb->n-1];
    set->pmedata = NULL;

    get_pme_nnodes(dd, &npmenodes_x, &npmenodes_y);

    fac = 1;
    do
    {
        /* Avoid infinite while loop, which can occur at the minimum grid size.
         * Note that in practice load balancing will stop before this point.
         * The factor 2.1 allows for the extreme case in which only grids
         * of powers of 2 are allowed (the current code supports more grids).
         */
        if (fac > 2.1)
        {
            pme_lb->n--;

            return FALSE;
        }

        fac *= 1.01;
        clear_ivec(set->grid);
        sp = calc_grid(NULL, pme_lb->box_start,
                       fac*pme_lb->setup[pme_lb->cur].spacing,
                       &set->grid[XX],
                       &set->grid[YY],
                       &set->grid[ZZ]);

        /* As here we can't easily check if one of the PME nodes
         * uses threading, we do a conservative grid check.
         * This means we can't use pme_order or less grid lines
         * per PME node along x, which is not a strong restriction.
         */
        gmx_pme_check_restrictions(pme_order,
                                   set->grid[XX], set->grid[YY], set->grid[ZZ],
                                   npmenodes_x, npmenodes_y,
                                   TRUE,
                                   FALSE,
                                   &grid_ok);
    }
    while (sp <= 1.001*pme_lb->setup[pme_lb->cur].spacing || !grid_ok);

    set->rcut_coulomb = pme_lb->cut_spacing*sp;
    if (set->rcut_coulomb < pme_lb->rcut_coulomb_start)
    {
        /* This is unlikely, but can happen when e.g. continuing from
         * a checkpoint after equilibration where the box shrank a lot.
         * We want to avoid rcoulomb getting smaller than rvdw
         * and there might be more issues with decreasing rcoulomb.
         */
        set->rcut_coulomb = pme_lb->rcut_coulomb_start;
    }

    if (pme_lb->cutoff_scheme == ecutsVERLET)
    {
        set->rlist        = set->rcut_coulomb + pme_lb->rbuf_coulomb;
        /* We dont use LR lists with Verlet, but this avoids if-statements in further checks */
        set->rlistlong    = set->rlist;
    }
    else
    {
        tmpr_coulomb          = set->rcut_coulomb + pme_lb->rbuf_coulomb;
        tmpr_vdw              = pme_lb->rcut_vdw + pme_lb->rbuf_vdw;
        set->rlist            = min(tmpr_coulomb, tmpr_vdw);
        set->rlistlong        = max(tmpr_coulomb, tmpr_vdw);

        /* Set the long-range update frequency */
        if (set->rlist == set->rlistlong)
        {
            /* No long-range interactions if the short-/long-range cutoffs are identical */
            set->nstcalclr = 0;
        }
        else if (pme_lb->nstcalclr_start == 0 || pme_lb->nstcalclr_start == 1)
        {
            /* We were not doing long-range before, but now we are since rlist!=rlistlong */
            set->nstcalclr = 1;
        }
        else
        {
            /* We were already doing long-range interactions from the start */
            if (pme_lb->rcut_vdw > pme_lb->rcut_coulomb_start)
            {
                /* We were originally doing long-range VdW-only interactions.
                 * If rvdw is still longer than rcoulomb we keep the original nstcalclr,
                 * but if the coulomb cutoff has become longer we should update the long-range
                 * part every step.
                 */
                set->nstcalclr = (tmpr_vdw > tmpr_coulomb) ? pme_lb->nstcalclr_start : 1;
            }
            else
            {
                /* We were not doing any long-range interaction from the start,
                 * since it is not possible to do twin-range coulomb for the PME interaction.
                 */
                set->nstcalclr = 1;
            }
        }
    }

    set->spacing      = sp;
    /* The grid efficiency is the size wrt a grid with uniform x/y/z spacing */
    set->grid_efficiency = 1;
    for (d = 0; d < DIM; d++)
    {
        set->grid_efficiency *= (set->grid[d]*sp)/norm(pme_lb->box_start[d]);
    }
    /* The Ewald coefficient is inversly proportional to the cut-off */
    set->ewaldcoeff_q =
        pme_lb->setup[0].ewaldcoeff_q*pme_lb->setup[0].rcut_coulomb/set->rcut_coulomb;
    /* We set ewaldcoeff_lj in set, even when LJ-PME is not used */
    set->ewaldcoeff_lj =
        pme_lb->setup[0].ewaldcoeff_lj*pme_lb->setup[0].rcut_coulomb/set->rcut_coulomb;

    set->count   = 0;
    set->cycles  = 0;

    if (debug)
    {
        fprintf(debug, "PME loadbal: grid %d %d %d, coulomb cutoff %f\n",
                set->grid[XX], set->grid[YY], set->grid[ZZ], set->rcut_coulomb);
    }
    return TRUE;
}
Ejemplo n.º 6
0
void dd_move_f_specat(gmx_domdec_t *dd, gmx_domdec_specat_comm_t *spac,
                      rvec *f, rvec *fshift)
{
    gmx_specatsend_t *spas;
    rvec             *vbuf;
    int               n, n0, n1, d, dim, dir, i;
    ivec              vis;
    int               is;
    gmx_bool          bPBC, bScrew;

    n = spac->at_end;
    for (d = dd->ndim-1; d >= 0; d--)
    {
        dim = dd->dim[d];
        if (dd->nc[dim] > 2)
        {
            /* Pulse the grid forward and backward */
            spas = spac->spas[d];
            n0   = spas[0].nrecv;
            n1   = spas[1].nrecv;
            n   -= n1 + n0;
            vbuf = spac->vbuf;
            /* Send and receive the coordinates */
            dd_sendrecv2_rvec(dd, d,
                              f+n+n1, n0, vbuf, spas[0].nsend,
                              f+n, n1, vbuf+spas[0].nsend, spas[1].nsend);
            for (dir = 0; dir < 2; dir++)
            {
                bPBC   = ((dir == 0 && dd->ci[dim] == 0) ||
                          (dir == 1 && dd->ci[dim] == dd->nc[dim]-1));
                bScrew = (bPBC && dd->bScrewPBC && dim == XX);

                spas = &spac->spas[d][dir];
                /* Sum the buffer into the required forces */
                if (!bPBC || (!bScrew && fshift == NULL))
                {
                    for (i = 0; i < spas->nsend; i++)
                    {
                        rvec_inc(f[spas->a[i]], *vbuf);
                        vbuf++;
                    }
                }
                else
                {
                    clear_ivec(vis);
                    vis[dim] = (dir == 0 ? 1 : -1);
                    is       = IVEC2IS(vis);
                    if (!bScrew)
                    {
                        /* Sum and add to shift forces */
                        for (i = 0; i < spas->nsend; i++)
                        {
                            rvec_inc(f[spas->a[i]], *vbuf);
                            rvec_inc(fshift[is], *vbuf);
                            vbuf++;
                        }
                    }
                    else
                    {
                        /* Rotate the forces */
                        for (i = 0; i < spas->nsend; i++)
                        {
                            f[spas->a[i]][XX] += (*vbuf)[XX];
                            f[spas->a[i]][YY] -= (*vbuf)[YY];
                            f[spas->a[i]][ZZ] -= (*vbuf)[ZZ];
                            if (fshift)
                            {
                                rvec_inc(fshift[is], *vbuf);
                            }
                            vbuf++;
                        }
                    }
                }
            }
        }
        else
        {
            /* Two cells, so we only need to communicate one way */
            spas = &spac->spas[d][0];
            n   -= spas->nrecv;
            /* Send and receive the coordinates */
            dd_sendrecv_rvec(dd, d, dddirForward,
                             f+n, spas->nrecv, spac->vbuf, spas->nsend);
            /* Sum the buffer into the required forces */
            if (dd->bScrewPBC && dim == XX &&
                (dd->ci[dim] == 0 ||
                 dd->ci[dim] == dd->nc[dim]-1))
            {
                for (i = 0; i < spas->nsend; i++)
                {
                    /* Rotate the force */
                    f[spas->a[i]][XX] += spac->vbuf[i][XX];
                    f[spas->a[i]][YY] -= spac->vbuf[i][YY];
                    f[spas->a[i]][ZZ] -= spac->vbuf[i][ZZ];
                }
            }
            else
            {
                for (i = 0; i < spas->nsend; i++)
                {
                    rvec_inc(f[spas->a[i]], spac->vbuf[i]);
                }
            }
        }
    }
}
Ejemplo n.º 7
0
static real optimize_ncells(FILE *fplog,
                            int nnodes_tot,int npme_only,
                            bool bDynLoadBal,real dlb_scale,
                            gmx_mtop_t *mtop,matrix box,gmx_ddbox_t *ddbox,
                            t_inputrec *ir,
                            gmx_domdec_t *dd,
                            real cellsize_limit,real cutoff,
                            bool bInterCGBondeds,bool bInterCGMultiBody,
                            ivec nc)
{
    int npp,npme,ndiv,*div,*mdiv,d,nmax;
    bool bExcl_pbcdx;
    float pbcdxr;
    real limit;
    ivec itry;
    
    limit  = cellsize_limit;
    
    dd->nc[XX] = 1;
    dd->nc[YY] = 1;
    dd->nc[ZZ] = 1;

    npp = nnodes_tot - npme_only;
    if (EEL_PME(ir->coulombtype))
    {
        npme = (npme_only > 0 ? npme_only : npp);
    }
    else
    {
        npme = 0;
    }
    
    if (bInterCGBondeds)
    {
        /* For Ewald exclusions pbc_dx is not called */
        bExcl_pbcdx =
            (EEL_EXCL_FORCES(ir->coulombtype) && !EEL_FULL(ir->coulombtype));
        pbcdxr = (double)n_bonded_dx(mtop,bExcl_pbcdx)/(double)mtop->natoms;
    }
    else
    {
        /* Every molecule is a single charge group: no pbc required */
        pbcdxr = 0;
    }
    /* Add a margin for DLB and/or pressure scaling */
    if (bDynLoadBal)
    {
        if (dlb_scale >= 1.0)
        {
            gmx_fatal(FARGS,"The value for option -dds should be smaller than 1");
        }
        if (fplog)
        {
            fprintf(fplog,"Scaling the initial minimum size with 1/%g (option -dds) = %g\n",dlb_scale,1/dlb_scale);
        }
        limit /= dlb_scale;
    }
    else if (ir->epc != epcNO)
    {
        if (fplog)
        {
            fprintf(fplog,"To account for pressure scaling, scaling the initial minimum size with %g\n",DD_GRID_MARGIN_PRES_SCALE);
            limit *= DD_GRID_MARGIN_PRES_SCALE;
        }
    }
    
    if (fplog)
    {
        fprintf(fplog,"Optimizing the DD grid for %d cells with a minimum initial size of %.3f nm\n",npp,limit);

        if (limit > 0)
        {
            fprintf(fplog,"The maximum allowed number of cells is:");
            for(d=0; d<DIM; d++)
            {
                nmax = (int)(ddbox->box_size[d]*ddbox->skew_fac[d]/limit);
                if (d >= ddbox->npbcdim && nmax < 2)
                {
                    nmax = 2;
                }
                fprintf(fplog," %c %d",'X' + d,nmax);
            }
            fprintf(fplog,"\n");
        }
    }
    
    if (debug)
    {
        fprintf(debug,"Average nr of pbc_dx calls per atom %.2f\n",pbcdxr);
    }
    
    /* Decompose npp in factors */
    ndiv = factorize(npp,&div,&mdiv);
    
    itry[XX] = 1;
    itry[YY] = 1;
    itry[ZZ] = 1;
    clear_ivec(nc);
    assign_factors(dd,limit,cutoff,box,ddbox,ir,pbcdxr,
                   npme,ndiv,div,mdiv,itry,nc);
    
    sfree(div);
    sfree(mdiv);
    
    return limit;
}
Ejemplo n.º 8
0
static gmx_bool pme_loadbal_increase_cutoff(pme_load_balancing_t pme_lb,
                                            int                  pme_order)
{
    pme_setup_t *set;
    real         fac, sp;
    real         tmpr_coulomb, tmpr_vdw;
    int          d;

    /* Try to add a new setup with next larger cut-off to the list */
    pme_lb->n++;
    srenew(pme_lb->setup, pme_lb->n);
    set          = &pme_lb->setup[pme_lb->n-1];
    set->pmedata = NULL;

    fac = 1;
    do
    {
        fac *= 1.01;
        clear_ivec(set->grid);
        sp = calc_grid(NULL, pme_lb->box_start,
                       fac*pme_lb->setup[pme_lb->cur].spacing,
                       &set->grid[XX],
                       &set->grid[YY],
                       &set->grid[ZZ]);

        /* In parallel we can't have grids smaller than 2*pme_order,
         * and we would anyhow not gain much speed at these grid sizes.
         */
        for (d = 0; d < DIM; d++)
        {
            if (set->grid[d] <= 2*pme_order)
            {
                pme_lb->n--;

                return FALSE;
            }
        }
    }
    while (sp <= 1.001*pme_lb->setup[pme_lb->cur].spacing);

    set->rcut_coulomb = pme_lb->cut_spacing*sp;

    if (pme_lb->cutoff_scheme == ecutsVERLET)
    {
        set->rlist        = set->rcut_coulomb + pme_lb->rbuf_coulomb;
        /* We dont use LR lists with Verlet, but this avoids if-statements in further checks */
        set->rlistlong    = set->rlist;
    }
    else
    {
        tmpr_coulomb          = set->rcut_coulomb + pme_lb->rbuf_coulomb;
        tmpr_vdw              = pme_lb->rcut_vdw + pme_lb->rbuf_vdw;
        set->rlist            = min(tmpr_coulomb, tmpr_vdw);
        set->rlistlong        = max(tmpr_coulomb, tmpr_vdw);

        /* Set the long-range update frequency */
        if (set->rlist == set->rlistlong)
        {
            /* No long-range interactions if the short-/long-range cutoffs are identical */
            set->nstcalclr = 0;
        }
        else if (pme_lb->nstcalclr_start == 0 || pme_lb->nstcalclr_start == 1)
        {
            /* We were not doing long-range before, but now we are since rlist!=rlistlong */
            set->nstcalclr = 1;
        }
        else
        {
            /* We were already doing long-range interactions from the start */
            if (pme_lb->rcut_vdw > pme_lb->rcut_coulomb_start)
            {
                /* We were originally doing long-range VdW-only interactions.
                 * If rvdw is still longer than rcoulomb we keep the original nstcalclr,
                 * but if the coulomb cutoff has become longer we should update the long-range
                 * part every step.
                 */
                set->nstcalclr = (tmpr_vdw > tmpr_coulomb) ? pme_lb->nstcalclr_start : 1;
            }
            else
            {
                /* We were not doing any long-range interaction from the start,
                 * since it is not possible to do twin-range coulomb for the PME interaction.
                 */
                set->nstcalclr = 1;
            }
        }
    }

    set->spacing      = sp;
    /* The grid efficiency is the size wrt a grid with uniform x/y/z spacing */
    set->grid_efficiency = 1;
    for (d = 0; d < DIM; d++)
    {
        set->grid_efficiency *= (set->grid[d]*sp)/norm(pme_lb->box_start[d]);
    }
    /* The Ewald coefficient is inversly proportional to the cut-off */
    set->ewaldcoeff =
        pme_lb->setup[0].ewaldcoeff*pme_lb->setup[0].rcut_coulomb/set->rcut_coulomb;

    set->count   = 0;
    set->cycles  = 0;

    if (debug)
    {
        fprintf(debug, "PME loadbal: grid %d %d %d, coulomb cutoff %f\n",
                set->grid[XX], set->grid[YY], set->grid[ZZ], set->rcut_coulomb);
    }
    return TRUE;
}
Ejemplo n.º 9
0
void update_QMMMrec(t_commrec      *cr,
                    t_forcerec     *fr,
                    rvec            x[],
                    t_mdatoms      *md,
                    matrix          box,
                    gmx_localtop_t *top)
{
    /* updates the coordinates of both QM atoms and MM atoms and stores
     * them in the QMMMrec.
     *
     * NOTE: is NOT yet working if there are no PBC. Also in ns.c, simple
     * ns needs to be fixed!
     */
    int
    mm_max = 0, mm_nr = 0, mm_nr_new, i, j, is, k, shift;
    t_j_particle
    *mm_j_particles = NULL, *qm_i_particles = NULL;
    t_QMMMrec
    *qr;
    t_nblist
    *QMMMlist;
    rvec
    dx, crd;
    t_QMrec
    *qm;
    t_MMrec
    *mm;
    t_pbc
    pbc;
    int
    *parallelMMarray = NULL;
    real
    c12au, c6au;

    c6au  = (HARTREE2KJ*AVOGADRO*gmx::power6(BOHR2NM));
    c12au = (HARTREE2KJ*AVOGADRO*gmx::power12(BOHR2NM));

    /* every cpu has this array. On every processor we fill this array
     * with 1's and 0's. 1's indicate the atoms is a QM atom on the
     * current cpu in a later stage these arrays are all summed. indexes
     * > 0 indicate the atom is a QM atom. Every node therefore knows
     * whcih atoms are part of the QM subsystem.
     */
    /* copy some pointers */
    qr          = fr->qr;
    mm          = qr->mm;
    QMMMlist    = fr->QMMMlist;



    /*  init_pbc(box);  needs to be called first, see pbc.h */
    ivec null_ivec;
    clear_ivec(null_ivec);
    set_pbc_dd(&pbc, fr->ePBC, DOMAINDECOMP(cr) ? cr->dd->nc : null_ivec,
               FALSE, box);
    /* only in standard (normal) QMMM we need the neighbouring MM
     * particles to provide a electric field of point charges for the QM
     * atoms.
     */
    if (qr->QMMMscheme == eQMMMschemenormal) /* also implies 1 QM-layer */
    {
        /* we NOW create/update a number of QMMMrec entries:
         *
         * 1) the shiftQM, containing the shifts of the QM atoms
         *
         * 2) the indexMM array, containing the index of the MM atoms
         *
         * 3) the shiftMM, containing the shifts of the MM atoms
         *
         * 4) the shifted coordinates of the MM atoms
         *
         * the shifts are used for computing virial of the QM/MM particles.
         */
        qm = qr->qm[0]; /* in case of normal QMMM, there is only one group */
        snew(qm_i_particles, QMMMlist->nri);
        if (QMMMlist->nri)
        {
            qm_i_particles[0].shift = XYZ2IS(0, 0, 0);
            for (i = 0; i < QMMMlist->nri; i++)
            {
                qm_i_particles[i].j     = QMMMlist->iinr[i];

                if (i)
                {
                    qm_i_particles[i].shift = pbc_dx_aiuc(&pbc, x[QMMMlist->iinr[0]],
                                                          x[QMMMlist->iinr[i]], dx);

                }
                /* However, since nri >= nrQMatoms, we do a quicksort, and throw
                 * out double, triple, etc. entries later, as we do for the MM
                 * list too.
                 */

                /* compute the shift for the MM j-particles with respect to
                 * the QM i-particle and store them.
                 */

                crd[0] = IS2X(QMMMlist->shift[i]) + IS2X(qm_i_particles[i].shift);
                crd[1] = IS2Y(QMMMlist->shift[i]) + IS2Y(qm_i_particles[i].shift);
                crd[2] = IS2Z(QMMMlist->shift[i]) + IS2Z(qm_i_particles[i].shift);
                is     = static_cast<int>(XYZ2IS(crd[0], crd[1], crd[2]));
                for (j = QMMMlist->jindex[i];
                        j < QMMMlist->jindex[i+1];
                        j++)
                {
                    if (mm_nr >= mm_max)
                    {
                        mm_max += 1000;
                        srenew(mm_j_particles, mm_max);
                    }

                    mm_j_particles[mm_nr].j     = QMMMlist->jjnr[j];
                    mm_j_particles[mm_nr].shift = is;
                    mm_nr++;
                }
            }

            /* quicksort QM and MM shift arrays and throw away multiple entries */



            qsort(qm_i_particles, QMMMlist->nri,
                  (size_t)sizeof(qm_i_particles[0]),
                  struct_comp);
            /* The mm_j_particles argument to qsort is not allowed to be NULL */
            if (mm_nr > 0)
            {
                qsort(mm_j_particles, mm_nr,
                      (size_t)sizeof(mm_j_particles[0]),
                      struct_comp);
            }
            /* remove multiples in the QM shift array, since in init_QMMM() we
             * went through the atom numbers from 0 to md.nr, the order sorted
             * here matches the one of QMindex already.
             */
            j = 0;
            for (i = 0; i < QMMMlist->nri; i++)
            {
                if (i == 0 || qm_i_particles[i].j != qm_i_particles[i-1].j)
                {
                    qm_i_particles[j++] = qm_i_particles[i];
                }
            }
            mm_nr_new = 0;
            if (qm->bTS || qm->bOPT)
            {
                /* only remove double entries for the MM array */
                for (i = 0; i < mm_nr; i++)
                {
                    if ((i == 0 || mm_j_particles[i].j != mm_j_particles[i-1].j)
                            && !md->bQM[mm_j_particles[i].j])
                    {
                        mm_j_particles[mm_nr_new++] = mm_j_particles[i];
                    }
                }
            }
            /* we also remove mm atoms that have no charges!
             * actually this is already done in the ns.c
             */
            else
            {
                for (i = 0; i < mm_nr; i++)
                {
                    if ((i == 0 || mm_j_particles[i].j != mm_j_particles[i-1].j)
                            && !md->bQM[mm_j_particles[i].j]
                            && (md->chargeA[mm_j_particles[i].j]
                                || (md->chargeB && md->chargeB[mm_j_particles[i].j])))
                    {
                        mm_j_particles[mm_nr_new++] = mm_j_particles[i];
                    }
                }
            }
            mm_nr = mm_nr_new;
            /* store the data retrieved above into the QMMMrec
             */
            k = 0;
            /* Keep the compiler happy,
             * shift will always be set in the loop for i=0
             */
            shift = 0;
            for (i = 0; i < qm->nrQMatoms; i++)
            {
                /* not all qm particles might have appeared as i
                 * particles. They might have been part of the same charge
                 * group for instance.
                 */
                if (qm->indexQM[i] == qm_i_particles[k].j)
                {
                    shift = qm_i_particles[k++].shift;
                }
                /* use previous shift, assuming they belong the same charge
                 * group anyway,
                 */

                qm->shiftQM[i] = shift;
            }
        }
        /* parallel excecution */
        if (PAR(cr))
        {
            snew(parallelMMarray, 2*(md->nr));
            /* only MM particles have a 1 at their atomnumber. The second part
             * of the array contains the shifts. Thus:
             * p[i]=1/0 depending on wether atomnumber i is a MM particle in the QM
             * step or not. p[i+md->nr] is the shift of atomnumber i.
             */
            for (i = 0; i < 2*(md->nr); i++)
            {
                parallelMMarray[i] = 0;
            }

            for (i = 0; i < mm_nr; i++)
            {
                parallelMMarray[mm_j_particles[i].j]          = 1;
                parallelMMarray[mm_j_particles[i].j+(md->nr)] = mm_j_particles[i].shift;
            }
            gmx_sumi(md->nr, parallelMMarray, cr);
            mm_nr = 0;

            mm_max = 0;
            for (i = 0; i < md->nr; i++)
            {
                if (parallelMMarray[i])
                {
                    if (mm_nr >= mm_max)
                    {
                        mm_max += 1000;
                        srenew(mm->indexMM, mm_max);
                        srenew(mm->shiftMM, mm_max);
                    }
                    mm->indexMM[mm_nr]   = i;
                    mm->shiftMM[mm_nr++] = parallelMMarray[i+md->nr]/parallelMMarray[i];
                }
            }
            mm->nrMMatoms = mm_nr;
            free(parallelMMarray);
        }
        /* serial execution */
        else
        {
            mm->nrMMatoms = mm_nr;
            srenew(mm->shiftMM, mm_nr);
            srenew(mm->indexMM, mm_nr);
            for (i = 0; i < mm_nr; i++)
            {
                mm->indexMM[i] = mm_j_particles[i].j;
                mm->shiftMM[i] = mm_j_particles[i].shift;
            }

        }
        /* (re) allocate memory for the MM coordiate array. The QM
         * coordinate array was already allocated in init_QMMM, and is
         * only (re)filled in the update_QMMM_coordinates routine
         */
        srenew(mm->xMM, mm->nrMMatoms);
        /* now we (re) fill the array that contains the MM charges with
         * the forcefield charges. If requested, these charges will be
         * scaled by a factor
         */
        srenew(mm->MMcharges, mm->nrMMatoms);
        for (i = 0; i < mm->nrMMatoms; i++) /* no free energy yet */
        {
            mm->MMcharges[i] = md->chargeA[mm->indexMM[i]]*mm->scalefactor;
        }
        if (qm->bTS || qm->bOPT)
        {
            /* store (copy) the c6 and c12 parameters into the MMrec struct
             */
            srenew(mm->c6, mm->nrMMatoms);
            srenew(mm->c12, mm->nrMMatoms);
            for (i = 0; i < mm->nrMMatoms; i++)
            {
                /* nbfp now includes the 6.0/12.0 derivative prefactors */
                mm->c6[i]  = C6(fr->nbfp, top->idef.atnr, md->typeA[mm->indexMM[i]], md->typeA[mm->indexMM[i]])/c6au/6.0;
                mm->c12[i] = C12(fr->nbfp, top->idef.atnr, md->typeA[mm->indexMM[i]], md->typeA[mm->indexMM[i]])/c12au/12.0;
            }
            punch_QMMM_excl(qr->qm[0], mm, &(top->excls));
        }
        /* the next routine fills the coordinate fields in the QMMM rec of
         * both the qunatum atoms and the MM atoms, using the shifts
         * calculated above.
         */

        update_QMMM_coord(x, fr, qr->qm[0], qr->mm);
        free(qm_i_particles);
        free(mm_j_particles);
    }
    else /* ONIOM */ /* ????? */
    {
        mm->nrMMatoms = 0;
        /* do for each layer */
        for (j = 0; j < qr->nrQMlayers; j++)
        {
            qm             = qr->qm[j];
            qm->shiftQM[0] = XYZ2IS(0, 0, 0);
            for (i = 1; i < qm->nrQMatoms; i++)
            {
                qm->shiftQM[i] = pbc_dx_aiuc(&pbc, x[qm->indexQM[0]], x[qm->indexQM[i]],
                                             dx);
            }
            update_QMMM_coord(x, fr, qm, mm);
        }
    }
} /* update_QMMM_rec */
Ejemplo n.º 10
0
int pbc_dx_aiuc(const t_pbc *pbc, const rvec x1, const rvec x2, rvec dx)
{
    int  i, j, is;
    rvec dx_start, trial;
    real d2min, d2trial;
    ivec ishift, ishift_start;

    rvec_sub(x1, x2, dx);
    clear_ivec(ishift);

    switch (pbc->ePBCDX)
    {
        case epbcdxRECTANGULAR:
            for (i = 0; i < DIM; i++)
            {
                if (dx[i] > pbc->hbox_diag[i])
                {
                    dx[i] -=  pbc->fbox_diag[i];
                    ishift[i]--;
                }
                else if (dx[i] <= pbc->mhbox_diag[i])
                {
                    dx[i] +=  pbc->fbox_diag[i];
                    ishift[i]++;
                }
            }
            break;
        case epbcdxTRICLINIC:
            /* For triclinic boxes the performance difference between
             * if/else and two while loops is negligible.
             * However, the while version can cause extreme delays
             * before a simulation crashes due to large forces which
             * can cause unlimited displacements.
             * Also allowing multiple shifts would index fshift beyond bounds.
             */
            for (i = DIM-1; i >= 1; i--)
            {
                if (dx[i] > pbc->hbox_diag[i])
                {
                    for (j = i; j >= 0; j--)
                    {
                        dx[j] -= pbc->box[i][j];
                    }
                    ishift[i]--;
                }
                else if (dx[i] <= pbc->mhbox_diag[i])
                {
                    for (j = i; j >= 0; j--)
                    {
                        dx[j] += pbc->box[i][j];
                    }
                    ishift[i]++;
                }
            }
            /* Allow 2 shifts in x */
            if (dx[XX] > pbc->hbox_diag[XX])
            {
                dx[XX] -= pbc->fbox_diag[XX];
                ishift[XX]--;
                if (dx[XX] > pbc->hbox_diag[XX])
                {
                    dx[XX] -= pbc->fbox_diag[XX];
                    ishift[XX]--;
                }
            }
            else if (dx[XX] <= pbc->mhbox_diag[XX])
            {
                dx[XX] += pbc->fbox_diag[XX];
                ishift[XX]++;
                if (dx[XX] <= pbc->mhbox_diag[XX])
                {
                    dx[XX] += pbc->fbox_diag[XX];
                    ishift[XX]++;
                }
            }
            /* dx is the distance in a rectangular box */
            d2min = norm2(dx);
            if (d2min > pbc->max_cutoff2)
            {
                copy_rvec(dx, dx_start);
                copy_ivec(ishift, ishift_start);
                d2min = norm2(dx);
                /* Now try all possible shifts, when the distance is within max_cutoff
                 * it must be the shortest possible distance.
                 */
                i = 0;
                while ((d2min > pbc->max_cutoff2) && (i < pbc->ntric_vec))
                {
                    rvec_add(dx_start, pbc->tric_vec[i], trial);
                    d2trial = norm2(trial);
                    if (d2trial < d2min)
                    {
                        copy_rvec(trial, dx);
                        ivec_add(ishift_start, pbc->tric_shift[i], ishift);
                        d2min = d2trial;
                    }
                    i++;
                }
            }
            break;
        case epbcdx2D_RECT:
            for (i = 0; i < DIM; i++)
            {
                if (i != pbc->dim)
                {
                    if (dx[i] > pbc->hbox_diag[i])
                    {
                        dx[i] -= pbc->fbox_diag[i];
                        ishift[i]--;
                    }
                    else if (dx[i] <= pbc->mhbox_diag[i])
                    {
                        dx[i] += pbc->fbox_diag[i];
                        ishift[i]++;
                    }
                }
            }
            break;
        case epbcdx2D_TRIC:
            d2min = 0;
            for (i = DIM-1; i >= 1; i--)
            {
                if (i != pbc->dim)
                {
                    if (dx[i] > pbc->hbox_diag[i])
                    {
                        for (j = i; j >= 0; j--)
                        {
                            dx[j] -= pbc->box[i][j];
                        }
                        ishift[i]--;
                    }
                    else if (dx[i] <= pbc->mhbox_diag[i])
                    {
                        for (j = i; j >= 0; j--)
                        {
                            dx[j] += pbc->box[i][j];
                        }
                        ishift[i]++;
                    }
                    d2min += dx[i]*dx[i];
                }
            }
            if (pbc->dim != XX)
            {
                /* Allow 2 shifts in x */
                if (dx[XX] > pbc->hbox_diag[XX])
                {
                    dx[XX] -= pbc->fbox_diag[XX];
                    ishift[XX]--;
                    if (dx[XX] > pbc->hbox_diag[XX])
                    {
                        dx[XX] -= pbc->fbox_diag[XX];
                        ishift[XX]--;
                    }
                }
                else if (dx[XX] <= pbc->mhbox_diag[XX])
                {
                    dx[XX] += pbc->fbox_diag[XX];
                    ishift[XX]++;
                    if (dx[XX] <= pbc->mhbox_diag[XX])
                    {
                        dx[XX] += pbc->fbox_diag[XX];
                        ishift[XX]++;
                    }
                }
                d2min += dx[XX]*dx[XX];
            }
            if (d2min > pbc->max_cutoff2)
            {
                copy_rvec(dx, dx_start);
                copy_ivec(ishift, ishift_start);
                /* Now try all possible shifts, when the distance is within max_cutoff
                 * it must be the shortest possible distance.
                 */
                i = 0;
                while ((d2min > pbc->max_cutoff2) && (i < pbc->ntric_vec))
                {
                    rvec_add(dx_start, pbc->tric_vec[i], trial);
                    d2trial = 0;
                    for (j = 0; j < DIM; j++)
                    {
                        if (j != pbc->dim)
                        {
                            d2trial += trial[j]*trial[j];
                        }
                    }
                    if (d2trial < d2min)
                    {
                        copy_rvec(trial, dx);
                        ivec_add(ishift_start, pbc->tric_shift[i], ishift);
                        d2min = d2trial;
                    }
                    i++;
                }
            }
            break;
        case epbcdx1D_RECT:
            i = pbc->dim;
            if (dx[i] > pbc->hbox_diag[i])
            {
                dx[i] -= pbc->fbox_diag[i];
                ishift[i]--;
            }
            else if (dx[i] <= pbc->mhbox_diag[i])
            {
                dx[i] += pbc->fbox_diag[i];
                ishift[i]++;
            }
            break;
        case epbcdx1D_TRIC:
            i = pbc->dim;
            if (dx[i] > pbc->hbox_diag[i])
            {
                rvec_dec(dx, pbc->box[i]);
                ishift[i]--;
            }
            else if (dx[i] <= pbc->mhbox_diag[i])
            {
                rvec_inc(dx, pbc->box[i]);
                ishift[i]++;
            }
            break;
        case epbcdxSCREW_RECT:
            /* The shift definition requires x first */
            if (dx[XX] > pbc->hbox_diag[XX])
            {
                dx[XX] -= pbc->fbox_diag[XX];
                ishift[XX]--;
            }
            else if (dx[XX] <= pbc->mhbox_diag[XX])
            {
                dx[XX] += pbc->fbox_diag[XX];
                ishift[XX]++;
            }
            if (ishift[XX] == 1 || ishift[XX] == -1)
            {
                /* Rotate around the x-axis in the middle of the box */
                dx[YY] = pbc->box[YY][YY] - x1[YY] - x2[YY];
                dx[ZZ] = pbc->box[ZZ][ZZ] - x1[ZZ] - x2[ZZ];
            }
            /* Normal pbc for y and z */
            for (i = YY; i <= ZZ; i++)
            {
                if (dx[i] > pbc->hbox_diag[i])
                {
                    dx[i] -= pbc->fbox_diag[i];
                    ishift[i]--;
                }
                else if (dx[i] <= pbc->mhbox_diag[i])
                {
                    dx[i] += pbc->fbox_diag[i];
                    ishift[i]++;
                }
            }
            break;
        case epbcdxNOPBC:
        case epbcdxUNSUPPORTED:
            break;
        default:
            gmx_fatal(FARGS, "Internal error in pbc_dx_aiuc, set_pbc_dd or set_pbc has not been called");
            break;
    }

    is = IVEC2IS(ishift);
    if (debug)
    {
        range_check_mesg(is, 0, SHIFTS, "PBC shift vector index range check.");
    }

    return is;
}