コード例 #1
0
ファイル: mtop_util.c プロジェクト: TTarenzi/MMCG-HAdResS
int gmx_mtop_ftype_count(const gmx_mtop_t *mtop,int ftype)
{
    gmx_mtop_ilistloop_t iloop;
    t_ilist *il;
    int n,nmol;

    n = 0;

    iloop = gmx_mtop_ilistloop_init(mtop);
    while (gmx_mtop_ilistloop_next(iloop,&il,&nmol))
    {
        n += nmol*il[ftype].nr/(1+NRAL(ftype));
    }

    return n;
}
コード例 #2
0
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;
}
コード例 #3
0
ファイル: orires.c プロジェクト: aar2163/GROMACS
void init_orires(FILE *fplog,const gmx_mtop_t *mtop,
                 rvec xref[],
                 const t_inputrec *ir,
                 const gmx_multisim_t *ms,t_oriresdata *od,
                 t_state *state)
{
    int    i,j,d,ex,nmol,nr,*nr_ex;
    double mtot;
    rvec   com;
    gmx_mtop_ilistloop_t iloop;
    t_ilist *il;
    gmx_mtop_atomloop_all_t aloop;
    t_atom *atom;

    od->fc  = ir->orires_fc;
    od->nex = 0;
    od->S   = NULL;

    od->nr = gmx_mtop_ftype_count(mtop,F_ORIRES);
    if (od->nr == 0)
    {
        return;
    }
    
    nr_ex = NULL;
    
    iloop = gmx_mtop_ilistloop_init(mtop);
    while (gmx_mtop_ilistloop_next(iloop,&il,&nmol))
    {
        for(i=0; i<il[F_ORIRES].nr; i+=3)
        {
            ex = mtop->ffparams.iparams[il[F_ORIRES].iatoms[i]].orires.ex;
            if (ex >= od->nex)
            {
                srenew(nr_ex,ex+1);
                for(j=od->nex; j<ex+1; j++)
                {
                    nr_ex[j] = 0;
            }
                od->nex = ex+1;
            }
            nr_ex[ex]++;
        }
    }
    snew(od->S,od->nex);
    /* When not doing time averaging, the instaneous and time averaged data
     * are indentical and the pointers can point to the same memory.
     */
    snew(od->Dinsl,od->nr);
    if (ms)
    {
        snew(od->Dins,od->nr);
    }
    else
    {
        od->Dins = od->Dinsl;
    }

    if (ir->orires_tau == 0)
    {
        od->Dtav = od->Dins;
        od->edt  = 0.0;
        od->edt1 = 1.0;
    }
    else
    {
        snew(od->Dtav,od->nr);
        od->edt  = exp(-ir->delta_t/ir->orires_tau);
        od->edt1 = 1.0 - od->edt;

        /* Extend the state with the orires history */
        state->flags |= (1<<estORIRE_INITF);
        state->hist.orire_initf = 1;
        state->flags |= (1<<estORIRE_DTAV);
        state->hist.norire_Dtav = od->nr*5;
        snew(state->hist.orire_Dtav,state->hist.norire_Dtav);
    }

    snew(od->oinsl,od->nr);
    if (ms)
    {
        snew(od->oins,od->nr);
    }
    else
    {
        od->oins = od->oinsl;
    }
    if (ir->orires_tau == 0) {
        od->otav = od->oins;
    }
    else
    {
        snew(od->otav,od->nr);
    }
    snew(od->tmp,od->nex);
    snew(od->TMP,od->nex);
    for(ex=0; ex<od->nex; ex++)
    {
        snew(od->TMP[ex],5);
        for(i=0; i<5; i++)
        {
            snew(od->TMP[ex][i],5);
        }
    }
    
    od->nref = 0;
    for(i=0; i<mtop->natoms; i++)
    {
        if (ggrpnr(&mtop->groups,egcORFIT,i) == 0)
        {
            od->nref++;
        }
    }
    snew(od->mref,od->nref);
    snew(od->xref,od->nref);
    snew(od->xtmp,od->nref);
    
    snew(od->eig,od->nex*12);
    
    /* Determine the reference structure on the master node.
     * Copy it to the other nodes after checking multi compatibility,
     * so we are sure the subsystems match before copying.
     */
    clear_rvec(com);
    mtot = 0.0;
    j = 0;
    aloop = gmx_mtop_atomloop_all_init(mtop);
    while(gmx_mtop_atomloop_all_next(aloop,&i,&atom))
    {
        if (mtop->groups.grpnr[egcORFIT] == NULL ||
            mtop->groups.grpnr[egcORFIT][i] == 0)
        {
            /* Not correct for free-energy with changing masses */
            od->mref[j] = atom->m;
            if (ms==NULL || MASTERSIM(ms))
            {
                copy_rvec(xref[i],od->xref[j]);
                for(d=0; d<DIM; d++)
                {
                    com[d] += od->mref[j]*xref[i][d];
                }
            }
            mtot += od->mref[j];
            j++;
        }
    }
    svmul(1.0/mtot,com,com);
    if (ms==NULL || MASTERSIM(ms))
    {
        for(j=0; j<od->nref; j++)
        {
            rvec_dec(od->xref[j],com);
        }
    }
    
    fprintf(fplog,"Found %d orientation experiments\n",od->nex);
    for(i=0; i<od->nex; i++)
    {
        fprintf(fplog,"  experiment %d has %d restraints\n",i+1,nr_ex[i]);
    }
    
    sfree(nr_ex);
    
    fprintf(fplog,"  the fit group consists of %d atoms and has total mass %g\n",
            od->nref,mtot);
    
    if (ms)
    {
        fprintf(fplog,"  the orientation restraints are ensemble averaged over %d systems\n",ms->nsim);
        
        check_multi_int(fplog,ms,od->nr,
                        "the number of orientation restraints");
        check_multi_int(fplog,ms,od->nref,
                        "the number of fit atoms for orientation restraining");
        check_multi_int(fplog,ms,ir->nsteps,"nsteps");
        /* Copy the reference coordinates from the master to the other nodes */
        gmx_sum_sim(DIM*od->nref,od->xref[0],ms);
    }
    
    please_cite(fplog,"Hess2003");
}
コード例 #4
0
void init_disres(FILE *fplog,const gmx_mtop_t *mtop,
                 t_inputrec *ir,const t_commrec *cr,gmx_bool bPartDecomp,
                 t_fcdata *fcd,t_state *state)
{
    int          fa,nmol,i,npair,np;
    t_iparams    *ip;
    t_disresdata *dd;
    history_t    *hist;
    gmx_mtop_ilistloop_t iloop;
    t_ilist      *il;
    char         *ptr;
    
    dd = &(fcd->disres);

    if (gmx_mtop_ftype_count(mtop,F_DISRES) == 0)
    {
        dd->nres = 0;

        return;
    }
    
    if (fplog)
    {
        fprintf(fplog,"Initializing the distance restraints\n");
    }
    

    if (ir->eDisre == edrEnsemble)
    {
        gmx_fatal(FARGS,"Sorry, distance restraints with ensemble averaging over multiple molecules in one system are not functional in this version of GROMACS");
    }

    dd->dr_weighting = ir->eDisreWeighting;
    dd->dr_fc        = ir->dr_fc;
    if (EI_DYNAMICS(ir->eI))
    {
        dd->dr_tau   = ir->dr_tau;
    }
    else
    {
        dd->dr_tau   = 0.0;
    }
    if (dd->dr_tau == 0.0)
    {
        dd->dr_bMixed = FALSE;
        dd->ETerm = 0.0;
    }
    else
    {
        dd->dr_bMixed = ir->bDisreMixed;
        dd->ETerm = exp(-(ir->delta_t/ir->dr_tau));
    }
    dd->ETerm1        = 1.0 - dd->ETerm;
    
    ip = mtop->ffparams.iparams;

    dd->nres  = 0;
    dd->npair = 0;
    iloop = gmx_mtop_ilistloop_init(mtop);
    while (gmx_mtop_ilistloop_next(iloop,&il,&nmol)) {
        np = 0;
        for(fa=0; fa<il[F_DISRES].nr; fa+=3)
        {
            np++;
            npair = mtop->ffparams.iparams[il[F_DISRES].iatoms[fa]].disres.npair;
            if (np == npair)
            {
                dd->nres  += (ir->eDisre==edrEnsemble ? 1 : nmol)*npair;
                dd->npair += nmol*npair;
                np = 0;
            }
        }
    }

    if (cr && PAR(cr) && !bPartDecomp)
    {
        /* Temporary check, will be removed when disre is implemented with DD */
        const char *notestr="NOTE: atoms involved in distance restraints should be within the longest cut-off distance, if this is not the case mdrun generates a fatal error, in that case use particle decomposition (mdrun option -pd)";
        
        if (MASTER(cr))
            fprintf(stderr,"\n%s\n\n",notestr);
        if (fplog)
            fprintf(fplog,"%s\n",notestr);

        if (dd->dr_tau != 0 || ir->eDisre == edrEnsemble || cr->ms != NULL ||
            dd->nres != dd->npair)
        {
            gmx_fatal(FARGS,"Time or ensemble averaged or multiple pair distance restraints do not work (yet) with domain decomposition, use particle decomposition (mdrun option -pd)");
        }
        if (ir->nstdisreout != 0)
        {
            if (fplog)
            {
                fprintf(fplog,"\nWARNING: Can not write distance restraint data to energy file with domain decomposition\n\n");
            }
            if (MASTER(cr))
            {
                fprintf(stderr,"\nWARNING: Can not write distance restraint data to energy file with domain decomposition\n");
            }
            ir->nstdisreout = 0;
        }
    }

    snew(dd->rt,dd->npair);
    
    if (dd->dr_tau != 0.0)
    {
        hist = &state->hist;
        /* Set the "history lack" factor to 1 */
        state->flags |= (1<<estDISRE_INITF);
        hist->disre_initf = 1.0;
        /* Allocate space for the r^-3 time averages */
        state->flags |= (1<<estDISRE_RM3TAV);
        hist->ndisrepairs = dd->npair;
        snew(hist->disre_rm3tav,hist->ndisrepairs);
    }
    /* Allocate space for a copy of rm3tav,
     * so we can call do_force without modifying the state.
     */
    snew(dd->rm3tav,dd->npair);

    /* Allocate Rt_6 and Rtav_6 consecutively in memory so they can be
     * averaged over the processors in one call (in calc_disre_R_6)
     */
    snew(dd->Rt_6,2*dd->nres);
    dd->Rtav_6 = &(dd->Rt_6[dd->nres]);

    ptr = getenv("GMX_DISRE_ENSEMBLE_SIZE");
    if (cr && cr->ms != NULL && ptr != NULL)
    {
#ifdef GMX_MPI
        dd->nsystems = 0;
        sscanf(ptr,"%d",&dd->nsystems);
        if (fplog)
        {
            fprintf(fplog,"Found GMX_DISRE_ENSEMBLE_SIZE set to %d systems per ensemble\n",dd->nsystems);
        }
        check_multi_int(fplog,cr->ms,dd->nsystems,
                        "the number of systems per ensemble");
        if (dd->nsystems <= 0 ||  cr->ms->nsim % dd->nsystems != 0)
        {
            gmx_fatal(FARGS,"The number of systems %d is not divisible by the number of systems per ensemble %d\n",cr->ms->nsim,dd->nsystems);
        }
        /* Split the inter-master communicator into different ensembles */
        MPI_Comm_split(cr->ms->mpi_comm_masters,
                       cr->ms->sim/dd->nsystems,
                       cr->ms->sim,
                       &dd->mpi_comm_ensemble);
        if (fplog)
        {
            fprintf(fplog,"Our ensemble consists of systems:");
            for(i=0; i<dd->nsystems; i++)
            {
                fprintf(fplog," %d",
                        (cr->ms->sim/dd->nsystems)*dd->nsystems+i);
            }
            fprintf(fplog,"\n");
            }
        snew(dd->Rtl_6,dd->nres);
#endif
    }
    else
    {
        dd->nsystems = 1;
        dd->Rtl_6 = dd->Rt_6;
    }
    
    if (dd->npair > 0)
    {
        if (fplog) {
            fprintf(fplog,"There are %d distance restraints involving %d atom pairs\n",dd->nres,dd->npair);
        }
        if (cr && cr->ms)
        {
            check_multi_int(fplog,cr->ms,fcd->disres.nres,
                            "the number of distance restraints");
        }
        please_cite(fplog,"Tropp80a");
        please_cite(fplog,"Torda89a");
    }
}
コード例 #5
0
ファイル: disre.cpp プロジェクト: aalhossary/gromacs-HREMD
void init_disres(FILE *fplog, const gmx_mtop_t *mtop,
                 t_inputrec *ir, const t_commrec *cr,
                 t_fcdata *fcd, t_state *state, gmx_bool bIsREMD)
{
    int                  fa, nmol, npair, np;
    t_disresdata        *dd;
    history_t           *hist;
    gmx_mtop_ilistloop_t iloop;
    t_ilist             *il;
    char                *ptr;

    dd = &(fcd->disres);

    if (gmx_mtop_ftype_count(mtop, F_DISRES) == 0)
    {
        dd->nres = 0;

        return;
    }

    if (fplog)
    {
        fprintf(fplog, "Initializing the distance restraints\n");
    }


    if (ir->eDisre == edrEnsemble)
    {
        gmx_fatal(FARGS, "Sorry, distance restraints with ensemble averaging over multiple molecules in one system are not functional in this version of GROMACS");
    }

    dd->dr_weighting = ir->eDisreWeighting;
    dd->dr_fc        = ir->dr_fc;
    if (EI_DYNAMICS(ir->eI))
    {
        dd->dr_tau   = ir->dr_tau;
    }
    else
    {
        dd->dr_tau   = 0.0;
    }
    if (dd->dr_tau == 0.0)
    {
        dd->dr_bMixed = FALSE;
        dd->ETerm     = 0.0;
    }
    else
    {
        dd->dr_bMixed = ir->bDisreMixed;
        dd->ETerm     = std::exp(-(ir->delta_t/ir->dr_tau));
    }
    dd->ETerm1        = 1.0 - dd->ETerm;

    dd->nres  = 0;
    dd->npair = 0;
    iloop     = gmx_mtop_ilistloop_init(mtop);
    while (gmx_mtop_ilistloop_next(iloop, &il, &nmol))
    {
        np = 0;
        for (fa = 0; fa < il[F_DISRES].nr; fa += 3)
        {
            np++;
            npair = mtop->ffparams.iparams[il[F_DISRES].iatoms[fa]].disres.npair;
            if (np == npair)
            {
                dd->nres  += (ir->eDisre == edrEnsemble ? 1 : nmol)*npair;
                dd->npair += nmol*npair;
                np         = 0;
            }
        }
    }

    if (cr && PAR(cr))
    {
        /* Temporary check, will be removed when disre is implemented with DD */
        const char *notestr = "NOTE: atoms involved in distance restraints should be within the same domain. If this is not the case mdrun generates a fatal error. If you encounter this, use a single MPI rank (Verlet+OpenMP+GPUs work fine).";

        if (MASTER(cr))
        {
            fprintf(stderr, "\n%s\n\n", notestr);
        }
        if (fplog)
        {
            fprintf(fplog, "%s\n", notestr);
        }

        if (dd->dr_tau != 0 || ir->eDisre == edrEnsemble || cr->ms != NULL ||
            dd->nres != dd->npair)
        {
            gmx_fatal(FARGS, "Time or ensemble averaged or multiple pair distance restraints do not work (yet) with domain decomposition, use a single MPI rank%s", cr->ms ? " per simulation" : "");
        }
        if (ir->nstdisreout != 0)
        {
            if (fplog)
            {
                fprintf(fplog, "\nWARNING: Can not write distance restraint data to energy file with domain decomposition\n\n");
            }
            if (MASTER(cr))
            {
                fprintf(stderr, "\nWARNING: Can not write distance restraint data to energy file with domain decomposition\n");
            }
            ir->nstdisreout = 0;
        }
    }

    snew(dd->rt, dd->npair);

    if (dd->dr_tau != 0.0)
    {
        hist = &state->hist;
        /* Set the "history lack" factor to 1 */
        state->flags     |= (1<<estDISRE_INITF);
        hist->disre_initf = 1.0;
        /* Allocate space for the r^-3 time averages */
        state->flags     |= (1<<estDISRE_RM3TAV);
        hist->ndisrepairs = dd->npair;
        snew(hist->disre_rm3tav, hist->ndisrepairs);
    }
    /* Allocate space for a copy of rm3tav,
     * so we can call do_force without modifying the state.
     */
    snew(dd->rm3tav, dd->npair);

    /* Allocate Rt_6 and Rtav_6 consecutively in memory so they can be
     * averaged over the processors in one call (in calc_disre_R_6)
     */
    snew(dd->Rt_6, 2*dd->nres);
    dd->Rtav_6 = &(dd->Rt_6[dd->nres]);

    ptr = getenv("GMX_DISRE_ENSEMBLE_SIZE");
    if (cr && cr->ms != NULL && ptr != NULL && !bIsREMD)
    {
#ifdef GMX_MPI
        dd->nsystems = 0;
        sscanf(ptr, "%d", &dd->nsystems);
        if (fplog)
        {
            fprintf(fplog, "Found GMX_DISRE_ENSEMBLE_SIZE set to %d systems per ensemble\n", dd->nsystems);
        }
        /* This check is only valid on MASTER(cr), so probably
         * ensemble-averaged distance restraints are broken on more
         * than one processor per simulation system. */
        if (MASTER(cr))
        {
            check_multi_int(fplog, cr->ms, dd->nsystems,
                            "the number of systems per ensemble",
                            FALSE);
        }
        gmx_bcast_sim(sizeof(int), &dd->nsystems, cr);

        /* We use to allow any value of nsystems which was a divisor
         * of ms->nsim. But this required an extra communicator which
         * was stored in t_fcdata. This pulled in mpi.h in nearly all C files.
         */
        if (!(cr->ms->nsim == 1 || cr->ms->nsim == dd->nsystems))
        {
            gmx_fatal(FARGS, "GMX_DISRE_ENSEMBLE_SIZE (%d) is not equal to 1 or the number of systems (option -multi) %d", dd->nsystems, cr->ms->nsim);
        }
        if (fplog)
        {
            fprintf(fplog, "Our ensemble consists of systems:");
            for (int i = 0; i < dd->nsystems; i++)
            {
                fprintf(fplog, " %d",
                        (cr->ms->sim/dd->nsystems)*dd->nsystems+i);
            }
            fprintf(fplog, "\n");
        }
        snew(dd->Rtl_6, dd->nres);
#endif
    }
    else
    {
        dd->nsystems = 1;
        dd->Rtl_6    = dd->Rt_6;
    }

    if (dd->npair > 0)
    {
        if (fplog)
        {
            fprintf(fplog, "There are %d distance restraints involving %d atom pairs\n", dd->nres, dd->npair);
        }
        /* Have to avoid g_disre de-referencing cr blindly, mdrun not
         * doing consistency checks for ensemble-averaged distance
         * restraints when that's not happening, and only doing those
         * checks from appropriate processes (since check_multi_int is
         * too broken to check whether the communication will
         * succeed...) */
        if (cr && cr->ms && dd->nsystems > 1 && MASTER(cr))
        {
            check_multi_int(fplog, cr->ms, fcd->disres.nres,
                            "the number of distance restraints",
                            FALSE);
        }
        please_cite(fplog, "Tropp80a");
        please_cite(fplog, "Torda89a");
    }
}