From: Mark Abraham Date: Sun, 23 Mar 2014 11:00:07 +0000 (+0100) Subject: Merge branch release-4-6 X-Git-Url: http://biod.pnpi.spb.ru/gitweb/?a=commitdiff_plain;h=a1a2efaeb7986311a622577761497dd4fcc5d132;p=alexxy%2Fgromacs.git Merge branch release-4-6 Change-Id: Ie4431fb0002cd36799c8e664dcf4e096e66bc0ea --- a1a2efaeb7986311a622577761497dd4fcc5d132 diff --cc src/gromacs/gmxpreprocess/readir.c index 5675b2fab0,0000000000..72da1996f0 mode 100644,000000..100644 --- a/src/gromacs/gmxpreprocess/readir.c +++ b/src/gromacs/gmxpreprocess/readir.c @@@ -1,4265 -1,0 +1,4277 @@@ +/* + * This file is part of the GROMACS molecular simulation package. + * + * Copyright (c) 1991-2000, University of Groningen, The Netherlands. + * Copyright (c) 2001-2004, The GROMACS development team. + * Copyright (c) 2013,2014, by the GROMACS development team, led by + * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl, + * and including many others, as listed in the AUTHORS file in the + * top-level source directory and at http://www.gromacs.org. + * + * GROMACS is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 + * of the License, or (at your option) any later version. + * + * GROMACS is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with GROMACS; if not, see + * http://www.gnu.org/licenses, or write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * If you want to redistribute modifications to GROMACS, please + * consider that scientific software is very special. Version + * control is crucial - bugs must be traceable. We will be happy to + * consider code for inclusion in the official distribution, but + * derived work must not be called official GROMACS. Details are found + * in the README & COPYING files - if they are missing, get the + * official version at http://www.gromacs.org. + * + * To help us fund GROMACS development, we humbly ask that you cite + * the research papers on the package. Check out http://www.gromacs.org. + */ +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include "sysstuff.h" +#include "smalloc.h" +#include "typedefs.h" +#include "physics.h" +#include "names.h" +#include "gmx_fatal.h" +#include "macros.h" +#include "index.h" +#include "symtab.h" +#include "string2.h" +#include "readinp.h" +#include "warninp.h" +#include "readir.h" +#include "toputil.h" +#include "index.h" +#include "network.h" +#include "vec.h" +#include "pbc.h" +#include "mtop_util.h" +#include "chargegroup.h" +#include "inputrec.h" +#include "calc_verletbuf.h" + +#define MAXPTR 254 +#define NOGID 255 + +/* Resource parameters + * Do not change any of these until you read the instruction + * in readinp.h. Some cpp's do not take spaces after the backslash + * (like the c-shell), which will give you a very weird compiler + * message. + */ + +typedef struct t_inputrec_strings +{ + char tcgrps[STRLEN], tau_t[STRLEN], ref_t[STRLEN], + acc[STRLEN], accgrps[STRLEN], freeze[STRLEN], frdim[STRLEN], + energy[STRLEN], user1[STRLEN], user2[STRLEN], vcm[STRLEN], x_compressed_groups[STRLEN], + couple_moltype[STRLEN], orirefitgrp[STRLEN], egptable[STRLEN], egpexcl[STRLEN], + wall_atomtype[STRLEN], wall_density[STRLEN], deform[STRLEN], QMMM[STRLEN]; + char fep_lambda[efptNR][STRLEN]; + char lambda_weights[STRLEN]; + char **pull_grp; + char **rot_grp; + char anneal[STRLEN], anneal_npoints[STRLEN], + anneal_time[STRLEN], anneal_temp[STRLEN]; + char QMmethod[STRLEN], QMbasis[STRLEN], QMcharge[STRLEN], QMmult[STRLEN], + bSH[STRLEN], CASorbitals[STRLEN], CASelectrons[STRLEN], SAon[STRLEN], + SAoff[STRLEN], SAsteps[STRLEN], bTS[STRLEN], bOPT[STRLEN]; + char efield_x[STRLEN], efield_xt[STRLEN], efield_y[STRLEN], + efield_yt[STRLEN], efield_z[STRLEN], efield_zt[STRLEN]; + +} gmx_inputrec_strings; + +static gmx_inputrec_strings *is = NULL; + +void init_inputrec_strings() +{ + if (is) + { + gmx_incons("Attempted to call init_inputrec_strings before calling done_inputrec_strings. Only one inputrec (i.e. .mdp file) can be parsed at a time."); + } + snew(is, 1); +} + +void done_inputrec_strings() +{ + sfree(is); + is = NULL; +} + +static char swapgrp[STRLEN], splitgrp0[STRLEN], splitgrp1[STRLEN], solgrp[STRLEN]; + +enum { + egrptpALL, /* All particles have to be a member of a group. */ + egrptpALL_GENREST, /* A rest group with name is generated for particles * + * that are not part of any group. */ + egrptpPART, /* As egrptpALL_GENREST, but no name is generated * + * for the rest group. */ + egrptpONE /* Merge all selected groups into one group, * + * make a rest group for the remaining particles. */ +}; + +static const char *constraints[eshNR+1] = { + "none", "h-bonds", "all-bonds", "h-angles", "all-angles", NULL +}; + +static const char *couple_lam[ecouplamNR+1] = { + "vdw-q", "vdw", "q", "none", NULL +}; + +void init_ir(t_inputrec *ir, t_gromppopts *opts) +{ + snew(opts->include, STRLEN); + snew(opts->define, STRLEN); + snew(ir->fepvals, 1); + snew(ir->expandedvals, 1); + snew(ir->simtempvals, 1); +} + +static void GetSimTemps(int ntemps, t_simtemp *simtemp, double *temperature_lambdas) +{ + + int i; + + for (i = 0; i < ntemps; i++) + { + /* simple linear scaling -- allows more control */ + if (simtemp->eSimTempScale == esimtempLINEAR) + { + simtemp->temperatures[i] = simtemp->simtemp_low + (simtemp->simtemp_high-simtemp->simtemp_low)*temperature_lambdas[i]; + } + else if (simtemp->eSimTempScale == esimtempGEOMETRIC) /* should give roughly equal acceptance for constant heat capacity . . . */ + { + simtemp->temperatures[i] = simtemp->simtemp_low * pow(simtemp->simtemp_high/simtemp->simtemp_low, (1.0*i)/(ntemps-1)); + } + else if (simtemp->eSimTempScale == esimtempEXPONENTIAL) + { + simtemp->temperatures[i] = simtemp->simtemp_low + (simtemp->simtemp_high-simtemp->simtemp_low)*((exp(temperature_lambdas[i])-1)/(exp(1.0)-1)); + } + else + { + char errorstr[128]; + sprintf(errorstr, "eSimTempScale=%d not defined", simtemp->eSimTempScale); + gmx_fatal(FARGS, errorstr); + } + } +} + + + +static void _low_check(gmx_bool b, char *s, warninp_t wi) +{ + if (b) + { + warning_error(wi, s); + } +} + +static void check_nst(const char *desc_nst, int nst, + const char *desc_p, int *p, + warninp_t wi) +{ + char buf[STRLEN]; + + if (*p > 0 && *p % nst != 0) + { + /* Round up to the next multiple of nst */ + *p = ((*p)/nst + 1)*nst; + sprintf(buf, "%s should be a multiple of %s, changing %s to %d\n", + desc_p, desc_nst, desc_p, *p); + warning(wi, buf); + } +} + +static gmx_bool ir_NVE(const t_inputrec *ir) +{ + return ((ir->eI == eiMD || EI_VV(ir->eI)) && ir->etc == etcNO); +} + +static int lcd(int n1, int n2) +{ + int d, i; + + d = 1; + for (i = 2; (i <= n1 && i <= n2); i++) + { + if (n1 % i == 0 && n2 % i == 0) + { + d = i; + } + } + + return d; +} + +static void process_interaction_modifier(const t_inputrec *ir, int *eintmod) +{ + if (*eintmod == eintmodPOTSHIFT_VERLET) + { + if (ir->cutoff_scheme == ecutsVERLET) + { + *eintmod = eintmodPOTSHIFT; + } + else + { + *eintmod = eintmodNONE; + } + } +} + +void check_ir(const char *mdparin, t_inputrec *ir, t_gromppopts *opts, + warninp_t wi) +/* Check internal consistency */ +{ + /* Strange macro: first one fills the err_buf, and then one can check + * the condition, which will print the message and increase the error + * counter. + */ +#define CHECK(b) _low_check(b, err_buf, wi) + char err_buf[256], warn_buf[STRLEN]; + int i, j; + int ns_type = 0; + real dt_coupl = 0; + real dt_pcoupl; + int nstcmin; + t_lambda *fep = ir->fepvals; + t_expanded *expand = ir->expandedvals; + + set_warning_line(wi, mdparin, -1); + + /* BASIC CUT-OFF STUFF */ + if (ir->rcoulomb < 0) + { + warning_error(wi, "rcoulomb should be >= 0"); + } + if (ir->rvdw < 0) + { + warning_error(wi, "rvdw should be >= 0"); + } + if (ir->rlist < 0 && + !(ir->cutoff_scheme == ecutsVERLET && ir->verletbuf_tol > 0)) + { + warning_error(wi, "rlist should be >= 0"); + } + + process_interaction_modifier(ir, &ir->coulomb_modifier); + process_interaction_modifier(ir, &ir->vdw_modifier); + + if (ir->cutoff_scheme == ecutsGROUP) + { + warning_note(wi, + "The group cutoff scheme is deprecated in Gromacs 5.0 and will be removed in a future " + "release when all interaction forms are supported for the verlet scheme. The verlet " + "scheme already scales better, and it is compatible with GPUs and other accelerators."); + + /* BASIC CUT-OFF STUFF */ + if (ir->rlist == 0 || + !((ir_coulomb_might_be_zero_at_cutoff(ir) && ir->rcoulomb > ir->rlist) || + (ir_vdw_might_be_zero_at_cutoff(ir) && ir->rvdw > ir->rlist))) + { + /* No switched potential and/or no twin-range: + * we can set the long-range cut-off to the maximum of the other cut-offs. + */ + ir->rlistlong = max_cutoff(ir->rlist, max_cutoff(ir->rvdw, ir->rcoulomb)); + } + else if (ir->rlistlong < 0) + { + ir->rlistlong = max_cutoff(ir->rlist, max_cutoff(ir->rvdw, ir->rcoulomb)); + sprintf(warn_buf, "rlistlong was not set, setting it to %g (no buffer)", + ir->rlistlong); + warning(wi, warn_buf); + } + if (ir->rlistlong == 0 && ir->ePBC != epbcNONE) + { + warning_error(wi, "Can not have an infinite cut-off with PBC"); + } + if (ir->rlistlong > 0 && (ir->rlist == 0 || ir->rlistlong < ir->rlist)) + { + warning_error(wi, "rlistlong can not be shorter than rlist"); + } + if (IR_TWINRANGE(*ir) && ir->nstlist <= 0) + { + warning_error(wi, "Can not have nstlist<=0 with twin-range interactions"); + } + } + + if (ir->rlistlong == ir->rlist) + { + ir->nstcalclr = 0; + } + else if (ir->rlistlong > ir->rlist && ir->nstcalclr == 0) + { + warning_error(wi, "With different cutoffs for electrostatics and VdW, nstcalclr must be -1 or a positive number"); + } + + if (ir->cutoff_scheme == ecutsVERLET) + { + real rc_max; + + /* Normal Verlet type neighbor-list, currently only limited feature support */ + if (inputrec2nboundeddim(ir) < 3) + { + warning_error(wi, "With Verlet lists only full pbc or pbc=xy with walls is supported"); + } + if (ir->rcoulomb != ir->rvdw) + { + warning_error(wi, "With Verlet lists rcoulomb!=rvdw is not supported"); + } + if (ir->vdwtype == evdwSHIFT || ir->vdwtype == evdwSWITCH) + { + if (ir->vdw_modifier == eintmodNONE || + ir->vdw_modifier == eintmodPOTSHIFT) + { + ir->vdw_modifier = (ir->vdwtype == evdwSHIFT ? eintmodFORCESWITCH : eintmodPOTSWITCH); + + sprintf(warn_buf, "Replacing vdwtype=%s by the equivalent combination of vdwtype=%s and vdw_modifier=%s", evdw_names[ir->vdwtype], evdw_names[evdwCUT], eintmod_names[ir->vdw_modifier]); + warning_note(wi, warn_buf); + + ir->vdwtype = evdwCUT; + } + else + { + sprintf(warn_buf, "Unsupported combination of vdwtype=%s and vdw_modifier=%s", evdw_names[ir->vdwtype], eintmod_names[ir->vdw_modifier]); + warning_error(wi, warn_buf); + } + } + + if (!(ir->vdwtype == evdwCUT || ir->vdwtype == evdwPME)) + { + warning_error(wi, "With Verlet lists only cut-off and PME LJ interactions are supported"); + } + if (!(ir->coulombtype == eelCUT || + (EEL_RF(ir->coulombtype) && ir->coulombtype != eelRF_NEC) || + EEL_PME(ir->coulombtype) || ir->coulombtype == eelEWALD)) + { + warning_error(wi, "With Verlet lists only cut-off, reaction-field, PME and Ewald electrostatics are supported"); + } + if (!(ir->coulomb_modifier == eintmodNONE || + ir->coulomb_modifier == eintmodPOTSHIFT)) + { + sprintf(warn_buf, "coulomb_modifier=%s is not supported with the Verlet cut-off scheme", eintmod_names[ir->coulomb_modifier]); + warning_error(wi, warn_buf); + } + + if (ir->nstlist <= 0) + { + warning_error(wi, "With Verlet lists nstlist should be larger than 0"); + } + + if (ir->nstlist < 10) + { + warning_note(wi, "With Verlet lists the optimal nstlist is >= 10, with GPUs >= 20. Note that with the Verlet scheme, nstlist has no effect on the accuracy of your simulation."); + } + + rc_max = max(ir->rvdw, ir->rcoulomb); + + if (ir->verletbuf_tol <= 0) + { + if (ir->verletbuf_tol == 0) + { + warning_error(wi, "Can not have Verlet buffer tolerance of exactly 0"); + } + + if (ir->rlist < rc_max) + { + warning_error(wi, "With verlet lists rlist can not be smaller than rvdw or rcoulomb"); + } + + if (ir->rlist == rc_max && ir->nstlist > 1) + { + warning_note(wi, "rlist is equal to rvdw and/or rcoulomb: there is no explicit Verlet buffer. The cluster pair list does have a buffering effect, but choosing a larger rlist might be necessary for good energy conservation."); + } + } + else + { + if (ir->rlist > rc_max) + { + warning_note(wi, "You have set rlist larger than the interaction cut-off, but you also have verlet-buffer-tolerance > 0. Will set rlist using verlet-buffer-tolerance."); + } + + if (ir->nstlist == 1) + { + /* No buffer required */ + ir->rlist = rc_max; + } + else + { + if (EI_DYNAMICS(ir->eI)) + { + if (inputrec2nboundeddim(ir) < 3) + { + warning_error(wi, "The box volume is required for calculating rlist from the energy drift with verlet-buffer-tolerance > 0. You are using at least one unbounded dimension, so no volume can be computed. Either use a finite box, or set rlist yourself together with verlet-buffer-tolerance = -1."); + } + /* Set rlist temporarily so we can continue processing */ + ir->rlist = rc_max; + } + else + { + /* Set the buffer to 5% of the cut-off */ + ir->rlist = (1.0 + verlet_buffer_ratio_nodynamics)*rc_max; + } + } + } + + /* No twin-range calculations with Verlet lists */ + ir->rlistlong = ir->rlist; + } + + if (ir->nstcalclr == -1) + { + /* if rlist=rlistlong, this will later be changed to nstcalclr=0 */ + ir->nstcalclr = ir->nstlist; + } + else if (ir->nstcalclr > 0) + { + if (ir->nstlist > 0 && (ir->nstlist % ir->nstcalclr != 0)) + { + warning_error(wi, "nstlist must be evenly divisible by nstcalclr. Use nstcalclr = -1 to automatically follow nstlist"); + } + } + else if (ir->nstcalclr < -1) + { + warning_error(wi, "nstcalclr must be a positive number (divisor of nstcalclr), or -1 to follow nstlist."); + } + + if (EEL_PME(ir->coulombtype) && ir->rcoulomb > ir->rvdw && ir->nstcalclr > 1) + { + warning_error(wi, "When used with PME, the long-range component of twin-range interactions must be updated every step (nstcalclr)"); + } + + /* GENERAL INTEGRATOR STUFF */ + if (!(ir->eI == eiMD || EI_VV(ir->eI))) + { + ir->etc = etcNO; + } + if (ir->eI == eiVVAK) + { + sprintf(warn_buf, "Integrator method %s is implemented primarily for validation purposes; for molecular dynamics, you should probably be using %s or %s", ei_names[eiVVAK], ei_names[eiMD], ei_names[eiVV]); + warning_note(wi, warn_buf); + } + if (!EI_DYNAMICS(ir->eI)) + { + ir->epc = epcNO; + } + if (EI_DYNAMICS(ir->eI)) + { + if (ir->nstcalcenergy < 0) + { + ir->nstcalcenergy = ir_optimal_nstcalcenergy(ir); + if (ir->nstenergy != 0 && ir->nstenergy < ir->nstcalcenergy) + { + /* nstcalcenergy larger than nstener does not make sense. + * We ideally want nstcalcenergy=nstener. + */ + if (ir->nstlist > 0) + { + ir->nstcalcenergy = lcd(ir->nstenergy, ir->nstlist); + } + else + { + ir->nstcalcenergy = ir->nstenergy; + } + } + } + else if ( (ir->nstenergy > 0 && ir->nstcalcenergy > ir->nstenergy) || + (ir->efep != efepNO && ir->fepvals->nstdhdl > 0 && + (ir->nstcalcenergy > ir->fepvals->nstdhdl) ) ) + + { + const char *nsten = "nstenergy"; + const char *nstdh = "nstdhdl"; + const char *min_name = nsten; + int min_nst = ir->nstenergy; + + /* find the smallest of ( nstenergy, nstdhdl ) */ + if (ir->efep != efepNO && ir->fepvals->nstdhdl > 0 && + (ir->nstenergy == 0 || ir->fepvals->nstdhdl < ir->nstenergy)) + { + min_nst = ir->fepvals->nstdhdl; + min_name = nstdh; + } + /* If the user sets nstenergy small, we should respect that */ + sprintf(warn_buf, + "Setting nstcalcenergy (%d) equal to %s (%d)", + ir->nstcalcenergy, min_name, min_nst); + warning_note(wi, warn_buf); + ir->nstcalcenergy = min_nst; + } + + if (ir->epc != epcNO) + { + if (ir->nstpcouple < 0) + { + ir->nstpcouple = ir_optimal_nstpcouple(ir); + } + } + if (IR_TWINRANGE(*ir)) + { + check_nst("nstlist", ir->nstlist, + "nstcalcenergy", &ir->nstcalcenergy, wi); + if (ir->epc != epcNO) + { + check_nst("nstlist", ir->nstlist, + "nstpcouple", &ir->nstpcouple, wi); + } + } + + if (ir->nstcalcenergy > 0) + { + if (ir->efep != efepNO) + { + /* nstdhdl should be a multiple of nstcalcenergy */ + check_nst("nstcalcenergy", ir->nstcalcenergy, + "nstdhdl", &ir->fepvals->nstdhdl, wi); + /* nstexpanded should be a multiple of nstcalcenergy */ + check_nst("nstcalcenergy", ir->nstcalcenergy, + "nstexpanded", &ir->expandedvals->nstexpanded, wi); + } + /* for storing exact averages nstenergy should be + * a multiple of nstcalcenergy + */ + check_nst("nstcalcenergy", ir->nstcalcenergy, + "nstenergy", &ir->nstenergy, wi); + } + } + + /* LD STUFF */ + if ((EI_SD(ir->eI) || ir->eI == eiBD) && + ir->bContinuation && ir->ld_seed != -1) + { + warning_note(wi, "You are doing a continuation with SD or BD, make sure that ld_seed is different from the previous run (using ld_seed=-1 will ensure this)"); + } + + /* TPI STUFF */ + if (EI_TPI(ir->eI)) + { + sprintf(err_buf, "TPI only works with pbc = %s", epbc_names[epbcXYZ]); + CHECK(ir->ePBC != epbcXYZ); + sprintf(err_buf, "TPI only works with ns = %s", ens_names[ensGRID]); + CHECK(ir->ns_type != ensGRID); + sprintf(err_buf, "with TPI nstlist should be larger than zero"); + CHECK(ir->nstlist <= 0); + sprintf(err_buf, "TPI does not work with full electrostatics other than PME"); + CHECK(EEL_FULL(ir->coulombtype) && !EEL_PME(ir->coulombtype)); + } + + /* SHAKE / LINCS */ + if ( (opts->nshake > 0) && (opts->bMorse) ) + { + sprintf(warn_buf, + "Using morse bond-potentials while constraining bonds is useless"); + warning(wi, warn_buf); + } + + if ((EI_SD(ir->eI) || ir->eI == eiBD) && + ir->bContinuation && ir->ld_seed != -1) + { + warning_note(wi, "You are doing a continuation with SD or BD, make sure that ld_seed is different from the previous run (using ld_seed=-1 will ensure this)"); + } + /* verify simulated tempering options */ + + if (ir->bSimTemp) + { + gmx_bool bAllTempZero = TRUE; + for (i = 0; i < fep->n_lambda; i++) + { + sprintf(err_buf, "Entry %d for %s must be between 0 and 1, instead is %g", i, efpt_names[efptTEMPERATURE], fep->all_lambda[efptTEMPERATURE][i]); + CHECK((fep->all_lambda[efptTEMPERATURE][i] < 0) || (fep->all_lambda[efptTEMPERATURE][i] > 1)); + if (fep->all_lambda[efptTEMPERATURE][i] > 0) + { + bAllTempZero = FALSE; + } + } + sprintf(err_buf, "if simulated tempering is on, temperature-lambdas may not be all zero"); + CHECK(bAllTempZero == TRUE); + + sprintf(err_buf, "Simulated tempering is currently only compatible with md-vv"); + CHECK(ir->eI != eiVV); + + /* check compatability of the temperature coupling with simulated tempering */ + + if (ir->etc == etcNOSEHOOVER) + { + sprintf(warn_buf, "Nose-Hoover based temperature control such as [%s] my not be entirelyconsistent with simulated tempering", etcoupl_names[ir->etc]); + warning_note(wi, warn_buf); + } + + /* check that the temperatures make sense */ + + sprintf(err_buf, "Higher simulated tempering temperature (%g) must be >= than the simulated tempering lower temperature (%g)", ir->simtempvals->simtemp_high, ir->simtempvals->simtemp_low); + CHECK(ir->simtempvals->simtemp_high <= ir->simtempvals->simtemp_low); + + sprintf(err_buf, "Higher simulated tempering temperature (%g) must be >= zero", ir->simtempvals->simtemp_high); + CHECK(ir->simtempvals->simtemp_high <= 0); + + sprintf(err_buf, "Lower simulated tempering temperature (%g) must be >= zero", ir->simtempvals->simtemp_low); + CHECK(ir->simtempvals->simtemp_low <= 0); + } + + /* verify free energy options */ + + if (ir->efep != efepNO) + { + fep = ir->fepvals; + sprintf(err_buf, "The soft-core power is %d and can only be 1 or 2", + fep->sc_power); + CHECK(fep->sc_alpha != 0 && fep->sc_power != 1 && fep->sc_power != 2); + + sprintf(err_buf, "The soft-core sc-r-power is %d and can only be 6 or 48", + (int)fep->sc_r_power); + CHECK(fep->sc_alpha != 0 && fep->sc_r_power != 6.0 && fep->sc_r_power != 48.0); + + sprintf(err_buf, "Can't use postive delta-lambda (%g) if initial state/lambda does not start at zero", fep->delta_lambda); + CHECK(fep->delta_lambda > 0 && ((fep->init_fep_state > 0) || (fep->init_lambda > 0))); + + sprintf(err_buf, "Can't use postive delta-lambda (%g) with expanded ensemble simulations", fep->delta_lambda); + CHECK(fep->delta_lambda > 0 && (ir->efep == efepEXPANDED)); + + sprintf(err_buf, "Can only use expanded ensemble with md-vv for now; should be supported for other integrators in 5.0"); + CHECK(!(EI_VV(ir->eI)) && (ir->efep == efepEXPANDED)); + + sprintf(err_buf, "Free-energy not implemented for Ewald"); + CHECK(ir->coulombtype == eelEWALD); + + /* check validty of lambda inputs */ + if (fep->n_lambda == 0) + { + /* Clear output in case of no states:*/ + sprintf(err_buf, "init-lambda-state set to %d: no lambda states are defined.", fep->init_fep_state); + CHECK((fep->init_fep_state >= 0) && (fep->n_lambda == 0)); + } + else + { + sprintf(err_buf, "initial thermodynamic state %d does not exist, only goes to %d", fep->init_fep_state, fep->n_lambda-1); + CHECK((fep->init_fep_state >= fep->n_lambda)); + } + + sprintf(err_buf, "Lambda state must be set, either with init-lambda-state or with init-lambda"); + CHECK((fep->init_fep_state < 0) && (fep->init_lambda < 0)); + + sprintf(err_buf, "init-lambda=%g while init-lambda-state=%d. Lambda state must be set either with init-lambda-state or with init-lambda, but not both", + fep->init_lambda, fep->init_fep_state); + CHECK((fep->init_fep_state >= 0) && (fep->init_lambda >= 0)); + + + + if ((fep->init_lambda >= 0) && (fep->delta_lambda == 0)) + { + int n_lambda_terms; + n_lambda_terms = 0; + for (i = 0; i < efptNR; i++) + { + if (fep->separate_dvdl[i]) + { + n_lambda_terms++; + } + } + if (n_lambda_terms > 1) + { + sprintf(warn_buf, "If lambda vector states (fep-lambdas, coul-lambdas etc.) are set, don't use init-lambda to set lambda state (except for slow growth). Use init-lambda-state instead."); + warning(wi, warn_buf); + } + + if (n_lambda_terms < 2 && fep->n_lambda > 0) + { + warning_note(wi, + "init-lambda is deprecated for setting lambda state (except for slow growth). Use init-lambda-state instead."); + } + } + + for (j = 0; j < efptNR; j++) + { + for (i = 0; i < fep->n_lambda; i++) + { + sprintf(err_buf, "Entry %d for %s must be between 0 and 1, instead is %g", i, efpt_names[j], fep->all_lambda[j][i]); + CHECK((fep->all_lambda[j][i] < 0) || (fep->all_lambda[j][i] > 1)); + } + } + + if ((fep->sc_alpha > 0) && (!fep->bScCoul)) + { + for (i = 0; i < fep->n_lambda; i++) + { + sprintf(err_buf, "For state %d, vdw-lambdas (%f) is changing with vdw softcore, while coul-lambdas (%f) is nonzero without coulomb softcore: this will lead to crashes, and is not supported.", i, fep->all_lambda[efptVDW][i], + fep->all_lambda[efptCOUL][i]); + CHECK((fep->sc_alpha > 0) && + (((fep->all_lambda[efptCOUL][i] > 0.0) && + (fep->all_lambda[efptCOUL][i] < 1.0)) && + ((fep->all_lambda[efptVDW][i] > 0.0) && + (fep->all_lambda[efptVDW][i] < 1.0)))); + } + } + + if ((fep->bScCoul) && (EEL_PME(ir->coulombtype))) + { + real sigma, lambda, r_sc; + + sigma = 0.34; + /* Maximum estimate for A and B charges equal with lambda power 1 */ + lambda = 0.5; + r_sc = pow(lambda*fep->sc_alpha*pow(sigma/ir->rcoulomb, fep->sc_r_power) + 1.0, 1.0/fep->sc_r_power); + sprintf(warn_buf, "With PME there is a minor soft core effect present at the cut-off, proportional to (LJsigma/rcoulomb)^%g. This could have a minor effect on energy conservation, but usually other effects dominate. With a common sigma value of %g nm the fraction of the particle-particle potential at the cut-off at lambda=%g is around %.1e, while ewald-rtol is %.1e.", + fep->sc_r_power, + sigma, lambda, r_sc - 1.0, ir->ewald_rtol); + warning_note(wi, warn_buf); + } + + /* Free Energy Checks -- In an ideal world, slow growth and FEP would + be treated differently, but that's the next step */ + + for (i = 0; i < efptNR; i++) + { + for (j = 0; j < fep->n_lambda; j++) + { + sprintf(err_buf, "%s[%d] must be between 0 and 1", efpt_names[i], j); + CHECK((fep->all_lambda[i][j] < 0) || (fep->all_lambda[i][j] > 1)); + } + } + } + + if ((ir->bSimTemp) || (ir->efep == efepEXPANDED)) + { + fep = ir->fepvals; + expand = ir->expandedvals; + + /* checking equilibration of weights inputs for validity */ + + sprintf(err_buf, "weight-equil-number-all-lambda (%d) is ignored if lmc-weights-equil is not equal to %s", + expand->equil_n_at_lam, elmceq_names[elmceqNUMATLAM]); + CHECK((expand->equil_n_at_lam > 0) && (expand->elmceq != elmceqNUMATLAM)); + + sprintf(err_buf, "weight-equil-number-samples (%d) is ignored if lmc-weights-equil is not equal to %s", + expand->equil_samples, elmceq_names[elmceqSAMPLES]); + CHECK((expand->equil_samples > 0) && (expand->elmceq != elmceqSAMPLES)); + + sprintf(err_buf, "weight-equil-number-steps (%d) is ignored if lmc-weights-equil is not equal to %s", + expand->equil_steps, elmceq_names[elmceqSTEPS]); + CHECK((expand->equil_steps > 0) && (expand->elmceq != elmceqSTEPS)); + + sprintf(err_buf, "weight-equil-wl-delta (%d) is ignored if lmc-weights-equil is not equal to %s", + expand->equil_samples, elmceq_names[elmceqWLDELTA]); + CHECK((expand->equil_wl_delta > 0) && (expand->elmceq != elmceqWLDELTA)); + + sprintf(err_buf, "weight-equil-count-ratio (%f) is ignored if lmc-weights-equil is not equal to %s", + expand->equil_ratio, elmceq_names[elmceqRATIO]); + CHECK((expand->equil_ratio > 0) && (expand->elmceq != elmceqRATIO)); + + sprintf(err_buf, "weight-equil-number-all-lambda (%d) must be a positive integer if lmc-weights-equil=%s", + expand->equil_n_at_lam, elmceq_names[elmceqNUMATLAM]); + CHECK((expand->equil_n_at_lam <= 0) && (expand->elmceq == elmceqNUMATLAM)); + + sprintf(err_buf, "weight-equil-number-samples (%d) must be a positive integer if lmc-weights-equil=%s", + expand->equil_samples, elmceq_names[elmceqSAMPLES]); + CHECK((expand->equil_samples <= 0) && (expand->elmceq == elmceqSAMPLES)); + + sprintf(err_buf, "weight-equil-number-steps (%d) must be a positive integer if lmc-weights-equil=%s", + expand->equil_steps, elmceq_names[elmceqSTEPS]); + CHECK((expand->equil_steps <= 0) && (expand->elmceq == elmceqSTEPS)); + + sprintf(err_buf, "weight-equil-wl-delta (%f) must be > 0 if lmc-weights-equil=%s", + expand->equil_wl_delta, elmceq_names[elmceqWLDELTA]); + CHECK((expand->equil_wl_delta <= 0) && (expand->elmceq == elmceqWLDELTA)); + + sprintf(err_buf, "weight-equil-count-ratio (%f) must be > 0 if lmc-weights-equil=%s", + expand->equil_ratio, elmceq_names[elmceqRATIO]); + CHECK((expand->equil_ratio <= 0) && (expand->elmceq == elmceqRATIO)); + + sprintf(err_buf, "lmc-weights-equil=%s only possible when lmc-stats = %s or lmc-stats %s", + elmceq_names[elmceqWLDELTA], elamstats_names[elamstatsWL], elamstats_names[elamstatsWWL]); + CHECK((expand->elmceq == elmceqWLDELTA) && (!EWL(expand->elamstats))); + + sprintf(err_buf, "lmc-repeats (%d) must be greater than 0", expand->lmc_repeats); + CHECK((expand->lmc_repeats <= 0)); + sprintf(err_buf, "minimum-var-min (%d) must be greater than 0", expand->minvarmin); + CHECK((expand->minvarmin <= 0)); + sprintf(err_buf, "weight-c-range (%d) must be greater or equal to 0", expand->c_range); + CHECK((expand->c_range < 0)); + sprintf(err_buf, "init-lambda-state (%d) must be zero if lmc-forced-nstart (%d)> 0 and lmc-move != 'no'", + fep->init_fep_state, expand->lmc_forced_nstart); + CHECK((fep->init_fep_state != 0) && (expand->lmc_forced_nstart > 0) && (expand->elmcmove != elmcmoveNO)); + sprintf(err_buf, "lmc-forced-nstart (%d) must not be negative", expand->lmc_forced_nstart); + CHECK((expand->lmc_forced_nstart < 0)); + sprintf(err_buf, "init-lambda-state (%d) must be in the interval [0,number of lambdas)", fep->init_fep_state); + CHECK((fep->init_fep_state < 0) || (fep->init_fep_state >= fep->n_lambda)); + + sprintf(err_buf, "init-wl-delta (%f) must be greater than or equal to 0", expand->init_wl_delta); + CHECK((expand->init_wl_delta < 0)); + sprintf(err_buf, "wl-ratio (%f) must be between 0 and 1", expand->wl_ratio); + CHECK((expand->wl_ratio <= 0) || (expand->wl_ratio >= 1)); + sprintf(err_buf, "wl-scale (%f) must be between 0 and 1", expand->wl_scale); + CHECK((expand->wl_scale <= 0) || (expand->wl_scale >= 1)); + + /* if there is no temperature control, we need to specify an MC temperature */ + sprintf(err_buf, "If there is no temperature control, and lmc-mcmove!= 'no',mc_temperature must be set to a positive number"); + if (expand->nstTij > 0) + { + sprintf(err_buf, "nst-transition-matrix (%d) must be an integer multiple of nstlog (%d)", + expand->nstTij, ir->nstlog); + CHECK((mod(expand->nstTij, ir->nstlog) != 0)); + } + } + + /* PBC/WALLS */ + sprintf(err_buf, "walls only work with pbc=%s", epbc_names[epbcXY]); + CHECK(ir->nwall && ir->ePBC != epbcXY); + + /* VACUUM STUFF */ + if (ir->ePBC != epbcXYZ && ir->nwall != 2) + { + if (ir->ePBC == epbcNONE) + { + if (ir->epc != epcNO) + { + warning(wi, "Turning off pressure coupling for vacuum system"); + ir->epc = epcNO; + } + } + else + { + sprintf(err_buf, "Can not have pressure coupling with pbc=%s", + epbc_names[ir->ePBC]); + CHECK(ir->epc != epcNO); + } + sprintf(err_buf, "Can not have Ewald with pbc=%s", epbc_names[ir->ePBC]); + CHECK(EEL_FULL(ir->coulombtype)); + + sprintf(err_buf, "Can not have dispersion correction with pbc=%s", + epbc_names[ir->ePBC]); + CHECK(ir->eDispCorr != edispcNO); + } + + if (ir->rlist == 0.0) + { + sprintf(err_buf, "can only have neighborlist cut-off zero (=infinite)\n" + "with coulombtype = %s or coulombtype = %s\n" + "without periodic boundary conditions (pbc = %s) and\n" + "rcoulomb and rvdw set to zero", + eel_names[eelCUT], eel_names[eelUSER], epbc_names[epbcNONE]); + CHECK(((ir->coulombtype != eelCUT) && (ir->coulombtype != eelUSER)) || + (ir->ePBC != epbcNONE) || + (ir->rcoulomb != 0.0) || (ir->rvdw != 0.0)); + + if (ir->nstlist < 0) + { + warning_error(wi, "Can not have heuristic neighborlist updates without cut-off"); + } + if (ir->nstlist > 0) + { + warning_note(wi, "Simulating without cut-offs can be (slightly) faster with nstlist=0, nstype=simple and only one MPI rank"); + } + } + + /* COMM STUFF */ + if (ir->nstcomm == 0) + { + ir->comm_mode = ecmNO; + } + if (ir->comm_mode != ecmNO) + { + if (ir->nstcomm < 0) + { + warning(wi, "If you want to remove the rotation around the center of mass, you should set comm_mode = Angular instead of setting nstcomm < 0. nstcomm is modified to its absolute value"); + ir->nstcomm = abs(ir->nstcomm); + } + + if (ir->nstcalcenergy > 0 && ir->nstcomm < ir->nstcalcenergy) + { + warning_note(wi, "nstcomm < nstcalcenergy defeats the purpose of nstcalcenergy, setting nstcomm to nstcalcenergy"); + ir->nstcomm = ir->nstcalcenergy; + } + + if (ir->comm_mode == ecmANGULAR) + { + sprintf(err_buf, "Can not remove the rotation around the center of mass with periodic molecules"); + CHECK(ir->bPeriodicMols); + if (ir->ePBC != epbcNONE) + { + warning(wi, "Removing the rotation around the center of mass in a periodic system (this is not a problem when you have only one molecule)."); + } + } + } + + if (EI_STATE_VELOCITY(ir->eI) && ir->ePBC == epbcNONE && ir->comm_mode != ecmANGULAR) + { + warning_note(wi, "Tumbling and or flying ice-cubes: We are not removing rotation around center of mass in a non-periodic system. You should probably set comm_mode = ANGULAR."); + } + + sprintf(err_buf, "Twin-range neighbour searching (NS) with simple NS" + " algorithm not implemented"); + CHECK(((ir->rcoulomb > ir->rlist) || (ir->rvdw > ir->rlist)) + && (ir->ns_type == ensSIMPLE)); + + /* TEMPERATURE COUPLING */ + if (ir->etc == etcYES) + { + ir->etc = etcBERENDSEN; + warning_note(wi, "Old option for temperature coupling given: " + "changing \"yes\" to \"Berendsen\"\n"); + } + + if ((ir->etc == etcNOSEHOOVER) || (ir->epc == epcMTTK)) + { + if (ir->opts.nhchainlength < 1) + { + sprintf(warn_buf, "number of Nose-Hoover chains (currently %d) cannot be less than 1,reset to 1\n", ir->opts.nhchainlength); + ir->opts.nhchainlength = 1; + warning(wi, warn_buf); + } + + if (ir->etc == etcNOSEHOOVER && !EI_VV(ir->eI) && ir->opts.nhchainlength > 1) + { + warning_note(wi, "leapfrog does not yet support Nose-Hoover chains, nhchainlength reset to 1"); + ir->opts.nhchainlength = 1; + } + } + else + { + ir->opts.nhchainlength = 0; + } + + if (ir->eI == eiVVAK) + { + sprintf(err_buf, "%s implemented primarily for validation, and requires nsttcouple = 1 and nstpcouple = 1.", + ei_names[eiVVAK]); + CHECK((ir->nsttcouple != 1) || (ir->nstpcouple != 1)); + } + + if (ETC_ANDERSEN(ir->etc)) + { + sprintf(err_buf, "%s temperature control not supported for integrator %s.", etcoupl_names[ir->etc], ei_names[ir->eI]); + CHECK(!(EI_VV(ir->eI))); + + for (i = 0; i < ir->opts.ngtc; i++) + { + sprintf(err_buf, "all tau_t must currently be equal using Andersen temperature control, violated for group %d", i); + CHECK(ir->opts.tau_t[0] != ir->opts.tau_t[i]); + sprintf(err_buf, "all tau_t must be postive using Andersen temperature control, tau_t[%d]=%10.6f", + i, ir->opts.tau_t[i]); + CHECK(ir->opts.tau_t[i] < 0); + } + if (ir->nstcomm > 0 && (ir->etc == etcANDERSEN)) + { + sprintf(warn_buf, "Center of mass removal not necessary for %s. All velocities of coupled groups are rerandomized periodically, so flying ice cube errors will not occur.", etcoupl_names[ir->etc]); + warning_note(wi, warn_buf); + } + + sprintf(err_buf, "nstcomm must be 1, not %d for %s, as velocities of atoms in coupled groups are randomized every time step", ir->nstcomm, etcoupl_names[ir->etc]); + CHECK(ir->nstcomm > 1 && (ir->etc == etcANDERSEN)); + + for (i = 0; i < ir->opts.ngtc; i++) + { + int nsteps = (int)(ir->opts.tau_t[i]/ir->delta_t); + sprintf(err_buf, "tau_t/delta_t for group %d for temperature control method %s must be a multiple of nstcomm (%d), as velocities of atoms in coupled groups are randomized every time step. The input tau_t (%8.3f) leads to %d steps per randomization", i, etcoupl_names[ir->etc], ir->nstcomm, ir->opts.tau_t[i], nsteps); + CHECK((nsteps % ir->nstcomm) && (ir->etc == etcANDERSENMASSIVE)); + } + } + if (ir->etc == etcBERENDSEN) + { + sprintf(warn_buf, "The %s thermostat does not generate the correct kinetic energy distribution. You might want to consider using the %s thermostat.", + ETCOUPLTYPE(ir->etc), ETCOUPLTYPE(etcVRESCALE)); + warning_note(wi, warn_buf); + } + + if ((ir->etc == etcNOSEHOOVER || ETC_ANDERSEN(ir->etc)) + && ir->epc == epcBERENDSEN) + { + sprintf(warn_buf, "Using Berendsen pressure coupling invalidates the " + "true ensemble for the thermostat"); + warning(wi, warn_buf); + } + + /* PRESSURE COUPLING */ + if (ir->epc == epcISOTROPIC) + { + ir->epc = epcBERENDSEN; + warning_note(wi, "Old option for pressure coupling given: " + "changing \"Isotropic\" to \"Berendsen\"\n"); + } + + if (ir->epc != epcNO) + { + dt_pcoupl = ir->nstpcouple*ir->delta_t; + + sprintf(err_buf, "tau-p must be > 0 instead of %g\n", ir->tau_p); + CHECK(ir->tau_p <= 0); + + if (ir->tau_p/dt_pcoupl < pcouple_min_integration_steps(ir->epc)) + { + sprintf(warn_buf, "For proper integration of the %s barostat, tau-p (%g) should be at least %d times larger than nstpcouple*dt (%g)", + EPCOUPLTYPE(ir->epc), ir->tau_p, pcouple_min_integration_steps(ir->epc), dt_pcoupl); + warning(wi, warn_buf); + } + + sprintf(err_buf, "compressibility must be > 0 when using pressure" + " coupling %s\n", EPCOUPLTYPE(ir->epc)); + CHECK(ir->compress[XX][XX] < 0 || ir->compress[YY][YY] < 0 || + ir->compress[ZZ][ZZ] < 0 || + (trace(ir->compress) == 0 && ir->compress[YY][XX] <= 0 && + ir->compress[ZZ][XX] <= 0 && ir->compress[ZZ][YY] <= 0)); + + if (epcPARRINELLORAHMAN == ir->epc && opts->bGenVel) + { + sprintf(warn_buf, + "You are generating velocities so I am assuming you " + "are equilibrating a system. You are using " + "%s pressure coupling, but this can be " + "unstable for equilibration. If your system crashes, try " + "equilibrating first with Berendsen pressure coupling. If " + "you are not equilibrating the system, you can probably " + "ignore this warning.", + epcoupl_names[ir->epc]); + warning(wi, warn_buf); + } + } + + if (EI_VV(ir->eI)) + { + if (ir->epc > epcNO) + { + if ((ir->epc != epcBERENDSEN) && (ir->epc != epcMTTK)) + { + warning_error(wi, "for md-vv and md-vv-avek, can only use Berendsen and Martyna-Tuckerman-Tobias-Klein (MTTK) equations for pressure control; MTTK is equivalent to Parrinello-Rahman."); + } + } + } ++ else ++ { ++ if (ir->epc == epcMTTK) ++ { ++ warning_error(wi, "MTTK pressure coupling requires a Velocity-verlet integrator"); ++ } ++ } + + /* ELECTROSTATICS */ + /* More checks are in triple check (grompp.c) */ + + if (ir->coulombtype == eelSWITCH) + { + sprintf(warn_buf, "coulombtype = %s is only for testing purposes and can lead to serious " + "artifacts, advice: use coulombtype = %s", + eel_names[ir->coulombtype], + eel_names[eelRF_ZERO]); + warning(wi, warn_buf); + } + + if (ir->epsilon_r != 1 && ir->implicit_solvent == eisGBSA) + { + sprintf(warn_buf, "epsilon-r = %g with GB implicit solvent, will use this value for inner dielectric", ir->epsilon_r); + warning_note(wi, warn_buf); + } + + if (EEL_RF(ir->coulombtype) && ir->epsilon_rf == 1 && ir->epsilon_r != 1) + { + sprintf(warn_buf, "epsilon-r = %g and epsilon-rf = 1 with reaction field, proceeding assuming old format and exchanging epsilon-r and epsilon-rf", ir->epsilon_r); + warning(wi, warn_buf); + ir->epsilon_rf = ir->epsilon_r; + ir->epsilon_r = 1.0; + } + + if (getenv("GALACTIC_DYNAMICS") == NULL) + { + sprintf(err_buf, "epsilon-r must be >= 0 instead of %g\n", ir->epsilon_r); + CHECK(ir->epsilon_r < 0); + } + + if (EEL_RF(ir->coulombtype)) + { + /* reaction field (at the cut-off) */ + + if (ir->coulombtype == eelRF_ZERO) + { + sprintf(warn_buf, "With coulombtype = %s, epsilon-rf must be 0, assuming you meant epsilon_rf=0", + eel_names[ir->coulombtype]); + CHECK(ir->epsilon_rf != 0); + ir->epsilon_rf = 0.0; + } + + sprintf(err_buf, "epsilon-rf must be >= epsilon-r"); + CHECK((ir->epsilon_rf < ir->epsilon_r && ir->epsilon_rf != 0) || + (ir->epsilon_r == 0)); + if (ir->epsilon_rf == ir->epsilon_r) + { + sprintf(warn_buf, "Using epsilon-rf = epsilon-r with %s does not make sense", + eel_names[ir->coulombtype]); + warning(wi, warn_buf); + } + } + /* Allow rlist>rcoulomb for tabulated long range stuff. This just + * means the interaction is zero outside rcoulomb, but it helps to + * provide accurate energy conservation. + */ + if (ir_coulomb_might_be_zero_at_cutoff(ir)) + { + if (ir_coulomb_switched(ir)) + { + sprintf(err_buf, + "With coulombtype = %s rcoulomb_switch must be < rcoulomb. Or, better: Use the potential modifier options!", + eel_names[ir->coulombtype]); + CHECK(ir->rcoulomb_switch >= ir->rcoulomb); + } + } + else if (ir->coulombtype == eelCUT || EEL_RF(ir->coulombtype)) + { + if (ir->cutoff_scheme == ecutsGROUP && ir->coulomb_modifier == eintmodNONE) + { + sprintf(err_buf, "With coulombtype = %s, rcoulomb should be >= rlist unless you use a potential modifier", + eel_names[ir->coulombtype]); + CHECK(ir->rlist > ir->rcoulomb); + } + } + + if (ir->coulombtype == eelSWITCH || ir->coulombtype == eelSHIFT || + ir->vdwtype == evdwSWITCH || ir->vdwtype == evdwSHIFT) + { + sprintf(warn_buf, + "The switch/shift interaction settings are just for compatibility; you will get better " + "performance from applying potential modifiers to your interactions!\n"); + warning_note(wi, warn_buf); + } + + if (ir->coulombtype == eelPMESWITCH) + { + if (ir->rcoulomb_switch/ir->rcoulomb < 0.9499) + { + sprintf(warn_buf, "The switching range for %s should be 5%% or less, energy conservation will be good anyhow, since ewald_rtol = %g", + eel_names[ir->coulombtype], + ir->ewald_rtol); + warning(wi, warn_buf); + } + } + + if (EEL_FULL(ir->coulombtype)) + { + if (ir->coulombtype == eelPMESWITCH || ir->coulombtype == eelPMEUSER || + ir->coulombtype == eelPMEUSERSWITCH) + { + sprintf(err_buf, "With coulombtype = %s, rcoulomb must be <= rlist", + eel_names[ir->coulombtype]); + CHECK(ir->rcoulomb > ir->rlist); + } + else if (ir->cutoff_scheme == ecutsGROUP && ir->coulomb_modifier == eintmodNONE) + { + if (ir->coulombtype == eelPME || ir->coulombtype == eelP3M_AD) + { + sprintf(err_buf, + "With coulombtype = %s (without modifier), rcoulomb must be equal to rlist,\n" + "or rlistlong if nstcalclr=1. For optimal energy conservation,consider using\n" + "a potential modifier.", eel_names[ir->coulombtype]); + if (ir->nstcalclr == 1) + { + CHECK(ir->rcoulomb != ir->rlist && ir->rcoulomb != ir->rlistlong); + } + else + { + CHECK(ir->rcoulomb != ir->rlist); + } + } + } + } + + if (EEL_PME(ir->coulombtype) || EVDW_PME(ir->vdwtype)) + { + if (ir->pme_order < 3) + { + warning_error(wi, "pme-order can not be smaller than 3"); + } + } + + if (ir->nwall == 2 && EEL_FULL(ir->coulombtype)) + { + if (ir->ewald_geometry == eewg3D) + { + sprintf(warn_buf, "With pbc=%s you should use ewald-geometry=%s", + epbc_names[ir->ePBC], eewg_names[eewg3DC]); + warning(wi, warn_buf); + } + /* This check avoids extra pbc coding for exclusion corrections */ + sprintf(err_buf, "wall-ewald-zfac should be >= 2"); + CHECK(ir->wall_ewald_zfac < 2); + } + + if (ir_vdw_switched(ir)) + { + sprintf(err_buf, "With switched vdw forces or potentials, rvdw-switch must be < rvdw"); + CHECK(ir->rvdw_switch >= ir->rvdw); + + if (ir->rvdw_switch < 0.5*ir->rvdw) + { + sprintf(warn_buf, "You are applying a switch function to vdw forces or potentials from %g to %g nm, which is more than half the interaction range, whereas switch functions are intended to act only close to the cut-off.", + ir->rvdw_switch, ir->rvdw); + warning_note(wi, warn_buf); + } + } + else if (ir->vdwtype == evdwCUT || ir->vdwtype == evdwPME) + { + if (ir->cutoff_scheme == ecutsGROUP && ir->vdw_modifier == eintmodNONE) + { + sprintf(err_buf, "With vdwtype = %s, rvdw must be >= rlist unless you use a potential modifier", evdw_names[ir->vdwtype]); + CHECK(ir->rlist > ir->rvdw); + } + } + + if (ir->cutoff_scheme == ecutsGROUP) + { + if (((ir->coulomb_modifier != eintmodNONE && ir->rcoulomb == ir->rlist) || + (ir->vdw_modifier != eintmodNONE && ir->rvdw == ir->rlist)) && + ir->nstlist != 1) + { + warning_note(wi, "With exact cut-offs, rlist should be " + "larger than rcoulomb and rvdw, so that there " + "is a buffer region for particle motion " + "between neighborsearch steps"); + } + + if (ir_coulomb_is_zero_at_cutoff(ir) && ir->rlistlong <= ir->rcoulomb) + { + sprintf(warn_buf, "For energy conservation with switch/shift potentials, %s should be 0.1 to 0.3 nm larger than rcoulomb.", + IR_TWINRANGE(*ir) ? "rlistlong" : "rlist"); + warning_note(wi, warn_buf); + } + if (ir_vdw_switched(ir) && (ir->rlistlong <= ir->rvdw)) + { + sprintf(warn_buf, "For energy conservation with switch/shift potentials, %s should be 0.1 to 0.3 nm larger than rvdw.", + IR_TWINRANGE(*ir) ? "rlistlong" : "rlist"); + warning_note(wi, warn_buf); + } + } + + if (ir->vdwtype == evdwUSER && ir->eDispCorr != edispcNO) + { + warning_note(wi, "You have selected user tables with dispersion correction, the dispersion will be corrected to -C6/r^6 beyond rvdw_switch (the tabulated interaction between rvdw_switch and rvdw will not be double counted). Make sure that you really want dispersion correction to -C6/r^6."); + } + + if (ir->nstlist == -1) + { + sprintf(err_buf, "With nstlist=-1 rvdw and rcoulomb should be smaller than rlist to account for diffusion and possibly charge-group radii"); + CHECK(ir->rvdw >= ir->rlist || ir->rcoulomb >= ir->rlist); + } + sprintf(err_buf, "nstlist can not be smaller than -1"); + CHECK(ir->nstlist < -1); + + if (ir->eI == eiLBFGS && (ir->coulombtype == eelCUT || ir->vdwtype == evdwCUT) + && ir->rvdw != 0) + { + warning(wi, "For efficient BFGS minimization, use switch/shift/pme instead of cut-off."); + } + + if (ir->eI == eiLBFGS && ir->nbfgscorr <= 0) + { + warning(wi, "Using L-BFGS with nbfgscorr<=0 just gets you steepest descent."); + } + + /* ENERGY CONSERVATION */ + if (ir_NVE(ir) && ir->cutoff_scheme == ecutsGROUP) + { + if (!ir_vdw_might_be_zero_at_cutoff(ir) && ir->rvdw > 0 && ir->vdw_modifier == eintmodNONE) + { + sprintf(warn_buf, "You are using a cut-off for VdW interactions with NVE, for good energy conservation use vdwtype = %s (possibly with DispCorr)", + evdw_names[evdwSHIFT]); + warning_note(wi, warn_buf); + } + if (!ir_coulomb_might_be_zero_at_cutoff(ir) && ir->rcoulomb > 0) + { + sprintf(warn_buf, "You are using a cut-off for electrostatics with NVE, for good energy conservation use coulombtype = %s or %s", + eel_names[eelPMESWITCH], eel_names[eelRF_ZERO]); + warning_note(wi, warn_buf); + } + } + + /* IMPLICIT SOLVENT */ + if (ir->coulombtype == eelGB_NOTUSED) + { + ir->coulombtype = eelCUT; + ir->implicit_solvent = eisGBSA; + fprintf(stderr, "Note: Old option for generalized born electrostatics given:\n" + "Changing coulombtype from \"generalized-born\" to \"cut-off\" and instead\n" + "setting implicit-solvent value to \"GBSA\" in input section.\n"); + } + + if (ir->sa_algorithm == esaSTILL) + { + sprintf(err_buf, "Still SA algorithm not available yet, use %s or %s instead\n", esa_names[esaAPPROX], esa_names[esaNO]); + CHECK(ir->sa_algorithm == esaSTILL); + } + + if (ir->implicit_solvent == eisGBSA) + { + sprintf(err_buf, "With GBSA implicit solvent, rgbradii must be equal to rlist."); + CHECK(ir->rgbradii != ir->rlist); + + if (ir->coulombtype != eelCUT) + { + sprintf(err_buf, "With GBSA, coulombtype must be equal to %s\n", eel_names[eelCUT]); + CHECK(ir->coulombtype != eelCUT); + } + if (ir->vdwtype != evdwCUT) + { + sprintf(err_buf, "With GBSA, vdw-type must be equal to %s\n", evdw_names[evdwCUT]); + CHECK(ir->vdwtype != evdwCUT); + } + if (ir->nstgbradii < 1) + { + sprintf(warn_buf, "Using GBSA with nstgbradii<1, setting nstgbradii=1"); + warning_note(wi, warn_buf); + ir->nstgbradii = 1; + } + if (ir->sa_algorithm == esaNO) + { + sprintf(warn_buf, "No SA (non-polar) calculation requested together with GB. Are you sure this is what you want?\n"); + warning_note(wi, warn_buf); + } + if (ir->sa_surface_tension < 0 && ir->sa_algorithm != esaNO) + { + sprintf(warn_buf, "Value of sa_surface_tension is < 0. Changing it to 2.05016 or 2.25936 kJ/nm^2/mol for Still and HCT/OBC respectively\n"); + warning_note(wi, warn_buf); + + if (ir->gb_algorithm == egbSTILL) + { + ir->sa_surface_tension = 0.0049 * CAL2JOULE * 100; + } + else + { + ir->sa_surface_tension = 0.0054 * CAL2JOULE * 100; + } + } + if (ir->sa_surface_tension == 0 && ir->sa_algorithm != esaNO) + { + sprintf(err_buf, "Surface tension set to 0 while SA-calculation requested\n"); + CHECK(ir->sa_surface_tension == 0 && ir->sa_algorithm != esaNO); + } + + } + + if (ir->bAdress) + { + if (ir->cutoff_scheme != ecutsGROUP) + { + warning_error(wi, "AdresS simulation supports only cutoff-scheme=group"); + } + if (!EI_SD(ir->eI)) + { + warning_error(wi, "AdresS simulation supports only stochastic dynamics"); + } + if (ir->epc != epcNO) + { + warning_error(wi, "AdresS simulation does not support pressure coupling"); + } + if (EEL_FULL(ir->coulombtype)) + { + warning_error(wi, "AdresS simulation does not support long-range electrostatics"); + } + } +} + +/* count the number of text elemets separated by whitespace in a string. + str = the input string + maxptr = the maximum number of allowed elements + ptr = the output array of pointers to the first character of each element + returns: the number of elements. */ +int str_nelem(const char *str, int maxptr, char *ptr[]) +{ + int np = 0; + char *copy0, *copy; + + copy0 = strdup(str); + copy = copy0; + ltrim(copy); + while (*copy != '\0') + { + if (np >= maxptr) + { + gmx_fatal(FARGS, "Too many groups on line: '%s' (max is %d)", + str, maxptr); + } + if (ptr) + { + ptr[np] = copy; + } + np++; + while ((*copy != '\0') && !isspace(*copy)) + { + copy++; + } + if (*copy != '\0') + { + *copy = '\0'; + copy++; + } + ltrim(copy); + } + if (ptr == NULL) + { + sfree(copy0); + } + + return np; +} + +/* interpret a number of doubles from a string and put them in an array, + after allocating space for them. + str = the input string + n = the (pre-allocated) number of doubles read + r = the output array of doubles. */ +static void parse_n_real(char *str, int *n, real **r) +{ + char *ptr[MAXPTR]; + int i; + + *n = str_nelem(str, MAXPTR, ptr); + + snew(*r, *n); + for (i = 0; i < *n; i++) + { + (*r)[i] = strtod(ptr[i], NULL); + } +} + +static void do_fep_params(t_inputrec *ir, char fep_lambda[][STRLEN], char weights[STRLEN]) +{ + + int i, j, max_n_lambda, nweights, nfep[efptNR]; + t_lambda *fep = ir->fepvals; + t_expanded *expand = ir->expandedvals; + real **count_fep_lambdas; + gmx_bool bOneLambda = TRUE; + + snew(count_fep_lambdas, efptNR); + + /* FEP input processing */ + /* first, identify the number of lambda values for each type. + All that are nonzero must have the same number */ + + for (i = 0; i < efptNR; i++) + { + parse_n_real(fep_lambda[i], &(nfep[i]), &(count_fep_lambdas[i])); + } + + /* now, determine the number of components. All must be either zero, or equal. */ + + max_n_lambda = 0; + for (i = 0; i < efptNR; i++) + { + if (nfep[i] > max_n_lambda) + { + max_n_lambda = nfep[i]; /* here's a nonzero one. All of them + must have the same number if its not zero.*/ + break; + } + } + + for (i = 0; i < efptNR; i++) + { + if (nfep[i] == 0) + { + ir->fepvals->separate_dvdl[i] = FALSE; + } + else if (nfep[i] == max_n_lambda) + { + if (i != efptTEMPERATURE) /* we treat this differently -- not really a reason to compute the derivative with + respect to the temperature currently */ + { + ir->fepvals->separate_dvdl[i] = TRUE; + } + } + else + { + gmx_fatal(FARGS, "Number of lambdas (%d) for FEP type %s not equal to number of other types (%d)", + nfep[i], efpt_names[i], max_n_lambda); + } + } + /* we don't print out dhdl if the temperature is changing, since we can't correctly define dhdl in this case */ + ir->fepvals->separate_dvdl[efptTEMPERATURE] = FALSE; + + /* the number of lambdas is the number we've read in, which is either zero + or the same for all */ + fep->n_lambda = max_n_lambda; + + /* allocate space for the array of lambda values */ + snew(fep->all_lambda, efptNR); + /* if init_lambda is defined, we need to set lambda */ + if ((fep->init_lambda > 0) && (fep->n_lambda == 0)) + { + ir->fepvals->separate_dvdl[efptFEP] = TRUE; + } + /* otherwise allocate the space for all of the lambdas, and transfer the data */ + for (i = 0; i < efptNR; i++) + { + snew(fep->all_lambda[i], fep->n_lambda); + if (nfep[i] > 0) /* if it's zero, then the count_fep_lambda arrays + are zero */ + { + for (j = 0; j < fep->n_lambda; j++) + { + fep->all_lambda[i][j] = (double)count_fep_lambdas[i][j]; + } + sfree(count_fep_lambdas[i]); + } + } + sfree(count_fep_lambdas); + + /* "fep-vals" is either zero or the full number. If zero, we'll need to define fep-lambdas for internal + bookkeeping -- for now, init_lambda */ + + if ((nfep[efptFEP] == 0) && (fep->init_lambda >= 0)) + { + for (i = 0; i < fep->n_lambda; i++) + { + fep->all_lambda[efptFEP][i] = fep->init_lambda; + } + } + + /* check to see if only a single component lambda is defined, and soft core is defined. + In this case, turn on coulomb soft core */ + + if (max_n_lambda == 0) + { + bOneLambda = TRUE; + } + else + { + for (i = 0; i < efptNR; i++) + { + if ((nfep[i] != 0) && (i != efptFEP)) + { + bOneLambda = FALSE; + } + } + } + if ((bOneLambda) && (fep->sc_alpha > 0)) + { + fep->bScCoul = TRUE; + } + + /* Fill in the others with the efptFEP if they are not explicitly + specified (i.e. nfep[i] == 0). This means if fep is not defined, + they are all zero. */ + + for (i = 0; i < efptNR; i++) + { + if ((nfep[i] == 0) && (i != efptFEP)) + { + for (j = 0; j < fep->n_lambda; j++) + { + fep->all_lambda[i][j] = fep->all_lambda[efptFEP][j]; + } + } + } + + + /* make it easier if sc_r_power = 48 by increasing it to the 4th power, to be in the right scale. */ + if (fep->sc_r_power == 48) + { + if (fep->sc_alpha > 0.1) + { + gmx_fatal(FARGS, "sc_alpha (%f) for sc_r_power = 48 should usually be between 0.001 and 0.004", fep->sc_alpha); + } + } + + expand = ir->expandedvals; + /* now read in the weights */ + parse_n_real(weights, &nweights, &(expand->init_lambda_weights)); + if (nweights == 0) + { + snew(expand->init_lambda_weights, fep->n_lambda); /* initialize to zero */ + } + else if (nweights != fep->n_lambda) + { + gmx_fatal(FARGS, "Number of weights (%d) is not equal to number of lambda values (%d)", + nweights, fep->n_lambda); + } + if ((expand->nstexpanded < 0) && (ir->efep != efepNO)) + { + expand->nstexpanded = fep->nstdhdl; + /* if you don't specify nstexpanded when doing expanded ensemble free energy calcs, it is set to nstdhdl */ + } + if ((expand->nstexpanded < 0) && ir->bSimTemp) + { + expand->nstexpanded = 2*(int)(ir->opts.tau_t[0]/ir->delta_t); + /* if you don't specify nstexpanded when doing expanded ensemble simulated tempering, it is set to + 2*tau_t just to be careful so it's not to frequent */ + } +} + + +static void do_simtemp_params(t_inputrec *ir) +{ + + snew(ir->simtempvals->temperatures, ir->fepvals->n_lambda); + GetSimTemps(ir->fepvals->n_lambda, ir->simtempvals, ir->fepvals->all_lambda[efptTEMPERATURE]); + + return; +} + +static void do_wall_params(t_inputrec *ir, + char *wall_atomtype, char *wall_density, + t_gromppopts *opts) +{ + int nstr, i; + char *names[MAXPTR]; + double dbl; + + opts->wall_atomtype[0] = NULL; + opts->wall_atomtype[1] = NULL; + + ir->wall_atomtype[0] = -1; + ir->wall_atomtype[1] = -1; + ir->wall_density[0] = 0; + ir->wall_density[1] = 0; + + if (ir->nwall > 0) + { + nstr = str_nelem(wall_atomtype, MAXPTR, names); + if (nstr != ir->nwall) + { + gmx_fatal(FARGS, "Expected %d elements for wall_atomtype, found %d", + ir->nwall, nstr); + } + for (i = 0; i < ir->nwall; i++) + { + opts->wall_atomtype[i] = strdup(names[i]); + } + + if (ir->wall_type == ewt93 || ir->wall_type == ewt104) + { + nstr = str_nelem(wall_density, MAXPTR, names); + if (nstr != ir->nwall) + { + gmx_fatal(FARGS, "Expected %d elements for wall-density, found %d", ir->nwall, nstr); + } + for (i = 0; i < ir->nwall; i++) + { + sscanf(names[i], "%lf", &dbl); + if (dbl <= 0) + { + gmx_fatal(FARGS, "wall-density[%d] = %f\n", i, dbl); + } + ir->wall_density[i] = dbl; + } + } + } +} + +static void add_wall_energrps(gmx_groups_t *groups, int nwall, t_symtab *symtab) +{ + int i; + t_grps *grps; + char str[STRLEN]; + + if (nwall > 0) + { + srenew(groups->grpname, groups->ngrpname+nwall); + grps = &(groups->grps[egcENER]); + srenew(grps->nm_ind, grps->nr+nwall); + for (i = 0; i < nwall; i++) + { + sprintf(str, "wall%d", i); + groups->grpname[groups->ngrpname] = put_symtab(symtab, str); + grps->nm_ind[grps->nr++] = groups->ngrpname++; + } + } +} + +void read_expandedparams(int *ninp_p, t_inpfile **inp_p, + t_expanded *expand, warninp_t wi) +{ + int ninp, nerror = 0; + t_inpfile *inp; + + ninp = *ninp_p; + inp = *inp_p; + + /* read expanded ensemble parameters */ + CCTYPE ("expanded ensemble variables"); + ITYPE ("nstexpanded", expand->nstexpanded, -1); + EETYPE("lmc-stats", expand->elamstats, elamstats_names); + EETYPE("lmc-move", expand->elmcmove, elmcmove_names); + EETYPE("lmc-weights-equil", expand->elmceq, elmceq_names); + ITYPE ("weight-equil-number-all-lambda", expand->equil_n_at_lam, -1); + ITYPE ("weight-equil-number-samples", expand->equil_samples, -1); + ITYPE ("weight-equil-number-steps", expand->equil_steps, -1); + RTYPE ("weight-equil-wl-delta", expand->equil_wl_delta, -1); + RTYPE ("weight-equil-count-ratio", expand->equil_ratio, -1); + CCTYPE("Seed for Monte Carlo in lambda space"); + ITYPE ("lmc-seed", expand->lmc_seed, -1); + RTYPE ("mc-temperature", expand->mc_temp, -1); + ITYPE ("lmc-repeats", expand->lmc_repeats, 1); + ITYPE ("lmc-gibbsdelta", expand->gibbsdeltalam, -1); + ITYPE ("lmc-forced-nstart", expand->lmc_forced_nstart, 0); + EETYPE("symmetrized-transition-matrix", expand->bSymmetrizedTMatrix, yesno_names); + ITYPE("nst-transition-matrix", expand->nstTij, -1); + ITYPE ("mininum-var-min", expand->minvarmin, 100); /*default is reasonable */ + ITYPE ("weight-c-range", expand->c_range, 0); /* default is just C=0 */ + RTYPE ("wl-scale", expand->wl_scale, 0.8); + RTYPE ("wl-ratio", expand->wl_ratio, 0.8); + RTYPE ("init-wl-delta", expand->init_wl_delta, 1.0); + EETYPE("wl-oneovert", expand->bWLoneovert, yesno_names); + + *ninp_p = ninp; + *inp_p = inp; + + return; +} + +void get_ir(const char *mdparin, const char *mdparout, + t_inputrec *ir, t_gromppopts *opts, + warninp_t wi) +{ + char *dumstr[2]; + double dumdub[2][6]; + t_inpfile *inp; + const char *tmp; + int i, j, m, ninp; + char warn_buf[STRLEN]; + t_lambda *fep = ir->fepvals; + t_expanded *expand = ir->expandedvals; + + init_inputrec_strings(); + inp = read_inpfile(mdparin, &ninp, wi); + + snew(dumstr[0], STRLEN); + snew(dumstr[1], STRLEN); + + if (-1 == search_einp(ninp, inp, "cutoff-scheme")) + { + sprintf(warn_buf, + "%s did not specify a value for the .mdp option " + "\"cutoff-scheme\". Probably it was first intended for use " + "with GROMACS before 4.6. In 4.6, the Verlet scheme was " + "introduced, but the group scheme was still the default. " + "The default is now the Verlet scheme, so you will observe " + "different behaviour.", mdparin); + warning_note(wi, warn_buf); + } + + /* remove the following deprecated commands */ + REM_TYPE("title"); + REM_TYPE("cpp"); + REM_TYPE("domain-decomposition"); + REM_TYPE("andersen-seed"); + REM_TYPE("dihre"); + REM_TYPE("dihre-fc"); + REM_TYPE("dihre-tau"); + REM_TYPE("nstdihreout"); + REM_TYPE("nstcheckpoint"); + + /* replace the following commands with the clearer new versions*/ + REPL_TYPE("unconstrained-start", "continuation"); + REPL_TYPE("foreign-lambda", "fep-lambdas"); + REPL_TYPE("verlet-buffer-drift", "verlet-buffer-tolerance"); + REPL_TYPE("nstxtcout", "nstxout-compressed"); + REPL_TYPE("xtc-grps", "compressed-x-grps"); + REPL_TYPE("xtc-precision", "compressed-x-precision"); + + CCTYPE ("VARIOUS PREPROCESSING OPTIONS"); + CTYPE ("Preprocessor information: use cpp syntax."); + CTYPE ("e.g.: -I/home/joe/doe -I/home/mary/roe"); + STYPE ("include", opts->include, NULL); + CTYPE ("e.g.: -DPOSRES -DFLEXIBLE (note these variable names are case sensitive)"); + STYPE ("define", opts->define, NULL); + + CCTYPE ("RUN CONTROL PARAMETERS"); + EETYPE("integrator", ir->eI, ei_names); + CTYPE ("Start time and timestep in ps"); + RTYPE ("tinit", ir->init_t, 0.0); + RTYPE ("dt", ir->delta_t, 0.001); + STEPTYPE ("nsteps", ir->nsteps, 0); + CTYPE ("For exact run continuation or redoing part of a run"); + STEPTYPE ("init-step", ir->init_step, 0); + CTYPE ("Part index is updated automatically on checkpointing (keeps files separate)"); + ITYPE ("simulation-part", ir->simulation_part, 1); + CTYPE ("mode for center of mass motion removal"); + EETYPE("comm-mode", ir->comm_mode, ecm_names); + CTYPE ("number of steps for center of mass motion removal"); + ITYPE ("nstcomm", ir->nstcomm, 100); + CTYPE ("group(s) for center of mass motion removal"); + STYPE ("comm-grps", is->vcm, NULL); + + CCTYPE ("LANGEVIN DYNAMICS OPTIONS"); + CTYPE ("Friction coefficient (amu/ps) and random seed"); + RTYPE ("bd-fric", ir->bd_fric, 0.0); + STEPTYPE ("ld-seed", ir->ld_seed, -1); + + /* Em stuff */ + CCTYPE ("ENERGY MINIMIZATION OPTIONS"); + CTYPE ("Force tolerance and initial step-size"); + RTYPE ("emtol", ir->em_tol, 10.0); + RTYPE ("emstep", ir->em_stepsize, 0.01); + CTYPE ("Max number of iterations in relax-shells"); + ITYPE ("niter", ir->niter, 20); + CTYPE ("Step size (ps^2) for minimization of flexible constraints"); + RTYPE ("fcstep", ir->fc_stepsize, 0); + CTYPE ("Frequency of steepest descents steps when doing CG"); + ITYPE ("nstcgsteep", ir->nstcgsteep, 1000); + ITYPE ("nbfgscorr", ir->nbfgscorr, 10); + + CCTYPE ("TEST PARTICLE INSERTION OPTIONS"); + RTYPE ("rtpi", ir->rtpi, 0.05); + + /* Output options */ + CCTYPE ("OUTPUT CONTROL OPTIONS"); + CTYPE ("Output frequency for coords (x), velocities (v) and forces (f)"); + ITYPE ("nstxout", ir->nstxout, 0); + ITYPE ("nstvout", ir->nstvout, 0); + ITYPE ("nstfout", ir->nstfout, 0); + ir->nstcheckpoint = 1000; + CTYPE ("Output frequency for energies to log file and energy file"); + ITYPE ("nstlog", ir->nstlog, 1000); + ITYPE ("nstcalcenergy", ir->nstcalcenergy, 100); + ITYPE ("nstenergy", ir->nstenergy, 1000); + CTYPE ("Output frequency and precision for .xtc file"); + ITYPE ("nstxout-compressed", ir->nstxout_compressed, 0); + RTYPE ("compressed-x-precision", ir->x_compression_precision, 1000.0); + CTYPE ("This selects the subset of atoms for the compressed"); + CTYPE ("trajectory file. You can select multiple groups. By"); + CTYPE ("default, all atoms will be written."); + STYPE ("compressed-x-grps", is->x_compressed_groups, NULL); + CTYPE ("Selection of energy groups"); + STYPE ("energygrps", is->energy, NULL); + + /* Neighbor searching */ + CCTYPE ("NEIGHBORSEARCHING PARAMETERS"); + CTYPE ("cut-off scheme (Verlet: particle based cut-offs, group: using charge groups)"); + EETYPE("cutoff-scheme", ir->cutoff_scheme, ecutscheme_names); + CTYPE ("nblist update frequency"); + ITYPE ("nstlist", ir->nstlist, 10); + CTYPE ("ns algorithm (simple or grid)"); + EETYPE("ns-type", ir->ns_type, ens_names); + /* set ndelta to the optimal value of 2 */ + ir->ndelta = 2; + CTYPE ("Periodic boundary conditions: xyz, no, xy"); + EETYPE("pbc", ir->ePBC, epbc_names); + EETYPE("periodic-molecules", ir->bPeriodicMols, yesno_names); + CTYPE ("Allowed energy error due to the Verlet buffer in kJ/mol/ps per atom,"); + CTYPE ("a value of -1 means: use rlist"); + RTYPE("verlet-buffer-tolerance", ir->verletbuf_tol, 0.005); + CTYPE ("nblist cut-off"); + RTYPE ("rlist", ir->rlist, 1.0); + CTYPE ("long-range cut-off for switched potentials"); + RTYPE ("rlistlong", ir->rlistlong, -1); + ITYPE ("nstcalclr", ir->nstcalclr, -1); + + /* Electrostatics */ + CCTYPE ("OPTIONS FOR ELECTROSTATICS AND VDW"); + CTYPE ("Method for doing electrostatics"); + EETYPE("coulombtype", ir->coulombtype, eel_names); + EETYPE("coulomb-modifier", ir->coulomb_modifier, eintmod_names); + CTYPE ("cut-off lengths"); + RTYPE ("rcoulomb-switch", ir->rcoulomb_switch, 0.0); + RTYPE ("rcoulomb", ir->rcoulomb, 1.0); + CTYPE ("Relative dielectric constant for the medium and the reaction field"); + RTYPE ("epsilon-r", ir->epsilon_r, 1.0); + RTYPE ("epsilon-rf", ir->epsilon_rf, 0.0); + CTYPE ("Method for doing Van der Waals"); + EETYPE("vdw-type", ir->vdwtype, evdw_names); + EETYPE("vdw-modifier", ir->vdw_modifier, eintmod_names); + CTYPE ("cut-off lengths"); + RTYPE ("rvdw-switch", ir->rvdw_switch, 0.0); + RTYPE ("rvdw", ir->rvdw, 1.0); + CTYPE ("Apply long range dispersion corrections for Energy and Pressure"); + EETYPE("DispCorr", ir->eDispCorr, edispc_names); + CTYPE ("Extension of the potential lookup tables beyond the cut-off"); + RTYPE ("table-extension", ir->tabext, 1.0); + CTYPE ("Separate tables between energy group pairs"); + STYPE ("energygrp-table", is->egptable, NULL); + CTYPE ("Spacing for the PME/PPPM FFT grid"); + RTYPE ("fourierspacing", ir->fourier_spacing, 0.12); + CTYPE ("FFT grid size, when a value is 0 fourierspacing will be used"); + ITYPE ("fourier-nx", ir->nkx, 0); + ITYPE ("fourier-ny", ir->nky, 0); + ITYPE ("fourier-nz", ir->nkz, 0); + CTYPE ("EWALD/PME/PPPM parameters"); + ITYPE ("pme-order", ir->pme_order, 4); + RTYPE ("ewald-rtol", ir->ewald_rtol, 0.00001); + RTYPE ("ewald-rtol-lj", ir->ewald_rtol_lj, 0.001); + EETYPE("lj-pme-comb-rule", ir->ljpme_combination_rule, eljpme_names); + EETYPE("ewald-geometry", ir->ewald_geometry, eewg_names); + RTYPE ("epsilon-surface", ir->epsilon_surface, 0.0); + EETYPE("optimize-fft", ir->bOptFFT, yesno_names); + + CCTYPE("IMPLICIT SOLVENT ALGORITHM"); + EETYPE("implicit-solvent", ir->implicit_solvent, eis_names); + + CCTYPE ("GENERALIZED BORN ELECTROSTATICS"); + CTYPE ("Algorithm for calculating Born radii"); + EETYPE("gb-algorithm", ir->gb_algorithm, egb_names); + CTYPE ("Frequency of calculating the Born radii inside rlist"); + ITYPE ("nstgbradii", ir->nstgbradii, 1); + CTYPE ("Cutoff for Born radii calculation; the contribution from atoms"); + CTYPE ("between rlist and rgbradii is updated every nstlist steps"); + RTYPE ("rgbradii", ir->rgbradii, 1.0); + CTYPE ("Dielectric coefficient of the implicit solvent"); + RTYPE ("gb-epsilon-solvent", ir->gb_epsilon_solvent, 80.0); + CTYPE ("Salt concentration in M for Generalized Born models"); + RTYPE ("gb-saltconc", ir->gb_saltconc, 0.0); + CTYPE ("Scaling factors used in the OBC GB model. Default values are OBC(II)"); + RTYPE ("gb-obc-alpha", ir->gb_obc_alpha, 1.0); + RTYPE ("gb-obc-beta", ir->gb_obc_beta, 0.8); + RTYPE ("gb-obc-gamma", ir->gb_obc_gamma, 4.85); + RTYPE ("gb-dielectric-offset", ir->gb_dielectric_offset, 0.009); + EETYPE("sa-algorithm", ir->sa_algorithm, esa_names); + CTYPE ("Surface tension (kJ/mol/nm^2) for the SA (nonpolar surface) part of GBSA"); + CTYPE ("The value -1 will set default value for Still/HCT/OBC GB-models."); + RTYPE ("sa-surface-tension", ir->sa_surface_tension, -1); + + /* Coupling stuff */ + CCTYPE ("OPTIONS FOR WEAK COUPLING ALGORITHMS"); + CTYPE ("Temperature coupling"); + EETYPE("tcoupl", ir->etc, etcoupl_names); + ITYPE ("nsttcouple", ir->nsttcouple, -1); + ITYPE("nh-chain-length", ir->opts.nhchainlength, 10); + EETYPE("print-nose-hoover-chain-variables", ir->bPrintNHChains, yesno_names); + CTYPE ("Groups to couple separately"); + STYPE ("tc-grps", is->tcgrps, NULL); + CTYPE ("Time constant (ps) and reference temperature (K)"); + STYPE ("tau-t", is->tau_t, NULL); + STYPE ("ref-t", is->ref_t, NULL); + CTYPE ("pressure coupling"); + EETYPE("pcoupl", ir->epc, epcoupl_names); + EETYPE("pcoupltype", ir->epct, epcoupltype_names); + ITYPE ("nstpcouple", ir->nstpcouple, -1); + CTYPE ("Time constant (ps), compressibility (1/bar) and reference P (bar)"); + RTYPE ("tau-p", ir->tau_p, 1.0); + STYPE ("compressibility", dumstr[0], NULL); + STYPE ("ref-p", dumstr[1], NULL); + CTYPE ("Scaling of reference coordinates, No, All or COM"); + EETYPE ("refcoord-scaling", ir->refcoord_scaling, erefscaling_names); + + /* QMMM */ + CCTYPE ("OPTIONS FOR QMMM calculations"); + EETYPE("QMMM", ir->bQMMM, yesno_names); + CTYPE ("Groups treated Quantum Mechanically"); + STYPE ("QMMM-grps", is->QMMM, NULL); + CTYPE ("QM method"); + STYPE("QMmethod", is->QMmethod, NULL); + CTYPE ("QMMM scheme"); + EETYPE("QMMMscheme", ir->QMMMscheme, eQMMMscheme_names); + CTYPE ("QM basisset"); + STYPE("QMbasis", is->QMbasis, NULL); + CTYPE ("QM charge"); + STYPE ("QMcharge", is->QMcharge, NULL); + CTYPE ("QM multiplicity"); + STYPE ("QMmult", is->QMmult, NULL); + CTYPE ("Surface Hopping"); + STYPE ("SH", is->bSH, NULL); + CTYPE ("CAS space options"); + STYPE ("CASorbitals", is->CASorbitals, NULL); + STYPE ("CASelectrons", is->CASelectrons, NULL); + STYPE ("SAon", is->SAon, NULL); + STYPE ("SAoff", is->SAoff, NULL); + STYPE ("SAsteps", is->SAsteps, NULL); + CTYPE ("Scale factor for MM charges"); + RTYPE ("MMChargeScaleFactor", ir->scalefactor, 1.0); + CTYPE ("Optimization of QM subsystem"); + STYPE ("bOPT", is->bOPT, NULL); + STYPE ("bTS", is->bTS, NULL); + + /* Simulated annealing */ + CCTYPE("SIMULATED ANNEALING"); + CTYPE ("Type of annealing for each temperature group (no/single/periodic)"); + STYPE ("annealing", is->anneal, NULL); + CTYPE ("Number of time points to use for specifying annealing in each group"); + STYPE ("annealing-npoints", is->anneal_npoints, NULL); + CTYPE ("List of times at the annealing points for each group"); + STYPE ("annealing-time", is->anneal_time, NULL); + CTYPE ("Temp. at each annealing point, for each group."); + STYPE ("annealing-temp", is->anneal_temp, NULL); + + /* Startup run */ + CCTYPE ("GENERATE VELOCITIES FOR STARTUP RUN"); + EETYPE("gen-vel", opts->bGenVel, yesno_names); + RTYPE ("gen-temp", opts->tempi, 300.0); + ITYPE ("gen-seed", opts->seed, -1); + + /* Shake stuff */ + CCTYPE ("OPTIONS FOR BONDS"); + EETYPE("constraints", opts->nshake, constraints); + CTYPE ("Type of constraint algorithm"); + EETYPE("constraint-algorithm", ir->eConstrAlg, econstr_names); + CTYPE ("Do not constrain the start configuration"); + EETYPE("continuation", ir->bContinuation, yesno_names); + CTYPE ("Use successive overrelaxation to reduce the number of shake iterations"); + EETYPE("Shake-SOR", ir->bShakeSOR, yesno_names); + CTYPE ("Relative tolerance of shake"); + RTYPE ("shake-tol", ir->shake_tol, 0.0001); + CTYPE ("Highest order in the expansion of the constraint coupling matrix"); + ITYPE ("lincs-order", ir->nProjOrder, 4); + CTYPE ("Number of iterations in the final step of LINCS. 1 is fine for"); + CTYPE ("normal simulations, but use 2 to conserve energy in NVE runs."); + CTYPE ("For energy minimization with constraints it should be 4 to 8."); + ITYPE ("lincs-iter", ir->nLincsIter, 1); + CTYPE ("Lincs will write a warning to the stderr if in one step a bond"); + CTYPE ("rotates over more degrees than"); + RTYPE ("lincs-warnangle", ir->LincsWarnAngle, 30.0); + CTYPE ("Convert harmonic bonds to morse potentials"); + EETYPE("morse", opts->bMorse, yesno_names); + + /* Energy group exclusions */ + CCTYPE ("ENERGY GROUP EXCLUSIONS"); + CTYPE ("Pairs of energy groups for which all non-bonded interactions are excluded"); + STYPE ("energygrp-excl", is->egpexcl, NULL); + + /* Walls */ + CCTYPE ("WALLS"); + CTYPE ("Number of walls, type, atom types, densities and box-z scale factor for Ewald"); + ITYPE ("nwall", ir->nwall, 0); + EETYPE("wall-type", ir->wall_type, ewt_names); + RTYPE ("wall-r-linpot", ir->wall_r_linpot, -1); + STYPE ("wall-atomtype", is->wall_atomtype, NULL); + STYPE ("wall-density", is->wall_density, NULL); + RTYPE ("wall-ewald-zfac", ir->wall_ewald_zfac, 3); + + /* COM pulling */ + CCTYPE("COM PULLING"); + CTYPE("Pull type: no, umbrella, constraint or constant-force"); + EETYPE("pull", ir->ePull, epull_names); + if (ir->ePull != epullNO) + { + snew(ir->pull, 1); + is->pull_grp = read_pullparams(&ninp, &inp, ir->pull, &opts->pull_start, wi); + } + + /* Enforced rotation */ + CCTYPE("ENFORCED ROTATION"); + CTYPE("Enforced rotation: No or Yes"); + EETYPE("rotation", ir->bRot, yesno_names); + if (ir->bRot) + { + snew(ir->rot, 1); + is->rot_grp = read_rotparams(&ninp, &inp, ir->rot, wi); + } + + /* Refinement */ + CCTYPE("NMR refinement stuff"); + CTYPE ("Distance restraints type: No, Simple or Ensemble"); + EETYPE("disre", ir->eDisre, edisre_names); + CTYPE ("Force weighting of pairs in one distance restraint: Conservative or Equal"); + EETYPE("disre-weighting", ir->eDisreWeighting, edisreweighting_names); + CTYPE ("Use sqrt of the time averaged times the instantaneous violation"); + EETYPE("disre-mixed", ir->bDisreMixed, yesno_names); + RTYPE ("disre-fc", ir->dr_fc, 1000.0); + RTYPE ("disre-tau", ir->dr_tau, 0.0); + CTYPE ("Output frequency for pair distances to energy file"); + ITYPE ("nstdisreout", ir->nstdisreout, 100); + CTYPE ("Orientation restraints: No or Yes"); + EETYPE("orire", opts->bOrire, yesno_names); + CTYPE ("Orientation restraints force constant and tau for time averaging"); + RTYPE ("orire-fc", ir->orires_fc, 0.0); + RTYPE ("orire-tau", ir->orires_tau, 0.0); + STYPE ("orire-fitgrp", is->orirefitgrp, NULL); + CTYPE ("Output frequency for trace(SD) and S to energy file"); + ITYPE ("nstorireout", ir->nstorireout, 100); + + /* free energy variables */ + CCTYPE ("Free energy variables"); + EETYPE("free-energy", ir->efep, efep_names); + STYPE ("couple-moltype", is->couple_moltype, NULL); + EETYPE("couple-lambda0", opts->couple_lam0, couple_lam); + EETYPE("couple-lambda1", opts->couple_lam1, couple_lam); + EETYPE("couple-intramol", opts->bCoupleIntra, yesno_names); + + RTYPE ("init-lambda", fep->init_lambda, -1); /* start with -1 so + we can recognize if + it was not entered */ + ITYPE ("init-lambda-state", fep->init_fep_state, -1); + RTYPE ("delta-lambda", fep->delta_lambda, 0.0); + ITYPE ("nstdhdl", fep->nstdhdl, 50); + STYPE ("fep-lambdas", is->fep_lambda[efptFEP], NULL); + STYPE ("mass-lambdas", is->fep_lambda[efptMASS], NULL); + STYPE ("coul-lambdas", is->fep_lambda[efptCOUL], NULL); + STYPE ("vdw-lambdas", is->fep_lambda[efptVDW], NULL); + STYPE ("bonded-lambdas", is->fep_lambda[efptBONDED], NULL); + STYPE ("restraint-lambdas", is->fep_lambda[efptRESTRAINT], NULL); + STYPE ("temperature-lambdas", is->fep_lambda[efptTEMPERATURE], NULL); + ITYPE ("calc-lambda-neighbors", fep->lambda_neighbors, 1); + STYPE ("init-lambda-weights", is->lambda_weights, NULL); + EETYPE("dhdl-print-energy", fep->bPrintEnergy, yesno_names); + RTYPE ("sc-alpha", fep->sc_alpha, 0.0); + ITYPE ("sc-power", fep->sc_power, 1); + RTYPE ("sc-r-power", fep->sc_r_power, 6.0); + RTYPE ("sc-sigma", fep->sc_sigma, 0.3); + EETYPE("sc-coul", fep->bScCoul, yesno_names); + ITYPE ("dh_hist_size", fep->dh_hist_size, 0); + RTYPE ("dh_hist_spacing", fep->dh_hist_spacing, 0.1); + EETYPE("separate-dhdl-file", fep->separate_dhdl_file, + separate_dhdl_file_names); + EETYPE("dhdl-derivatives", fep->dhdl_derivatives, dhdl_derivatives_names); + ITYPE ("dh_hist_size", fep->dh_hist_size, 0); + RTYPE ("dh_hist_spacing", fep->dh_hist_spacing, 0.1); + + /* Non-equilibrium MD stuff */ + CCTYPE("Non-equilibrium MD stuff"); + STYPE ("acc-grps", is->accgrps, NULL); + STYPE ("accelerate", is->acc, NULL); + STYPE ("freezegrps", is->freeze, NULL); + STYPE ("freezedim", is->frdim, NULL); + RTYPE ("cos-acceleration", ir->cos_accel, 0); + STYPE ("deform", is->deform, NULL); + + /* simulated tempering variables */ + CCTYPE("simulated tempering variables"); + EETYPE("simulated-tempering", ir->bSimTemp, yesno_names); + EETYPE("simulated-tempering-scaling", ir->simtempvals->eSimTempScale, esimtemp_names); + RTYPE("sim-temp-low", ir->simtempvals->simtemp_low, 300.0); + RTYPE("sim-temp-high", ir->simtempvals->simtemp_high, 300.0); + + /* expanded ensemble variables */ + if (ir->efep == efepEXPANDED || ir->bSimTemp) + { + read_expandedparams(&ninp, &inp, expand, wi); + } + + /* Electric fields */ + CCTYPE("Electric fields"); + CTYPE ("Format is number of terms (int) and for all terms an amplitude (real)"); + CTYPE ("and a phase angle (real)"); + STYPE ("E-x", is->efield_x, NULL); + STYPE ("E-xt", is->efield_xt, NULL); + STYPE ("E-y", is->efield_y, NULL); + STYPE ("E-yt", is->efield_yt, NULL); + STYPE ("E-z", is->efield_z, NULL); + STYPE ("E-zt", is->efield_zt, NULL); + + CCTYPE("Ion/water position swapping for computational electrophysiology setups"); + CTYPE("Swap positions along direction: no, X, Y, Z"); + EETYPE("swapcoords", ir->eSwapCoords, eSwapTypes_names); + if (ir->eSwapCoords != eswapNO) + { + snew(ir->swap, 1); + CTYPE("Swap attempt frequency"); + ITYPE("swap-frequency", ir->swap->nstswap, 1); + CTYPE("Two index groups that contain the compartment-partitioning atoms"); + STYPE("split-group0", splitgrp0, NULL); + STYPE("split-group1", splitgrp1, NULL); + CTYPE("Use center of mass of split groups (yes/no), otherwise center of geometry is used"); + EETYPE("massw-split0", ir->swap->massw_split[0], yesno_names); + EETYPE("massw-split1", ir->swap->massw_split[1], yesno_names); + + CTYPE("Group name of ions that can be exchanged with solvent molecules"); + STYPE("swap-group", swapgrp, NULL); + CTYPE("Group name of solvent molecules"); + STYPE("solvent-group", solgrp, NULL); + + CTYPE("Split cylinder: radius, upper and lower extension (nm) (this will define the channels)"); + CTYPE("Note that the split cylinder settings do not have an influence on the swapping protocol,"); + CTYPE("however, if correctly defined, the ion permeation events are counted per channel"); + RTYPE("cyl0-r", ir->swap->cyl0r, 2.0); + RTYPE("cyl0-up", ir->swap->cyl0u, 1.0); + RTYPE("cyl0-down", ir->swap->cyl0l, 1.0); + RTYPE("cyl1-r", ir->swap->cyl1r, 2.0); + RTYPE("cyl1-up", ir->swap->cyl1u, 1.0); + RTYPE("cyl1-down", ir->swap->cyl1l, 1.0); + + CTYPE("Average the number of ions per compartment over these many swap attempt steps"); + ITYPE("coupl-steps", ir->swap->nAverage, 10); + CTYPE("Requested number of anions and cations for each of the two compartments"); + CTYPE("-1 means fix the numbers as found in time step 0"); + ITYPE("anionsA", ir->swap->nanions[0], -1); + ITYPE("cationsA", ir->swap->ncations[0], -1); + ITYPE("anionsB", ir->swap->nanions[1], -1); + ITYPE("cationsB", ir->swap->ncations[1], -1); + CTYPE("Start to swap ions if threshold difference to requested count is reached"); + RTYPE("threshold", ir->swap->threshold, 1.0); + } + + /* AdResS defined thingies */ + CCTYPE ("AdResS parameters"); + EETYPE("adress", ir->bAdress, yesno_names); + if (ir->bAdress) + { + snew(ir->adress, 1); + read_adressparams(&ninp, &inp, ir->adress, wi); + } + + /* User defined thingies */ + CCTYPE ("User defined thingies"); + STYPE ("user1-grps", is->user1, NULL); + STYPE ("user2-grps", is->user2, NULL); + ITYPE ("userint1", ir->userint1, 0); + ITYPE ("userint2", ir->userint2, 0); + ITYPE ("userint3", ir->userint3, 0); + ITYPE ("userint4", ir->userint4, 0); + RTYPE ("userreal1", ir->userreal1, 0); + RTYPE ("userreal2", ir->userreal2, 0); + RTYPE ("userreal3", ir->userreal3, 0); + RTYPE ("userreal4", ir->userreal4, 0); +#undef CTYPE + + write_inpfile(mdparout, ninp, inp, FALSE, wi); + for (i = 0; (i < ninp); i++) + { + sfree(inp[i].name); + sfree(inp[i].value); + } + sfree(inp); + + /* Process options if necessary */ + for (m = 0; m < 2; m++) + { + for (i = 0; i < 2*DIM; i++) + { + dumdub[m][i] = 0.0; + } + if (ir->epc) + { + switch (ir->epct) + { + case epctISOTROPIC: + if (sscanf(dumstr[m], "%lf", &(dumdub[m][XX])) != 1) + { + warning_error(wi, "Pressure coupling not enough values (I need 1)"); + } + dumdub[m][YY] = dumdub[m][ZZ] = dumdub[m][XX]; + break; + case epctSEMIISOTROPIC: + case epctSURFACETENSION: + if (sscanf(dumstr[m], "%lf%lf", + &(dumdub[m][XX]), &(dumdub[m][ZZ])) != 2) + { + warning_error(wi, "Pressure coupling not enough values (I need 2)"); + } + dumdub[m][YY] = dumdub[m][XX]; + break; + case epctANISOTROPIC: + if (sscanf(dumstr[m], "%lf%lf%lf%lf%lf%lf", + &(dumdub[m][XX]), &(dumdub[m][YY]), &(dumdub[m][ZZ]), + &(dumdub[m][3]), &(dumdub[m][4]), &(dumdub[m][5])) != 6) + { + warning_error(wi, "Pressure coupling not enough values (I need 6)"); + } + break; + default: + gmx_fatal(FARGS, "Pressure coupling type %s not implemented yet", + epcoupltype_names[ir->epct]); + } + } + } + clear_mat(ir->ref_p); + clear_mat(ir->compress); + for (i = 0; i < DIM; i++) + { + ir->ref_p[i][i] = dumdub[1][i]; + ir->compress[i][i] = dumdub[0][i]; + } + if (ir->epct == epctANISOTROPIC) + { + ir->ref_p[XX][YY] = dumdub[1][3]; + ir->ref_p[XX][ZZ] = dumdub[1][4]; + ir->ref_p[YY][ZZ] = dumdub[1][5]; + if (ir->ref_p[XX][YY] != 0 && ir->ref_p[XX][ZZ] != 0 && ir->ref_p[YY][ZZ] != 0) + { + warning(wi, "All off-diagonal reference pressures are non-zero. Are you sure you want to apply a threefold shear stress?\n"); + } + ir->compress[XX][YY] = dumdub[0][3]; + ir->compress[XX][ZZ] = dumdub[0][4]; + ir->compress[YY][ZZ] = dumdub[0][5]; + for (i = 0; i < DIM; i++) + { + for (m = 0; m < i; m++) + { + ir->ref_p[i][m] = ir->ref_p[m][i]; + ir->compress[i][m] = ir->compress[m][i]; + } + } + } + + if (ir->comm_mode == ecmNO) + { + ir->nstcomm = 0; + } + + opts->couple_moltype = NULL; + if (strlen(is->couple_moltype) > 0) + { + if (ir->efep != efepNO) + { + opts->couple_moltype = strdup(is->couple_moltype); + if (opts->couple_lam0 == opts->couple_lam1) + { + warning(wi, "The lambda=0 and lambda=1 states for coupling are identical"); + } + if (ir->eI == eiMD && (opts->couple_lam0 == ecouplamNONE || + opts->couple_lam1 == ecouplamNONE)) + { + warning(wi, "For proper sampling of the (nearly) decoupled state, stochastic dynamics should be used"); + } + } + else + { + warning(wi, "Can not couple a molecule with free_energy = no"); + } + } + /* FREE ENERGY AND EXPANDED ENSEMBLE OPTIONS */ + if (ir->efep != efepNO) + { + if (fep->delta_lambda > 0) + { + ir->efep = efepSLOWGROWTH; + } + } + + if (ir->bSimTemp) + { + fep->bPrintEnergy = TRUE; + /* always print out the energy to dhdl if we are doing expanded ensemble, since we need the total energy + if the temperature is changing. */ + } + + if ((ir->efep != efepNO) || ir->bSimTemp) + { + ir->bExpanded = FALSE; + if ((ir->efep == efepEXPANDED) || ir->bSimTemp) + { + ir->bExpanded = TRUE; + } + do_fep_params(ir, is->fep_lambda, is->lambda_weights); + if (ir->bSimTemp) /* done after fep params */ + { + do_simtemp_params(ir); + } + } + else + { + ir->fepvals->n_lambda = 0; + } + + /* WALL PARAMETERS */ + + do_wall_params(ir, is->wall_atomtype, is->wall_density, opts); + + /* ORIENTATION RESTRAINT PARAMETERS */ + + if (opts->bOrire && str_nelem(is->orirefitgrp, MAXPTR, NULL) != 1) + { + warning_error(wi, "ERROR: Need one orientation restraint fit group\n"); + } + + /* DEFORMATION PARAMETERS */ + + clear_mat(ir->deform); + for (i = 0; i < 6; i++) + { + dumdub[0][i] = 0; + } + m = sscanf(is->deform, "%lf %lf %lf %lf %lf %lf", + &(dumdub[0][0]), &(dumdub[0][1]), &(dumdub[0][2]), + &(dumdub[0][3]), &(dumdub[0][4]), &(dumdub[0][5])); + for (i = 0; i < 3; i++) + { + ir->deform[i][i] = dumdub[0][i]; + } + ir->deform[YY][XX] = dumdub[0][3]; + ir->deform[ZZ][XX] = dumdub[0][4]; + ir->deform[ZZ][YY] = dumdub[0][5]; + if (ir->epc != epcNO) + { + for (i = 0; i < 3; i++) + { + for (j = 0; j <= i; j++) + { + if (ir->deform[i][j] != 0 && ir->compress[i][j] != 0) + { + warning_error(wi, "A box element has deform set and compressibility > 0"); + } + } + } + for (i = 0; i < 3; i++) + { + for (j = 0; j < i; j++) + { + if (ir->deform[i][j] != 0) + { + for (m = j; m < DIM; m++) + { + if (ir->compress[m][j] != 0) + { + sprintf(warn_buf, "An off-diagonal box element has deform set while compressibility > 0 for the same component of another box vector, this might lead to spurious periodicity effects."); + warning(wi, warn_buf); + } + } + } + } + } + } + + /* Ion/water position swapping checks */ + if (ir->eSwapCoords != eswapNO) + { + if (ir->swap->nstswap < 1) + { + warning_error(wi, "swap_frequency must be 1 or larger when ion swapping is requested"); + } + if (ir->swap->nAverage < 1) + { + warning_error(wi, "coupl_steps must be 1 or larger.\n"); + } + if (ir->swap->threshold < 1.0) + { + warning_error(wi, "Ion count threshold must be at least 1.\n"); + } + } + + sfree(dumstr[0]); + sfree(dumstr[1]); +} + +static int search_QMstring(const char *s, int ng, const char *gn[]) +{ + /* same as normal search_string, but this one searches QM strings */ + int i; + + for (i = 0; (i < ng); i++) + { + if (gmx_strcasecmp(s, gn[i]) == 0) + { + return i; + } + } + + gmx_fatal(FARGS, "this QM method or basisset (%s) is not implemented\n!", s); + + return -1; + +} /* search_QMstring */ + +/* We would like gn to be const as well, but C doesn't allow this */ +int search_string(const char *s, int ng, char *gn[]) +{ + int i; + + for (i = 0; (i < ng); i++) + { + if (gmx_strcasecmp(s, gn[i]) == 0) + { + return i; + } + } + + gmx_fatal(FARGS, + "Group %s referenced in the .mdp file was not found in the index file.\n" + "Group names must match either [moleculetype] names or custom index group\n" + "names, in which case you must supply an index file to the '-n' option\n" + "of grompp.", + s); + + return -1; +} + +static gmx_bool do_numbering(int natoms, gmx_groups_t *groups, int ng, char *ptrs[], + t_blocka *block, char *gnames[], + int gtype, int restnm, + int grptp, gmx_bool bVerbose, + warninp_t wi) +{ + unsigned short *cbuf; + t_grps *grps = &(groups->grps[gtype]); + int i, j, gid, aj, ognr, ntot = 0; + const char *title; + gmx_bool bRest; + char warn_buf[STRLEN]; + + if (debug) + { + fprintf(debug, "Starting numbering %d groups of type %d\n", ng, gtype); + } + + title = gtypes[gtype]; + + snew(cbuf, natoms); + /* Mark all id's as not set */ + for (i = 0; (i < natoms); i++) + { + cbuf[i] = NOGID; + } + + snew(grps->nm_ind, ng+1); /* +1 for possible rest group */ + for (i = 0; (i < ng); i++) + { + /* Lookup the group name in the block structure */ + gid = search_string(ptrs[i], block->nr, gnames); + if ((grptp != egrptpONE) || (i == 0)) + { + grps->nm_ind[grps->nr++] = gid; + } + if (debug) + { + fprintf(debug, "Found gid %d for group %s\n", gid, ptrs[i]); + } + + /* Now go over the atoms in the group */ + for (j = block->index[gid]; (j < block->index[gid+1]); j++) + { + + aj = block->a[j]; + + /* Range checking */ + if ((aj < 0) || (aj >= natoms)) + { + gmx_fatal(FARGS, "Invalid atom number %d in indexfile", aj); + } + /* Lookup up the old group number */ + ognr = cbuf[aj]; + if (ognr != NOGID) + { + gmx_fatal(FARGS, "Atom %d in multiple %s groups (%d and %d)", + aj+1, title, ognr+1, i+1); + } + else + { + /* Store the group number in buffer */ + if (grptp == egrptpONE) + { + cbuf[aj] = 0; + } + else + { + cbuf[aj] = i; + } + ntot++; + } + } + } + + /* Now check whether we have done all atoms */ + bRest = FALSE; + if (ntot != natoms) + { + if (grptp == egrptpALL) + { + gmx_fatal(FARGS, "%d atoms are not part of any of the %s groups", + natoms-ntot, title); + } + else if (grptp == egrptpPART) + { + sprintf(warn_buf, "%d atoms are not part of any of the %s groups", + natoms-ntot, title); + warning_note(wi, warn_buf); + } + /* Assign all atoms currently unassigned to a rest group */ + for (j = 0; (j < natoms); j++) + { + if (cbuf[j] == NOGID) + { + cbuf[j] = grps->nr; + bRest = TRUE; + } + } + if (grptp != egrptpPART) + { + if (bVerbose) + { + fprintf(stderr, + "Making dummy/rest group for %s containing %d elements\n", + title, natoms-ntot); + } + /* Add group name "rest" */ + grps->nm_ind[grps->nr] = restnm; + + /* Assign the rest name to all atoms not currently assigned to a group */ + for (j = 0; (j < natoms); j++) + { + if (cbuf[j] == NOGID) + { + cbuf[j] = grps->nr; + } + } + grps->nr++; + } + } + + if (grps->nr == 1 && (ntot == 0 || ntot == natoms)) + { + /* All atoms are part of one (or no) group, no index required */ + groups->ngrpnr[gtype] = 0; + groups->grpnr[gtype] = NULL; + } + else + { + groups->ngrpnr[gtype] = natoms; + snew(groups->grpnr[gtype], natoms); + for (j = 0; (j < natoms); j++) + { + groups->grpnr[gtype][j] = cbuf[j]; + } + } + + sfree(cbuf); + + return (bRest && grptp == egrptpPART); +} + +static void calc_nrdf(gmx_mtop_t *mtop, t_inputrec *ir, char **gnames) +{ + t_grpopts *opts; + gmx_groups_t *groups; + t_pull *pull; + int natoms, ai, aj, i, j, d, g, imin, jmin; + t_iatom *ia; + int *nrdf2, *na_vcm, na_tot; + double *nrdf_tc, *nrdf_vcm, nrdf_uc, n_sub = 0; + gmx_mtop_atomloop_all_t aloop; + t_atom *atom; + int mb, mol, ftype, as; + gmx_molblock_t *molb; + gmx_moltype_t *molt; + + /* Calculate nrdf. + * First calc 3xnr-atoms for each group + * then subtract half a degree of freedom for each constraint + * + * Only atoms and nuclei contribute to the degrees of freedom... + */ + + opts = &ir->opts; + + groups = &mtop->groups; + natoms = mtop->natoms; + + /* Allocate one more for a possible rest group */ + /* We need to sum degrees of freedom into doubles, + * since floats give too low nrdf's above 3 million atoms. + */ + snew(nrdf_tc, groups->grps[egcTC].nr+1); + snew(nrdf_vcm, groups->grps[egcVCM].nr+1); + snew(na_vcm, groups->grps[egcVCM].nr+1); + + for (i = 0; i < groups->grps[egcTC].nr; i++) + { + nrdf_tc[i] = 0; + } + for (i = 0; i < groups->grps[egcVCM].nr+1; i++) + { + nrdf_vcm[i] = 0; + } + + snew(nrdf2, natoms); + aloop = gmx_mtop_atomloop_all_init(mtop); + while (gmx_mtop_atomloop_all_next(aloop, &i, &atom)) + { + nrdf2[i] = 0; + if (atom->ptype == eptAtom || atom->ptype == eptNucleus) + { + g = ggrpnr(groups, egcFREEZE, i); + /* Double count nrdf for particle i */ + for (d = 0; d < DIM; d++) + { + if (opts->nFreeze[g][d] == 0) + { + nrdf2[i] += 2; + } + } + nrdf_tc [ggrpnr(groups, egcTC, i)] += 0.5*nrdf2[i]; + nrdf_vcm[ggrpnr(groups, egcVCM, i)] += 0.5*nrdf2[i]; + } + } + + as = 0; + for (mb = 0; mb < mtop->nmolblock; mb++) + { + molb = &mtop->molblock[mb]; + molt = &mtop->moltype[molb->type]; + atom = molt->atoms.atom; + for (mol = 0; mol < molb->nmol; mol++) + { + for (ftype = F_CONSTR; ftype <= F_CONSTRNC; ftype++) + { + ia = molt->ilist[ftype].iatoms; + for (i = 0; i < molt->ilist[ftype].nr; ) + { + /* Subtract degrees of freedom for the constraints, + * if the particles still have degrees of freedom left. + * If one of the particles is a vsite or a shell, then all + * constraint motion will go there, but since they do not + * contribute to the constraints the degrees of freedom do not + * change. + */ + ai = as + ia[1]; + aj = as + ia[2]; + if (((atom[ia[1]].ptype == eptNucleus) || + (atom[ia[1]].ptype == eptAtom)) && + ((atom[ia[2]].ptype == eptNucleus) || + (atom[ia[2]].ptype == eptAtom))) + { + if (nrdf2[ai] > 0) + { + jmin = 1; + } + else + { + jmin = 2; + } + if (nrdf2[aj] > 0) + { + imin = 1; + } + else + { + imin = 2; + } + imin = min(imin, nrdf2[ai]); + jmin = min(jmin, nrdf2[aj]); + nrdf2[ai] -= imin; + nrdf2[aj] -= jmin; + nrdf_tc [ggrpnr(groups, egcTC, ai)] -= 0.5*imin; + nrdf_tc [ggrpnr(groups, egcTC, aj)] -= 0.5*jmin; + nrdf_vcm[ggrpnr(groups, egcVCM, ai)] -= 0.5*imin; + nrdf_vcm[ggrpnr(groups, egcVCM, aj)] -= 0.5*jmin; + } + ia += interaction_function[ftype].nratoms+1; + i += interaction_function[ftype].nratoms+1; + } + } + ia = molt->ilist[F_SETTLE].iatoms; + for (i = 0; i < molt->ilist[F_SETTLE].nr; ) + { + /* Subtract 1 dof from every atom in the SETTLE */ + for (j = 0; j < 3; j++) + { + ai = as + ia[1+j]; + imin = min(2, nrdf2[ai]); + nrdf2[ai] -= imin; + nrdf_tc [ggrpnr(groups, egcTC, ai)] -= 0.5*imin; + nrdf_vcm[ggrpnr(groups, egcVCM, ai)] -= 0.5*imin; + } + ia += 4; + i += 4; + } + as += molt->atoms.nr; + } + } + + if (ir->ePull == epullCONSTRAINT) + { + /* Correct nrdf for the COM constraints. + * We correct using the TC and VCM group of the first atom + * in the reference and pull group. If atoms in one pull group + * belong to different TC or VCM groups it is anyhow difficult + * to determine the optimal nrdf assignment. + */ + pull = ir->pull; + + for (i = 0; i < pull->ncoord; i++) + { + imin = 1; + + for (j = 0; j < 2; j++) + { + const t_pull_group *pgrp; + + pgrp = &pull->group[pull->coord[i].group[j]]; + + if (pgrp->nat > 0) + { + /* Subtract 1/2 dof from each group */ + ai = pgrp->ind[0]; + nrdf_tc [ggrpnr(groups, egcTC, ai)] -= 0.5*imin; + nrdf_vcm[ggrpnr(groups, egcVCM, ai)] -= 0.5*imin; + if (nrdf_tc[ggrpnr(groups, egcTC, ai)] < 0) + { + gmx_fatal(FARGS, "Center of mass pulling constraints caused the number of degrees of freedom for temperature coupling group %s to be negative", gnames[groups->grps[egcTC].nm_ind[ggrpnr(groups, egcTC, ai)]]); + } + } + else + { + /* We need to subtract the whole DOF from group j=1 */ + imin += 1; + } + } + } + } + + if (ir->nstcomm != 0) + { + /* Subtract 3 from the number of degrees of freedom in each vcm group + * when com translation is removed and 6 when rotation is removed + * as well. + */ + switch (ir->comm_mode) + { + case ecmLINEAR: + n_sub = ndof_com(ir); + break; + case ecmANGULAR: + n_sub = 6; + break; + default: + n_sub = 0; + gmx_incons("Checking comm_mode"); + } + + for (i = 0; i < groups->grps[egcTC].nr; i++) + { + /* Count the number of atoms of TC group i for every VCM group */ + for (j = 0; j < groups->grps[egcVCM].nr+1; j++) + { + na_vcm[j] = 0; + } + na_tot = 0; + for (ai = 0; ai < natoms; ai++) + { + if (ggrpnr(groups, egcTC, ai) == i) + { + na_vcm[ggrpnr(groups, egcVCM, ai)]++; + na_tot++; + } + } + /* Correct for VCM removal according to the fraction of each VCM + * group present in this TC group. + */ + nrdf_uc = nrdf_tc[i]; + if (debug) + { + fprintf(debug, "T-group[%d] nrdf_uc = %g, n_sub = %g\n", + i, nrdf_uc, n_sub); + } + nrdf_tc[i] = 0; + for (j = 0; j < groups->grps[egcVCM].nr+1; j++) + { + if (nrdf_vcm[j] > n_sub) + { + nrdf_tc[i] += nrdf_uc*((double)na_vcm[j]/(double)na_tot)* + (nrdf_vcm[j] - n_sub)/nrdf_vcm[j]; + } + if (debug) + { + fprintf(debug, " nrdf_vcm[%d] = %g, nrdf = %g\n", + j, nrdf_vcm[j], nrdf_tc[i]); + } + } + } + } + for (i = 0; (i < groups->grps[egcTC].nr); i++) + { + opts->nrdf[i] = nrdf_tc[i]; + if (opts->nrdf[i] < 0) + { + opts->nrdf[i] = 0; + } + fprintf(stderr, + "Number of degrees of freedom in T-Coupling group %s is %.2f\n", + gnames[groups->grps[egcTC].nm_ind[i]], opts->nrdf[i]); + } + + sfree(nrdf2); + sfree(nrdf_tc); + sfree(nrdf_vcm); + sfree(na_vcm); +} + +static void decode_cos(char *s, t_cosines *cosine) +{ + char *t; + char format[STRLEN], f1[STRLEN]; + double a, phi; + int i; + + t = strdup(s); + trim(t); + + cosine->n = 0; + cosine->a = NULL; + cosine->phi = NULL; + if (strlen(t)) + { + sscanf(t, "%d", &(cosine->n)); + if (cosine->n <= 0) + { + cosine->n = 0; + } + else + { + snew(cosine->a, cosine->n); + snew(cosine->phi, cosine->n); + + sprintf(format, "%%*d"); + for (i = 0; (i < cosine->n); i++) + { + strcpy(f1, format); + strcat(f1, "%lf%lf"); + if (sscanf(t, f1, &a, &phi) < 2) + { + gmx_fatal(FARGS, "Invalid input for electric field shift: '%s'", t); + } + cosine->a[i] = a; + cosine->phi[i] = phi; + strcat(format, "%*lf%*lf"); + } + } + } + sfree(t); +} + +static gmx_bool do_egp_flag(t_inputrec *ir, gmx_groups_t *groups, + const char *option, const char *val, int flag) +{ + /* The maximum number of energy group pairs would be MAXPTR*(MAXPTR+1)/2. + * But since this is much larger than STRLEN, such a line can not be parsed. + * The real maximum is the number of names that fit in a string: STRLEN/2. + */ +#define EGP_MAX (STRLEN/2) + int nelem, i, j, k, nr; + char *names[EGP_MAX]; + char ***gnames; + gmx_bool bSet; + + gnames = groups->grpname; + + nelem = str_nelem(val, EGP_MAX, names); + if (nelem % 2 != 0) + { + gmx_fatal(FARGS, "The number of groups for %s is odd", option); + } + nr = groups->grps[egcENER].nr; + bSet = FALSE; + for (i = 0; i < nelem/2; i++) + { + j = 0; + while ((j < nr) && + gmx_strcasecmp(names[2*i], *(gnames[groups->grps[egcENER].nm_ind[j]]))) + { + j++; + } + if (j == nr) + { + gmx_fatal(FARGS, "%s in %s is not an energy group\n", + names[2*i], option); + } + k = 0; + while ((k < nr) && + gmx_strcasecmp(names[2*i+1], *(gnames[groups->grps[egcENER].nm_ind[k]]))) + { + k++; + } + if (k == nr) + { + gmx_fatal(FARGS, "%s in %s is not an energy group\n", + names[2*i+1], option); + } + if ((j < nr) && (k < nr)) + { + ir->opts.egp_flags[nr*j+k] |= flag; + ir->opts.egp_flags[nr*k+j] |= flag; + bSet = TRUE; + } + } + + return bSet; +} + + +static void make_swap_groups( + t_swapcoords *swap, + char *swapgname, + char *splitg0name, + char *splitg1name, + char *solgname, + t_blocka *grps, + char **gnames) +{ + int ig = -1, i = 0, j; + char *splitg; + + + /* Just a quick check here, more thorough checks are in mdrun */ + if (strcmp(splitg0name, splitg1name) == 0) + { + gmx_fatal(FARGS, "The split groups can not both be '%s'.", splitg0name); + } + + /* First get the swap group index atoms */ + ig = search_string(swapgname, grps->nr, gnames); + swap->nat = grps->index[ig+1] - grps->index[ig]; + if (swap->nat > 0) + { + fprintf(stderr, "Swap group '%s' contains %d atoms.\n", swapgname, swap->nat); + snew(swap->ind, swap->nat); + for (i = 0; i < swap->nat; i++) + { + swap->ind[i] = grps->a[grps->index[ig]+i]; + } + } + else + { + gmx_fatal(FARGS, "You defined an empty group of atoms for swapping."); + } + + /* Now do so for the split groups */ + for (j = 0; j < 2; j++) + { + if (j == 0) + { + splitg = splitg0name; + } + else + { + splitg = splitg1name; + } + + ig = search_string(splitg, grps->nr, gnames); + swap->nat_split[j] = grps->index[ig+1] - grps->index[ig]; + if (swap->nat_split[j] > 0) + { + fprintf(stderr, "Split group %d '%s' contains %d atom%s.\n", + j, splitg, swap->nat_split[j], (swap->nat_split[j] > 1) ? "s" : ""); + snew(swap->ind_split[j], swap->nat_split[j]); + for (i = 0; i < swap->nat_split[j]; i++) + { + swap->ind_split[j][i] = grps->a[grps->index[ig]+i]; + } + } + else + { + gmx_fatal(FARGS, "Split group %d has to contain at least 1 atom!", j); + } + } + + /* Now get the solvent group index atoms */ + ig = search_string(solgname, grps->nr, gnames); + swap->nat_sol = grps->index[ig+1] - grps->index[ig]; + if (swap->nat_sol > 0) + { + fprintf(stderr, "Solvent group '%s' contains %d atoms.\n", solgname, swap->nat_sol); + snew(swap->ind_sol, swap->nat_sol); + for (i = 0; i < swap->nat_sol; i++) + { + swap->ind_sol[i] = grps->a[grps->index[ig]+i]; + } + } + else + { + gmx_fatal(FARGS, "You defined an empty group of solvent. Cannot exchange ions."); + } +} + + +void do_index(const char* mdparin, const char *ndx, + gmx_mtop_t *mtop, + gmx_bool bVerbose, + t_inputrec *ir, rvec *v, + warninp_t wi) +{ + t_blocka *grps; + gmx_groups_t *groups; + int natoms; + t_symtab *symtab; + t_atoms atoms_all; + char warnbuf[STRLEN], **gnames; + int nr, ntcg, ntau_t, nref_t, nacc, nofg, nSA, nSA_points, nSA_time, nSA_temp; + real tau_min; + int nstcmin; + int nacg, nfreeze, nfrdim, nenergy, nvcm, nuser; + char *ptr1[MAXPTR], *ptr2[MAXPTR], *ptr3[MAXPTR]; + int i, j, k, restnm; + real SAtime; + gmx_bool bExcl, bTable, bSetTCpar, bAnneal, bRest; + int nQMmethod, nQMbasis, nQMcharge, nQMmult, nbSH, nCASorb, nCASelec, + nSAon, nSAoff, nSAsteps, nQMg, nbOPT, nbTS; + char warn_buf[STRLEN]; + + if (bVerbose) + { + fprintf(stderr, "processing index file...\n"); + } + debug_gmx(); + if (ndx == NULL) + { + snew(grps, 1); + snew(grps->index, 1); + snew(gnames, 1); + atoms_all = gmx_mtop_global_atoms(mtop); + analyse(&atoms_all, grps, &gnames, FALSE, TRUE); + free_t_atoms(&atoms_all, FALSE); + } + else + { + grps = init_index(ndx, &gnames); + } + + groups = &mtop->groups; + natoms = mtop->natoms; + symtab = &mtop->symtab; + + snew(groups->grpname, grps->nr+1); + + for (i = 0; (i < grps->nr); i++) + { + groups->grpname[i] = put_symtab(symtab, gnames[i]); + } + groups->grpname[i] = put_symtab(symtab, "rest"); + restnm = i; + srenew(gnames, grps->nr+1); + gnames[restnm] = *(groups->grpname[i]); + groups->ngrpname = grps->nr+1; + + set_warning_line(wi, mdparin, -1); + + ntau_t = str_nelem(is->tau_t, MAXPTR, ptr1); + nref_t = str_nelem(is->ref_t, MAXPTR, ptr2); + ntcg = str_nelem(is->tcgrps, MAXPTR, ptr3); + if ((ntau_t != ntcg) || (nref_t != ntcg)) + { + gmx_fatal(FARGS, "Invalid T coupling input: %d groups, %d ref-t values and " + "%d tau-t values", ntcg, nref_t, ntau_t); + } + + bSetTCpar = (ir->etc || EI_SD(ir->eI) || ir->eI == eiBD || EI_TPI(ir->eI)); + do_numbering(natoms, groups, ntcg, ptr3, grps, gnames, egcTC, + restnm, bSetTCpar ? egrptpALL : egrptpALL_GENREST, bVerbose, wi); + nr = groups->grps[egcTC].nr; + ir->opts.ngtc = nr; + snew(ir->opts.nrdf, nr); + snew(ir->opts.tau_t, nr); + snew(ir->opts.ref_t, nr); + if (ir->eI == eiBD && ir->bd_fric == 0) + { + fprintf(stderr, "bd-fric=0, so tau-t will be used as the inverse friction constant(s)\n"); + } + + if (bSetTCpar) + { + if (nr != nref_t) + { + gmx_fatal(FARGS, "Not enough ref-t and tau-t values!"); + } + + tau_min = 1e20; + for (i = 0; (i < nr); i++) + { + ir->opts.tau_t[i] = strtod(ptr1[i], NULL); + if ((ir->eI == eiBD || ir->eI == eiSD2) && ir->opts.tau_t[i] <= 0) + { + sprintf(warn_buf, "With integrator %s tau-t should be larger than 0", ei_names[ir->eI]); + warning_error(wi, warn_buf); + } + + if (ir->etc != etcVRESCALE && ir->opts.tau_t[i] == 0) + { + warning_note(wi, "tau-t = -1 is the value to signal that a group should not have temperature coupling. Treating your use of tau-t = 0 as if you used -1."); + } + + if (ir->opts.tau_t[i] >= 0) + { + tau_min = min(tau_min, ir->opts.tau_t[i]); + } + } + if (ir->etc != etcNO && ir->nsttcouple == -1) + { + ir->nsttcouple = ir_optimal_nsttcouple(ir); + } + + if (EI_VV(ir->eI)) + { + if ((ir->etc == etcNOSEHOOVER) && (ir->epc == epcBERENDSEN)) + { + gmx_fatal(FARGS, "Cannot do Nose-Hoover temperature with Berendsen pressure control with md-vv; use either vrescale temperature with berendsen pressure or Nose-Hoover temperature with MTTK pressure"); + } + if ((ir->epc == epcMTTK) && (ir->etc > etcNO)) + { + if (ir->nstpcouple != ir->nsttcouple) + { + int mincouple = min(ir->nstpcouple, ir->nsttcouple); + ir->nstpcouple = ir->nsttcouple = mincouple; + sprintf(warn_buf, "for current Trotter decomposition methods with vv, nsttcouple and nstpcouple must be equal. Both have been reset to min(nsttcouple,nstpcouple) = %d", mincouple); + warning_note(wi, warn_buf); + } + } + } + /* velocity verlet with averaged kinetic energy KE = 0.5*(v(t+1/2) - v(t-1/2)) is implemented + primarily for testing purposes, and does not work with temperature coupling other than 1 */ + + if (ETC_ANDERSEN(ir->etc)) + { + if (ir->nsttcouple != 1) + { + ir->nsttcouple = 1; + sprintf(warn_buf, "Andersen temperature control methods assume nsttcouple = 1; there is no need for larger nsttcouple > 1, since no global parameters are computed. nsttcouple has been reset to 1"); + warning_note(wi, warn_buf); + } + } + nstcmin = tcouple_min_integration_steps(ir->etc); + if (nstcmin > 1) + { + if (tau_min/(ir->delta_t*ir->nsttcouple) < nstcmin) + { + sprintf(warn_buf, "For proper integration of the %s thermostat, tau-t (%g) should be at least %d times larger than nsttcouple*dt (%g)", + ETCOUPLTYPE(ir->etc), + tau_min, nstcmin, + ir->nsttcouple*ir->delta_t); + warning(wi, warn_buf); + } + } + for (i = 0; (i < nr); i++) + { + ir->opts.ref_t[i] = strtod(ptr2[i], NULL); + if (ir->opts.ref_t[i] < 0) + { + gmx_fatal(FARGS, "ref-t for group %d negative", i); + } + } + /* set the lambda mc temperature to the md integrator temperature (which should be defined + if we are in this conditional) if mc_temp is negative */ + if (ir->expandedvals->mc_temp < 0) + { + ir->expandedvals->mc_temp = ir->opts.ref_t[0]; /*for now, set to the first reft */ + } + } + + /* Simulated annealing for each group. There are nr groups */ + nSA = str_nelem(is->anneal, MAXPTR, ptr1); + if (nSA == 1 && (ptr1[0][0] == 'n' || ptr1[0][0] == 'N')) + { + nSA = 0; + } + if (nSA > 0 && nSA != nr) + { + gmx_fatal(FARGS, "Not enough annealing values: %d (for %d groups)\n", nSA, nr); + } + else + { + snew(ir->opts.annealing, nr); + snew(ir->opts.anneal_npoints, nr); + snew(ir->opts.anneal_time, nr); + snew(ir->opts.anneal_temp, nr); + for (i = 0; i < nr; i++) + { + ir->opts.annealing[i] = eannNO; + ir->opts.anneal_npoints[i] = 0; + ir->opts.anneal_time[i] = NULL; + ir->opts.anneal_temp[i] = NULL; + } + if (nSA > 0) + { + bAnneal = FALSE; + for (i = 0; i < nr; i++) + { + if (ptr1[i][0] == 'n' || ptr1[i][0] == 'N') + { + ir->opts.annealing[i] = eannNO; + } + else if (ptr1[i][0] == 's' || ptr1[i][0] == 'S') + { + ir->opts.annealing[i] = eannSINGLE; + bAnneal = TRUE; + } + else if (ptr1[i][0] == 'p' || ptr1[i][0] == 'P') + { + ir->opts.annealing[i] = eannPERIODIC; + bAnneal = TRUE; + } + } + if (bAnneal) + { + /* Read the other fields too */ + nSA_points = str_nelem(is->anneal_npoints, MAXPTR, ptr1); + if (nSA_points != nSA) + { + gmx_fatal(FARGS, "Found %d annealing-npoints values for %d groups\n", nSA_points, nSA); + } + for (k = 0, i = 0; i < nr; i++) + { + ir->opts.anneal_npoints[i] = strtol(ptr1[i], NULL, 10); + if (ir->opts.anneal_npoints[i] == 1) + { + gmx_fatal(FARGS, "Please specify at least a start and an end point for annealing\n"); + } + snew(ir->opts.anneal_time[i], ir->opts.anneal_npoints[i]); + snew(ir->opts.anneal_temp[i], ir->opts.anneal_npoints[i]); + k += ir->opts.anneal_npoints[i]; + } + + nSA_time = str_nelem(is->anneal_time, MAXPTR, ptr1); + if (nSA_time != k) + { + gmx_fatal(FARGS, "Found %d annealing-time values, wanter %d\n", nSA_time, k); + } + nSA_temp = str_nelem(is->anneal_temp, MAXPTR, ptr2); + if (nSA_temp != k) + { + gmx_fatal(FARGS, "Found %d annealing-temp values, wanted %d\n", nSA_temp, k); + } + + for (i = 0, k = 0; i < nr; i++) + { + + for (j = 0; j < ir->opts.anneal_npoints[i]; j++) + { + ir->opts.anneal_time[i][j] = strtod(ptr1[k], NULL); + ir->opts.anneal_temp[i][j] = strtod(ptr2[k], NULL); + if (j == 0) + { + if (ir->opts.anneal_time[i][0] > (ir->init_t+GMX_REAL_EPS)) + { + gmx_fatal(FARGS, "First time point for annealing > init_t.\n"); + } + } + else + { + /* j>0 */ + if (ir->opts.anneal_time[i][j] < ir->opts.anneal_time[i][j-1]) + { + gmx_fatal(FARGS, "Annealing timepoints out of order: t=%f comes after t=%f\n", + ir->opts.anneal_time[i][j], ir->opts.anneal_time[i][j-1]); + } + } + if (ir->opts.anneal_temp[i][j] < 0) + { + gmx_fatal(FARGS, "Found negative temperature in annealing: %f\n", ir->opts.anneal_temp[i][j]); + } + k++; + } + } + /* Print out some summary information, to make sure we got it right */ + for (i = 0, k = 0; i < nr; i++) + { + if (ir->opts.annealing[i] != eannNO) + { + j = groups->grps[egcTC].nm_ind[i]; + fprintf(stderr, "Simulated annealing for group %s: %s, %d timepoints\n", + *(groups->grpname[j]), eann_names[ir->opts.annealing[i]], + ir->opts.anneal_npoints[i]); + fprintf(stderr, "Time (ps) Temperature (K)\n"); + /* All terms except the last one */ + for (j = 0; j < (ir->opts.anneal_npoints[i]-1); j++) + { + fprintf(stderr, "%9.1f %5.1f\n", ir->opts.anneal_time[i][j], ir->opts.anneal_temp[i][j]); + } + + /* Finally the last one */ + j = ir->opts.anneal_npoints[i]-1; + if (ir->opts.annealing[i] == eannSINGLE) + { + fprintf(stderr, "%9.1f- %5.1f\n", ir->opts.anneal_time[i][j], ir->opts.anneal_temp[i][j]); + } + else + { + fprintf(stderr, "%9.1f %5.1f\n", ir->opts.anneal_time[i][j], ir->opts.anneal_temp[i][j]); + if (fabs(ir->opts.anneal_temp[i][j]-ir->opts.anneal_temp[i][0]) > GMX_REAL_EPS) + { + warning_note(wi, "There is a temperature jump when your annealing loops back.\n"); + } + } + } + } + } + } + } + + if (ir->ePull != epullNO) + { + make_pull_groups(ir->pull, is->pull_grp, grps, gnames); + + make_pull_coords(ir->pull); + } + + if (ir->bRot) + { + make_rotation_groups(ir->rot, is->rot_grp, grps, gnames); + } + + if (ir->eSwapCoords != eswapNO) + { + make_swap_groups(ir->swap, swapgrp, splitgrp0, splitgrp1, solgrp, grps, gnames); + } + + nacc = str_nelem(is->acc, MAXPTR, ptr1); + nacg = str_nelem(is->accgrps, MAXPTR, ptr2); + if (nacg*DIM != nacc) + { + gmx_fatal(FARGS, "Invalid Acceleration input: %d groups and %d acc. values", + nacg, nacc); + } + do_numbering(natoms, groups, nacg, ptr2, grps, gnames, egcACC, + restnm, egrptpALL_GENREST, bVerbose, wi); + nr = groups->grps[egcACC].nr; + snew(ir->opts.acc, nr); + ir->opts.ngacc = nr; + + for (i = k = 0; (i < nacg); i++) + { + for (j = 0; (j < DIM); j++, k++) + { + ir->opts.acc[i][j] = strtod(ptr1[k], NULL); + } + } + for (; (i < nr); i++) + { + for (j = 0; (j < DIM); j++) + { + ir->opts.acc[i][j] = 0; + } + } + + nfrdim = str_nelem(is->frdim, MAXPTR, ptr1); + nfreeze = str_nelem(is->freeze, MAXPTR, ptr2); + if (nfrdim != DIM*nfreeze) + { + gmx_fatal(FARGS, "Invalid Freezing input: %d groups and %d freeze values", + nfreeze, nfrdim); + } + do_numbering(natoms, groups, nfreeze, ptr2, grps, gnames, egcFREEZE, + restnm, egrptpALL_GENREST, bVerbose, wi); + nr = groups->grps[egcFREEZE].nr; + ir->opts.ngfrz = nr; + snew(ir->opts.nFreeze, nr); + for (i = k = 0; (i < nfreeze); i++) + { + for (j = 0; (j < DIM); j++, k++) + { + ir->opts.nFreeze[i][j] = (gmx_strncasecmp(ptr1[k], "Y", 1) == 0); + if (!ir->opts.nFreeze[i][j]) + { + if (gmx_strncasecmp(ptr1[k], "N", 1) != 0) + { + sprintf(warnbuf, "Please use Y(ES) or N(O) for freezedim only " + "(not %s)", ptr1[k]); + warning(wi, warn_buf); + } + } + } + } + for (; (i < nr); i++) + { + for (j = 0; (j < DIM); j++) + { + ir->opts.nFreeze[i][j] = 0; + } + } + + nenergy = str_nelem(is->energy, MAXPTR, ptr1); + do_numbering(natoms, groups, nenergy, ptr1, grps, gnames, egcENER, + restnm, egrptpALL_GENREST, bVerbose, wi); + add_wall_energrps(groups, ir->nwall, symtab); + ir->opts.ngener = groups->grps[egcENER].nr; + nvcm = str_nelem(is->vcm, MAXPTR, ptr1); + bRest = + do_numbering(natoms, groups, nvcm, ptr1, grps, gnames, egcVCM, + restnm, nvcm == 0 ? egrptpALL_GENREST : egrptpPART, bVerbose, wi); + if (bRest) + { + warning(wi, "Some atoms are not part of any center of mass motion removal group.\n" + "This may lead to artifacts.\n" + "In most cases one should use one group for the whole system."); + } + + /* Now we have filled the freeze struct, so we can calculate NRDF */ + calc_nrdf(mtop, ir, gnames); + + if (v && NULL) + { + real fac, ntot = 0; + + /* Must check per group! */ + for (i = 0; (i < ir->opts.ngtc); i++) + { + ntot += ir->opts.nrdf[i]; + } + if (ntot != (DIM*natoms)) + { + fac = sqrt(ntot/(DIM*natoms)); + if (bVerbose) + { + fprintf(stderr, "Scaling velocities by a factor of %.3f to account for constraints\n" + "and removal of center of mass motion\n", fac); + } + for (i = 0; (i < natoms); i++) + { + svmul(fac, v[i], v[i]); + } + } + } + + nuser = str_nelem(is->user1, MAXPTR, ptr1); + do_numbering(natoms, groups, nuser, ptr1, grps, gnames, egcUser1, + restnm, egrptpALL_GENREST, bVerbose, wi); + nuser = str_nelem(is->user2, MAXPTR, ptr1); + do_numbering(natoms, groups, nuser, ptr1, grps, gnames, egcUser2, + restnm, egrptpALL_GENREST, bVerbose, wi); + nuser = str_nelem(is->x_compressed_groups, MAXPTR, ptr1); + do_numbering(natoms, groups, nuser, ptr1, grps, gnames, egcCompressedX, + restnm, egrptpONE, bVerbose, wi); + nofg = str_nelem(is->orirefitgrp, MAXPTR, ptr1); + do_numbering(natoms, groups, nofg, ptr1, grps, gnames, egcORFIT, + restnm, egrptpALL_GENREST, bVerbose, wi); + + /* QMMM input processing */ + nQMg = str_nelem(is->QMMM, MAXPTR, ptr1); + nQMmethod = str_nelem(is->QMmethod, MAXPTR, ptr2); + nQMbasis = str_nelem(is->QMbasis, MAXPTR, ptr3); + if ((nQMmethod != nQMg) || (nQMbasis != nQMg)) + { + gmx_fatal(FARGS, "Invalid QMMM input: %d groups %d basissets" + " and %d methods\n", nQMg, nQMbasis, nQMmethod); + } + /* group rest, if any, is always MM! */ + do_numbering(natoms, groups, nQMg, ptr1, grps, gnames, egcQMMM, + restnm, egrptpALL_GENREST, bVerbose, wi); + nr = nQMg; /*atoms->grps[egcQMMM].nr;*/ + ir->opts.ngQM = nQMg; + snew(ir->opts.QMmethod, nr); + snew(ir->opts.QMbasis, nr); + for (i = 0; i < nr; i++) + { + /* input consists of strings: RHF CASSCF PM3 .. These need to be + * converted to the corresponding enum in names.c + */ + ir->opts.QMmethod[i] = search_QMstring(ptr2[i], eQMmethodNR, + eQMmethod_names); + ir->opts.QMbasis[i] = search_QMstring(ptr3[i], eQMbasisNR, + eQMbasis_names); + + } + nQMmult = str_nelem(is->QMmult, MAXPTR, ptr1); + nQMcharge = str_nelem(is->QMcharge, MAXPTR, ptr2); + nbSH = str_nelem(is->bSH, MAXPTR, ptr3); + snew(ir->opts.QMmult, nr); + snew(ir->opts.QMcharge, nr); + snew(ir->opts.bSH, nr); + + for (i = 0; i < nr; i++) + { + ir->opts.QMmult[i] = strtol(ptr1[i], NULL, 10); + ir->opts.QMcharge[i] = strtol(ptr2[i], NULL, 10); + ir->opts.bSH[i] = (gmx_strncasecmp(ptr3[i], "Y", 1) == 0); + } + + nCASelec = str_nelem(is->CASelectrons, MAXPTR, ptr1); + nCASorb = str_nelem(is->CASorbitals, MAXPTR, ptr2); + snew(ir->opts.CASelectrons, nr); + snew(ir->opts.CASorbitals, nr); + for (i = 0; i < nr; i++) + { + ir->opts.CASelectrons[i] = strtol(ptr1[i], NULL, 10); + ir->opts.CASorbitals[i] = strtol(ptr2[i], NULL, 10); + } + /* special optimization options */ + + nbOPT = str_nelem(is->bOPT, MAXPTR, ptr1); + nbTS = str_nelem(is->bTS, MAXPTR, ptr2); + snew(ir->opts.bOPT, nr); + snew(ir->opts.bTS, nr); + for (i = 0; i < nr; i++) + { + ir->opts.bOPT[i] = (gmx_strncasecmp(ptr1[i], "Y", 1) == 0); + ir->opts.bTS[i] = (gmx_strncasecmp(ptr2[i], "Y", 1) == 0); + } + nSAon = str_nelem(is->SAon, MAXPTR, ptr1); + nSAoff = str_nelem(is->SAoff, MAXPTR, ptr2); + nSAsteps = str_nelem(is->SAsteps, MAXPTR, ptr3); + snew(ir->opts.SAon, nr); + snew(ir->opts.SAoff, nr); + snew(ir->opts.SAsteps, nr); + + for (i = 0; i < nr; i++) + { + ir->opts.SAon[i] = strtod(ptr1[i], NULL); + ir->opts.SAoff[i] = strtod(ptr2[i], NULL); + ir->opts.SAsteps[i] = strtol(ptr3[i], NULL, 10); + } + /* end of QMMM input */ + + if (bVerbose) + { + for (i = 0; (i < egcNR); i++) + { + fprintf(stderr, "%-16s has %d element(s):", gtypes[i], groups->grps[i].nr); + for (j = 0; (j < groups->grps[i].nr); j++) + { + fprintf(stderr, " %s", *(groups->grpname[groups->grps[i].nm_ind[j]])); + } + fprintf(stderr, "\n"); + } + } + + nr = groups->grps[egcENER].nr; + snew(ir->opts.egp_flags, nr*nr); + + bExcl = do_egp_flag(ir, groups, "energygrp-excl", is->egpexcl, EGP_EXCL); + if (bExcl && ir->cutoff_scheme == ecutsVERLET) + { + warning_error(wi, "Energy group exclusions are not (yet) implemented for the Verlet scheme"); + } + if (bExcl && EEL_FULL(ir->coulombtype)) + { + warning(wi, "Can not exclude the lattice Coulomb energy between energy groups"); + } + + bTable = do_egp_flag(ir, groups, "energygrp-table", is->egptable, EGP_TABLE); + if (bTable && !(ir->vdwtype == evdwUSER) && + !(ir->coulombtype == eelUSER) && !(ir->coulombtype == eelPMEUSER) && + !(ir->coulombtype == eelPMEUSERSWITCH)) + { + gmx_fatal(FARGS, "Can only have energy group pair tables in combination with user tables for VdW and/or Coulomb"); + } + + decode_cos(is->efield_x, &(ir->ex[XX])); + decode_cos(is->efield_xt, &(ir->et[XX])); + decode_cos(is->efield_y, &(ir->ex[YY])); + decode_cos(is->efield_yt, &(ir->et[YY])); + decode_cos(is->efield_z, &(ir->ex[ZZ])); + decode_cos(is->efield_zt, &(ir->et[ZZ])); + + if (ir->bAdress) + { + do_adress_index(ir->adress, groups, gnames, &(ir->opts), wi); + } + + for (i = 0; (i < grps->nr); i++) + { + sfree(gnames[i]); + } + sfree(gnames); + done_blocka(grps); + sfree(grps); + +} + + + +static void check_disre(gmx_mtop_t *mtop) +{ + gmx_ffparams_t *ffparams; + t_functype *functype; + t_iparams *ip; + int i, ndouble, ftype; + int label, old_label; + + if (gmx_mtop_ftype_count(mtop, F_DISRES) > 0) + { + ffparams = &mtop->ffparams; + functype = ffparams->functype; + ip = ffparams->iparams; + ndouble = 0; + old_label = -1; + for (i = 0; i < ffparams->ntypes; i++) + { + ftype = functype[i]; + if (ftype == F_DISRES) + { + label = ip[i].disres.label; + if (label == old_label) + { + fprintf(stderr, "Distance restraint index %d occurs twice\n", label); + ndouble++; + } + old_label = label; + } + } + if (ndouble > 0) + { + gmx_fatal(FARGS, "Found %d double distance restraint indices,\n" + "probably the parameters for multiple pairs in one restraint " + "are not identical\n", ndouble); + } + } +} + +static gmx_bool absolute_reference(t_inputrec *ir, gmx_mtop_t *sys, + gmx_bool posres_only, + ivec AbsRef) +{ + int d, g, i; + gmx_mtop_ilistloop_t iloop; + t_ilist *ilist; + int nmol; + t_iparams *pr; + + clear_ivec(AbsRef); + + if (!posres_only) + { + /* Check the COM */ + for (d = 0; d < DIM; d++) + { + AbsRef[d] = (d < ndof_com(ir) ? 0 : 1); + } + /* Check for freeze groups */ + for (g = 0; g < ir->opts.ngfrz; g++) + { + for (d = 0; d < DIM; d++) + { + if (ir->opts.nFreeze[g][d] != 0) + { + AbsRef[d] = 1; + } + } + } + } + + /* Check for position restraints */ + iloop = gmx_mtop_ilistloop_init(sys); + while (gmx_mtop_ilistloop_next(iloop, &ilist, &nmol)) + { + if (nmol > 0 && + (AbsRef[XX] == 0 || AbsRef[YY] == 0 || AbsRef[ZZ] == 0)) + { + for (i = 0; i < ilist[F_POSRES].nr; i += 2) + { + pr = &sys->ffparams.iparams[ilist[F_POSRES].iatoms[i]]; + for (d = 0; d < DIM; d++) + { + if (pr->posres.fcA[d] != 0) + { + AbsRef[d] = 1; + } + } + } + for (i = 0; i < ilist[F_FBPOSRES].nr; i += 2) + { + /* Check for flat-bottom posres */ + pr = &sys->ffparams.iparams[ilist[F_FBPOSRES].iatoms[i]]; + if (pr->fbposres.k != 0) + { + switch (pr->fbposres.geom) + { + case efbposresSPHERE: + AbsRef[XX] = AbsRef[YY] = AbsRef[ZZ] = 1; + break; + case efbposresCYLINDER: + AbsRef[XX] = AbsRef[YY] = 1; + break; + case efbposresX: /* d=XX */ + case efbposresY: /* d=YY */ + case efbposresZ: /* d=ZZ */ + d = pr->fbposres.geom - efbposresX; + AbsRef[d] = 1; + break; + default: + gmx_fatal(FARGS, " Invalid geometry for flat-bottom position restraint.\n" + "Expected nr between 1 and %d. Found %d\n", efbposresNR-1, + pr->fbposres.geom); + } + } + } + } + } + + return (AbsRef[XX] != 0 && AbsRef[YY] != 0 && AbsRef[ZZ] != 0); +} + +static void +check_combination_rule_differences(const gmx_mtop_t *mtop, int state, + gmx_bool *bC6ParametersWorkWithGeometricRules, + gmx_bool *bC6ParametersWorkWithLBRules, + gmx_bool *bLBRulesPossible) +{ + int ntypes, tpi, tpj, thisLBdiff, thisgeomdiff; + int *typecount; + real tol; + double geometricdiff, LBdiff; + double c6i, c6j, c12i, c12j; + double c6, c6_geometric, c6_LB; + double sigmai, sigmaj, epsi, epsj; + gmx_bool bCanDoLBRules, bCanDoGeometricRules; + const char *ptr; + + /* A tolerance of 1e-5 seems reasonable for (possibly hand-typed) + * force-field floating point parameters. + */ + tol = 1e-5; + ptr = getenv("GMX_LJCOMB_TOL"); + if (ptr != NULL) + { + double dbl; + + sscanf(ptr, "%lf", &dbl); + tol = dbl; + } + + *bC6ParametersWorkWithLBRules = TRUE; + *bC6ParametersWorkWithGeometricRules = TRUE; + bCanDoLBRules = TRUE; + bCanDoGeometricRules = TRUE; + ntypes = mtop->ffparams.atnr; + snew(typecount, ntypes); + gmx_mtop_count_atomtypes(mtop, state, typecount); + geometricdiff = LBdiff = 0.0; + *bLBRulesPossible = TRUE; + for (tpi = 0; tpi < ntypes; ++tpi) + { + c6i = mtop->ffparams.iparams[(ntypes + 1) * tpi].lj.c6; + c12i = mtop->ffparams.iparams[(ntypes + 1) * tpi].lj.c12; + for (tpj = tpi; tpj < ntypes; ++tpj) + { + c6j = mtop->ffparams.iparams[(ntypes + 1) * tpj].lj.c6; + c12j = mtop->ffparams.iparams[(ntypes + 1) * tpj].lj.c12; + c6 = mtop->ffparams.iparams[ntypes * tpi + tpj].lj.c6; + c6_geometric = sqrt(c6i * c6j); + if (!gmx_numzero(c6_geometric)) + { + if (!gmx_numzero(c12i) && !gmx_numzero(c12j)) + { + sigmai = pow(c12i / c6i, 1.0/6.0); + sigmaj = pow(c12j / c6j, 1.0/6.0); + epsi = c6i * c6i /(4.0 * c12i); + epsj = c6j * c6j /(4.0 * c12j); + c6_LB = 4.0 * pow(epsi * epsj, 1.0/2.0) * pow(0.5 * (sigmai + sigmaj), 6); + } + else + { + *bLBRulesPossible = FALSE; + c6_LB = c6_geometric; + } + bCanDoLBRules = gmx_within_tol(c6_LB, c6, tol); + } + + if (FALSE == bCanDoLBRules) + { + *bC6ParametersWorkWithLBRules = FALSE; + } + + bCanDoGeometricRules = gmx_within_tol(c6_geometric, c6, tol); + + if (FALSE == bCanDoGeometricRules) + { + *bC6ParametersWorkWithGeometricRules = FALSE; + } + } + } + sfree(typecount); +} + +static void +check_combination_rules(const t_inputrec *ir, const gmx_mtop_t *mtop, + warninp_t wi) +{ + char err_buf[256]; + gmx_bool bLBRulesPossible, bC6ParametersWorkWithGeometricRules, bC6ParametersWorkWithLBRules; + + check_combination_rule_differences(mtop, 0, + &bC6ParametersWorkWithGeometricRules, + &bC6ParametersWorkWithLBRules, + &bLBRulesPossible); + if (ir->ljpme_combination_rule == eljpmeLB) + { + if (FALSE == bC6ParametersWorkWithLBRules || FALSE == bLBRulesPossible) + { + warning(wi, "You are using arithmetic-geometric combination rules " + "in LJ-PME, but your non-bonded C6 parameters do not " + "follow these rules."); + } + } + else + { + if (FALSE == bC6ParametersWorkWithGeometricRules) + { + if (ir->eDispCorr != edispcNO) + { + warning_note(wi, "You are using geometric combination rules in " + "LJ-PME, but your non-bonded C6 parameters do " + "not follow these rules. " + "This will introduce very small errors in the forces and energies in " + "your simulations. Dispersion correction will correct total energy " + "and/or pressure for isotropic systems, but not forces or surface tensions."); + } + else + { + warning_note(wi, "You are using geometric combination rules in " + "LJ-PME, but your non-bonded C6 parameters do " + "not follow these rules. " + "This will introduce very small errors in the forces and energies in " + "your simulations. If your system is homogeneous, consider using dispersion correction " + "for the total energy and pressure."); + } + } + } +} + +void triple_check(const char *mdparin, t_inputrec *ir, gmx_mtop_t *sys, + warninp_t wi) +{ + char err_buf[256]; + int i, m, c, nmol, npct; + gmx_bool bCharge, bAcc; + real gdt_max, *mgrp, mt; + rvec acc; + gmx_mtop_atomloop_block_t aloopb; + gmx_mtop_atomloop_all_t aloop; + t_atom *atom; + ivec AbsRef; + char warn_buf[STRLEN]; + + set_warning_line(wi, mdparin, -1); + + if (EI_DYNAMICS(ir->eI) && !EI_SD(ir->eI) && ir->eI != eiBD && + ir->comm_mode == ecmNO && + !(absolute_reference(ir, sys, FALSE, AbsRef) || ir->nsteps <= 10) && + !ETC_ANDERSEN(ir->etc)) + { + warning(wi, "You are not using center of mass motion removal (mdp option comm-mode), numerical rounding errors can lead to build up of kinetic energy of the center of mass"); + } + + /* Check for pressure coupling with absolute position restraints */ + if (ir->epc != epcNO && ir->refcoord_scaling == erscNO) + { + absolute_reference(ir, sys, TRUE, AbsRef); + { + for (m = 0; m < DIM; m++) + { + if (AbsRef[m] && norm2(ir->compress[m]) > 0) + { + warning(wi, "You are using pressure coupling with absolute position restraints, this will give artifacts. Use the refcoord_scaling option."); + break; + } + } + } + } + + bCharge = FALSE; + aloopb = gmx_mtop_atomloop_block_init(sys); + while (gmx_mtop_atomloop_block_next(aloopb, &atom, &nmol)) + { + if (atom->q != 0 || atom->qB != 0) + { + bCharge = TRUE; + } + } + + if (!bCharge) + { + if (EEL_FULL(ir->coulombtype)) + { + sprintf(err_buf, + "You are using full electrostatics treatment %s for a system without charges.\n" + "This costs a lot of performance for just processing zeros, consider using %s instead.\n", + EELTYPE(ir->coulombtype), EELTYPE(eelCUT)); + warning(wi, err_buf); + } + } + else + { + if (ir->coulombtype == eelCUT && ir->rcoulomb > 0 && !ir->implicit_solvent) + { + sprintf(err_buf, + "You are using a plain Coulomb cut-off, which might produce artifacts.\n" + "You might want to consider using %s electrostatics.\n", + EELTYPE(eelPME)); + warning_note(wi, err_buf); + } + } + + /* Check if combination rules used in LJ-PME are the same as in the force field */ + if (EVDW_PME(ir->vdwtype)) + { + check_combination_rules(ir, sys, wi); + } + + /* Generalized reaction field */ + if (ir->opts.ngtc == 0) + { + sprintf(err_buf, "No temperature coupling while using coulombtype %s", + eel_names[eelGRF]); + CHECK(ir->coulombtype == eelGRF); + } + else + { + sprintf(err_buf, "When using coulombtype = %s" + " ref-t for temperature coupling should be > 0", + eel_names[eelGRF]); + CHECK((ir->coulombtype == eelGRF) && (ir->opts.ref_t[0] <= 0)); + } + + if (ir->eI == eiSD1 && + (gmx_mtop_ftype_count(sys, F_CONSTR) > 0 || + gmx_mtop_ftype_count(sys, F_SETTLE) > 0)) + { + sprintf(warn_buf, "With constraints integrator %s is less accurate, consider using %s instead", ei_names[ir->eI], ei_names[eiSD2]); + warning_note(wi, warn_buf); + } + + bAcc = FALSE; + for (i = 0; (i < sys->groups.grps[egcACC].nr); i++) + { + for (m = 0; (m < DIM); m++) + { + if (fabs(ir->opts.acc[i][m]) > 1e-6) + { + bAcc = TRUE; + } + } + } + if (bAcc) + { + clear_rvec(acc); + snew(mgrp, sys->groups.grps[egcACC].nr); + aloop = gmx_mtop_atomloop_all_init(sys); + while (gmx_mtop_atomloop_all_next(aloop, &i, &atom)) + { + mgrp[ggrpnr(&sys->groups, egcACC, i)] += atom->m; + } + mt = 0.0; + for (i = 0; (i < sys->groups.grps[egcACC].nr); i++) + { + for (m = 0; (m < DIM); m++) + { + acc[m] += ir->opts.acc[i][m]*mgrp[i]; + } + mt += mgrp[i]; + } + for (m = 0; (m < DIM); m++) + { + if (fabs(acc[m]) > 1e-6) + { + const char *dim[DIM] = { "X", "Y", "Z" }; + fprintf(stderr, + "Net Acceleration in %s direction, will %s be corrected\n", + dim[m], ir->nstcomm != 0 ? "" : "not"); + if (ir->nstcomm != 0 && m < ndof_com(ir)) + { + acc[m] /= mt; + for (i = 0; (i < sys->groups.grps[egcACC].nr); i++) + { + ir->opts.acc[i][m] -= acc[m]; + } + } + } + } + sfree(mgrp); + } + + if (ir->efep != efepNO && ir->fepvals->sc_alpha != 0 && + !gmx_within_tol(sys->ffparams.reppow, 12.0, 10*GMX_DOUBLE_EPS)) + { + gmx_fatal(FARGS, "Soft-core interactions are only supported with VdW repulsion power 12"); + } + + if (ir->ePull != epullNO) + { + gmx_bool bPullAbsoluteRef; + + bPullAbsoluteRef = FALSE; + for (i = 0; i < ir->pull->ncoord; i++) + { + bPullAbsoluteRef = bPullAbsoluteRef || + ir->pull->coord[i].group[0] == 0 || + ir->pull->coord[i].group[1] == 0; + } + if (bPullAbsoluteRef) + { + absolute_reference(ir, sys, FALSE, AbsRef); + for (m = 0; m < DIM; m++) + { + if (ir->pull->dim[m] && !AbsRef[m]) + { + warning(wi, "You are using an absolute reference for pulling, but the rest of the system does not have an absolute reference. This will lead to artifacts."); + break; + } + } + } + + if (ir->pull->eGeom == epullgDIRPBC) + { + for (i = 0; i < 3; i++) + { + for (m = 0; m <= i; m++) + { + if ((ir->epc != epcNO && ir->compress[i][m] != 0) || + ir->deform[i][m] != 0) + { + for (c = 0; c < ir->pull->ncoord; c++) + { + if (ir->pull->coord[c].vec[m] != 0) + { + gmx_fatal(FARGS, "Can not have dynamic box while using pull geometry '%s' (dim %c)", EPULLGEOM(ir->pull->eGeom), 'x'+m); + } + } + } + } + } + } + } + + check_disre(sys); +} + +void double_check(t_inputrec *ir, matrix box, gmx_bool bConstr, warninp_t wi) +{ + real min_size; + gmx_bool bTWIN; + char warn_buf[STRLEN]; + const char *ptr; + + ptr = check_box(ir->ePBC, box); + if (ptr) + { + warning_error(wi, ptr); + } + + if (bConstr && ir->eConstrAlg == econtSHAKE) + { + if (ir->shake_tol <= 0.0) + { + sprintf(warn_buf, "ERROR: shake-tol must be > 0 instead of %g\n", + ir->shake_tol); + warning_error(wi, warn_buf); + } + + if (IR_TWINRANGE(*ir) && ir->nstlist > 1) + { + sprintf(warn_buf, "With twin-range cut-off's and SHAKE the virial and the pressure are incorrect."); + if (ir->epc == epcNO) + { + warning(wi, warn_buf); + } + else + { + warning_error(wi, warn_buf); + } + } + } + + if ( (ir->eConstrAlg == econtLINCS) && bConstr) + { + /* If we have Lincs constraints: */ + if (ir->eI == eiMD && ir->etc == etcNO && + ir->eConstrAlg == econtLINCS && ir->nLincsIter == 1) + { + sprintf(warn_buf, "For energy conservation with LINCS, lincs_iter should be 2 or larger.\n"); + warning_note(wi, warn_buf); + } + + if ((ir->eI == eiCG || ir->eI == eiLBFGS) && (ir->nProjOrder < 8)) + { + sprintf(warn_buf, "For accurate %s with LINCS constraints, lincs-order should be 8 or more.", ei_names[ir->eI]); + warning_note(wi, warn_buf); + } + if (ir->epc == epcMTTK) + { + warning_error(wi, "MTTK not compatible with lincs -- use shake instead."); + } + } + ++ if (bConstr && ir->epc == epcMTTK) ++ { ++ warning_note(wi, "MTTK with constraints is deprecated, and will be removed in GROMACS 5.1"); ++ } ++ + if (ir->LincsWarnAngle > 90.0) + { + sprintf(warn_buf, "lincs-warnangle can not be larger than 90 degrees, setting it to 90.\n"); + warning(wi, warn_buf); + ir->LincsWarnAngle = 90.0; + } + + if (ir->ePBC != epbcNONE) + { + if (ir->nstlist == 0) + { + warning(wi, "With nstlist=0 atoms are only put into the box at step 0, therefore drifting atoms might cause the simulation to crash."); + } + bTWIN = (ir->rlistlong > ir->rlist); + if (ir->ns_type == ensGRID) + { + if (sqr(ir->rlistlong) >= max_cutoff2(ir->ePBC, box)) + { + sprintf(warn_buf, "ERROR: The cut-off length is longer than half the shortest box vector or longer than the smallest box diagonal element. Increase the box size or decrease %s.\n", + bTWIN ? (ir->rcoulomb == ir->rlistlong ? "rcoulomb" : "rvdw") : "rlist"); + warning_error(wi, warn_buf); + } + } + else + { + min_size = min(box[XX][XX], min(box[YY][YY], box[ZZ][ZZ])); + if (2*ir->rlistlong >= min_size) + { + sprintf(warn_buf, "ERROR: One of the box lengths is smaller than twice the cut-off length. Increase the box size or decrease rlist."); + warning_error(wi, warn_buf); + if (TRICLINIC(box)) + { + fprintf(stderr, "Grid search might allow larger cut-off's than simple search with triclinic boxes."); + } + } + } + } +} + +void check_chargegroup_radii(const gmx_mtop_t *mtop, const t_inputrec *ir, + rvec *x, + warninp_t wi) +{ + real rvdw1, rvdw2, rcoul1, rcoul2; + char warn_buf[STRLEN]; + + calc_chargegroup_radii(mtop, x, &rvdw1, &rvdw2, &rcoul1, &rcoul2); + + if (rvdw1 > 0) + { + printf("Largest charge group radii for Van der Waals: %5.3f, %5.3f nm\n", + rvdw1, rvdw2); + } + if (rcoul1 > 0) + { + printf("Largest charge group radii for Coulomb: %5.3f, %5.3f nm\n", + rcoul1, rcoul2); + } + + if (ir->rlist > 0) + { + if (rvdw1 + rvdw2 > ir->rlist || + rcoul1 + rcoul2 > ir->rlist) + { + sprintf(warn_buf, + "The sum of the two largest charge group radii (%f) " + "is larger than rlist (%f)\n", + max(rvdw1+rvdw2, rcoul1+rcoul2), ir->rlist); + warning(wi, warn_buf); + } + else + { + /* Here we do not use the zero at cut-off macro, + * since user defined interactions might purposely + * not be zero at the cut-off. + */ + if (ir_vdw_is_zero_at_cutoff(ir) && + rvdw1 + rvdw2 > ir->rlistlong - ir->rvdw) + { + sprintf(warn_buf, "The sum of the two largest charge group " + "radii (%f) is larger than %s (%f) - rvdw (%f).\n" + "With exact cut-offs, better performance can be " + "obtained with cutoff-scheme = %s, because it " + "does not use charge groups at all.", + rvdw1+rvdw2, + ir->rlistlong > ir->rlist ? "rlistlong" : "rlist", + ir->rlistlong, ir->rvdw, + ecutscheme_names[ecutsVERLET]); + if (ir_NVE(ir)) + { + warning(wi, warn_buf); + } + else + { + warning_note(wi, warn_buf); + } + } + if (ir_coulomb_is_zero_at_cutoff(ir) && + rcoul1 + rcoul2 > ir->rlistlong - ir->rcoulomb) + { + sprintf(warn_buf, "The sum of the two largest charge group radii (%f) is larger than %s (%f) - rcoulomb (%f).\n" + "With exact cut-offs, better performance can be obtained with cutoff-scheme = %s, because it does not use charge groups at all.", + rcoul1+rcoul2, + ir->rlistlong > ir->rlist ? "rlistlong" : "rlist", + ir->rlistlong, ir->rcoulomb, + ecutscheme_names[ecutsVERLET]); + if (ir_NVE(ir)) + { + warning(wi, warn_buf); + } + else + { + warning_note(wi, warn_buf); + } + } + } + } +} diff --cc src/gromacs/legacyheaders/domdec.h index 6a0fb7c7ca,0000000000..efeef40614 mode 100644,000000..100644 --- a/src/gromacs/legacyheaders/domdec.h +++ b/src/gromacs/legacyheaders/domdec.h @@@ -1,332 -1,0 +1,332 @@@ +/* + * This file is part of the GROMACS molecular simulation package. + * + * Copyright (c) 2005,2006,2007,2008,2009,2010,2012,2013,2014, by the GROMACS development team, led by + * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl, + * and including many others, as listed in the AUTHORS file in the + * top-level source directory and at http://www.gromacs.org. + * + * GROMACS is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 + * of the License, or (at your option) any later version. + * + * GROMACS is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with GROMACS; if not, see + * http://www.gnu.org/licenses, or write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * If you want to redistribute modifications to GROMACS, please + * consider that scientific software is very special. Version + * control is crucial - bugs must be traceable. We will be happy to + * consider code for inclusion in the official distribution, but + * derived work must not be called official GROMACS. Details are found + * in the README & COPYING files - if they are missing, get the + * official version at http://www.gromacs.org. + * + * To help us fund GROMACS development, we humbly ask that you cite + * the research papers on the package. Check out http://www.gromacs.org. + */ + +#ifndef _domdec_h +#define _domdec_h + +#include "typedefs.h" +#include "vsite.h" +#include "genborn.h" + +#ifdef __cplusplus +extern "C" { +#endif + +int ddglatnr(gmx_domdec_t *dd, int i); +/* Returns the global topology atom number belonging to local atom index i. + * This function is intended for writing ascii output + * and returns atom numbers starting at 1. + * When dd=NULL returns i+1. + */ + +t_block *dd_charge_groups_global(gmx_domdec_t *dd); +/* Return a block struct for the charge groups of the whole system */ + +gmx_bool dd_filled_nsgrid_home(gmx_domdec_t *dd); +/* Is the ns grid already filled with the home particles? */ + +void dd_store_state(gmx_domdec_t *dd, t_state *state); +/* Store the global cg indices of the home cgs in state, + * so it can be reset, even after a new DD partitioning. + */ + +gmx_domdec_zones_t *domdec_zones(gmx_domdec_t *dd); + +void dd_get_ns_ranges(gmx_domdec_t *dd, int icg, + int *jcg0, int *jcg1, ivec shift0, ivec shift1); + +int dd_natoms_vsite(gmx_domdec_t *dd); + +void dd_get_constraint_range(gmx_domdec_t *dd, + int *at_start, int *at_end); + +real dd_cutoff_mbody(gmx_domdec_t *dd); + +real dd_cutoff_twobody(gmx_domdec_t *dd); + +void get_pme_nnodes(const gmx_domdec_t *dd, + int *npmenodes_x, int *npmenodes_y); +/* Get the number of PME nodes along x and y, can be called with dd=NULL */ + +gmx_bool gmx_pmeonlynode(t_commrec *cr, int nodeid); +/* Return if nodeid in cr->mpi_comm_mysim is a PME-only node */ + +void get_pme_ddnodes(t_commrec *cr, int pmenodeid, + int *nmy_ddnodes, int **my_ddnodes, int *node_peer); +/* Returns the set of DD nodes that communicate with pme node cr->nodeid */ + +int dd_pme_maxshift_x(gmx_domdec_t *dd); +/* Returns the maximum shift for coordinate communication in PME, dim x */ + +int dd_pme_maxshift_y(gmx_domdec_t *dd); +/* Returns the maximum shift for coordinate communication in PME, dim y */ + +void make_dd_communicators(FILE *fplog, t_commrec *cr, int dd_node_order); + +gmx_domdec_t * +init_domain_decomposition(FILE *fplog, + t_commrec *cr, + unsigned long Flags, + ivec nc, + real comm_distance_min, real rconstr, + const char *dlb_opt, real dlb_scale, + const char *sizex, const char *sizey, const char *sizez, + gmx_mtop_t *mtop, t_inputrec *ir, + matrix box, rvec *x, + gmx_ddbox_t *ddbox, + int *npme_x, int *npme_y); + +void dd_init_bondeds(FILE *fplog, + gmx_domdec_t *dd, gmx_mtop_t *mtop, + gmx_vsite_t *vsite, + t_inputrec *ir, gmx_bool bBCheck, cginfo_mb_t *cginfo_mb); +/* Initialize data structures for bonded interactions */ + +gmx_bool dd_bonded_molpbc(gmx_domdec_t *dd, int ePBC); +/* Returns if we need to do pbc for calculating bonded interactions */ + +void set_dd_parameters(FILE *fplog, gmx_domdec_t *dd, real dlb_scale, + t_inputrec *ir, + gmx_ddbox_t *ddbox); +/* Set DD grid dimensions and limits, + * should be called after calling dd_init_bondeds. + */ + +gmx_bool change_dd_cutoff(t_commrec *cr, t_state *state, t_inputrec *ir, + real cutoff_req ); +/* Change the DD non-bonded communication cut-off. + * This could fail when trying to increase the cut-off, + * then FALSE will be returned and the cut-off is not modified. + */ + +void change_dd_dlb_cutoff_limit(t_commrec *cr); +/* Domain boundary changes due to the DD dynamic load balancing can limit + * the cut-off distance that can be set in change_dd_cutoff. This function + * limits the DLB such that using the currently set cut-off should still be + * possible after subsequently setting a shorter cut-off with change_dd_cutoff. + */ + +void dd_setup_dlb_resource_sharing(t_commrec *cr, + const gmx_hw_info_t *hwinfo, + const gmx_hw_opt_t *hw_opt); +/* When domains (PP MPI ranks) share a GPU, the individual GPU wait times + * are meaningless, as it depends on the order in which tasks on the same + * GPU finish. Therefore there wait times need to be averaged over the ranks + * sharing the same GPU. This function sets up the communication for that. + */ + +void setup_dd_grid(FILE *fplog, gmx_domdec_t *dd); + +void dd_collect_vec(gmx_domdec_t *dd, + t_state *state_local, rvec *lv, rvec *v); + +void dd_collect_state(gmx_domdec_t *dd, + t_state *state_local, t_state *state); + +enum { + ddCyclStep, ddCyclPPduringPME, ddCyclF, ddCyclWaitGPU, ddCyclPME, ddCyclNr +}; + +void dd_cycles_add(gmx_domdec_t *dd, float cycles, int ddCycl); +/* Add the wallcycle count to the DD counter */ + +void dd_force_flop_start(gmx_domdec_t *dd, t_nrnb *nrnb); +/* Start the force flop count */ + +void dd_force_flop_stop(gmx_domdec_t *dd, t_nrnb *nrnb); +/* Stop the force flop count */ + +float dd_pme_f_ratio(gmx_domdec_t *dd); +/* Return the PME/PP force load ratio, or -1 if nothing was measured. + * Should only be called on the DD master node. + */ + +void dd_move_x(gmx_domdec_t *dd, matrix box, rvec x[]); +/* Communicate the coordinates to the neighboring cells and do pbc. */ + +void dd_move_f(gmx_domdec_t *dd, rvec f[], rvec *fshift); +/* Sum the forces over the neighboring cells. + * When fshift!=NULL the shift forces are updated to obtain + * the correct virial from the single sum including f. + */ + +void dd_atom_spread_real(gmx_domdec_t *dd, real v[]); +/* Communicate a real for each atom to the neighboring cells. */ + +void dd_atom_sum_real(gmx_domdec_t *dd, real v[]); +/* Sum the contributions to a real for each atom over the neighboring cells. */ + +void dd_partition_system(FILE *fplog, + gmx_int64_t step, + t_commrec *cr, + gmx_bool bMasterState, + int nstglobalcomm, + t_state *state_global, + gmx_mtop_t *top_global, + t_inputrec *ir, + t_state *state_local, + rvec **f, + t_mdatoms *mdatoms, + gmx_localtop_t *top_local, + t_forcerec *fr, + gmx_vsite_t *vsite, + gmx_shellfc_t shellfc, + gmx_constr_t constr, + t_nrnb *nrnb, + gmx_wallcycle_t wcycle, + gmx_bool bVerbose); +/* Partition the system over the nodes. + * step is only used for printing error messages. + * If bMasterState==TRUE then state_global from the master node is used, + * else state_local is redistributed between the nodes. + * When f!=NULL, *f will be reallocated to the size of state_local. + */ + +void reset_dd_statistics_counters(gmx_domdec_t *dd); +/* Reset all the statistics and counters for total run counting */ + +void print_dd_statistics(t_commrec *cr, t_inputrec *ir, FILE *fplog); + +/* In domdec_con.c */ + +void dd_move_f_vsites(gmx_domdec_t *dd, rvec *f, rvec *fshift); + +void dd_clear_f_vsites(gmx_domdec_t *dd, rvec *f); + +void dd_move_x_constraints(gmx_domdec_t *dd, matrix box, - rvec *x0, rvec *x1); - /* Move x0 and also x1 if x1!=NULL */ ++ rvec *x0, rvec *x1, gmx_bool bX1IsCoord); ++/* Move x0 and also x1 if x1!=NULL. bX1IsCoord tells if to do PBC on x1 */ + +void dd_move_x_vsites(gmx_domdec_t *dd, matrix box, rvec *x); + +int *dd_constraints_nlocalatoms(gmx_domdec_t *dd); + +void dd_clear_local_constraint_indices(gmx_domdec_t *dd); + +void dd_clear_local_vsite_indices(gmx_domdec_t *dd); + +int dd_make_local_vsites(gmx_domdec_t *dd, int at_start, t_ilist *lil); + +int dd_make_local_constraints(gmx_domdec_t *dd, int at_start, + const gmx_mtop_t *mtop, + const int *cginfo, + gmx_constr_t constr, int nrec, + t_ilist *il_local); + +void init_domdec_constraints(gmx_domdec_t *dd, + gmx_mtop_t *mtop); + +void init_domdec_vsites(gmx_domdec_t *dd, int n_intercg_vsite); + + +/* In domdec_top.c */ + +void dd_print_missing_interactions(FILE *fplog, t_commrec *cr, + int local_count, gmx_mtop_t *top_global, t_state *state_local); + +void dd_make_reverse_top(FILE *fplog, + gmx_domdec_t *dd, gmx_mtop_t *mtop, + gmx_vsite_t *vsite, + t_inputrec *ir, gmx_bool bBCheck); + +void dd_make_local_cgs(gmx_domdec_t *dd, t_block *lcgs); + +void dd_make_local_top(gmx_domdec_t *dd, gmx_domdec_zones_t *zones, + int npbcdim, matrix box, + rvec cellsize_min, ivec npulse, + t_forcerec *fr, + rvec *cgcm_or_x, + gmx_vsite_t *vsite, + gmx_mtop_t *top, gmx_localtop_t *ltop); + +void dd_sort_local_top(gmx_domdec_t *dd, t_mdatoms *mdatoms, + gmx_localtop_t *ltop); +/* Sort ltop->ilist when we are doing free energy. */ + +gmx_localtop_t *dd_init_local_top(gmx_mtop_t *top_global); + +void dd_init_local_state(gmx_domdec_t *dd, + t_state *state_global, t_state *local_state); + +t_blocka *make_charge_group_links(gmx_mtop_t *mtop, gmx_domdec_t *dd, + cginfo_mb_t *cginfo_mb); + +void dd_bonded_cg_distance(FILE *fplog, gmx_mtop_t *mtop, + t_inputrec *ir, rvec *x, matrix box, + gmx_bool bBCheck, + real *r_2b, real *r_mb); + +void write_dd_pdb(const char *fn, gmx_int64_t step, const char *title, + gmx_mtop_t *mtop, + t_commrec *cr, + int natoms, rvec x[], matrix box); +/* Dump a pdb file with the current DD home + communicated atoms. + * When natoms=-1, dump all known atoms. + */ + + +/* In domdec_setup.c */ + +real comm_box_frac(ivec dd_nc, real cutoff, gmx_ddbox_t *ddbox); +/* Returns the volume fraction of the system that is communicated */ + +real dd_choose_grid(FILE *fplog, + t_commrec *cr, gmx_domdec_t *dd, t_inputrec *ir, + gmx_mtop_t *mtop, matrix box, gmx_ddbox_t *ddbox, + gmx_bool bDynLoadBal, real dlb_scale, + real cellsize_limit, real cutoff_dd, + gmx_bool bInterCGBondeds); +/* Determines the optimal DD cell setup dd->nc and possibly npmenodes + * for the system. + * On the master node returns the actual cellsize limit used. + */ + + +/* In domdec_box.c */ + +void set_ddbox(gmx_domdec_t *dd, gmx_bool bMasterState, t_commrec *cr_sum, + t_inputrec *ir, matrix box, + gmx_bool bCalcUnboundedSize, t_block *cgs, rvec *x, + gmx_ddbox_t *ddbox); + +void set_ddbox_cr(t_commrec *cr, ivec *dd_nc, + t_inputrec *ir, matrix box, t_block *cgs, rvec *x, + gmx_ddbox_t *ddbox); + +#ifdef __cplusplus +} +#endif + +#endif /* _domdec_h */ diff --cc src/gromacs/mdlib/clincs.c index ab70d9cc26,0000000000..0476d24151 mode 100644,000000..100644 --- a/src/gromacs/mdlib/clincs.c +++ b/src/gromacs/mdlib/clincs.c @@@ -1,1704 -1,0 +1,1704 @@@ +/* + * This file is part of the GROMACS molecular simulation package. + * + * Copyright (c) 1991-2000, University of Groningen, The Netherlands. + * Copyright (c) 2001-2004, The GROMACS development team. + * Copyright (c) 2013,2014, by the GROMACS development team, led by + * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl, + * and including many others, as listed in the AUTHORS file in the + * top-level source directory and at http://www.gromacs.org. + * + * GROMACS is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 + * of the License, or (at your option) any later version. + * + * GROMACS is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with GROMACS; if not, see + * http://www.gnu.org/licenses, or write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * If you want to redistribute modifications to GROMACS, please + * consider that scientific software is very special. Version + * control is crucial - bugs must be traceable. We will be happy to + * consider code for inclusion in the official distribution, but + * derived work must not be called official GROMACS. Details are found + * in the README & COPYING files - if they are missing, get the + * official version at http://www.gromacs.org. + * + * To help us fund GROMACS development, we humbly ask that you cite + * the research papers on the package. Check out http://www.gromacs.org. + */ +/* This file is completely threadsafe - keep it that way! */ +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include "main.h" +#include "constr.h" +#include "copyrite.h" +#include "physics.h" +#include "vec.h" +#include "pbc.h" +#include "smalloc.h" +#include "mdrun.h" +#include "nrnb.h" +#include "domdec.h" +#include "mtop_util.h" +#include "gmx_omp_nthreads.h" + +#include "gromacs/fileio/gmxfio.h" +#include "gromacs/utility/gmxomp.h" + +typedef struct { + int b0; /* first constraint for this thread */ + int b1; /* b1-1 is the last constraint for this thread */ + int nind; /* number of indices */ + int *ind; /* constraint index for updating atom data */ + int nind_r; /* number of indices */ + int *ind_r; /* constraint index for updating atom data */ + int ind_nalloc; /* allocation size of ind and ind_r */ + tensor vir_r_m_dr; /* temporary variable for virial calculation */ +} lincs_thread_t; + +typedef struct gmx_lincsdata { + int ncg; /* the global number of constraints */ + int ncg_flex; /* the global number of flexible constraints */ + int ncg_triangle; /* the global number of constraints in triangles */ + int nIter; /* the number of iterations */ + int nOrder; /* the order of the matrix expansion */ + int nc; /* the number of constraints */ + int nc_alloc; /* the number we allocated memory for */ + int ncc; /* the number of constraint connections */ + int ncc_alloc; /* the number we allocated memory for */ + real matlam; /* the FE lambda value used for filling blc and blmf */ + real *bllen0; /* the reference distance in topology A */ + real *ddist; /* the reference distance in top B - the r.d. in top A */ + int *bla; /* the atom pairs involved in the constraints */ + real *blc; /* 1/sqrt(invmass1 + invmass2) */ + real *blc1; /* as blc, but with all masses 1 */ + int *blnr; /* index into blbnb and blmf */ + int *blbnb; /* list of constraint connections */ + int ntriangle; /* the local number of constraints in triangles */ + int *triangle; /* the list of triangle constraints */ + int *tri_bits; /* the bits tell if the matrix element should be used */ + int ncc_triangle; /* the number of constraint connections in triangles */ + gmx_bool bCommIter; /* communicate before each LINCS interation */ + real *blmf; /* matrix of mass factors for constraint connections */ + real *blmf1; /* as blmf, but with all masses 1 */ + real *bllen; /* the reference bond length */ + int nth; /* The number of threads doing LINCS */ + lincs_thread_t *th; /* LINCS thread division */ + unsigned *atf; /* atom flags for thread parallelization */ + int atf_nalloc; /* allocation size of atf */ + /* arrays for temporary storage in the LINCS algorithm */ + rvec *tmpv; + real *tmpncc; + real *tmp1; + real *tmp2; + real *tmp3; + real *tmp4; + real *mlambda; /* the Lagrange multipliers * -1 */ + /* storage for the constraint RMS relative deviation output */ + real rmsd_data[3]; +} t_gmx_lincsdata; + +real *lincs_rmsd_data(struct gmx_lincsdata *lincsd) +{ + return lincsd->rmsd_data; +} + +real lincs_rmsd(struct gmx_lincsdata *lincsd, gmx_bool bSD2) +{ + if (lincsd->rmsd_data[0] > 0) + { + return sqrt(lincsd->rmsd_data[bSD2 ? 2 : 1]/lincsd->rmsd_data[0]); + } + else + { + return 0; + } +} + +/* Do a set of nrec LINCS matrix multiplications. + * This function will return with up to date thread-local + * constraint data, without an OpenMP barrier. + */ +static void lincs_matrix_expand(const struct gmx_lincsdata *lincsd, + int b0, int b1, + const real *blcc, + real *rhs1, real *rhs2, real *sol) +{ + int nrec, rec, b, j, n, nr0, nr1; + real mvb, *swap; + int ntriangle, tb, bits; + const int *blnr = lincsd->blnr, *blbnb = lincsd->blbnb; + const int *triangle = lincsd->triangle, *tri_bits = lincsd->tri_bits; + + ntriangle = lincsd->ntriangle; + nrec = lincsd->nOrder; + + for (rec = 0; rec < nrec; rec++) + { +#pragma omp barrier + for (b = b0; b < b1; b++) + { + mvb = 0; + for (n = blnr[b]; n < blnr[b+1]; n++) + { + j = blbnb[n]; + mvb = mvb + blcc[n]*rhs1[j]; + } + rhs2[b] = mvb; + sol[b] = sol[b] + mvb; + } + swap = rhs1; + rhs1 = rhs2; + rhs2 = swap; + } /* nrec*(ncons+2*nrtot) flops */ + + if (ntriangle > 0) + { + /* Perform an extra nrec recursions for only the constraints + * involved in rigid triangles. + * In this way their accuracy should come close to those of the other + * constraints, since traingles of constraints can produce eigenvalues + * around 0.7, while the effective eigenvalue for bond constraints + * is around 0.4 (and 0.7*0.7=0.5). + */ + /* We need to copy the temporary array, since only the elements + * for constraints involved in triangles are updated and then + * the pointers are swapped. This saving copying the whole arrary. + * We need barrier as other threads might still be reading from rhs2. + */ +#pragma omp barrier + for (b = b0; b < b1; b++) + { + rhs2[b] = rhs1[b]; + } +#pragma omp barrier +#pragma omp master + { + for (rec = 0; rec < nrec; rec++) + { + for (tb = 0; tb < ntriangle; tb++) + { + b = triangle[tb]; + bits = tri_bits[tb]; + mvb = 0; + nr0 = blnr[b]; + nr1 = blnr[b+1]; + for (n = nr0; n < nr1; n++) + { + if (bits & (1<<(n-nr0))) + { + j = blbnb[n]; + mvb = mvb + blcc[n]*rhs1[j]; + } + } + rhs2[b] = mvb; + sol[b] = sol[b] + mvb; + } + swap = rhs1; + rhs1 = rhs2; + rhs2 = swap; + } + } /* flops count is missing here */ + + /* We need a barrier here as the calling routine will continue + * to operate on the thread-local constraints without barrier. + */ +#pragma omp barrier + } +} + +static void lincs_update_atoms_noind(int ncons, const int *bla, + real prefac, + const real *fac, rvec *r, + const real *invmass, + rvec *x) +{ + int b, i, j; + real mvb, im1, im2, tmp0, tmp1, tmp2; + + if (invmass != NULL) + { + for (b = 0; b < ncons; b++) + { + i = bla[2*b]; + j = bla[2*b+1]; + mvb = prefac*fac[b]; + im1 = invmass[i]; + im2 = invmass[j]; + tmp0 = r[b][0]*mvb; + tmp1 = r[b][1]*mvb; + tmp2 = r[b][2]*mvb; + x[i][0] -= tmp0*im1; + x[i][1] -= tmp1*im1; + x[i][2] -= tmp2*im1; + x[j][0] += tmp0*im2; + x[j][1] += tmp1*im2; + x[j][2] += tmp2*im2; + } /* 16 ncons flops */ + } + else + { + for (b = 0; b < ncons; b++) + { + i = bla[2*b]; + j = bla[2*b+1]; + mvb = prefac*fac[b]; + tmp0 = r[b][0]*mvb; + tmp1 = r[b][1]*mvb; + tmp2 = r[b][2]*mvb; + x[i][0] -= tmp0; + x[i][1] -= tmp1; + x[i][2] -= tmp2; + x[j][0] += tmp0; + x[j][1] += tmp1; + x[j][2] += tmp2; + } + } +} + +static void lincs_update_atoms_ind(int ncons, const int *ind, const int *bla, + real prefac, + const real *fac, rvec *r, + const real *invmass, + rvec *x) +{ + int bi, b, i, j; + real mvb, im1, im2, tmp0, tmp1, tmp2; + + if (invmass != NULL) + { + for (bi = 0; bi < ncons; bi++) + { + b = ind[bi]; + i = bla[2*b]; + j = bla[2*b+1]; + mvb = prefac*fac[b]; + im1 = invmass[i]; + im2 = invmass[j]; + tmp0 = r[b][0]*mvb; + tmp1 = r[b][1]*mvb; + tmp2 = r[b][2]*mvb; + x[i][0] -= tmp0*im1; + x[i][1] -= tmp1*im1; + x[i][2] -= tmp2*im1; + x[j][0] += tmp0*im2; + x[j][1] += tmp1*im2; + x[j][2] += tmp2*im2; + } /* 16 ncons flops */ + } + else + { + for (bi = 0; bi < ncons; bi++) + { + b = ind[bi]; + i = bla[2*b]; + j = bla[2*b+1]; + mvb = prefac*fac[b]; + tmp0 = r[b][0]*mvb; + tmp1 = r[b][1]*mvb; + tmp2 = r[b][2]*mvb; + x[i][0] -= tmp0; + x[i][1] -= tmp1; + x[i][2] -= tmp2; + x[j][0] += tmp0; + x[j][1] += tmp1; + x[j][2] += tmp2; + } /* 16 ncons flops */ + } +} + +static void lincs_update_atoms(struct gmx_lincsdata *li, int th, + real prefac, + const real *fac, rvec *r, + const real *invmass, + rvec *x) +{ + if (li->nth == 1) + { + /* Single thread, we simply update for all constraints */ + lincs_update_atoms_noind(li->nc, li->bla, prefac, fac, r, invmass, x); + } + else + { + /* Update the atom vector components for our thread local + * constraints that only access our local atom range. + * This can be done without a barrier. + */ + lincs_update_atoms_ind(li->th[th].nind, li->th[th].ind, + li->bla, prefac, fac, r, invmass, x); + + if (li->th[li->nth].nind > 0) + { + /* Update the constraints that operate on atoms + * in multiple thread atom blocks on the master thread. + */ +#pragma omp barrier +#pragma omp master + { + lincs_update_atoms_ind(li->th[li->nth].nind, + li->th[li->nth].ind, + li->bla, prefac, fac, r, invmass, x); + } + } + } +} + +/* LINCS projection, works on derivatives of the coordinates */ +static void do_lincsp(rvec *x, rvec *f, rvec *fp, t_pbc *pbc, + struct gmx_lincsdata *lincsd, int th, + real *invmass, + int econq, real *dvdlambda, + gmx_bool bCalcVir, tensor rmdf) +{ + int b0, b1, b, i, j, k, n; + real tmp0, tmp1, tmp2, im1, im2, mvb, rlen, len, wfac, lam; + rvec dx; + int *bla, *blnr, *blbnb; + rvec *r; + real *blc, *blmf, *blcc, *rhs1, *rhs2, *sol; + + b0 = lincsd->th[th].b0; + b1 = lincsd->th[th].b1; + + bla = lincsd->bla; + r = lincsd->tmpv; + blnr = lincsd->blnr; + blbnb = lincsd->blbnb; + if (econq != econqForce) + { + /* Use mass-weighted parameters */ + blc = lincsd->blc; + blmf = lincsd->blmf; + } + else + { + /* Use non mass-weighted parameters */ + blc = lincsd->blc1; + blmf = lincsd->blmf1; + } + blcc = lincsd->tmpncc; + rhs1 = lincsd->tmp1; + rhs2 = lincsd->tmp2; + sol = lincsd->tmp3; + + /* Compute normalized i-j vectors */ + if (pbc) + { + for (b = b0; b < b1; b++) + { + pbc_dx_aiuc(pbc, x[bla[2*b]], x[bla[2*b+1]], dx); + unitv(dx, r[b]); + } + } + else + { + for (b = b0; b < b1; b++) + { + rvec_sub(x[bla[2*b]], x[bla[2*b+1]], dx); + unitv(dx, r[b]); + } /* 16 ncons flops */ + } + +#pragma omp barrier + for (b = b0; b < b1; b++) + { + tmp0 = r[b][0]; + tmp1 = r[b][1]; + tmp2 = r[b][2]; + i = bla[2*b]; + j = bla[2*b+1]; + for (n = blnr[b]; n < blnr[b+1]; n++) + { + k = blbnb[n]; + blcc[n] = blmf[n]*(tmp0*r[k][0] + tmp1*r[k][1] + tmp2*r[k][2]); + } /* 6 nr flops */ + mvb = blc[b]*(tmp0*(f[i][0] - f[j][0]) + + tmp1*(f[i][1] - f[j][1]) + + tmp2*(f[i][2] - f[j][2])); + rhs1[b] = mvb; + sol[b] = mvb; + /* 7 flops */ + } + /* Together: 23*ncons + 6*nrtot flops */ + + lincs_matrix_expand(lincsd, b0, b1, blcc, rhs1, rhs2, sol); + /* nrec*(ncons+2*nrtot) flops */ + + if (econq == econqDeriv_FlexCon) + { + /* We only want to constraint the flexible constraints, + * so we mask out the normal ones by setting sol to 0. + */ + for (b = b0; b < b1; b++) + { + if (!(lincsd->bllen0[b] == 0 && lincsd->ddist[b] == 0)) + { + sol[b] = 0; + } + } + } + + /* We multiply sol by blc, so we can use lincs_update_atoms for OpenMP */ + for (b = b0; b < b1; b++) + { + sol[b] *= blc[b]; + } + + /* When constraining forces, we should not use mass weighting, + * so we pass invmass=NULL, which results in the use of 1 for all atoms. + */ + lincs_update_atoms(lincsd, th, 1.0, sol, r, + (econq != econqForce) ? invmass : NULL, fp); + + if (dvdlambda != NULL) + { +#pragma omp barrier + for (b = b0; b < b1; b++) + { + *dvdlambda -= sol[b]*lincsd->ddist[b]; + } + /* 10 ncons flops */ + } + + if (bCalcVir) + { + /* Constraint virial, + * determines sum r_bond x delta f, + * where delta f is the constraint correction + * of the quantity that is being constrained. + */ + for (b = b0; b < b1; b++) + { + mvb = lincsd->bllen[b]*sol[b]; + for (i = 0; i < DIM; i++) + { + tmp1 = mvb*r[b][i]; + for (j = 0; j < DIM; j++) + { + rmdf[i][j] += tmp1*r[b][j]; + } + } + } /* 23 ncons flops */ + } +} + +static void do_lincs(rvec *x, rvec *xp, matrix box, t_pbc *pbc, + struct gmx_lincsdata *lincsd, int th, + real *invmass, + t_commrec *cr, + gmx_bool bCalcLambda, + real wangle, int *warn, + real invdt, rvec *v, + gmx_bool bCalcVir, tensor vir_r_m_dr) +{ + int b0, b1, b, i, j, k, n, iter; + real tmp0, tmp1, tmp2, im1, im2, mvb, rlen, len, len2, dlen2, wfac; + rvec dx; + int *bla, *blnr, *blbnb; + rvec *r; + real *blc, *blmf, *bllen, *blcc, *rhs1, *rhs2, *sol, *blc_sol, *mlambda; + int *nlocat; + + b0 = lincsd->th[th].b0; + b1 = lincsd->th[th].b1; + + bla = lincsd->bla; + r = lincsd->tmpv; + blnr = lincsd->blnr; + blbnb = lincsd->blbnb; + blc = lincsd->blc; + blmf = lincsd->blmf; + bllen = lincsd->bllen; + blcc = lincsd->tmpncc; + rhs1 = lincsd->tmp1; + rhs2 = lincsd->tmp2; + sol = lincsd->tmp3; + blc_sol = lincsd->tmp4; + mlambda = lincsd->mlambda; + + if (DOMAINDECOMP(cr) && cr->dd->constraints) + { + nlocat = dd_constraints_nlocalatoms(cr->dd); + } + else + { + nlocat = NULL; + } + + if (pbc) + { + /* Compute normalized i-j vectors */ + for (b = b0; b < b1; b++) + { + pbc_dx_aiuc(pbc, x[bla[2*b]], x[bla[2*b+1]], dx); + unitv(dx, r[b]); + } +#pragma omp barrier + for (b = b0; b < b1; b++) + { + for (n = blnr[b]; n < blnr[b+1]; n++) + { + blcc[n] = blmf[n]*iprod(r[b], r[blbnb[n]]); + } + pbc_dx_aiuc(pbc, xp[bla[2*b]], xp[bla[2*b+1]], dx); + mvb = blc[b]*(iprod(r[b], dx) - bllen[b]); + rhs1[b] = mvb; + sol[b] = mvb; + } + } + else + { + /* Compute normalized i-j vectors */ + for (b = b0; b < b1; b++) + { + i = bla[2*b]; + j = bla[2*b+1]; + tmp0 = x[i][0] - x[j][0]; + tmp1 = x[i][1] - x[j][1]; + tmp2 = x[i][2] - x[j][2]; + rlen = gmx_invsqrt(tmp0*tmp0+tmp1*tmp1+tmp2*tmp2); + r[b][0] = rlen*tmp0; + r[b][1] = rlen*tmp1; + r[b][2] = rlen*tmp2; + } /* 16 ncons flops */ + +#pragma omp barrier + for (b = b0; b < b1; b++) + { + tmp0 = r[b][0]; + tmp1 = r[b][1]; + tmp2 = r[b][2]; + len = bllen[b]; + i = bla[2*b]; + j = bla[2*b+1]; + for (n = blnr[b]; n < blnr[b+1]; n++) + { + k = blbnb[n]; + blcc[n] = blmf[n]*(tmp0*r[k][0] + tmp1*r[k][1] + tmp2*r[k][2]); + } /* 6 nr flops */ + mvb = blc[b]*(tmp0*(xp[i][0] - xp[j][0]) + + tmp1*(xp[i][1] - xp[j][1]) + + tmp2*(xp[i][2] - xp[j][2]) - len); + rhs1[b] = mvb; + sol[b] = mvb; + /* 10 flops */ + } + /* Together: 26*ncons + 6*nrtot flops */ + } + + lincs_matrix_expand(lincsd, b0, b1, blcc, rhs1, rhs2, sol); + /* nrec*(ncons+2*nrtot) flops */ + + for (b = b0; b < b1; b++) + { + mlambda[b] = blc[b]*sol[b]; + } + + /* Update the coordinates */ + lincs_update_atoms(lincsd, th, 1.0, mlambda, r, invmass, xp); + + /* + ******** Correction for centripetal effects ******** + */ + + wfac = cos(DEG2RAD*wangle); + wfac = wfac*wfac; + + for (iter = 0; iter < lincsd->nIter; iter++) + { + if ((lincsd->bCommIter && DOMAINDECOMP(cr) && cr->dd->constraints)) + { +#pragma omp barrier +#pragma omp master + { + /* Communicate the corrected non-local coordinates */ + if (DOMAINDECOMP(cr)) + { - dd_move_x_constraints(cr->dd, box, xp, NULL); ++ dd_move_x_constraints(cr->dd, box, xp, NULL, FALSE); + } + } + } + +#pragma omp barrier + for (b = b0; b < b1; b++) + { + len = bllen[b]; + if (pbc) + { + pbc_dx_aiuc(pbc, xp[bla[2*b]], xp[bla[2*b+1]], dx); + } + else + { + rvec_sub(xp[bla[2*b]], xp[bla[2*b+1]], dx); + } + len2 = len*len; + dlen2 = 2*len2 - norm2(dx); + if (dlen2 < wfac*len2 && (nlocat == NULL || nlocat[b])) + { + *warn = b; + } + if (dlen2 > 0) + { + mvb = blc[b]*(len - dlen2*gmx_invsqrt(dlen2)); + } + else + { + mvb = blc[b]*len; + } + rhs1[b] = mvb; + sol[b] = mvb; + } /* 20*ncons flops */ + + lincs_matrix_expand(lincsd, b0, b1, blcc, rhs1, rhs2, sol); + /* nrec*(ncons+2*nrtot) flops */ + + for (b = b0; b < b1; b++) + { + mvb = blc[b]*sol[b]; + blc_sol[b] = mvb; + mlambda[b] += mvb; + } + + /* Update the coordinates */ + lincs_update_atoms(lincsd, th, 1.0, blc_sol, r, invmass, xp); + } + /* nit*ncons*(37+9*nrec) flops */ + + if (v != NULL) + { + /* Update the velocities */ + lincs_update_atoms(lincsd, th, invdt, mlambda, r, invmass, v); + /* 16 ncons flops */ + } + + if (nlocat != NULL && bCalcLambda) + { + /* In lincs_update_atoms thread might cross-read mlambda */ +#pragma omp barrier + + /* Only account for local atoms */ + for (b = b0; b < b1; b++) + { + mlambda[b] *= 0.5*nlocat[b]; + } + } + + if (bCalcVir) + { + /* Constraint virial */ + for (b = b0; b < b1; b++) + { + tmp0 = -bllen[b]*mlambda[b]; + for (i = 0; i < DIM; i++) + { + tmp1 = tmp0*r[b][i]; + for (j = 0; j < DIM; j++) + { + vir_r_m_dr[i][j] -= tmp1*r[b][j]; + } + } + } /* 22 ncons flops */ + } + + /* Total: + * 26*ncons + 6*nrtot + nrec*(ncons+2*nrtot) + * + nit * (20*ncons + nrec*(ncons+2*nrtot) + 17 ncons) + * + * (26+nrec)*ncons + (6+2*nrec)*nrtot + * + nit * ((37+nrec)*ncons + 2*nrec*nrtot) + * if nit=1 + * (63+nrec)*ncons + (6+4*nrec)*nrtot + */ +} + +void set_lincs_matrix(struct gmx_lincsdata *li, real *invmass, real lambda) +{ + int i, a1, a2, n, k, sign, center; + int end, nk, kk; + const real invsqrt2 = 0.7071067811865475244; + + for (i = 0; (i < li->nc); i++) + { + a1 = li->bla[2*i]; + a2 = li->bla[2*i+1]; + li->blc[i] = gmx_invsqrt(invmass[a1] + invmass[a2]); + li->blc1[i] = invsqrt2; + } + + /* Construct the coupling coefficient matrix blmf */ + li->ntriangle = 0; + li->ncc_triangle = 0; + for (i = 0; (i < li->nc); i++) + { + a1 = li->bla[2*i]; + a2 = li->bla[2*i+1]; + for (n = li->blnr[i]; (n < li->blnr[i+1]); n++) + { + k = li->blbnb[n]; + if (a1 == li->bla[2*k] || a2 == li->bla[2*k+1]) + { + sign = -1; + } + else + { + sign = 1; + } + if (a1 == li->bla[2*k] || a1 == li->bla[2*k+1]) + { + center = a1; + end = a2; + } + else + { + center = a2; + end = a1; + } + li->blmf[n] = sign*invmass[center]*li->blc[i]*li->blc[k]; + li->blmf1[n] = sign*0.5; + if (li->ncg_triangle > 0) + { + /* Look for constraint triangles */ + for (nk = li->blnr[k]; (nk < li->blnr[k+1]); nk++) + { + kk = li->blbnb[nk]; + if (kk != i && kk != k && + (li->bla[2*kk] == end || li->bla[2*kk+1] == end)) + { + if (li->ntriangle == 0 || + li->triangle[li->ntriangle-1] < i) + { + /* Add this constraint to the triangle list */ + li->triangle[li->ntriangle] = i; + li->tri_bits[li->ntriangle] = 0; + li->ntriangle++; + if (li->blnr[i+1] - li->blnr[i] > sizeof(li->tri_bits[0])*8 - 1) + { + gmx_fatal(FARGS, "A constraint is connected to %d constraints, this is more than the %d allowed for constraints participating in triangles", + li->blnr[i+1] - li->blnr[i], + sizeof(li->tri_bits[0])*8-1); + } + } + li->tri_bits[li->ntriangle-1] |= (1<<(n-li->blnr[i])); + li->ncc_triangle++; + } + } + } + } + } + + if (debug) + { + fprintf(debug, "Of the %d constraints %d participate in triangles\n", + li->nc, li->ntriangle); + fprintf(debug, "There are %d couplings of which %d in triangles\n", + li->ncc, li->ncc_triangle); + } + + /* Set matlam, + * so we know with which lambda value the masses have been set. + */ + li->matlam = lambda; +} + +static int count_triangle_constraints(t_ilist *ilist, t_blocka *at2con) +{ + int ncon1, ncon_tot; + int c0, a00, a01, n1, c1, a10, a11, ac1, n2, c2, a20, a21; + int ncon_triangle; + gmx_bool bTriangle; + t_iatom *ia1, *ia2, *iap; + + ncon1 = ilist[F_CONSTR].nr/3; + ncon_tot = ncon1 + ilist[F_CONSTRNC].nr/3; + + ia1 = ilist[F_CONSTR].iatoms; + ia2 = ilist[F_CONSTRNC].iatoms; + + ncon_triangle = 0; + for (c0 = 0; c0 < ncon_tot; c0++) + { + bTriangle = FALSE; + iap = constr_iatomptr(ncon1, ia1, ia2, c0); + a00 = iap[1]; + a01 = iap[2]; + for (n1 = at2con->index[a01]; n1 < at2con->index[a01+1]; n1++) + { + c1 = at2con->a[n1]; + if (c1 != c0) + { + iap = constr_iatomptr(ncon1, ia1, ia2, c1); + a10 = iap[1]; + a11 = iap[2]; + if (a10 == a01) + { + ac1 = a11; + } + else + { + ac1 = a10; + } + for (n2 = at2con->index[ac1]; n2 < at2con->index[ac1+1]; n2++) + { + c2 = at2con->a[n2]; + if (c2 != c0 && c2 != c1) + { + iap = constr_iatomptr(ncon1, ia1, ia2, c2); + a20 = iap[1]; + a21 = iap[2]; + if (a20 == a00 || a21 == a00) + { + bTriangle = TRUE; + } + } + } + } + } + if (bTriangle) + { + ncon_triangle++; + } + } + + return ncon_triangle; +} + +static gmx_bool more_than_two_sequential_constraints(const t_ilist *ilist, + const t_blocka *at2con) +{ + t_iatom *ia1, *ia2, *iap; + int ncon1, ncon_tot, c; + int a1, a2; + gmx_bool bMoreThanTwoSequentialConstraints; + + ncon1 = ilist[F_CONSTR].nr/3; + ncon_tot = ncon1 + ilist[F_CONSTRNC].nr/3; + + ia1 = ilist[F_CONSTR].iatoms; + ia2 = ilist[F_CONSTRNC].iatoms; + + bMoreThanTwoSequentialConstraints = FALSE; + for (c = 0; c < ncon_tot && !bMoreThanTwoSequentialConstraints; c++) + { + iap = constr_iatomptr(ncon1, ia1, ia2, c); + a1 = iap[1]; + a2 = iap[2]; + /* Check if this constraint has constraints connected at both atoms */ + if (at2con->index[a1+1] - at2con->index[a1] > 1 && + at2con->index[a2+1] - at2con->index[a2] > 1) + { + bMoreThanTwoSequentialConstraints = TRUE; + } + } + + return bMoreThanTwoSequentialConstraints; +} + +static int int_comp(const void *a, const void *b) +{ + return (*(int *)a) - (*(int *)b); +} + +gmx_lincsdata_t init_lincs(FILE *fplog, gmx_mtop_t *mtop, + int nflexcon_global, t_blocka *at2con, + gmx_bool bPLINCS, int nIter, int nProjOrder) +{ + struct gmx_lincsdata *li; + int mb; + gmx_moltype_t *molt; + + if (fplog) + { + fprintf(fplog, "\nInitializing%s LINear Constraint Solver\n", + bPLINCS ? " Parallel" : ""); + } + + snew(li, 1); + + li->ncg = + gmx_mtop_ftype_count(mtop, F_CONSTR) + + gmx_mtop_ftype_count(mtop, F_CONSTRNC); + li->ncg_flex = nflexcon_global; + + li->nIter = nIter; + li->nOrder = nProjOrder; + + li->ncg_triangle = 0; + li->bCommIter = FALSE; + for (mb = 0; mb < mtop->nmolblock; mb++) + { + molt = &mtop->moltype[mtop->molblock[mb].type]; + li->ncg_triangle += + mtop->molblock[mb].nmol* + count_triangle_constraints(molt->ilist, + &at2con[mtop->molblock[mb].type]); + if (bPLINCS && li->bCommIter == FALSE) + { + /* Check if we need to communicate not only before LINCS, + * but also before each iteration. + * The check for only two sequential constraints is only + * useful for the common case of H-bond only constraints. + * With more effort we could also make it useful for small + * molecules with nr. sequential constraints <= nOrder-1. + */ + li->bCommIter = (li->nOrder < 1 || more_than_two_sequential_constraints(molt->ilist, &at2con[mtop->molblock[mb].type])); + } + } + if (debug && bPLINCS) + { + fprintf(debug, "PLINCS communication before each iteration: %d\n", + li->bCommIter); + } + + /* LINCS can run on any number of threads. + * Currently the number is fixed for the whole simulation, + * but it could be set in set_lincs(). + */ + li->nth = gmx_omp_nthreads_get(emntLINCS); + if (li->nth == 1) + { + snew(li->th, 1); + } + else + { + /* Allocate an extra elements for "thread-overlap" constraints */ + snew(li->th, li->nth+1); + } + if (debug) + { + fprintf(debug, "LINCS: using %d threads\n", li->nth); + } + + if (bPLINCS || li->ncg_triangle > 0) + { + please_cite(fplog, "Hess2008a"); + } + else + { + please_cite(fplog, "Hess97a"); + } + + if (fplog) + { + fprintf(fplog, "The number of constraints is %d\n", li->ncg); + if (bPLINCS) + { + fprintf(fplog, "There are inter charge-group constraints,\n" + "will communicate selected coordinates each lincs iteration\n"); + } + if (li->ncg_triangle > 0) + { + fprintf(fplog, + "%d constraints are involved in constraint triangles,\n" + "will apply an additional matrix expansion of order %d for couplings\n" + "between constraints inside triangles\n", + li->ncg_triangle, li->nOrder); + } + } + + return li; +} + +/* Sets up the work division over the threads */ +static void lincs_thread_setup(struct gmx_lincsdata *li, int natoms) +{ + lincs_thread_t *li_m; + int th; + unsigned *atf; + int a; + + if (natoms > li->atf_nalloc) + { + li->atf_nalloc = over_alloc_large(natoms); + srenew(li->atf, li->atf_nalloc); + } + + atf = li->atf; + /* Clear the atom flags */ + for (a = 0; a < natoms; a++) + { + atf[a] = 0; + } + + for (th = 0; th < li->nth; th++) + { + lincs_thread_t *li_th; + int b; + + li_th = &li->th[th]; + + /* The constraints are divided equally over the threads */ + li_th->b0 = (li->nc* th )/li->nth; + li_th->b1 = (li->nc*(th+1))/li->nth; + + if (th < sizeof(*atf)*8) + { + /* For each atom set a flag for constraints from each */ + for (b = li_th->b0; b < li_th->b1; b++) + { + atf[li->bla[b*2] ] |= (1U<bla[b*2+1]] |= (1U<nth) schedule(static) + for (th = 0; th < li->nth; th++) + { + lincs_thread_t *li_th; + unsigned mask; + int b; + + li_th = &li->th[th]; + + if (li_th->b1 - li_th->b0 > li_th->ind_nalloc) + { + li_th->ind_nalloc = over_alloc_large(li_th->b1-li_th->b0); + srenew(li_th->ind, li_th->ind_nalloc); + srenew(li_th->ind_r, li_th->ind_nalloc); + } + + if (th < sizeof(*atf)*8) + { + mask = (1U<nind = 0; + li_th->nind_r = 0; + for (b = li_th->b0; b < li_th->b1; b++) + { + /* We let the constraint with the lowest thread index + * operate on atoms with constraints from multiple threads. + */ + if (((atf[li->bla[b*2]] & mask) == 0) && + ((atf[li->bla[b*2+1]] & mask) == 0)) + { + /* Add the constraint to the local atom update index */ + li_th->ind[li_th->nind++] = b; + } + else + { + /* Add the constraint to the rest block */ + li_th->ind_r[li_th->nind_r++] = b; + } + } + } + else + { + /* We are out of bits, assign all constraints to rest */ + for (b = li_th->b0; b < li_th->b1; b++) + { + li_th->ind_r[li_th->nind_r++] = b; + } + } + } + + /* We need to copy all constraints which have not be assigned + * to a thread to a separate list which will be handled by one thread. + */ + li_m = &li->th[li->nth]; + + li_m->nind = 0; + for (th = 0; th < li->nth; th++) + { + lincs_thread_t *li_th; + int b; + + li_th = &li->th[th]; + + if (li_m->nind + li_th->nind_r > li_m->ind_nalloc) + { + li_m->ind_nalloc = over_alloc_large(li_m->nind+li_th->nind_r); + srenew(li_m->ind, li_m->ind_nalloc); + } + + for (b = 0; b < li_th->nind_r; b++) + { + li_m->ind[li_m->nind++] = li_th->ind_r[b]; + } + + if (debug) + { + fprintf(debug, "LINCS thread %d: %d constraints\n", + th, li_th->nind); + } + } + + if (debug) + { + fprintf(debug, "LINCS thread r: %d constraints\n", + li_m->nind); + } +} + + +void set_lincs(t_idef *idef, t_mdatoms *md, + gmx_bool bDynamics, t_commrec *cr, + struct gmx_lincsdata *li) +{ + int start, natoms, nflexcon; + t_blocka at2con; + t_iatom *iatom; + int i, k, ncc_alloc, ni, con, nconnect, concon; + int type, a1, a2; + real lenA = 0, lenB; + gmx_bool bLocal; + + li->nc = 0; + li->ncc = 0; + /* Zero the thread index ranges. + * Otherwise without local constraints we could return with old ranges. + */ + for (i = 0; i < li->nth; i++) + { + li->th[i].b0 = 0; + li->th[i].b1 = 0; + li->th[i].nind = 0; + } + if (li->nth > 1) + { + li->th[li->nth].nind = 0; + } + + /* This is the local topology, so there are only F_CONSTR constraints */ + if (idef->il[F_CONSTR].nr == 0) + { + /* There are no constraints, + * we do not need to fill any data structures. + */ + return; + } + + if (debug) + { + fprintf(debug, "Building the LINCS connectivity\n"); + } + + if (DOMAINDECOMP(cr)) + { + if (cr->dd->constraints) + { + dd_get_constraint_range(cr->dd, &start, &natoms); + } + else + { + natoms = cr->dd->nat_home; + } + start = 0; + } + else + { + start = 0; + natoms = md->homenr; + } + at2con = make_at2con(start, natoms, idef->il, idef->iparams, bDynamics, + &nflexcon); + + + if (idef->il[F_CONSTR].nr/3 > li->nc_alloc || li->nc_alloc == 0) + { + li->nc_alloc = over_alloc_dd(idef->il[F_CONSTR].nr/3); + srenew(li->bllen0, li->nc_alloc); + srenew(li->ddist, li->nc_alloc); + srenew(li->bla, 2*li->nc_alloc); + srenew(li->blc, li->nc_alloc); + srenew(li->blc1, li->nc_alloc); + srenew(li->blnr, li->nc_alloc+1); + srenew(li->bllen, li->nc_alloc); + srenew(li->tmpv, li->nc_alloc); + srenew(li->tmp1, li->nc_alloc); + srenew(li->tmp2, li->nc_alloc); + srenew(li->tmp3, li->nc_alloc); + srenew(li->tmp4, li->nc_alloc); + srenew(li->mlambda, li->nc_alloc); + if (li->ncg_triangle > 0) + { + /* This is allocating too much, but it is difficult to improve */ + srenew(li->triangle, li->nc_alloc); + srenew(li->tri_bits, li->nc_alloc); + } + } + + iatom = idef->il[F_CONSTR].iatoms; + + ncc_alloc = li->ncc_alloc; + li->blnr[0] = 0; + + ni = idef->il[F_CONSTR].nr/3; + + con = 0; + nconnect = 0; + li->blnr[con] = nconnect; + for (i = 0; i < ni; i++) + { + bLocal = TRUE; + type = iatom[3*i]; + a1 = iatom[3*i+1]; + a2 = iatom[3*i+2]; + lenA = idef->iparams[type].constr.dA; + lenB = idef->iparams[type].constr.dB; + /* Skip the flexible constraints when not doing dynamics */ + if (bDynamics || lenA != 0 || lenB != 0) + { + li->bllen0[con] = lenA; + li->ddist[con] = lenB - lenA; + /* Set the length to the topology A length */ + li->bllen[con] = li->bllen0[con]; + li->bla[2*con] = a1; + li->bla[2*con+1] = a2; + /* Construct the constraint connection matrix blbnb */ + for (k = at2con.index[a1-start]; k < at2con.index[a1-start+1]; k++) + { + concon = at2con.a[k]; + if (concon != i) + { + if (nconnect >= ncc_alloc) + { + ncc_alloc = over_alloc_small(nconnect+1); + srenew(li->blbnb, ncc_alloc); + } + li->blbnb[nconnect++] = concon; + } + } + for (k = at2con.index[a2-start]; k < at2con.index[a2-start+1]; k++) + { + concon = at2con.a[k]; + if (concon != i) + { + if (nconnect+1 > ncc_alloc) + { + ncc_alloc = over_alloc_small(nconnect+1); + srenew(li->blbnb, ncc_alloc); + } + li->blbnb[nconnect++] = concon; + } + } + li->blnr[con+1] = nconnect; + + if (cr->dd == NULL) + { + /* Order the blbnb matrix to optimize memory access */ + qsort(&(li->blbnb[li->blnr[con]]), li->blnr[con+1]-li->blnr[con], + sizeof(li->blbnb[0]), int_comp); + } + /* Increase the constraint count */ + con++; + } + } + + done_blocka(&at2con); + + /* This is the real number of constraints, + * without dynamics the flexible constraints are not present. + */ + li->nc = con; + + li->ncc = li->blnr[con]; + if (cr->dd == NULL) + { + /* Since the matrix is static, we can free some memory */ + ncc_alloc = li->ncc; + srenew(li->blbnb, ncc_alloc); + } + + if (ncc_alloc > li->ncc_alloc) + { + li->ncc_alloc = ncc_alloc; + srenew(li->blmf, li->ncc_alloc); + srenew(li->blmf1, li->ncc_alloc); + srenew(li->tmpncc, li->ncc_alloc); + } + + if (debug) + { + fprintf(debug, "Number of constraints is %d, couplings %d\n", + li->nc, li->ncc); + } + + if (li->nth == 1) + { + li->th[0].b0 = 0; + li->th[0].b1 = li->nc; + } + else + { + lincs_thread_setup(li, md->nr); + } + + set_lincs_matrix(li, md->invmass, md->lambda); +} + +static void lincs_warning(FILE *fplog, + gmx_domdec_t *dd, rvec *x, rvec *xprime, t_pbc *pbc, + int ncons, int *bla, real *bllen, real wangle, + int maxwarn, int *warncount) +{ + int b, i, j; + rvec v0, v1; + real wfac, d0, d1, cosine; + char buf[STRLEN]; + + wfac = cos(DEG2RAD*wangle); + + sprintf(buf, "bonds that rotated more than %g degrees:\n" + " atom 1 atom 2 angle previous, current, constraint length\n", + wangle); + fprintf(stderr, "%s", buf); + if (fplog) + { + fprintf(fplog, "%s", buf); + } + + for (b = 0; b < ncons; b++) + { + i = bla[2*b]; + j = bla[2*b+1]; + if (pbc) + { + pbc_dx_aiuc(pbc, x[i], x[j], v0); + pbc_dx_aiuc(pbc, xprime[i], xprime[j], v1); + } + else + { + rvec_sub(x[i], x[j], v0); + rvec_sub(xprime[i], xprime[j], v1); + } + d0 = norm(v0); + d1 = norm(v1); + cosine = iprod(v0, v1)/(d0*d1); + if (cosine < wfac) + { + sprintf(buf, " %6d %6d %5.1f %8.4f %8.4f %8.4f\n", + ddglatnr(dd, i), ddglatnr(dd, j), + RAD2DEG*acos(cosine), d0, d1, bllen[b]); + fprintf(stderr, "%s", buf); + if (fplog) + { + fprintf(fplog, "%s", buf); + } + if (!gmx_isfinite(d1)) + { + gmx_fatal(FARGS, "Bond length not finite."); + } + + (*warncount)++; + } + } + if (*warncount > maxwarn) + { + too_many_constraint_warnings(econtLINCS, *warncount); + } +} + +static void cconerr(gmx_domdec_t *dd, + int ncons, int *bla, real *bllen, rvec *x, t_pbc *pbc, + real *ncons_loc, real *ssd, real *max, int *imax) +{ + real len, d, ma, ssd2, r2; + int *nlocat, count, b, im; + rvec dx; + + if (dd && dd->constraints) + { + nlocat = dd_constraints_nlocalatoms(dd); + } + else + { + nlocat = 0; + } + + ma = 0; + ssd2 = 0; + im = 0; + count = 0; + for (b = 0; b < ncons; b++) + { + if (pbc) + { + pbc_dx_aiuc(pbc, x[bla[2*b]], x[bla[2*b+1]], dx); + } + else + { + rvec_sub(x[bla[2*b]], x[bla[2*b+1]], dx); + } + r2 = norm2(dx); + len = r2*gmx_invsqrt(r2); + d = fabs(len/bllen[b]-1); + if (d > ma && (nlocat == NULL || nlocat[b])) + { + ma = d; + im = b; + } + if (nlocat == NULL) + { + ssd2 += d*d; + count++; + } + else + { + ssd2 += nlocat[b]*d*d; + count += nlocat[b]; + } + } + + *ncons_loc = (nlocat ? 0.5 : 1)*count; + *ssd = (nlocat ? 0.5 : 1)*ssd2; + *max = ma; + *imax = im; +} + +gmx_bool constrain_lincs(FILE *fplog, gmx_bool bLog, gmx_bool bEner, + t_inputrec *ir, + gmx_int64_t step, + struct gmx_lincsdata *lincsd, t_mdatoms *md, + t_commrec *cr, + rvec *x, rvec *xprime, rvec *min_proj, + matrix box, t_pbc *pbc, + real lambda, real *dvdlambda, + real invdt, rvec *v, + gmx_bool bCalcVir, tensor vir_r_m_dr, + int econq, + t_nrnb *nrnb, + int maxwarn, int *warncount) +{ + char buf[STRLEN], buf2[22], buf3[STRLEN]; + int i, warn, p_imax, error; + real ncons_loc, p_ssd, p_max = 0; + rvec dx; + gmx_bool bOK; + + bOK = TRUE; + + if (lincsd->nc == 0 && cr->dd == NULL) + { + if (bLog || bEner) + { + lincsd->rmsd_data[0] = 0; + if (ir->eI == eiSD2 && v == NULL) + { + i = 2; + } + else + { + i = 1; + } + lincsd->rmsd_data[i] = 0; + } + + return bOK; + } + + if (econq == econqCoord) + { + if (ir->efep != efepNO) + { + if (md->nMassPerturbed && lincsd->matlam != md->lambda) + { + set_lincs_matrix(lincsd, md->invmass, md->lambda); + } + + for (i = 0; i < lincsd->nc; i++) + { + lincsd->bllen[i] = lincsd->bllen0[i] + lambda*lincsd->ddist[i]; + } + } + + if (lincsd->ncg_flex) + { + /* Set the flexible constraint lengths to the old lengths */ + if (pbc != NULL) + { + for (i = 0; i < lincsd->nc; i++) + { + if (lincsd->bllen[i] == 0) + { + pbc_dx_aiuc(pbc, x[lincsd->bla[2*i]], x[lincsd->bla[2*i+1]], dx); + lincsd->bllen[i] = norm(dx); + } + } + } + else + { + for (i = 0; i < lincsd->nc; i++) + { + if (lincsd->bllen[i] == 0) + { + lincsd->bllen[i] = + sqrt(distance2(x[lincsd->bla[2*i]], + x[lincsd->bla[2*i+1]])); + } + } + } + } + + if (bLog && fplog) + { + cconerr(cr->dd, lincsd->nc, lincsd->bla, lincsd->bllen, xprime, pbc, + &ncons_loc, &p_ssd, &p_max, &p_imax); + } + + /* This warn var can be updated by multiple threads + * at the same time. But as we only need to detect + * if a warning occured or not, this is not an issue. + */ + warn = -1; + + /* The OpenMP parallel region of constrain_lincs for coords */ +#pragma omp parallel num_threads(lincsd->nth) + { + int th = gmx_omp_get_thread_num(); + + clear_mat(lincsd->th[th].vir_r_m_dr); + + do_lincs(x, xprime, box, pbc, lincsd, th, + md->invmass, cr, + bCalcVir || (ir->efep != efepNO), + ir->LincsWarnAngle, &warn, + invdt, v, bCalcVir, + th == 0 ? vir_r_m_dr : lincsd->th[th].vir_r_m_dr); + } + + if (ir->efep != efepNO) + { + real dt_2, dvdl = 0; + + dt_2 = 1.0/(ir->delta_t*ir->delta_t); + for (i = 0; (i < lincsd->nc); i++) + { + dvdl -= lincsd->mlambda[i]*dt_2*lincsd->ddist[i]; + } + *dvdlambda += dvdl; + } + + if (bLog && fplog && lincsd->nc > 0) + { + fprintf(fplog, " Rel. Constraint Deviation: RMS MAX between atoms\n"); + fprintf(fplog, " Before LINCS %.6f %.6f %6d %6d\n", + sqrt(p_ssd/ncons_loc), p_max, + ddglatnr(cr->dd, lincsd->bla[2*p_imax]), + ddglatnr(cr->dd, lincsd->bla[2*p_imax+1])); + } + if (bLog || bEner) + { + cconerr(cr->dd, lincsd->nc, lincsd->bla, lincsd->bllen, xprime, pbc, + &ncons_loc, &p_ssd, &p_max, &p_imax); + /* Check if we are doing the second part of SD */ + if (ir->eI == eiSD2 && v == NULL) + { + i = 2; + } + else + { + i = 1; + } + lincsd->rmsd_data[0] = ncons_loc; + lincsd->rmsd_data[i] = p_ssd; + } + else + { + lincsd->rmsd_data[0] = 0; + lincsd->rmsd_data[1] = 0; + lincsd->rmsd_data[2] = 0; + } + if (bLog && fplog && lincsd->nc > 0) + { + fprintf(fplog, + " After LINCS %.6f %.6f %6d %6d\n\n", + sqrt(p_ssd/ncons_loc), p_max, + ddglatnr(cr->dd, lincsd->bla[2*p_imax]), + ddglatnr(cr->dd, lincsd->bla[2*p_imax+1])); + } + + if (warn >= 0) + { + if (maxwarn >= 0) + { + cconerr(cr->dd, lincsd->nc, lincsd->bla, lincsd->bllen, xprime, pbc, + &ncons_loc, &p_ssd, &p_max, &p_imax); + if (MULTISIM(cr)) + { + sprintf(buf3, " in simulation %d", cr->ms->sim); + } + else + { + buf3[0] = 0; + } + sprintf(buf, "\nStep %s, time %g (ps) LINCS WARNING%s\n" + "relative constraint deviation after LINCS:\n" + "rms %.6f, max %.6f (between atoms %d and %d)\n", + gmx_step_str(step, buf2), ir->init_t+step*ir->delta_t, + buf3, + sqrt(p_ssd/ncons_loc), p_max, + ddglatnr(cr->dd, lincsd->bla[2*p_imax]), + ddglatnr(cr->dd, lincsd->bla[2*p_imax+1])); + if (fplog) + { + fprintf(fplog, "%s", buf); + } + fprintf(stderr, "%s", buf); + lincs_warning(fplog, cr->dd, x, xprime, pbc, + lincsd->nc, lincsd->bla, lincsd->bllen, + ir->LincsWarnAngle, maxwarn, warncount); + } + bOK = (p_max < 0.5); + } + + if (lincsd->ncg_flex) + { + for (i = 0; (i < lincsd->nc); i++) + { + if (lincsd->bllen0[i] == 0 && lincsd->ddist[i] == 0) + { + lincsd->bllen[i] = 0; + } + } + } + } + else + { + /* The OpenMP parallel region of constrain_lincs for derivatives */ +#pragma omp parallel num_threads(lincsd->nth) + { + int th = gmx_omp_get_thread_num(); + + do_lincsp(x, xprime, min_proj, pbc, lincsd, th, + md->invmass, econq, ir->efep != efepNO ? dvdlambda : NULL, + bCalcVir, th == 0 ? vir_r_m_dr : lincsd->th[th].vir_r_m_dr); + } + } + + if (bCalcVir && lincsd->nth > 1) + { + for (i = 1; i < lincsd->nth; i++) + { + m_add(vir_r_m_dr, lincsd->th[i].vir_r_m_dr, vir_r_m_dr); + } + } + + /* count assuming nit=1 */ + inc_nrnb(nrnb, eNR_LINCS, lincsd->nc); + inc_nrnb(nrnb, eNR_LINCSMAT, (2+lincsd->nOrder)*lincsd->ncc); + if (lincsd->ntriangle > 0) + { + inc_nrnb(nrnb, eNR_LINCSMAT, lincsd->nOrder*lincsd->ncc_triangle); + } + if (v) + { + inc_nrnb(nrnb, eNR_CONSTR_V, lincsd->nc*2); + } + if (bCalcVir) + { + inc_nrnb(nrnb, eNR_CONSTR_VIR, lincsd->nc); + } + + return bOK; +} diff --cc src/gromacs/mdlib/constr.c index 4f01f54654,0000000000..a237a08457 mode 100644,000000..100644 --- a/src/gromacs/mdlib/constr.c +++ b/src/gromacs/mdlib/constr.c @@@ -1,1476 -1,0 +1,1476 @@@ +/* + * This file is part of the GROMACS molecular simulation package. + * + * Copyright (c) 1991-2000, University of Groningen, The Netherlands. + * Copyright (c) 2001-2004, The GROMACS development team. + * Copyright (c) 2013,2014, by the GROMACS development team, led by + * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl, + * and including many others, as listed in the AUTHORS file in the + * top-level source directory and at http://www.gromacs.org. + * + * GROMACS is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 + * of the License, or (at your option) any later version. + * + * GROMACS is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with GROMACS; if not, see + * http://www.gnu.org/licenses, or write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * If you want to redistribute modifications to GROMACS, please + * consider that scientific software is very special. Version + * control is crucial - bugs must be traceable. We will be happy to + * consider code for inclusion in the official distribution, but + * derived work must not be called official GROMACS. Details are found + * in the README & COPYING files - if they are missing, get the + * official version at http://www.gromacs.org. + * + * To help us fund GROMACS development, we humbly ask that you cite + * the research papers on the package. Check out http://www.gromacs.org. + */ +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "gromacs/fileio/confio.h" +#include "types/commrec.h" +#include "constr.h" +#include "copyrite.h" +#include "invblock.h" +#include "main.h" +#include "mdrun.h" +#include "nrnb.h" +#include "smalloc.h" +#include "vec.h" +#include "physics.h" +#include "names.h" +#include "txtdump.h" +#include "domdec.h" +#include "gromacs/fileio/pdbio.h" +#include "splitter.h" +#include "mtop_util.h" +#include "gromacs/fileio/gmxfio.h" +#include "macros.h" +#include "gmx_omp_nthreads.h" +#include "gromacs/essentialdynamics/edsam.h" +#include "gromacs/pulling/pull.h" + + +typedef struct gmx_constr { + int ncon_tot; /* The total number of constraints */ + int nflexcon; /* The number of flexible constraints */ + int n_at2con_mt; /* The size of at2con = #moltypes */ + t_blocka *at2con_mt; /* A list of atoms to constraints */ + int n_at2settle_mt; /* The size of at2settle = #moltypes */ + int **at2settle_mt; /* A list of atoms to settles */ + gmx_bool bInterCGsettles; + gmx_lincsdata_t lincsd; /* LINCS data */ + gmx_shakedata_t shaked; /* SHAKE data */ + gmx_settledata_t settled; /* SETTLE data */ + int nblocks; /* The number of SHAKE blocks */ + int *sblock; /* The SHAKE blocks */ + int sblock_nalloc; /* The allocation size of sblock */ + real *lagr; /* Lagrange multipliers for SHAKE */ + int lagr_nalloc; /* The allocation size of lagr */ + int maxwarn; /* The maximum number of warnings */ + int warncount_lincs; + int warncount_settle; + gmx_edsam_t ed; /* The essential dynamics data */ + + tensor *vir_r_m_dr_th; /* Thread local working data */ + int *settle_error; /* Thread local working data */ + + gmx_mtop_t *warn_mtop; /* Only used for printing warnings */ +} t_gmx_constr; + +typedef struct { + atom_id iatom[3]; + atom_id blocknr; +} t_sortblock; + +static void *init_vetavars(t_vetavars *vars, + gmx_bool constr_deriv, + real veta, real vetanew, t_inputrec *ir, gmx_ekindata_t *ekind, gmx_bool bPscal) +{ + double g; + int i; + + /* first, set the alpha integrator variable */ + if ((ir->opts.nrdf[0] > 0) && bPscal) + { + vars->alpha = 1.0 + DIM/((double)ir->opts.nrdf[0]); + } + else + { + vars->alpha = 1.0; + } + g = 0.5*veta*ir->delta_t; + vars->rscale = exp(g)*series_sinhx(g); + g = -0.25*vars->alpha*veta*ir->delta_t; + vars->vscale = exp(g)*series_sinhx(g); + vars->rvscale = vars->vscale*vars->rscale; + vars->veta = vetanew; + + if (constr_deriv) + { + snew(vars->vscale_nhc, ir->opts.ngtc); + if ((ekind == NULL) || (!bPscal)) + { + for (i = 0; i < ir->opts.ngtc; i++) + { + vars->vscale_nhc[i] = 1; + } + } + else + { + for (i = 0; i < ir->opts.ngtc; i++) + { + vars->vscale_nhc[i] = ekind->tcstat[i].vscale_nhc; + } + } + } + else + { + vars->vscale_nhc = NULL; + } + + return vars; +} + +static void free_vetavars(t_vetavars *vars) +{ + if (vars->vscale_nhc != NULL) + { + sfree(vars->vscale_nhc); + } +} + +static int pcomp(const void *p1, const void *p2) +{ + int db; + atom_id min1, min2, max1, max2; + t_sortblock *a1 = (t_sortblock *)p1; + t_sortblock *a2 = (t_sortblock *)p2; + + db = a1->blocknr-a2->blocknr; + + if (db != 0) + { + return db; + } + + min1 = min(a1->iatom[1], a1->iatom[2]); + max1 = max(a1->iatom[1], a1->iatom[2]); + min2 = min(a2->iatom[1], a2->iatom[2]); + max2 = max(a2->iatom[1], a2->iatom[2]); + + if (min1 == min2) + { + return max1-max2; + } + else + { + return min1-min2; + } +} + +int n_flexible_constraints(struct gmx_constr *constr) +{ + int nflexcon; + + if (constr) + { + nflexcon = constr->nflexcon; + } + else + { + nflexcon = 0; + } + + return nflexcon; +} + +void too_many_constraint_warnings(int eConstrAlg, int warncount) +{ + const char *abort = "- aborting to avoid logfile runaway.\n" + "This normally happens when your system is not sufficiently equilibrated," + "or if you are changing lambda too fast in free energy simulations.\n"; + + gmx_fatal(FARGS, + "Too many %s warnings (%d)\n" + "If you know what you are doing you can %s" + "set the environment variable GMX_MAXCONSTRWARN to -1,\n" + "but normally it is better to fix the problem", + (eConstrAlg == econtLINCS) ? "LINCS" : "SETTLE", warncount, + (eConstrAlg == econtLINCS) ? + "adjust the lincs warning threshold in your mdp file\nor " : "\n"); +} + +static void write_constr_pdb(const char *fn, const char *title, + gmx_mtop_t *mtop, + int start, int homenr, t_commrec *cr, + rvec x[], matrix box) +{ + char fname[STRLEN], format[STRLEN]; + FILE *out; + int dd_ac0 = 0, dd_ac1 = 0, i, ii, resnr; + gmx_domdec_t *dd; + char *anm, *resnm; + + dd = NULL; + if (DOMAINDECOMP(cr)) + { + dd = cr->dd; + dd_get_constraint_range(dd, &dd_ac0, &dd_ac1); + start = 0; + homenr = dd_ac1; + } + + if (PAR(cr)) + { + sprintf(fname, "%s_n%d.pdb", fn, cr->sim_nodeid); + } + else + { + sprintf(fname, "%s.pdb", fn); + } + sprintf(format, "%s\n", get_pdbformat()); + + out = gmx_fio_fopen(fname, "w"); + + fprintf(out, "TITLE %s\n", title); + gmx_write_pdb_box(out, -1, box); + for (i = start; i < start+homenr; i++) + { + if (dd != NULL) + { + if (i >= dd->nat_home && i < dd_ac0) + { + continue; + } + ii = dd->gatindex[i]; + } + else + { + ii = i; + } + gmx_mtop_atominfo_global(mtop, ii, &anm, &resnr, &resnm); + fprintf(out, format, "ATOM", (ii+1)%100000, + anm, resnm, ' ', resnr%10000, ' ', + 10*x[i][XX], 10*x[i][YY], 10*x[i][ZZ]); + } + fprintf(out, "TER\n"); + + gmx_fio_fclose(out); +} + +static void dump_confs(FILE *fplog, gmx_int64_t step, gmx_mtop_t *mtop, + int start, int homenr, t_commrec *cr, + rvec x[], rvec xprime[], matrix box) +{ + char buf[256], buf2[22]; + + char *env = getenv("GMX_SUPPRESS_DUMP"); + if (env) + { + return; + } + + sprintf(buf, "step%sb", gmx_step_str(step, buf2)); + write_constr_pdb(buf, "initial coordinates", + mtop, start, homenr, cr, x, box); + sprintf(buf, "step%sc", gmx_step_str(step, buf2)); + write_constr_pdb(buf, "coordinates after constraining", + mtop, start, homenr, cr, xprime, box); + if (fplog) + { + fprintf(fplog, "Wrote pdb files with previous and current coordinates\n"); + } + fprintf(stderr, "Wrote pdb files with previous and current coordinates\n"); +} + +static void pr_sortblock(FILE *fp, const char *title, int nsb, t_sortblock sb[]) +{ + int i; + + fprintf(fp, "%s\n", title); + for (i = 0; (i < nsb); i++) + { + fprintf(fp, "i: %5d, iatom: (%5d %5d %5d), blocknr: %5d\n", + i, sb[i].iatom[0], sb[i].iatom[1], sb[i].iatom[2], + sb[i].blocknr); + } +} + +gmx_bool constrain(FILE *fplog, gmx_bool bLog, gmx_bool bEner, + struct gmx_constr *constr, + t_idef *idef, t_inputrec *ir, gmx_ekindata_t *ekind, + t_commrec *cr, + gmx_int64_t step, int delta_step, + t_mdatoms *md, + rvec *x, rvec *xprime, rvec *min_proj, + gmx_bool bMolPBC, matrix box, + real lambda, real *dvdlambda, + rvec *v, tensor *vir, + t_nrnb *nrnb, int econq, gmx_bool bPscal, + real veta, real vetanew) +{ + gmx_bool bOK, bDump; + int start, homenr, nrend; + int i, j, d; + int ncons, settle_error; + tensor vir_r_m_dr; + rvec *vstor; + real invdt, vir_fac, t; + t_ilist *settle; + int nsettle; + t_pbc pbc, *pbc_null; + char buf[22]; + t_vetavars vetavar; + int nth, th; + + if (econq == econqForceDispl && !EI_ENERGY_MINIMIZATION(ir->eI)) + { + gmx_incons("constrain called for forces displacements while not doing energy minimization, can not do this while the LINCS and SETTLE constraint connection matrices are mass weighted"); + } + + bOK = TRUE; + bDump = FALSE; + + start = 0; + homenr = md->homenr; + nrend = start+homenr; + + /* set constants for pressure control integration */ + init_vetavars(&vetavar, econq != econqCoord, + veta, vetanew, ir, ekind, bPscal); + + if (ir->delta_t == 0) + { + invdt = 0; + } + else + { + invdt = 1/ir->delta_t; + } + + if (ir->efep != efepNO && EI_DYNAMICS(ir->eI)) + { + /* Set the constraint lengths for the step at which this configuration + * is meant to be. The invmasses should not be changed. + */ + lambda += delta_step*ir->fepvals->delta_lambda; + } + + if (vir != NULL) + { + clear_mat(vir_r_m_dr); + } + + where(); + + settle = &idef->il[F_SETTLE]; + nsettle = settle->nr/(1+NRAL(F_SETTLE)); + + if (nsettle > 0) + { + nth = gmx_omp_nthreads_get(emntSETTLE); + } + else + { + nth = 1; + } + + if (nth > 1 && constr->vir_r_m_dr_th == NULL) + { + snew(constr->vir_r_m_dr_th, nth); + snew(constr->settle_error, nth); + } + + settle_error = -1; + + /* We do not need full pbc when constraints do not cross charge groups, + * i.e. when dd->constraint_comm==NULL. + * Note that PBC for constraints is different from PBC for bondeds. + * For constraints there is both forward and backward communication. + */ + if (ir->ePBC != epbcNONE && + (cr->dd || bMolPBC) && !(cr->dd && cr->dd->constraint_comm == NULL)) + { + /* With pbc=screw the screw has been changed to a shift + * by the constraint coordinate communication routine, + * so that here we can use normal pbc. + */ + pbc_null = set_pbc_dd(&pbc, ir->ePBC, cr->dd, FALSE, box); + } + else + { + pbc_null = NULL; + } + + /* Communicate the coordinates required for the non-local constraints + * for LINCS and/or SETTLE. + */ + if (cr->dd) + { - dd_move_x_constraints(cr->dd, box, x, xprime); ++ dd_move_x_constraints(cr->dd, box, x, xprime, econq == econqCoord); + } + + if (constr->lincsd != NULL) + { + bOK = constrain_lincs(fplog, bLog, bEner, ir, step, constr->lincsd, md, cr, + x, xprime, min_proj, + box, pbc_null, lambda, dvdlambda, + invdt, v, vir != NULL, vir_r_m_dr, + econq, nrnb, + constr->maxwarn, &constr->warncount_lincs); + if (!bOK && constr->maxwarn >= 0) + { + if (fplog != NULL) + { + fprintf(fplog, "Constraint error in algorithm %s at step %s\n", + econstr_names[econtLINCS], gmx_step_str(step, buf)); + } + bDump = TRUE; + } + } + + if (constr->nblocks > 0) + { + switch (econq) + { + case (econqCoord): + bOK = bshakef(fplog, constr->shaked, + md->invmass, constr->nblocks, constr->sblock, + idef, ir, x, xprime, nrnb, + constr->lagr, lambda, dvdlambda, + invdt, v, vir != NULL, vir_r_m_dr, + constr->maxwarn >= 0, econq, &vetavar); + break; + case (econqVeloc): + bOK = bshakef(fplog, constr->shaked, + md->invmass, constr->nblocks, constr->sblock, + idef, ir, x, min_proj, nrnb, + constr->lagr, lambda, dvdlambda, + invdt, NULL, vir != NULL, vir_r_m_dr, + constr->maxwarn >= 0, econq, &vetavar); + break; + default: + gmx_fatal(FARGS, "Internal error, SHAKE called for constraining something else than coordinates"); + break; + } + + if (!bOK && constr->maxwarn >= 0) + { + if (fplog != NULL) + { + fprintf(fplog, "Constraint error in algorithm %s at step %s\n", + econstr_names[econtSHAKE], gmx_step_str(step, buf)); + } + bDump = TRUE; + } + } + + if (nsettle > 0) + { + int calcvir_atom_end; + + if (vir == NULL) + { + calcvir_atom_end = 0; + } + else + { + calcvir_atom_end = md->homenr; + } + + switch (econq) + { + case econqCoord: +#pragma omp parallel for num_threads(nth) schedule(static) + for (th = 0; th < nth; th++) + { + int start_th, end_th; + + if (th > 0) + { + clear_mat(constr->vir_r_m_dr_th[th]); + } + + start_th = (nsettle* th )/nth; + end_th = (nsettle*(th+1))/nth; + if (start_th >= 0 && end_th - start_th > 0) + { + csettle(constr->settled, + end_th-start_th, + settle->iatoms+start_th*(1+NRAL(F_SETTLE)), + pbc_null, + x[0], xprime[0], + invdt, v ? v[0] : NULL, calcvir_atom_end, + th == 0 ? vir_r_m_dr : constr->vir_r_m_dr_th[th], + th == 0 ? &settle_error : &constr->settle_error[th], + &vetavar); + } + } + inc_nrnb(nrnb, eNR_SETTLE, nsettle); + if (v != NULL) + { + inc_nrnb(nrnb, eNR_CONSTR_V, nsettle*3); + } + if (vir != NULL) + { + inc_nrnb(nrnb, eNR_CONSTR_VIR, nsettle*3); + } + break; + case econqVeloc: + case econqDeriv: + case econqForce: + case econqForceDispl: +#pragma omp parallel for num_threads(nth) schedule(static) + for (th = 0; th < nth; th++) + { + int start_th, end_th; + + if (th > 0) + { + clear_mat(constr->vir_r_m_dr_th[th]); + } + + start_th = (nsettle* th )/nth; + end_th = (nsettle*(th+1))/nth; + + if (start_th >= 0 && end_th - start_th > 0) + { + settle_proj(constr->settled, econq, + end_th-start_th, + settle->iatoms+start_th*(1+NRAL(F_SETTLE)), + pbc_null, + x, + xprime, min_proj, calcvir_atom_end, + th == 0 ? vir_r_m_dr : constr->vir_r_m_dr_th[th], + &vetavar); + } + } + /* This is an overestimate */ + inc_nrnb(nrnb, eNR_SETTLE, nsettle); + break; + case econqDeriv_FlexCon: + /* Nothing to do, since the are no flexible constraints in settles */ + break; + default: + gmx_incons("Unknown constraint quantity for settle"); + } + } + + if (settle->nr > 0) + { + /* Combine virial and error info of the other threads */ + for (i = 1; i < nth; i++) + { + m_add(vir_r_m_dr, constr->vir_r_m_dr_th[i], vir_r_m_dr); + settle_error = constr->settle_error[i]; + } + + if (econq == econqCoord && settle_error >= 0) + { + bOK = FALSE; + if (constr->maxwarn >= 0) + { + char buf[256]; + sprintf(buf, + "\nstep " "%"GMX_PRId64 ": Water molecule starting at atom %d can not be " + "settled.\nCheck for bad contacts and/or reduce the timestep if appropriate.\n", + step, ddglatnr(cr->dd, settle->iatoms[settle_error*(1+NRAL(F_SETTLE))+1])); + if (fplog) + { + fprintf(fplog, "%s", buf); + } + fprintf(stderr, "%s", buf); + constr->warncount_settle++; + if (constr->warncount_settle > constr->maxwarn) + { + too_many_constraint_warnings(-1, constr->warncount_settle); + } + bDump = TRUE; + } + } + } + + free_vetavars(&vetavar); + + if (vir != NULL) + { + switch (econq) + { + case econqCoord: + vir_fac = 0.5/(ir->delta_t*ir->delta_t); + break; + case econqVeloc: + vir_fac = 0.5/ir->delta_t; + break; + case econqForce: + case econqForceDispl: + vir_fac = 0.5; + break; + default: + vir_fac = 0; + gmx_incons("Unsupported constraint quantity for virial"); + } + + if (EI_VV(ir->eI)) + { + vir_fac *= 2; /* only constraining over half the distance here */ + } + for (i = 0; i < DIM; i++) + { + for (j = 0; j < DIM; j++) + { + (*vir)[i][j] = vir_fac*vir_r_m_dr[i][j]; + } + } + } + + if (bDump) + { + dump_confs(fplog, step, constr->warn_mtop, start, homenr, cr, x, xprime, box); + } + + if (econq == econqCoord) + { + if (ir->ePull == epullCONSTRAINT) + { + if (EI_DYNAMICS(ir->eI)) + { + t = ir->init_t + (step + delta_step)*ir->delta_t; + } + else + { + t = ir->init_t; + } + set_pbc(&pbc, ir->ePBC, box); + pull_constraint(ir->pull, md, &pbc, cr, ir->delta_t, t, x, xprime, v, *vir); + } + if (constr->ed && delta_step > 0) + { + /* apply the essential dynamcs constraints here */ + do_edsam(ir, step, cr, xprime, v, box, constr->ed); + } + } + + return bOK; +} + +real *constr_rmsd_data(struct gmx_constr *constr) +{ + if (constr->lincsd) + { + return lincs_rmsd_data(constr->lincsd); + } + else + { + return NULL; + } +} + +real constr_rmsd(struct gmx_constr *constr, gmx_bool bSD2) +{ + if (constr->lincsd) + { + return lincs_rmsd(constr->lincsd, bSD2); + } + else + { + return 0; + } +} + +static void make_shake_sblock_serial(struct gmx_constr *constr, + t_idef *idef, t_mdatoms *md) +{ + int i, j, m, ncons; + int bstart, bnr; + t_blocka sblocks; + t_sortblock *sb; + t_iatom *iatom; + atom_id *inv_sblock; + + /* Since we are processing the local topology, + * the F_CONSTRNC ilist has been concatenated to the F_CONSTR ilist. + */ + ncons = idef->il[F_CONSTR].nr/3; + + init_blocka(&sblocks); + gen_sblocks(NULL, 0, md->homenr, idef, &sblocks, FALSE); + + /* + bstart=(idef->nodeid > 0) ? blocks->multinr[idef->nodeid-1] : 0; + nblocks=blocks->multinr[idef->nodeid] - bstart; + */ + bstart = 0; + constr->nblocks = sblocks.nr; + if (debug) + { + fprintf(debug, "ncons: %d, bstart: %d, nblocks: %d\n", + ncons, bstart, constr->nblocks); + } + + /* Calculate block number for each atom */ + inv_sblock = make_invblocka(&sblocks, md->nr); + + done_blocka(&sblocks); + + /* Store the block number in temp array and + * sort the constraints in order of the sblock number + * and the atom numbers, really sorting a segment of the array! + */ +#ifdef DEBUGIDEF + pr_idef(fplog, 0, "Before Sort", idef); +#endif + iatom = idef->il[F_CONSTR].iatoms; + snew(sb, ncons); + for (i = 0; (i < ncons); i++, iatom += 3) + { + for (m = 0; (m < 3); m++) + { + sb[i].iatom[m] = iatom[m]; + } + sb[i].blocknr = inv_sblock[iatom[1]]; + } + + /* Now sort the blocks */ + if (debug) + { + pr_sortblock(debug, "Before sorting", ncons, sb); + fprintf(debug, "Going to sort constraints\n"); + } + + qsort(sb, ncons, (size_t)sizeof(*sb), pcomp); + + if (debug) + { + pr_sortblock(debug, "After sorting", ncons, sb); + } + + iatom = idef->il[F_CONSTR].iatoms; + for (i = 0; (i < ncons); i++, iatom += 3) + { + for (m = 0; (m < 3); m++) + { + iatom[m] = sb[i].iatom[m]; + } + } +#ifdef DEBUGIDEF + pr_idef(fplog, 0, "After Sort", idef); +#endif + + j = 0; + snew(constr->sblock, constr->nblocks+1); + bnr = -2; + for (i = 0; (i < ncons); i++) + { + if (sb[i].blocknr != bnr) + { + bnr = sb[i].blocknr; + constr->sblock[j++] = 3*i; + } + } + /* Last block... */ + constr->sblock[j++] = 3*ncons; + + if (j != (constr->nblocks+1)) + { + fprintf(stderr, "bstart: %d\n", bstart); + fprintf(stderr, "j: %d, nblocks: %d, ncons: %d\n", + j, constr->nblocks, ncons); + for (i = 0; (i < ncons); i++) + { + fprintf(stderr, "i: %5d sb[i].blocknr: %5u\n", i, sb[i].blocknr); + } + for (j = 0; (j <= constr->nblocks); j++) + { + fprintf(stderr, "sblock[%3d]=%5d\n", j, (int)constr->sblock[j]); + } + gmx_fatal(FARGS, "DEATH HORROR: " + "sblocks does not match idef->il[F_CONSTR]"); + } + sfree(sb); + sfree(inv_sblock); +} + +static void make_shake_sblock_dd(struct gmx_constr *constr, + t_ilist *ilcon, t_block *cgs, + gmx_domdec_t *dd) +{ + int ncons, c, cg; + t_iatom *iatom; + + if (dd->ncg_home+1 > constr->sblock_nalloc) + { + constr->sblock_nalloc = over_alloc_dd(dd->ncg_home+1); + srenew(constr->sblock, constr->sblock_nalloc); + } + + ncons = ilcon->nr/3; + iatom = ilcon->iatoms; + constr->nblocks = 0; + cg = 0; + for (c = 0; c < ncons; c++) + { + if (c == 0 || iatom[1] >= cgs->index[cg+1]) + { + constr->sblock[constr->nblocks++] = 3*c; + while (iatom[1] >= cgs->index[cg+1]) + { + cg++; + } + } + iatom += 3; + } + constr->sblock[constr->nblocks] = 3*ncons; +} + +t_blocka make_at2con(int start, int natoms, + t_ilist *ilist, t_iparams *iparams, + gmx_bool bDynamics, int *nflexiblecons) +{ + int *count, ncon, con, con_tot, nflexcon, ftype, i, a; + t_iatom *ia; + t_blocka at2con; + gmx_bool bFlexCon; + + snew(count, natoms); + nflexcon = 0; + for (ftype = F_CONSTR; ftype <= F_CONSTRNC; ftype++) + { + ncon = ilist[ftype].nr/3; + ia = ilist[ftype].iatoms; + for (con = 0; con < ncon; con++) + { + bFlexCon = (iparams[ia[0]].constr.dA == 0 && + iparams[ia[0]].constr.dB == 0); + if (bFlexCon) + { + nflexcon++; + } + if (bDynamics || !bFlexCon) + { + for (i = 1; i < 3; i++) + { + a = ia[i] - start; + count[a]++; + } + } + ia += 3; + } + } + *nflexiblecons = nflexcon; + + at2con.nr = natoms; + at2con.nalloc_index = at2con.nr+1; + snew(at2con.index, at2con.nalloc_index); + at2con.index[0] = 0; + for (a = 0; a < natoms; a++) + { + at2con.index[a+1] = at2con.index[a] + count[a]; + count[a] = 0; + } + at2con.nra = at2con.index[natoms]; + at2con.nalloc_a = at2con.nra; + snew(at2con.a, at2con.nalloc_a); + + /* The F_CONSTRNC constraints have constraint numbers + * that continue after the last F_CONSTR constraint. + */ + con_tot = 0; + for (ftype = F_CONSTR; ftype <= F_CONSTRNC; ftype++) + { + ncon = ilist[ftype].nr/3; + ia = ilist[ftype].iatoms; + for (con = 0; con < ncon; con++) + { + bFlexCon = (iparams[ia[0]].constr.dA == 0 && + iparams[ia[0]].constr.dB == 0); + if (bDynamics || !bFlexCon) + { + for (i = 1; i < 3; i++) + { + a = ia[i] - start; + at2con.a[at2con.index[a]+count[a]++] = con_tot; + } + } + con_tot++; + ia += 3; + } + } + + sfree(count); + + return at2con; +} + +static int *make_at2settle(int natoms, const t_ilist *ilist) +{ + int *at2s; + int a, stride, s; + + snew(at2s, natoms); + /* Set all to no settle */ + for (a = 0; a < natoms; a++) + { + at2s[a] = -1; + } + + stride = 1 + NRAL(F_SETTLE); + + for (s = 0; s < ilist->nr; s += stride) + { + at2s[ilist->iatoms[s+1]] = s/stride; + at2s[ilist->iatoms[s+2]] = s/stride; + at2s[ilist->iatoms[s+3]] = s/stride; + } + + return at2s; +} + +void set_constraints(struct gmx_constr *constr, + gmx_localtop_t *top, t_inputrec *ir, + t_mdatoms *md, t_commrec *cr) +{ + t_idef *idef; + int ncons; + t_ilist *settle; + int iO, iH; + + idef = &top->idef; + + if (constr->ncon_tot > 0) + { + /* We are using the local topology, + * so there are only F_CONSTR constraints. + */ + ncons = idef->il[F_CONSTR].nr/3; + + /* With DD we might also need to call LINCS with ncons=0 for + * communicating coordinates to other nodes that do have constraints. + */ + if (ir->eConstrAlg == econtLINCS) + { + set_lincs(idef, md, EI_DYNAMICS(ir->eI), cr, constr->lincsd); + } + if (ir->eConstrAlg == econtSHAKE) + { + if (cr->dd) + { + make_shake_sblock_dd(constr, &idef->il[F_CONSTR], &top->cgs, cr->dd); + } + else + { + make_shake_sblock_serial(constr, idef, md); + } + if (ncons > constr->lagr_nalloc) + { + constr->lagr_nalloc = over_alloc_dd(ncons); + srenew(constr->lagr, constr->lagr_nalloc); + } + } + } + + if (idef->il[F_SETTLE].nr > 0 && constr->settled == NULL) + { + settle = &idef->il[F_SETTLE]; + iO = settle->iatoms[1]; + iH = settle->iatoms[2]; + constr->settled = + settle_init(md->massT[iO], md->massT[iH], + md->invmass[iO], md->invmass[iH], + idef->iparams[settle->iatoms[0]].settle.doh, + idef->iparams[settle->iatoms[0]].settle.dhh); + } + + /* Make a selection of the local atoms for essential dynamics */ + if (constr->ed && cr->dd) + { + dd_make_local_ed_indices(cr->dd, constr->ed); + } +} + +static void constr_recur(t_blocka *at2con, + t_ilist *ilist, t_iparams *iparams, gmx_bool bTopB, + int at, int depth, int nc, int *path, + real r0, real r1, real *r2max, + int *count) +{ + int ncon1; + t_iatom *ia1, *ia2; + int c, con, a1; + gmx_bool bUse; + t_iatom *ia; + real len, rn0, rn1; + + (*count)++; + + ncon1 = ilist[F_CONSTR].nr/3; + ia1 = ilist[F_CONSTR].iatoms; + ia2 = ilist[F_CONSTRNC].iatoms; + + /* Loop over all constraints connected to this atom */ + for (c = at2con->index[at]; c < at2con->index[at+1]; c++) + { + con = at2con->a[c]; + /* Do not walk over already used constraints */ + bUse = TRUE; + for (a1 = 0; a1 < depth; a1++) + { + if (con == path[a1]) + { + bUse = FALSE; + } + } + if (bUse) + { + ia = constr_iatomptr(ncon1, ia1, ia2, con); + /* Flexible constraints currently have length 0, which is incorrect */ + if (!bTopB) + { + len = iparams[ia[0]].constr.dA; + } + else + { + len = iparams[ia[0]].constr.dB; + } + /* In the worst case the bond directions alternate */ + if (nc % 2 == 0) + { + rn0 = r0 + len; + rn1 = r1; + } + else + { + rn0 = r0; + rn1 = r1 + len; + } + /* Assume angles of 120 degrees between all bonds */ + if (rn0*rn0 + rn1*rn1 + rn0*rn1 > *r2max) + { + *r2max = rn0*rn0 + rn1*rn1 + r0*rn1; + if (debug) + { + fprintf(debug, "Found longer constraint distance: r0 %5.3f r1 %5.3f rmax %5.3f\n", rn0, rn1, sqrt(*r2max)); + for (a1 = 0; a1 < depth; a1++) + { + fprintf(debug, " %d %5.3f", + path[a1], + iparams[constr_iatomptr(ncon1, ia1, ia2, con)[0]].constr.dA); + } + fprintf(debug, " %d %5.3f\n", con, len); + } + } + /* Limit the number of recursions to 1000*nc, + * so a call does not take more than a second, + * even for highly connected systems. + */ + if (depth + 1 < nc && *count < 1000*nc) + { + if (ia[1] == at) + { + a1 = ia[2]; + } + else + { + a1 = ia[1]; + } + /* Recursion */ + path[depth] = con; + constr_recur(at2con, ilist, iparams, + bTopB, a1, depth+1, nc, path, rn0, rn1, r2max, count); + path[depth] = -1; + } + } + } +} + +static real constr_r_max_moltype(gmx_moltype_t *molt, t_iparams *iparams, + t_inputrec *ir) +{ + int natoms, nflexcon, *path, at, count; + + t_blocka at2con; + real r0, r1, r2maxA, r2maxB, rmax, lam0, lam1; + + if (molt->ilist[F_CONSTR].nr == 0 && + molt->ilist[F_CONSTRNC].nr == 0) + { + return 0; + } + + natoms = molt->atoms.nr; + + at2con = make_at2con(0, natoms, molt->ilist, iparams, + EI_DYNAMICS(ir->eI), &nflexcon); + snew(path, 1+ir->nProjOrder); + for (at = 0; at < 1+ir->nProjOrder; at++) + { + path[at] = -1; + } + + r2maxA = 0; + for (at = 0; at < natoms; at++) + { + r0 = 0; + r1 = 0; + + count = 0; + constr_recur(&at2con, molt->ilist, iparams, + FALSE, at, 0, 1+ir->nProjOrder, path, r0, r1, &r2maxA, &count); + } + if (ir->efep == efepNO) + { + rmax = sqrt(r2maxA); + } + else + { + r2maxB = 0; + for (at = 0; at < natoms; at++) + { + r0 = 0; + r1 = 0; + count = 0; + constr_recur(&at2con, molt->ilist, iparams, + TRUE, at, 0, 1+ir->nProjOrder, path, r0, r1, &r2maxB, &count); + } + lam0 = ir->fepvals->init_lambda; + if (EI_DYNAMICS(ir->eI)) + { + lam0 += ir->init_step*ir->fepvals->delta_lambda; + } + rmax = (1 - lam0)*sqrt(r2maxA) + lam0*sqrt(r2maxB); + if (EI_DYNAMICS(ir->eI)) + { + lam1 = ir->fepvals->init_lambda + (ir->init_step + ir->nsteps)*ir->fepvals->delta_lambda; + rmax = max(rmax, (1 - lam1)*sqrt(r2maxA) + lam1*sqrt(r2maxB)); + } + } + + done_blocka(&at2con); + sfree(path); + + return rmax; +} + +real constr_r_max(FILE *fplog, gmx_mtop_t *mtop, t_inputrec *ir) +{ + int mt; + real rmax; + + rmax = 0; + for (mt = 0; mt < mtop->nmoltype; mt++) + { + rmax = max(rmax, + constr_r_max_moltype(&mtop->moltype[mt], + mtop->ffparams.iparams, ir)); + } + + if (fplog) + { + fprintf(fplog, "Maximum distance for %d constraints, at 120 deg. angles, all-trans: %.3f nm\n", 1+ir->nProjOrder, rmax); + } + + return rmax; +} + +gmx_constr_t init_constraints(FILE *fplog, + gmx_mtop_t *mtop, t_inputrec *ir, + gmx_edsam_t ed, t_state *state, + t_commrec *cr) +{ + int ncon, nset, nmol, settle_type, i, natoms, mt, nflexcon; + struct gmx_constr *constr; + char *env; + t_ilist *ilist; + gmx_mtop_ilistloop_t iloop; + + ncon = + gmx_mtop_ftype_count(mtop, F_CONSTR) + + gmx_mtop_ftype_count(mtop, F_CONSTRNC); + nset = gmx_mtop_ftype_count(mtop, F_SETTLE); + + if (ncon+nset == 0 && ir->ePull != epullCONSTRAINT && ed == NULL) + { + return NULL; + } + + snew(constr, 1); + + constr->ncon_tot = ncon; + constr->nflexcon = 0; + if (ncon > 0) + { + constr->n_at2con_mt = mtop->nmoltype; + snew(constr->at2con_mt, constr->n_at2con_mt); + for (mt = 0; mt < mtop->nmoltype; mt++) + { + constr->at2con_mt[mt] = make_at2con(0, mtop->moltype[mt].atoms.nr, + mtop->moltype[mt].ilist, + mtop->ffparams.iparams, + EI_DYNAMICS(ir->eI), &nflexcon); + for (i = 0; i < mtop->nmolblock; i++) + { + if (mtop->molblock[i].type == mt) + { + constr->nflexcon += mtop->molblock[i].nmol*nflexcon; + } + } + } + + if (constr->nflexcon > 0) + { + if (fplog) + { + fprintf(fplog, "There are %d flexible constraints\n", + constr->nflexcon); + if (ir->fc_stepsize == 0) + { + fprintf(fplog, "\n" + "WARNING: step size for flexible constraining = 0\n" + " All flexible constraints will be rigid.\n" + " Will try to keep all flexible constraints at their original length,\n" + " but the lengths may exhibit some drift.\n\n"); + constr->nflexcon = 0; + } + } + if (constr->nflexcon > 0) + { + please_cite(fplog, "Hess2002"); + } + } + + if (ir->eConstrAlg == econtLINCS) + { + constr->lincsd = init_lincs(fplog, mtop, + constr->nflexcon, constr->at2con_mt, + DOMAINDECOMP(cr) && cr->dd->bInterCGcons, + ir->nLincsIter, ir->nProjOrder); + } + + if (ir->eConstrAlg == econtSHAKE) + { + if (DOMAINDECOMP(cr) && cr->dd->bInterCGcons) + { + gmx_fatal(FARGS, "SHAKE is not supported with domain decomposition and constraint that cross charge group boundaries, use LINCS"); + } + if (constr->nflexcon) + { + gmx_fatal(FARGS, "For this system also velocities and/or forces need to be constrained, this can not be done with SHAKE, you should select LINCS"); + } + please_cite(fplog, "Ryckaert77a"); + if (ir->bShakeSOR) + { + please_cite(fplog, "Barth95a"); + } + + constr->shaked = shake_init(); + } + } + + if (nset > 0) + { + please_cite(fplog, "Miyamoto92a"); + + constr->bInterCGsettles = inter_charge_group_settles(mtop); + + /* Check that we have only one settle type */ + settle_type = -1; + iloop = gmx_mtop_ilistloop_init(mtop); + while (gmx_mtop_ilistloop_next(iloop, &ilist, &nmol)) + { + for (i = 0; i < ilist[F_SETTLE].nr; i += 4) + { + if (settle_type == -1) + { + settle_type = ilist[F_SETTLE].iatoms[i]; + } + else if (ilist[F_SETTLE].iatoms[i] != settle_type) + { + gmx_fatal(FARGS, + "The [molecules] section of your topology specifies more than one block of\n" + "a [moleculetype] with a [settles] block. Only one such is allowed. If you\n" + "are trying to partition your solvent into different *groups* (e.g. for\n" + "freezing, T-coupling, etc.) then you are using the wrong approach. Index\n" + "files specify groups. Otherwise, you may wish to change the least-used\n" + "block of molecules with SETTLE constraints into 3 normal constraints."); + } + } + } + + constr->n_at2settle_mt = mtop->nmoltype; + snew(constr->at2settle_mt, constr->n_at2settle_mt); + for (mt = 0; mt < mtop->nmoltype; mt++) + { + constr->at2settle_mt[mt] = + make_at2settle(mtop->moltype[mt].atoms.nr, + &mtop->moltype[mt].ilist[F_SETTLE]); + } + } + + constr->maxwarn = 999; + env = getenv("GMX_MAXCONSTRWARN"); + if (env) + { + constr->maxwarn = 0; + sscanf(env, "%d", &constr->maxwarn); + if (fplog) + { + fprintf(fplog, + "Setting the maximum number of constraint warnings to %d\n", + constr->maxwarn); + } + if (MASTER(cr)) + { + fprintf(stderr, + "Setting the maximum number of constraint warnings to %d\n", + constr->maxwarn); + } + } + if (constr->maxwarn < 0 && fplog) + { + fprintf(fplog, "maxwarn < 0, will not stop on constraint errors\n"); + } + constr->warncount_lincs = 0; + constr->warncount_settle = 0; + + /* Initialize the essential dynamics sampling. + * Put the pointer to the ED struct in constr */ + constr->ed = ed; + if (ed != NULL || state->edsamstate.nED > 0) + { + init_edsam(mtop, ir, cr, ed, state->x, state->box, &state->edsamstate); + } + + constr->warn_mtop = mtop; + + return constr; +} + +const t_blocka *atom2constraints_moltype(gmx_constr_t constr) +{ + return constr->at2con_mt; +} + +const int **atom2settle_moltype(gmx_constr_t constr) +{ + return (const int **)constr->at2settle_mt; +} + + +gmx_bool inter_charge_group_constraints(const gmx_mtop_t *mtop) +{ + const gmx_moltype_t *molt; + const t_block *cgs; + const t_ilist *il; + int mb; + int nat, *at2cg, cg, a, ftype, i; + gmx_bool bInterCG; + + bInterCG = FALSE; + for (mb = 0; mb < mtop->nmolblock && !bInterCG; mb++) + { + molt = &mtop->moltype[mtop->molblock[mb].type]; + + if (molt->ilist[F_CONSTR].nr > 0 || + molt->ilist[F_CONSTRNC].nr > 0 || + molt->ilist[F_SETTLE].nr > 0) + { + cgs = &molt->cgs; + snew(at2cg, molt->atoms.nr); + for (cg = 0; cg < cgs->nr; cg++) + { + for (a = cgs->index[cg]; a < cgs->index[cg+1]; a++) + { + at2cg[a] = cg; + } + } + + for (ftype = F_CONSTR; ftype <= F_CONSTRNC; ftype++) + { + il = &molt->ilist[ftype]; + for (i = 0; i < il->nr && !bInterCG; i += 1+NRAL(ftype)) + { + if (at2cg[il->iatoms[i+1]] != at2cg[il->iatoms[i+2]]) + { + bInterCG = TRUE; + } + } + } + + sfree(at2cg); + } + } + + return bInterCG; +} + +gmx_bool inter_charge_group_settles(const gmx_mtop_t *mtop) +{ + const gmx_moltype_t *molt; + const t_block *cgs; + const t_ilist *il; + int mb; + int nat, *at2cg, cg, a, ftype, i; + gmx_bool bInterCG; + + bInterCG = FALSE; + for (mb = 0; mb < mtop->nmolblock && !bInterCG; mb++) + { + molt = &mtop->moltype[mtop->molblock[mb].type]; + + if (molt->ilist[F_SETTLE].nr > 0) + { + cgs = &molt->cgs; + snew(at2cg, molt->atoms.nr); + for (cg = 0; cg < cgs->nr; cg++) + { + for (a = cgs->index[cg]; a < cgs->index[cg+1]; a++) + { + at2cg[a] = cg; + } + } + + for (ftype = F_SETTLE; ftype <= F_SETTLE; ftype++) + { + il = &molt->ilist[ftype]; + for (i = 0; i < il->nr && !bInterCG; i += 1+NRAL(F_SETTLE)) + { + if (at2cg[il->iatoms[i+1]] != at2cg[il->iatoms[i+2]] || + at2cg[il->iatoms[i+1]] != at2cg[il->iatoms[i+3]]) + { + bInterCG = TRUE; + } + } + } + + sfree(at2cg); + } + } + + return bInterCG; +} + +/* helper functions for andersen temperature control, because the + * gmx_constr construct is only defined in constr.c. Return the list + * of blocks (get_sblock) and the number of blocks (get_nblocks). */ + +extern int *get_sblock(struct gmx_constr *constr) +{ + return constr->sblock; +} + +extern int get_nblocks(struct gmx_constr *constr) +{ + return constr->nblocks; +} diff --cc src/gromacs/mdlib/domdec_con.c index 31154f3460,0000000000..417992bfc9 mode 100644,000000..100644 --- a/src/gromacs/mdlib/domdec_con.c +++ b/src/gromacs/mdlib/domdec_con.c @@@ -1,1408 -1,0 +1,1411 @@@ +/* + * This file is part of the GROMACS molecular simulation package. + * + * Copyright (c) 2006,2007,2008,2009,2010,2012,2013,2014, by the GROMACS development team, led by + * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl, + * and including many others, as listed in the AUTHORS file in the + * top-level source directory and at http://www.gromacs.org. + * + * GROMACS is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 + * of the License, or (at your option) any later version. + * + * GROMACS is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with GROMACS; if not, see + * http://www.gnu.org/licenses, or write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * If you want to redistribute modifications to GROMACS, please + * consider that scientific software is very special. Version + * control is crucial - bugs must be traceable. We will be happy to + * consider code for inclusion in the official distribution, but + * derived work must not be called official GROMACS. Details are found + * in the README & COPYING files - if they are missing, get the + * official version at http://www.gromacs.org. + * + * To help us fund GROMACS development, we humbly ask that you cite + * the research papers on the package. Check out http://www.gromacs.org. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif +#include + +#include "smalloc.h" +#include "vec.h" +#include "constr.h" +#include "types/commrec.h" +#include "domdec.h" +#include "domdec_network.h" +#include "mtop_util.h" +#include "gmx_ga2la.h" +#include "gmx_hash.h" +#include "gmx_omp_nthreads.h" +#include "macros.h" + +typedef struct { + int nsend; + int *a; + int a_nalloc; + int nrecv; +} gmx_specatsend_t; + +typedef struct { + int *ind; + int nalloc; + int n; +} ind_req_t; + +typedef struct gmx_domdec_specat_comm { + /* The number of indices to receive during the setup */ + int nreq[DIM][2][2]; + /* The atoms to send */ + gmx_specatsend_t spas[DIM][2]; + gmx_bool *bSendAtom; + int bSendAtom_nalloc; + /* Send buffers */ + int *ibuf; + int ibuf_nalloc; + rvec *vbuf; + int vbuf_nalloc; + rvec *vbuf2; + int vbuf2_nalloc; + /* The range in the local buffer(s) for received atoms */ + int at_start; + int at_end; + + /* The atom indices we need from the surrounding cells. + * We can gather the indices over nthread threads. + */ + int nthread; + ind_req_t *ireq; +} gmx_domdec_specat_comm_t; + +typedef struct gmx_domdec_constraints { + int *molb_con_offset; + int *molb_ncon_mol; + /* The fully local and connected constraints */ + int ncon; + /* The global constraint number, only required for clearing gc_req */ + int *con_gl; + int *con_nlocat; + int con_nalloc; + /* Boolean that tells if a global constraint index has been requested */ + char *gc_req; + /* Global to local communicated constraint atom only index */ + gmx_hash_t ga2la; + + /* Multi-threading stuff */ + int nthread; + t_ilist *ils; +} gmx_domdec_constraints_t; + + +static 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]); + } + } + } + } +} + +void dd_move_f_vsites(gmx_domdec_t *dd, rvec *f, rvec *fshift) +{ + if (dd->vsite_comm) + { + dd_move_f_specat(dd, dd->vsite_comm, f, fshift); + } +} + +void dd_clear_f_vsites(gmx_domdec_t *dd, rvec *f) +{ + int i; + + if (dd->vsite_comm) + { + for (i = dd->vsite_comm->at_start; i < dd->vsite_comm->at_end; i++) + { + clear_rvec(f[i]); + } + } +} + +static void dd_move_x_specat(gmx_domdec_t *dd, gmx_domdec_specat_comm_t *spac, - matrix box, rvec *x0, rvec *x1) ++ matrix box, ++ rvec *x0, ++ rvec *x1, gmx_bool bX1IsCoord) +{ + gmx_specatsend_t *spas; + rvec *x, *vbuf, *rbuf; + int nvec, v, n, nn, ns0, ns1, nr0, nr1, nr, d, dim, dir, i; + gmx_bool bPBC, bScrew = FALSE; + rvec shift = {0, 0, 0}; + + nvec = 1; - if (x1) ++ if (x1 != NULL) + { + nvec++; + } + + n = spac->at_start; + for (d = 0; d < dd->ndim; d++) + { + dim = dd->dim[d]; + if (dd->nc[dim] > 2) + { + /* Pulse the grid forward and backward */ + vbuf = spac->vbuf; + for (dir = 0; dir < 2; dir++) + { + if (dir == 0 && dd->ci[dim] == 0) + { + bPBC = TRUE; + bScrew = (dd->bScrewPBC && dim == XX); + copy_rvec(box[dim], shift); + } + else if (dir == 1 && dd->ci[dim] == dd->nc[dim]-1) + { + bPBC = TRUE; + bScrew = (dd->bScrewPBC && dim == XX); + for (i = 0; i < DIM; i++) + { + shift[i] = -box[dim][i]; + } + } + else + { + bPBC = FALSE; + bScrew = FALSE; + } + spas = &spac->spas[d][dir]; + for (v = 0; v < nvec; v++) + { + x = (v == 0 ? x0 : x1); + /* Copy the required coordinates to the send buffer */ - if (!bPBC) ++ if (!bPBC || (v == 1 && !bX1IsCoord)) + { + /* Only copy */ + for (i = 0; i < spas->nsend; i++) + { + copy_rvec(x[spas->a[i]], *vbuf); + vbuf++; + } + } + else if (!bScrew) + { + /* Shift coordinates */ + for (i = 0; i < spas->nsend; i++) + { + rvec_add(x[spas->a[i]], shift, *vbuf); + vbuf++; + } + } + else + { + /* Shift and rotate coordinates */ + for (i = 0; i < spas->nsend; i++) + { + (*vbuf)[XX] = x[spas->a[i]][XX] + shift[XX]; + (*vbuf)[YY] = box[YY][YY] - x[spas->a[i]][YY] + shift[YY]; + (*vbuf)[ZZ] = box[ZZ][ZZ] - x[spas->a[i]][ZZ] + shift[ZZ]; + vbuf++; + } + } + } + } + /* Send and receive the coordinates */ + spas = spac->spas[d]; + ns0 = spas[0].nsend; + nr0 = spas[0].nrecv; + ns1 = spas[1].nsend; + nr1 = spas[1].nrecv; + if (nvec == 1) + { + dd_sendrecv2_rvec(dd, d, + spac->vbuf+ns0, ns1, x0+n, nr1, + spac->vbuf, ns0, x0+n+nr1, nr0); + } + else + { + /* Communicate both vectors in one buffer */ + rbuf = spac->vbuf2; + dd_sendrecv2_rvec(dd, d, + spac->vbuf+2*ns0, 2*ns1, rbuf, 2*nr1, + spac->vbuf, 2*ns0, rbuf+2*nr1, 2*nr0); + /* Split the buffer into the two vectors */ + nn = n; + for (dir = 1; dir >= 0; dir--) + { + nr = spas[dir].nrecv; + for (v = 0; v < 2; v++) + { + x = (v == 0 ? x0 : x1); + for (i = 0; i < nr; i++) + { + copy_rvec(*rbuf, x[nn+i]); + rbuf++; + } + } + nn += nr; + } + } + n += nr0 + nr1; + } + else + { + spas = &spac->spas[d][0]; + /* Copy the required coordinates to the send buffer */ + vbuf = spac->vbuf; + for (v = 0; v < nvec; v++) + { + x = (v == 0 ? x0 : x1); + if (dd->bScrewPBC && dim == XX && + (dd->ci[XX] == 0 || dd->ci[XX] == dd->nc[XX]-1)) + { + /* Here we only perform the rotation, the rest of the pbc + * is handled in the constraint or viste routines. + */ + for (i = 0; i < spas->nsend; i++) + { + (*vbuf)[XX] = x[spas->a[i]][XX]; + (*vbuf)[YY] = box[YY][YY] - x[spas->a[i]][YY]; + (*vbuf)[ZZ] = box[ZZ][ZZ] - x[spas->a[i]][ZZ]; + vbuf++; + } + } + else + { + for (i = 0; i < spas->nsend; i++) + { + copy_rvec(x[spas->a[i]], *vbuf); + vbuf++; + } + } + } + /* Send and receive the coordinates */ + if (nvec == 1) + { + dd_sendrecv_rvec(dd, d, dddirBackward, + spac->vbuf, spas->nsend, x0+n, spas->nrecv); + } + else + { + /* Communicate both vectors in one buffer */ + rbuf = spac->vbuf2; + dd_sendrecv_rvec(dd, d, dddirBackward, + spac->vbuf, 2*spas->nsend, rbuf, 2*spas->nrecv); + /* Split the buffer into the two vectors */ + nr = spas[0].nrecv; + for (v = 0; v < 2; v++) + { + x = (v == 0 ? x0 : x1); + for (i = 0; i < nr; i++) + { + copy_rvec(*rbuf, x[n+i]); + rbuf++; + } + } + } + n += spas->nrecv; + } + } +} + - void dd_move_x_constraints(gmx_domdec_t *dd, matrix box, rvec *x0, rvec *x1) ++void dd_move_x_constraints(gmx_domdec_t *dd, matrix box, ++ rvec *x0, rvec *x1, gmx_bool bX1IsCoord) +{ + if (dd->constraint_comm) + { - dd_move_x_specat(dd, dd->constraint_comm, box, x0, x1); ++ dd_move_x_specat(dd, dd->constraint_comm, box, x0, x1, bX1IsCoord); + } +} + +void dd_move_x_vsites(gmx_domdec_t *dd, matrix box, rvec *x) +{ + if (dd->vsite_comm) + { - dd_move_x_specat(dd, dd->vsite_comm, box, x, NULL); ++ dd_move_x_specat(dd, dd->vsite_comm, box, x, NULL, FALSE); + } +} + +int *dd_constraints_nlocalatoms(gmx_domdec_t *dd) +{ + if (dd->constraints) + { + return dd->constraints->con_nlocat; + } + else + { + return NULL; + } +} + +void dd_clear_local_constraint_indices(gmx_domdec_t *dd) +{ + gmx_domdec_constraints_t *dc; + int i; + + dc = dd->constraints; + + for (i = 0; i < dc->ncon; i++) + { + dc->gc_req[dc->con_gl[i]] = 0; + } + + if (dd->constraint_comm) + { + gmx_hash_clear_and_optimize(dc->ga2la); + } +} + +void dd_clear_local_vsite_indices(gmx_domdec_t *dd) +{ + int i; + + if (dd->vsite_comm) + { + gmx_hash_clear_and_optimize(dd->ga2la_vsite); + } +} + +static int setup_specat_communication(gmx_domdec_t *dd, + ind_req_t *ireq, + gmx_domdec_specat_comm_t *spac, + gmx_hash_t ga2la_specat, + int at_start, + int vbuf_fac, + const char *specat_type, + const char *add_err) +{ + int nsend[2], nlast, nsend_zero[2] = {0, 0}, *nsend_ptr; + int d, dim, ndir, dir, nr, ns, i, nrecv_local, n0, start, indr, ind, buf[2]; + int nat_tot_specat, nat_tot_prev, nalloc_old; + gmx_bool bPBC, bFirst; + gmx_specatsend_t *spas; + + if (debug) + { + fprintf(debug, "Begin setup_specat_communication for %s\n", specat_type); + } + + /* nsend[0]: the number of atoms requested by this node only, + * we communicate this for more efficients checks + * nsend[1]: the total number of requested atoms + */ + nsend[0] = ireq->n; + nsend[1] = nsend[0]; + nlast = nsend[1]; + for (d = dd->ndim-1; d >= 0; d--) + { + /* Pulse the grid forward and backward */ + dim = dd->dim[d]; + bPBC = (dim < dd->npbcdim); + if (dd->nc[dim] == 2) + { + /* Only 2 cells, so we only need to communicate once */ + ndir = 1; + } + else + { + ndir = 2; + } + for (dir = 0; dir < ndir; dir++) + { + if (!bPBC && + dd->nc[dim] > 2 && + ((dir == 0 && dd->ci[dim] == dd->nc[dim] - 1) || + (dir == 1 && dd->ci[dim] == 0))) + { + /* No pbc: the fist/last cell should not request atoms */ + nsend_ptr = nsend_zero; + } + else + { + nsend_ptr = nsend; + } + /* Communicate the number of indices */ + dd_sendrecv_int(dd, d, dir == 0 ? dddirForward : dddirBackward, + nsend_ptr, 2, spac->nreq[d][dir], 2); + nr = spac->nreq[d][dir][1]; + if (nlast+nr > ireq->nalloc) + { + ireq->nalloc = over_alloc_dd(nlast+nr); + srenew(ireq->ind, ireq->nalloc); + } + /* Communicate the indices */ + dd_sendrecv_int(dd, d, dir == 0 ? dddirForward : dddirBackward, + ireq->ind, nsend_ptr[1], ireq->ind+nlast, nr); + nlast += nr; + } + nsend[1] = nlast; + } + if (debug) + { + fprintf(debug, "Communicated the counts\n"); + } + + /* Search for the requested atoms and communicate the indices we have */ + nat_tot_specat = at_start; + nrecv_local = 0; + for (d = 0; d < dd->ndim; d++) + { + bFirst = (d == 0); + /* Pulse the grid forward and backward */ + if (dd->dim[d] >= dd->npbcdim || dd->nc[dd->dim[d]] > 2) + { + ndir = 2; + } + else + { + ndir = 1; + } + nat_tot_prev = nat_tot_specat; + for (dir = ndir-1; dir >= 0; dir--) + { + if (nat_tot_specat > spac->bSendAtom_nalloc) + { + nalloc_old = spac->bSendAtom_nalloc; + spac->bSendAtom_nalloc = over_alloc_dd(nat_tot_specat); + srenew(spac->bSendAtom, spac->bSendAtom_nalloc); + for (i = nalloc_old; i < spac->bSendAtom_nalloc; i++) + { + spac->bSendAtom[i] = FALSE; + } + } + spas = &spac->spas[d][dir]; + n0 = spac->nreq[d][dir][0]; + nr = spac->nreq[d][dir][1]; + if (debug) + { + fprintf(debug, "dim=%d, dir=%d, searching for %d atoms\n", + d, dir, nr); + } + start = nlast - nr; + spas->nsend = 0; + nsend[0] = 0; + for (i = 0; i < nr; i++) + { + indr = ireq->ind[start+i]; + ind = -1; + /* Check if this is a home atom and if so ind will be set */ + if (!ga2la_get_home(dd->ga2la, indr, &ind)) + { + /* Search in the communicated atoms */ + ind = gmx_hash_get_minone(ga2la_specat, indr); + } + if (ind >= 0) + { + if (i < n0 || !spac->bSendAtom[ind]) + { + if (spas->nsend+1 > spas->a_nalloc) + { + spas->a_nalloc = over_alloc_large(spas->nsend+1); + srenew(spas->a, spas->a_nalloc); + } + /* Store the local index so we know which coordinates + * to send out later. + */ + spas->a[spas->nsend] = ind; + spac->bSendAtom[ind] = TRUE; + if (spas->nsend+1 > spac->ibuf_nalloc) + { + spac->ibuf_nalloc = over_alloc_large(spas->nsend+1); + srenew(spac->ibuf, spac->ibuf_nalloc); + } + /* Store the global index so we can send it now */ + spac->ibuf[spas->nsend] = indr; + if (i < n0) + { + nsend[0]++; + } + spas->nsend++; + } + } + } + nlast = start; + /* Clear the local flags */ + for (i = 0; i < spas->nsend; i++) + { + spac->bSendAtom[spas->a[i]] = FALSE; + } + /* Send and receive the number of indices to communicate */ + nsend[1] = spas->nsend; + dd_sendrecv_int(dd, d, dir == 0 ? dddirBackward : dddirForward, + nsend, 2, buf, 2); + if (debug) + { + fprintf(debug, "Send to node %d, %d (%d) indices, " + "receive from node %d, %d (%d) indices\n", + dd->neighbor[d][1-dir], nsend[1], nsend[0], + dd->neighbor[d][dir], buf[1], buf[0]); + if (gmx_debug_at) + { + for (i = 0; i < spas->nsend; i++) + { + fprintf(debug, " %d", spac->ibuf[i]+1); + } + fprintf(debug, "\n"); + } + } + nrecv_local += buf[0]; + spas->nrecv = buf[1]; + if (nat_tot_specat + spas->nrecv > dd->gatindex_nalloc) + { + dd->gatindex_nalloc = + over_alloc_dd(nat_tot_specat + spas->nrecv); + srenew(dd->gatindex, dd->gatindex_nalloc); + } + /* Send and receive the indices */ + dd_sendrecv_int(dd, d, dir == 0 ? dddirBackward : dddirForward, + spac->ibuf, spas->nsend, + dd->gatindex+nat_tot_specat, spas->nrecv); + nat_tot_specat += spas->nrecv; + } + + /* Allocate the x/f communication buffers */ + ns = spac->spas[d][0].nsend; + nr = spac->spas[d][0].nrecv; + if (ndir == 2) + { + ns += spac->spas[d][1].nsend; + nr += spac->spas[d][1].nrecv; + } + if (vbuf_fac*ns > spac->vbuf_nalloc) + { + spac->vbuf_nalloc = over_alloc_dd(vbuf_fac*ns); + srenew(spac->vbuf, spac->vbuf_nalloc); + } + if (vbuf_fac == 2 && vbuf_fac*nr > spac->vbuf2_nalloc) + { + spac->vbuf2_nalloc = over_alloc_dd(vbuf_fac*nr); + srenew(spac->vbuf2, spac->vbuf2_nalloc); + } + + /* Make a global to local index for the communication atoms */ + for (i = nat_tot_prev; i < nat_tot_specat; i++) + { + gmx_hash_change_or_set(ga2la_specat, dd->gatindex[i], i); + } + } + + /* Check that in the end we got the number of atoms we asked for */ + if (nrecv_local != ireq->n) + { + if (debug) + { + fprintf(debug, "Requested %d, received %d (tot recv %d)\n", + ireq->n, nrecv_local, nat_tot_specat-at_start); + if (gmx_debug_at) + { + for (i = 0; i < ireq->n; i++) + { + ind = gmx_hash_get_minone(ga2la_specat, ireq->ind[i]); + fprintf(debug, " %s%d", + (ind >= 0) ? "" : "!", + ireq->ind[i]+1); + } + fprintf(debug, "\n"); + } + } + fprintf(stderr, "\nDD cell %d %d %d: Neighboring cells do not have atoms:", + dd->ci[XX], dd->ci[YY], dd->ci[ZZ]); + for (i = 0; i < ireq->n; i++) + { + if (gmx_hash_get_minone(ga2la_specat, ireq->ind[i]) < 0) + { + fprintf(stderr, " %d", ireq->ind[i]+1); + } + } + fprintf(stderr, "\n"); + gmx_fatal(FARGS, "DD cell %d %d %d could only obtain %d of the %d atoms that are connected via %ss from the neighboring cells. This probably means your %s lengths are too long compared to the domain decomposition cell size. Decrease the number of domain decomposition grid cells%s%s.", + dd->ci[XX], dd->ci[YY], dd->ci[ZZ], + nrecv_local, ireq->n, specat_type, + specat_type, add_err, + dd->bGridJump ? " or use the -rcon option of mdrun" : ""); + } + + spac->at_start = at_start; + spac->at_end = nat_tot_specat; + + if (debug) + { + fprintf(debug, "Done setup_specat_communication\n"); + } + + return nat_tot_specat; +} + +static void walk_out(int con, int con_offset, int a, int offset, int nrec, + int ncon1, const t_iatom *ia1, const t_iatom *ia2, + const t_blocka *at2con, + const gmx_ga2la_t ga2la, gmx_bool bHomeConnect, + gmx_domdec_constraints_t *dc, + gmx_domdec_specat_comm_t *dcc, + t_ilist *il_local, + ind_req_t *ireq) +{ + int a1_gl, a2_gl, a_loc, i, coni, b; + const t_iatom *iap; + + if (dc->gc_req[con_offset+con] == 0) + { + /* Add this non-home constraint to the list */ + if (dc->ncon+1 > dc->con_nalloc) + { + dc->con_nalloc = over_alloc_large(dc->ncon+1); + srenew(dc->con_gl, dc->con_nalloc); + srenew(dc->con_nlocat, dc->con_nalloc); + } + dc->con_gl[dc->ncon] = con_offset + con; + dc->con_nlocat[dc->ncon] = (bHomeConnect ? 1 : 0); + dc->gc_req[con_offset+con] = 1; + if (il_local->nr + 3 > il_local->nalloc) + { + il_local->nalloc = over_alloc_dd(il_local->nr+3); + srenew(il_local->iatoms, il_local->nalloc); + } + iap = constr_iatomptr(ncon1, ia1, ia2, con); + il_local->iatoms[il_local->nr++] = iap[0]; + a1_gl = offset + iap[1]; + a2_gl = offset + iap[2]; + /* The following indexing code can probably be optizimed */ + if (ga2la_get_home(ga2la, a1_gl, &a_loc)) + { + il_local->iatoms[il_local->nr++] = a_loc; + } + else + { + /* We set this index later */ + il_local->iatoms[il_local->nr++] = -a1_gl - 1; + } + if (ga2la_get_home(ga2la, a2_gl, &a_loc)) + { + il_local->iatoms[il_local->nr++] = a_loc; + } + else + { + /* We set this index later */ + il_local->iatoms[il_local->nr++] = -a2_gl - 1; + } + dc->ncon++; + } + /* Check to not ask for the same atom more than once */ + if (gmx_hash_get_minone(dc->ga2la, offset+a) == -1) + { + assert(dcc); + /* Add this non-home atom to the list */ + if (ireq->n+1 > ireq->nalloc) + { + ireq->nalloc = over_alloc_large(ireq->n+1); + srenew(ireq->ind, ireq->nalloc); + } + ireq->ind[ireq->n++] = offset + a; + /* Temporarily mark with -2, we get the index later */ + gmx_hash_set(dc->ga2la, offset+a, -2); + } + + if (nrec > 0) + { + for (i = at2con->index[a]; i < at2con->index[a+1]; i++) + { + coni = at2con->a[i]; + if (coni != con) + { + /* Walk further */ + iap = constr_iatomptr(ncon1, ia1, ia2, coni); + if (a == iap[1]) + { + b = iap[2]; + } + else + { + b = iap[1]; + } + if (!ga2la_get_home(ga2la, offset+b, &a_loc)) + { + walk_out(coni, con_offset, b, offset, nrec-1, + ncon1, ia1, ia2, at2con, + ga2la, FALSE, dc, dcc, il_local, ireq); + } + } + } + } +} + +static void atoms_to_settles(gmx_domdec_t *dd, + const gmx_mtop_t *mtop, + const int *cginfo, + const int **at2settle_mt, + int cg_start, int cg_end, + t_ilist *ils_local, + ind_req_t *ireq) +{ + gmx_ga2la_t ga2la; + gmx_mtop_atomlookup_t alook; + int settle; + int nral, sa; + int cg, a, a_gl, a_glsa, a_gls[3], a_locs[3]; + int mb, molnr, a_mol, offset; + const gmx_molblock_t *molb; + const t_iatom *ia1; + gmx_bool a_home[3]; + int nlocal; + gmx_bool bAssign; + + ga2la = dd->ga2la; + + alook = gmx_mtop_atomlookup_settle_init(mtop); + + nral = NRAL(F_SETTLE); + + for (cg = cg_start; cg < cg_end; cg++) + { + if (GET_CGINFO_SETTLE(cginfo[cg])) + { + for (a = dd->cgindex[cg]; a < dd->cgindex[cg+1]; a++) + { + a_gl = dd->gatindex[a]; + + gmx_mtop_atomnr_to_molblock_ind(alook, a_gl, &mb, &molnr, &a_mol); + molb = &mtop->molblock[mb]; + + settle = at2settle_mt[molb->type][a_mol]; + + if (settle >= 0) + { + offset = a_gl - a_mol; + + ia1 = mtop->moltype[molb->type].ilist[F_SETTLE].iatoms; + + bAssign = FALSE; + nlocal = 0; + for (sa = 0; sa < nral; sa++) + { + a_glsa = offset + ia1[settle*(1+nral)+1+sa]; + a_gls[sa] = a_glsa; + a_home[sa] = ga2la_get_home(ga2la, a_glsa, &a_locs[sa]); + if (a_home[sa]) + { + if (nlocal == 0 && a_gl == a_glsa) + { + bAssign = TRUE; + } + nlocal++; + } + } + + if (bAssign) + { + if (ils_local->nr+1+nral > ils_local->nalloc) + { + ils_local->nalloc = over_alloc_dd(ils_local->nr+1+nral); + srenew(ils_local->iatoms, ils_local->nalloc); + } + + ils_local->iatoms[ils_local->nr++] = ia1[settle*4]; + + for (sa = 0; sa < nral; sa++) + { + if (ga2la_get_home(ga2la, a_gls[sa], &a_locs[sa])) + { + ils_local->iatoms[ils_local->nr++] = a_locs[sa]; + } + else + { + ils_local->iatoms[ils_local->nr++] = -a_gls[sa] - 1; + /* Add this non-home atom to the list */ + if (ireq->n+1 > ireq->nalloc) + { + ireq->nalloc = over_alloc_large(ireq->n+1); + srenew(ireq->ind, ireq->nalloc); + } + ireq->ind[ireq->n++] = a_gls[sa]; + /* A check on double atom requests is + * not required for settle. + */ + } + } + } + } + } + } + } + + gmx_mtop_atomlookup_destroy(alook); +} + +static void atoms_to_constraints(gmx_domdec_t *dd, + const gmx_mtop_t *mtop, + const int *cginfo, + const t_blocka *at2con_mt, int nrec, + t_ilist *ilc_local, + ind_req_t *ireq) +{ + const t_blocka *at2con; + gmx_ga2la_t ga2la; + gmx_mtop_atomlookup_t alook; + int ncon1; + gmx_molblock_t *molb; + t_iatom *ia1, *ia2, *iap; + int nhome, cg, a, a_gl, a_mol, a_loc, b_lo, offset, mb, molnr, b_mol, i, con, con_offset; + gmx_domdec_constraints_t *dc; + gmx_domdec_specat_comm_t *dcc; + + dc = dd->constraints; + dcc = dd->constraint_comm; + + ga2la = dd->ga2la; + + alook = gmx_mtop_atomlookup_init(mtop); + + nhome = 0; + for (cg = 0; cg < dd->ncg_home; cg++) + { + if (GET_CGINFO_CONSTR(cginfo[cg])) + { + for (a = dd->cgindex[cg]; a < dd->cgindex[cg+1]; a++) + { + a_gl = dd->gatindex[a]; + + gmx_mtop_atomnr_to_molblock_ind(alook, a_gl, &mb, &molnr, &a_mol); + molb = &mtop->molblock[mb]; + + ncon1 = mtop->moltype[molb->type].ilist[F_CONSTR].nr/NRAL(F_SETTLE); + + ia1 = mtop->moltype[molb->type].ilist[F_CONSTR].iatoms; + ia2 = mtop->moltype[molb->type].ilist[F_CONSTRNC].iatoms; + + /* Calculate the global constraint number offset for the molecule. + * This is only required for the global index to make sure + * that we use each constraint only once. + */ + con_offset = + dc->molb_con_offset[mb] + molnr*dc->molb_ncon_mol[mb]; + + /* The global atom number offset for this molecule */ + offset = a_gl - a_mol; + at2con = &at2con_mt[molb->type]; + for (i = at2con->index[a_mol]; i < at2con->index[a_mol+1]; i++) + { + con = at2con->a[i]; + iap = constr_iatomptr(ncon1, ia1, ia2, con); + if (a_mol == iap[1]) + { + b_mol = iap[2]; + } + else + { + b_mol = iap[1]; + } + if (ga2la_get_home(ga2la, offset+b_mol, &a_loc)) + { + /* Add this fully home constraint at the first atom */ + if (a_mol < b_mol) + { + if (dc->ncon+1 > dc->con_nalloc) + { + dc->con_nalloc = over_alloc_large(dc->ncon+1); + srenew(dc->con_gl, dc->con_nalloc); + srenew(dc->con_nlocat, dc->con_nalloc); + } + dc->con_gl[dc->ncon] = con_offset + con; + dc->con_nlocat[dc->ncon] = 2; + if (ilc_local->nr + 3 > ilc_local->nalloc) + { + ilc_local->nalloc = over_alloc_dd(ilc_local->nr + 3); + srenew(ilc_local->iatoms, ilc_local->nalloc); + } + b_lo = a_loc; + ilc_local->iatoms[ilc_local->nr++] = iap[0]; + ilc_local->iatoms[ilc_local->nr++] = (a_gl == iap[1] ? a : b_lo); + ilc_local->iatoms[ilc_local->nr++] = (a_gl == iap[1] ? b_lo : a ); + dc->ncon++; + nhome++; + } + } + else + { + /* We need the nrec constraints coupled to this constraint, + * so we need to walk out of the home cell by nrec+1 atoms, + * since already atom bg is not locally present. + * Therefore we call walk_out with nrec recursions to go + * after this first call. + */ + walk_out(con, con_offset, b_mol, offset, nrec, + ncon1, ia1, ia2, at2con, + dd->ga2la, TRUE, dc, dcc, ilc_local, ireq); + } + } + } + } + } + + gmx_mtop_atomlookup_destroy(alook); + + if (debug) + { + fprintf(debug, + "Constraints: home %3d border %3d atoms: %3d\n", + nhome, dc->ncon-nhome, + dd->constraint_comm ? ireq->n : 0); + } +} + +int dd_make_local_constraints(gmx_domdec_t *dd, int at_start, + const gmx_mtop_t *mtop, + const int *cginfo, + gmx_constr_t constr, int nrec, + t_ilist *il_local) +{ + gmx_domdec_constraints_t *dc; + t_ilist *ilc_local, *ils_local; + ind_req_t *ireq; + const t_blocka *at2con_mt; + const int **at2settle_mt; + gmx_hash_t ga2la_specat; + int at_end, i, j; + t_iatom *iap; + + dc = dd->constraints; + + ilc_local = &il_local[F_CONSTR]; + ils_local = &il_local[F_SETTLE]; + + dc->ncon = 0; + ilc_local->nr = 0; + if (dd->constraint_comm) + { + at2con_mt = atom2constraints_moltype(constr); + ireq = &dd->constraint_comm->ireq[0]; + ireq->n = 0; + } + else + { + at2con_mt = NULL; + ireq = NULL; + } + + if (dd->bInterCGsettles) + { + at2settle_mt = atom2settle_moltype(constr); + ils_local->nr = 0; + } + else + { + /* Settle works inside charge groups, we assigned them already */ + at2settle_mt = NULL; + } + + if (at2settle_mt == NULL) + { + atoms_to_constraints(dd, mtop, cginfo, at2con_mt, nrec, + ilc_local, ireq); + } + else + { + int t0_set; + int thread; + + /* Do the constraints, if present, on the first thread. + * Do the settles on all other threads. + */ + t0_set = ((at2con_mt != NULL && dc->nthread > 1) ? 1 : 0); + +#pragma omp parallel for num_threads(dc->nthread) schedule(static) + for (thread = 0; thread < dc->nthread; thread++) + { + if (at2con_mt && thread == 0) + { + atoms_to_constraints(dd, mtop, cginfo, at2con_mt, nrec, + ilc_local, ireq); + } + + if (thread >= t0_set) + { + int cg0, cg1; + t_ilist *ilst; + ind_req_t *ireqt; + + /* Distribute the settle check+assignments over + * dc->nthread or dc->nthread-1 threads. + */ + cg0 = (dd->ncg_home*(thread-t0_set ))/(dc->nthread-t0_set); + cg1 = (dd->ncg_home*(thread-t0_set+1))/(dc->nthread-t0_set); + + if (thread == t0_set) + { + ilst = ils_local; + } + else + { + ilst = &dc->ils[thread]; + } + ilst->nr = 0; + + ireqt = &dd->constraint_comm->ireq[thread]; + if (thread > 0) + { + ireqt->n = 0; + } + + atoms_to_settles(dd, mtop, cginfo, at2settle_mt, + cg0, cg1, + ilst, ireqt); + } + } + + /* Combine the generate settles and requested indices */ + for (thread = 1; thread < dc->nthread; thread++) + { + t_ilist *ilst; + ind_req_t *ireqt; + int ia; + + if (thread > t0_set) + { + ilst = &dc->ils[thread]; + if (ils_local->nr + ilst->nr > ils_local->nalloc) + { + ils_local->nalloc = over_alloc_large(ils_local->nr + ilst->nr); + srenew(ils_local->iatoms, ils_local->nalloc); + } + for (ia = 0; ia < ilst->nr; ia++) + { + ils_local->iatoms[ils_local->nr+ia] = ilst->iatoms[ia]; + } + ils_local->nr += ilst->nr; + } + + ireqt = &dd->constraint_comm->ireq[thread]; + if (ireq->n+ireqt->n > ireq->nalloc) + { + ireq->nalloc = over_alloc_large(ireq->n+ireqt->n); + srenew(ireq->ind, ireq->nalloc); + } + for (ia = 0; ia < ireqt->n; ia++) + { + ireq->ind[ireq->n+ia] = ireqt->ind[ia]; + } + ireq->n += ireqt->n; + } + + if (debug) + { + fprintf(debug, "Settles: total %3d\n", ils_local->nr/4); + } + } + + if (dd->constraint_comm) + { + int nral1; + + at_end = + setup_specat_communication(dd, ireq, dd->constraint_comm, + dd->constraints->ga2la, + at_start, 2, + "constraint", " or lincs-order"); + + /* Fill in the missing indices */ + ga2la_specat = dd->constraints->ga2la; + + nral1 = 1 + NRAL(F_CONSTR); + for (i = 0; i < ilc_local->nr; i += nral1) + { + iap = ilc_local->iatoms + i; + for (j = 1; j < nral1; j++) + { + if (iap[j] < 0) + { + iap[j] = gmx_hash_get_minone(ga2la_specat, -iap[j]-1); + } + } + } + + nral1 = 1 + NRAL(F_SETTLE); + for (i = 0; i < ils_local->nr; i += nral1) + { + iap = ils_local->iatoms + i; + for (j = 1; j < nral1; j++) + { + if (iap[j] < 0) + { + iap[j] = gmx_hash_get_minone(ga2la_specat, -iap[j]-1); + } + } + } + } + else + { + at_end = at_start; + } + + return at_end; +} + +int dd_make_local_vsites(gmx_domdec_t *dd, int at_start, t_ilist *lil) +{ + gmx_domdec_specat_comm_t *spac; + ind_req_t *ireq; + gmx_hash_t ga2la_specat; + int ftype, nral, i, j, gat, a; + t_ilist *lilf; + t_iatom *iatoms; + int at_end; + + spac = dd->vsite_comm; + ireq = &spac->ireq[0]; + ga2la_specat = dd->ga2la_vsite; + + ireq->n = 0; + /* Loop over all the home vsites */ + for (ftype = 0; ftype < F_NRE; ftype++) + { + if (interaction_function[ftype].flags & IF_VSITE) + { + nral = NRAL(ftype); + lilf = &lil[ftype]; + for (i = 0; i < lilf->nr; i += 1+nral) + { + iatoms = lilf->iatoms + i; + /* Check if we have the other atoms */ + for (j = 1; j < 1+nral; j++) + { + if (iatoms[j] < 0) + { + /* This is not a home atom, + * we need to ask our neighbors. + */ + a = -iatoms[j] - 1; + /* Check to not ask for the same atom more than once */ + if (gmx_hash_get_minone(dd->ga2la_vsite, a) == -1) + { + /* Add this non-home atom to the list */ + if (ireq->n+1 > ireq->nalloc) + { + ireq->nalloc = over_alloc_large(ireq->n+1); + srenew(ireq->ind, ireq->nalloc); + } + ireq->ind[ireq->n++] = a; + /* Temporarily mark with -2, + * we get the index later. + */ + gmx_hash_set(ga2la_specat, a, -2); + } + } + } + } + } + } + + at_end = setup_specat_communication(dd, ireq, dd->vsite_comm, ga2la_specat, + at_start, 1, "vsite", ""); + + /* Fill in the missing indices */ + for (ftype = 0; ftype < F_NRE; ftype++) + { + if (interaction_function[ftype].flags & IF_VSITE) + { + nral = NRAL(ftype); + lilf = &lil[ftype]; + for (i = 0; i < lilf->nr; i += 1+nral) + { + iatoms = lilf->iatoms + i; + for (j = 1; j < 1+nral; j++) + { + if (iatoms[j] < 0) + { + iatoms[j] = gmx_hash_get_minone(ga2la_specat, -iatoms[j]-1); + } + } + } + } + } + + return at_end; +} + +static gmx_domdec_specat_comm_t *specat_comm_init(int nthread) +{ + gmx_domdec_specat_comm_t *spac; + + snew(spac, 1); + spac->nthread = nthread; + snew(spac->ireq, spac->nthread); + + return spac; +} + +void init_domdec_constraints(gmx_domdec_t *dd, + gmx_mtop_t *mtop) +{ + gmx_domdec_constraints_t *dc; + gmx_molblock_t *molb; + int mb, ncon, c, a; + + if (debug) + { + fprintf(debug, "Begin init_domdec_constraints\n"); + } + + snew(dd->constraints, 1); + dc = dd->constraints; + + snew(dc->molb_con_offset, mtop->nmolblock); + snew(dc->molb_ncon_mol, mtop->nmolblock); + + ncon = 0; + for (mb = 0; mb < mtop->nmolblock; mb++) + { + molb = &mtop->molblock[mb]; + dc->molb_con_offset[mb] = ncon; + dc->molb_ncon_mol[mb] = + mtop->moltype[molb->type].ilist[F_CONSTR].nr/3 + + mtop->moltype[molb->type].ilist[F_CONSTRNC].nr/3; + ncon += molb->nmol*dc->molb_ncon_mol[mb]; + } + + if (ncon > 0) + { + snew(dc->gc_req, ncon); + for (c = 0; c < ncon; c++) + { + dc->gc_req[c] = 0; + } + } + + /* Use a hash table for the global to local index. + * The number of keys is a rough estimate, it will be optimized later. + */ + dc->ga2la = gmx_hash_init(min(mtop->natoms/20, + mtop->natoms/(2*dd->nnodes))); + + dc->nthread = gmx_omp_nthreads_get(emntDomdec); + snew(dc->ils, dc->nthread); + + dd->constraint_comm = specat_comm_init(dc->nthread); +} + +void init_domdec_vsites(gmx_domdec_t *dd, int n_intercg_vsite) +{ + int i; + gmx_domdec_constraints_t *dc; + + if (debug) + { + fprintf(debug, "Begin init_domdec_vsites\n"); + } + + /* Use a hash table for the global to local index. + * The number of keys is a rough estimate, it will be optimized later. + */ + dd->ga2la_vsite = gmx_hash_init(min(n_intercg_vsite/20, + n_intercg_vsite/(2*dd->nnodes))); + + dd->vsite_comm = specat_comm_init(1); +}