Fixes to free energy code, output & g_bar compatibility.
authorSander Pronk <pronk@kth.se>
Thu, 10 Jan 2013 22:35:28 +0000 (23:35 +0100)
committerSander Pronk <pronk@kth.se>
Thu, 17 Jan 2013 15:37:50 +0000 (16:37 +0100)
- Fixes to the handling of the new free energy input options, so that
    input are more stringently checked and common errors are caught.
- Fixed g_bar so it can deal with output from mdrun (in dhdl.xvg and
    ener.edr input)
- Free energy output is now backward compatible with g_bar if 4.5-style
    settings are used
- Some mdp option documentation clarifications.

This fixes issue #1090

Still left to do:
- g_energy -odh relies on an ugly hack to get the xvg headers for free
  energy
output to work. That needs to be fixed so ener.edr can be interpreted
independently from the associated tpr file.
- The mdp documentation could use some more improvements.

Change-Id: I6198f935a94b7341d8dd85444e348ea8a55621ad

14 files changed:
include/mdebin.h
include/names.h
include/types/inputrec.h
share/html/online/mdp_opt.html
src/gmxlib/names.c
src/gmxlib/tpxio.c
src/gmxlib/txtdump.c
src/kernel/md.c
src/kernel/readir.c
src/mdlib/mdebin.c
src/mdlib/mdebin_bar.c
src/mdlib/mdebin_bar.h
src/tools/gmx_bar.c
src/tools/gmx_energy.c

index 49fca76d4b765d433593b9112a85e157db958e64..319a77769df7e76a250def9b93ba433be6441529 100644 (file)
@@ -56,6 +56,7 @@ extern "C" {
 /* forward declaration */
 typedef struct t_mde_delta_h_coll t_mde_delta_h_coll;
 
+
 /* This is the collection of energy averages collected during mdrun, and to 
    be written out to the .edr file. */
 typedef struct {
@@ -92,10 +93,25 @@ typedef struct {
   char   **print_grpnms;
 
   FILE   *fp_dhdl; /* the dhdl.xvg output file */
+  double *dE; /* energy components for dhdl.xvg output */
   t_mde_delta_h_coll *dhc; /* the delta U components (raw data + histogram) */
   real *temperatures;
 } t_mdebin;
 
+
+/* delta_h block type enum: the kinds of energies written out. */
+enum
+{
+    dhbtDH=0,    /* delta H BAR energy difference*/
+    dhbtDHDL=1,  /* dH/dlambda derivative */
+    dhbtEN,      /* System energy */
+    dhbtPV,      /* pV term */
+    dhbtEXPANDED, /* expanded ensemble statistics */
+    dhbtNR
+};
+
+
+
 t_mdebin *init_mdebin(ener_file_t fp_ene,
                              const gmx_mtop_t *mtop,
                              const t_inputrec *ir,
index 9b8778bd152d38a3660d1cf9b3e8d77d28373cc1..5f67f2d62fc4ef37bbdea1d4f9265bf1ba410086 100644 (file)
@@ -101,6 +101,8 @@ extern const char *efep_names[efepNR+1];
 GMX_LIBGMX_EXPORT
 extern const char *efpt_names[efptNR+1];
 GMX_LIBGMX_EXPORT
+extern const char *efpt_singular_names[efptNR+1];
+GMX_LIBGMX_EXPORT
 extern const char *elamstats_names[elamstatsNR+1];
 GMX_LIBGMX_EXPORT
 extern const char *elmcmove_names[elmcmoveNR+1];
index e5d93e9c3b6c2d82e3c01337c0b50f2e4fb28a00..0a67146bdf8fd897fe52dcf5d204e50ea90449f6 100644 (file)
@@ -133,7 +133,10 @@ typedef struct {
 
 typedef struct {
   int  nstdhdl;          /* The frequency for calculating dhdl           */
-  double init_lambda;    /* fractional value of lambda (usually will use init_fep_state, this will only be for slow growth, and for legacy free energy code)   */
+  double init_lambda;    /* fractional value of lambda (usually will use
+                            init_fep_state, this will only be for slow growth,
+                            and for legacy free energy code. Only has a
+                            valid value if positive)   */
   int init_fep_state;    /* the initial number of the state                   */
   double delta_lambda;  /* change of lambda per time step (fraction of (0.1) */
   gmx_bool bPrintEnergy; /* Whether to print the energy in the dhdl           */
index b15510e463173d0c6991c9ee1b32641a3827bbc4..804bc6d4547ce13f66a0246b46bbb179f9a9cfdf 100644 (file)
@@ -1491,54 +1491,53 @@ The potentials, bond-lengths and angles are interpolated linearly as
 described in the manual. When <b>sc-alpha</b> is larger than zero, soft-core
 potentials are used for the LJ and Coulomb interactions.</dd>
 </dl></dd>
-<dt><b>init-lambda: (0)</b></dt>
-<dd>starting value for lambda (float).  Generally, this should only be used with slow growth.  In other cases, <b>init-lambda-state</b> should be specified instead.</dd>
-<dt><b>init-lambda-state: (0)</b></dt>
-<dd>starting value for the lambda state (integer).  Specified which columm of the lambda vector should be used.</dd>
+<dt><b>init-lambda: (-1)</b></dt>
+<dd>starting value for lambda (float).  Generally, this should only be used with slow growth (i.e. nonzero <b>delta-lambda</b>).  In other cases, <b>init-lambda-state</b> should be specified instead. Must be greater than or equal to 0.</dd>
 <dt><b>delta-lambda: (0)</b></dt>
 <dd>increment per time step for lambda</dd>
+<dt><b>init-lambda-state: (-1)</b></dt>
+<dd>starting value for the lambda state (integer).  Specifies which columm of the lambda vector (<b>coul-lambdas</b>, <b>vdw-lambdas</b>, <b>bonded-lambdas</b>, <b>restraint-lambdas</b>, <b>mass-lambdas</b>, <b>temperature-lambdas</b>, <b>fep-lambdas</b>) should be used. This is a zero-based index: <b>init-lambda-state</b> 0 means the first column, and so on.</dd>
 <dt><b>fep-lambdas: ()</b></dt>
 <dd>Zero, one or more lambda values for which Delta H values will
-be determined and written to dhdl.xvg every <b>nstdhdl</b> steps.
+be determined and written to dhdl.xvg every <b>nstdhdl</b> steps. 
+Values must be between 0 and 1.
 Free energy differences between different lambda values can then
 be determined with <tt>g_bar</tt>. <b>fep-lambdas</b> is different from the other -lambdas keywords because
-all components of the lambda vector that are not specified will use <b>fep-lambdas</b>.</dd>
+all components of the lambda vector that are not specified will use <b>fep-lambdas</b> (including restraint-lambdas and therefore the pull code restraints).</dd>
 <dt><b>coul-lambdas: ()</b></dt>
 <dd>Zero, one or more lambda values for which Delta H values will
-be determined and written to dhdl.xvg every <b>nstdhdl</b> steps.
-Only the electrostatic interactions are controlled with this component of the lambda vector.</dd>
+be determined and written to dhdl.xvg every <b>nstdhdl</b> steps. Values must be between 0 and 1.
+Only the electrostatic interactions are controlled with this component of the lambda vector (and only if the lambda=0 and lambda=1 states have differing electrostatic interactions).</dd>
 <dt><b>vdw-lambdas: ()</b></dt>
 <dd>Zero, one or more lambda values for which Delta H values will
-be determined and written to dhdl.xvg every <b>nstdhdl</b> steps.
+be determined and written to dhdl.xvg every <b>nstdhdl</b> steps. Values must be between 0 and 1.
 Only the van der Waals interactions are controlled with this component of the lambda vector.</dd>
 <dt><b>bonded-lambdas: ()</b></dt>
 <dd>Zero, one or more lambda values for which Delta H values will
-be determined and written to dhdl.xvg every <b>nstdhdl</b> steps.
+be determined and written to dhdl.xvg every <b>nstdhdl</b> steps. Values must be between 0 and 1.
 Only the bonded interactions are controlled with this component of the lambda vector.</dd>
 <dt><b>restraint-lambdas: ()</b></dt>
 <dd>Zero, one or more lambda values for which Delta H values will
-be determined and written to dhdl.xvg every <b>nstdhdl</b> steps.
-Only the restraint interactions are controlled with this component of the lambda vector.</dd>
+be determined and written to dhdl.xvg every <b>nstdhdl</b> steps. Values must be between 0 and 1.
+Only the restraint interactions: dihedral restraints, and the pull code restraints are controlled with this component of the lambda vector. </dd>
 <dt><b>mass-lambdas: ()</b></dt>
 <dd>Zero, one or more lambda values for which Delta H values will
-be determined and written to dhdl.xvg every <b>nstdhdl</b> steps.
+be determined and written to dhdl.xvg every <b>nstdhdl</b> steps. Values must be between 0 and 1.
 Only the particle masses are controlled with this component of the lambda vector.</dd>
 <dt><b>temperature-lambdas: ()</b></dt>
 <dd>Zero, one or more lambda values for which Delta H values will
-be determined and written to dhdl.xvg every <b>nstdhdl</b> steps.
+be determined and written to dhdl.xvg every <b>nstdhdl</b> steps. Values must be between 0 and 1.
 Only the temperatures controlled with this component of the lambda vector.
 Note that these lambdas should not be used for replica exchange, only for simulated tempering.</dd>
 <dt><b>sc-alpha: (0)</b></dt>
-<dd>the soft-core parameter, a value of 0 results in linear interpolation of
-the LJ and Coulomb interactions</dd>
+<dd>the soft-core alpha parameter, a value of 0 results in linear interpolation of the LJ and Coulomb interactions</dd>
 <dt><b>sc-r-power: (6)</b></dt>
 <dd>the power of the radial term in the soft-core equation.  Possible values are 6 and 48. 6 is more standard, and is the default.  When 48 is used, then sc-alpha should generally be much lower (between 0.001 and 0.003).</dd>
 <dt><b>sc-coul: (no)</b></dt>
 <dd>Whether to apply the soft core free energy interations to the Columbic interaction. Default is no, as it is generally
 more efficient to turn of the Coulomic interactions linearly before turning off electrostatic interactions.</dd>
 <dt><b>sc-power: (0)</b></dt>
-<dd>the power for lambda in the soft-core function,
-only the values 1 and 2 are supported</dd>
+<dd>the power for lambda in the soft-core function, only the values 1 and 2 are supported</dd>
 <dt><b>sc-sigma: (0.3) [nm]</b></dt>
 <dd>the soft-core sigma for particles which have a C6 or C12 parameter equal
 to zero or a sigma smaller than <b>sc-sigma</b></dd>
@@ -1658,24 +1657,24 @@ simulation specified in the first group of <b>ref_t</b> is used.</dd>
 <dt><b>symmetrized-transition-matrix: (no) </b></dt>
 <dd>Whether to symmetrize the empirical transition matrix. In the infinite limit the matrix will be symmetric, but will diverge with statistical noise for short timescales.  Forced symmetrization, by using the matrix T_sym = 1/2 (T + transpose(T)), removes problems like the existence of (small magnitude) negative eigenvalues.</dd>
 <dt><b>mininum-var-min: (100)</b></dt>
-<dd> The <b>min-variance</b> strategy (option of <b>lmc-stats</b> is only valid for larger number of samples, and can get stuck if too few samples are used at each state.  <b>mininum-var-min</b> is the minimum number of samples that each state that are allowed before the <b>min-variance</b> strategy is activated if selected.
+<dd> The <b>min-variance</b> strategy (option of <b>lmc-stats</b> is only valid for larger number of samples, and can get stuck if too few samples are used at each state.  <b>mininum-var-min</b> is the minimum number of samples that each state that are allowed before the <b>min-variance</b> strategy is activated if selected.</dd>
 <dt><b>init-lambda-weights: </b></dt>
-<dd>The initial weights (free energies) used for the expanded ensemble states.  Default is a vector of zero weights. format is similar to the lambda vector settings in <b>fep-lambdas</b>, except the weights can be any floating point number.  Units are kT. Its length must match the lambda vector lengths.<dd>
-<dt><b>lmc-weights-equil: (no)</b><dt>
+<dd>The initial weights (free energies) used for the expanded ensemble states.  Default is a vector of zero weights. format is similar to the lambda vector settings in <b>fep-lambdas</b>, except the weights can be any floating point number.  Units are kT. Its length must match the lambda vector lengths.</dd>
+<dt><b>lmc-weights-equil: (no)</b></dt>
 <dd><dl compact>
-<dt><b>no</b><dt>
+<dt><b>no</b></dt>
 <dd>Expanded ensemble weights continue to be updated throughout the simulation.</dd>
-<dt><b>yes</b><dt>
+<dt><b>yes</b></dt>
 <dd>The input expanded ensemble weights are treated as equilibrated, and are not updated throughout the simulation.</dd>
-<dt><b>wl-delta</b><dt>
+<dt><b>wl-delta</b></dt>
 <dd>Expanded ensemble weight updating is stopped when the Wang-Landau incrementor falls below the value specified by <b>weight-equil-wl-delta</b>.</dd>
-<dt><b>number-all-lambda</b><dt>
+<dt><b>number-all-lambda</b></dt>
 <dd>Expanded ensemble weight updating is stopped when the number of samples at all of the lambda states is greater than the value specified by <b>weight-equil-number-all-lambda</b>.</dd>
-<dt><b>number-steps</b><dt>
+<dt><b>number-steps</b></dt>
 <dd>Expanded ensemble weight updating is stopped when the number of steps is greater than the level specified by <b>weight-equil-number-steps</b>.</dd>
-<dt><b>number-samples</b><dt>
+<dt><b>number-samples</b></dt>
 <dd>Expanded ensemble weight updating is stopped when the number of total samples across all lambda states is greater than the level specified by <b>weight-equil-number-samples</b>.</dd>
-<dt><b>count-ratio</b><dt>
+<dt><b>count-ratio</b></dt>
 <dd>Expanded ensemble weight updating is stopped when the ratio of samples at the least sampled lambda state and most sampled lambda state greater than the value specified by <b>weight-equil-count-ratio</b>.</dd> 
 </dl>
 <dt><b>simulated-tempering: (no)</b></dt>
@@ -1687,11 +1686,11 @@ simulation specified in the first group of <b>ref_t</b> is used.</dd>
 <dt><b>simulated-tempering-scaling: (linear)</b></dt>
 <dd>Controls the way that the temperatures at intermediate lambdas are calculated from the <b>temperature-lambda</b> part of the lambda vector.</dd>
 <dd><dl compact>
-<dt><b>linear</b><dt>
+<dt><b>linear</b></dt>
 <dd>Linearly interpolates the temperatures using the values of <b>temperature-lambda</b>,i.e. if <b>sim-temp-low</b>=300, <b>sim-temp-high</b>=400, then lambda=0.5 correspond to a temperature of 350. A nonlinear set of temperatures can always be implemented with uneven spacing in lambda.</dd>
-<dt><b>geometric</b><dt>
-<dd> Interpolates temperatures geometrically between <b>sim-temp-low</b> and <b>sim-temp-high</b>. The ith state has temperature <b>sim-temp-low</b> * (<b>sim-temp-high</b>/<b>sim-temp-low</b>)^(i/(ntemps-1)).  Should give roughly equal exchange for constant heat capacity, though of course things simulations that involve protein folding have very high heat capacity peaks.</dd>
-<dt><b>exponential</b><dt>
+<dt><b>geometric</b></dt>
+<dd> Interpolates temperatures geometrically between <b>sim-temp-low</b> and <b>sim-temp-high</b>. The i-th state has temperature <b>sim-temp-low</b> * (<b>sim-temp-high</b>/<b>sim-temp-low</b>) to the power (i/(ntemps-1)).  Should give roughly equal exchange for constant heat capacity, though of course things simulations that involve protein folding have very high heat capacity peaks.</dd>
+<dt><b>exponential</b></dt>
 <dd> Interpolates temperatures exponentially between <b>sim-temp-low</b> and <b>sim-temp-high</b>. The ith state has temperature
 <b>sim-temp-low</b> + (<b>sim-temp-high</b>-<b>sim-temp-low</b>)*((exp(<b>temperature-lambdas</b>[i])-1)/(exp(1.0)-1)).</dd>
 </dl>
index 17b04eac902ee41ea637e82bc87865ee2b7306c7..5db84fb01ae7c3caebf80610c6987f34e580f7e0 100644 (file)
@@ -155,6 +155,10 @@ const char *efpt_names[efptNR+1] = {
   "fep-lambdas", "mass-lambdas", "coul-lambdas", "vdw-lambdas", "bonded-lambdas", "restraint-lambdas", "temperature-lambdas", NULL
 };
 
+const char *efpt_singular_names[efptNR+1] = {
+  "fep-lambda", "mass-lambda", "coul-lambda", "vdw-lambda", "bonded-lambda", "restraint-lambda", "temperature-lambda", NULL
+};
+
 const char *elamstats_names[elamstatsNR+1] = {
   "no", "metropolis-transition", "barker-transition", "minvar", "wang-landau", "weighted-wang-landau", NULL
 };
index fac4d0af17e31e85f385fdf8c2004d05b043a51e..c5a74c515b61e6512667f3a351b579bf805efae3 100644 (file)
@@ -368,23 +368,41 @@ static void do_fepvals(t_fileio *fio,t_lambda *fepvals,gmx_bool bRead, int file_
   else if (file_version >= 64)
   {
       gmx_fio_do_int(fio,fepvals->n_lambda);
-      snew(fepvals->all_lambda,efptNR);
       if (bRead)
       {
-          snew(fepvals->all_lambda[efptFEP],fepvals->n_lambda);
+          int g;
+
+          snew(fepvals->all_lambda,efptNR);
+          /* still allocate the all_lambda array's contents. */
+          for(g=0;g<efptNR;g++)
+          {
+              if (fepvals->n_lambda > 0) {
+                  snew(fepvals->all_lambda[g],fepvals->n_lambda);
+              }
+          }
       }
-      bDum=gmx_fio_ndo_double(fio,fepvals->all_lambda[efptFEP],fepvals->n_lambda);
+      bDum=gmx_fio_ndo_double(fio,fepvals->all_lambda[efptFEP],
+                              fepvals->n_lambda);
       if (fepvals->init_lambda >= 0)
       {
+          int g,h;
+
           fepvals->separate_dvdl[efptFEP] = TRUE;
-      }
-      /* still allocate the all_lambda array's contents. */
-      for (g=0;g<efptNR;g++)
-      {
-          if (fepvals->n_lambda > 0) {
-              if (bRead)
+
+          if (bRead)
+          {
+              /* copy the contents of the efptFEP lambda component to all
+                 the other components */
+              for(g=0;g<efptNR;g++)
               {
-                  snew(fepvals->all_lambda[g],fepvals->n_lambda);
+                  for(h=0;h<fepvals->n_lambda;h++)
+                  {
+                      if (g!=efptFEP)
+                      {
+                          fepvals->all_lambda[g][h] =
+                                    fepvals->all_lambda[efptFEP][h];
+                      }
+                  }
               }
           }
       }
index d2e8b74f3507500f43508f6eb4c56c763244f728..39906a2fdd200ce147cb545b024553dee6b5af9d 100644 (file)
@@ -563,6 +563,20 @@ static void pr_fepvals(FILE *fp,int indent,t_lambda *fep, gmx_bool bMDPformat)
     if (fep->n_lambda > 0)
     {
         pr_indent(fp,indent);
+        fprintf(fp,"separate-dvdl%s\n",bMDPformat ? " = " : ":");
+        for(i=0; i<efptNR; i++)
+        {
+            fprintf(fp,"%18s = ",efpt_names[i]);
+            if (fep->separate_dvdl[i])
+            {
+                fprintf(fp,"  TRUE");
+            }
+            else
+            {
+                fprintf(fp,"  FALSE");
+            }
+            fprintf(fp,"\n");
+        }
         fprintf(fp,"all-lambdas%s\n",bMDPformat ? " = " : ":");
         for(i=0; i<efptNR; i++) {
             fprintf(fp,"%18s = ",efpt_names[i]);
index e349e139f22a18e1cabe09cd24589b33f4b6b46f..d4511a8ab51bb3d7c5a19808427bd0669492c796 100644 (file)
@@ -218,7 +218,6 @@ double do_md(FILE *fplog,t_commrec *cr,int nfile,const t_filenm fnm[],
        real        vetanew = 0;
     int         lamnew=0;
     /* for FEP */
-    int         fep_state=0;
     int         nstfep;
     real        rate;
     double      cycles;
index a03c66e4627ccb17e4efdb74c6ea1fd115afaa40..d47cef5dd2891456528dac161f69674be909579e 100644 (file)
@@ -430,11 +430,29 @@ void check_ir(const char *mdparin,t_inputrec *ir, t_gromppopts *opts,
                 }
             }
         }
-        else if (ir->nstenergy > 0 && 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->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 nstenergy (%d)",ir->nstcalcenergy,ir->nstenergy);
-            ir->nstcalcenergy = ir->nstenergy;
+            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)
@@ -455,13 +473,8 @@ void check_ir(const char *mdparin,t_inputrec *ir, t_gromppopts *opts,
             }
         }
 
-        if (ir->nstcalcenergy > 1)
+        if (ir->nstcalcenergy > 0)
         {
-            /* for storing exact averages nstenergy should be
-             * a multiple of nstcalcenergy
-             */
-            check_nst("nstcalcenergy",ir->nstcalcenergy,
-                      "nstenergy",&ir->nstenergy,wi);
             if (ir->efep != efepNO)
             {
                 /* nstdhdl should be a multiple of nstcalcenergy */
@@ -469,8 +482,13 @@ void check_ir(const char *mdparin,t_inputrec *ir, t_gromppopts *opts,
                           "nstdhdl",&ir->fepvals->nstdhdl,wi);
                 /* nstexpanded should be a multiple of nstcalcenergy */
                 check_nst("nstcalcenergy",ir->nstcalcenergy,
-                          "nstdhdl",&ir->expandedvals->nstexpanded,wi);
+                          "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);
         }
     }
 
@@ -562,7 +580,7 @@ void check_ir(const char *mdparin,t_inputrec *ir, t_gromppopts *opts,
       }
 
       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)));
+      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));
@@ -571,8 +589,50 @@ void check_ir(const char *mdparin,t_inputrec *ir, t_gromppopts *opts,
       CHECK(ir->coulombtype==eelEWALD);
 
       /* check validty of lambda inputs */
-      sprintf(err_buf,"initial thermodynamic state %d does not exist, only goes to %d",fep->init_fep_state,fep->n_lambda);
-      CHECK((fep->init_fep_state > fep->n_lambda));
+      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++)
       {
@@ -1307,7 +1367,7 @@ static void do_fep_params(t_inputrec *ir, char fep_lambda[][STRLEN],char weights
     /* "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) && (fep->init_lambda <= 1))
+    if ((nfep[efptFEP] == 0) && (fep->init_lambda >= 0))
     {
         for (i=0;i<fep->n_lambda;i++)
         {
@@ -1844,9 +1904,9 @@ void get_ir(const char *mdparin,const char *mdparout,
   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,0);
+  ITYPE ("init-lambda-state", fep->init_fep_state,-1);
   RTYPE ("delta-lambda",fep->delta_lambda,0.0);
-  ITYPE ("nstdhdl",fep->nstdhdl, 100);
+  ITYPE ("nstdhdl",fep->nstdhdl, 50);
   STYPE ("fep-lambdas", fep_lambda[efptFEP], NULL);
   STYPE ("mass-lambdas", fep_lambda[efptMASS], NULL);
   STYPE ("coul-lambdas", fep_lambda[efptCOUL], NULL);
index c2cd494e18423e886835be8f0779390a0f17052e..d988e180e1db23603180b5a195fe978e19c71fe6 100644 (file)
@@ -589,10 +589,12 @@ t_mdebin *init_mdebin(ener_file_t fp_ene,
             mde_delta_h_coll_init(md->dhc, ir);
         }
         md->fp_dhdl = NULL;
+        snew(md->dE,ir->fepvals->n_lambda);
     }
     else
     {
         md->fp_dhdl = fp_dhdl;
+        snew(md->dE,ir->fepvals->n_lambda);
     }
     if (ir->bSimTemp) {
         int i;
@@ -605,6 +607,66 @@ t_mdebin *init_mdebin(ener_file_t fp_ene,
     return md;
 }
 
+/* print a lambda vector to a string
+   fep = the inputrec's FEP input data
+   i = the index of the lambda vector
+   get_native_lambda = whether to print the native lambda
+   get_names = whether to print the names rather than the values
+   str = the pre-allocated string buffer to print to. */
+static void print_lambda_vector(t_lambda *fep, int i,
+                                gmx_bool get_native_lambda, gmx_bool get_names,
+                                char *str)
+{
+    size_t nps=0, np;
+    int j,k=0;
+    int Nsep=0;
+
+    for (j=0;j<efptNR;j++)
+    {
+        if (fep->separate_dvdl[j])
+            Nsep ++;
+    }
+    str[0]=0; /* reset the string */
+    if (Nsep > 1)
+    {
+        str += sprintf(str, "("); /* set the opening parenthesis*/
+    }
+    for (j=0;j<efptNR;j++)
+    {
+        if (fep->separate_dvdl[j])
+        {
+            double lam;
+            if (!get_names)
+            {
+                if (get_native_lambda && fep->init_lambda >= 0)
+                {
+                    str += sprintf(str,"%.4f", fep->init_lambda);
+                }
+                else
+                {
+                    str += sprintf(str,"%.4f", fep->all_lambda[j][i]);
+                }
+            }
+            else
+            {
+                str += sprintf(str,"%s", efpt_singular_names[j]);
+            }
+            /* print comma for the next item */
+            if (k<Nsep-1)
+            {
+                str += sprintf(str,", ");
+            }
+            k++;
+        }
+    }
+    if (Nsep > 1)
+    {
+        /* and add the closing parenthesis */
+        str += sprintf(str, ")");
+    }
+}
+
+
 extern FILE *open_dhdl(const char *filename,const t_inputrec *ir,
                        const output_env_t oenv)
 {
@@ -613,17 +675,26 @@ extern FILE *open_dhdl(const char *filename,const t_inputrec *ir,
         *lambdastate="\\lambda state",*remain="remaining";
     char title[STRLEN],label_x[STRLEN],label_y[STRLEN];
     int  i,np,nps,nsets,nsets_de,nsetsbegin;
-    t_lambda *fep;
+    int n_lambda_terms=0;
+    t_lambda *fep=ir->fepvals; /* for simplicity */
+    t_expanded *expand=ir->expandedvals;
     char **setname;
-    char buf[STRLEN];
+    char buf[STRLEN], lambda_vec_str[STRLEN], lambda_name_str[STRLEN];
     int bufplace=0;
 
     int nsets_dhdl = 0;
     int s = 0;
     int nsetsextend;
+    gmx_bool write_pV = FALSE;
 
-    /* for simplicity */
-    fep = ir->fepvals;
+    /* count the number of different lambda terms */
+    for (i=0;i<efptNR;i++)
+    {
+        if (fep->separate_dvdl[i])
+        {
+            n_lambda_terms++;
+        }
+    }
 
     if (fep->n_lambda == 0)
     {
@@ -649,30 +720,36 @@ extern FILE *open_dhdl(const char *filename,const t_inputrec *ir,
     }
     if (ir->efep != efepSLOWGROWTH)
     {
-        if (fep->n_lambda == 0)
+        if ( (fep->init_lambda >= 0)  && (n_lambda_terms == 1 ))
         {
-            sprintf(&(buf[bufplace]),"%s = %g",
-                    lambda,fep->init_lambda);
+            /* compatibility output */
+            sprintf(&(buf[bufplace]),"%s = %.4f", lambda,fep->init_lambda);
         }
         else
         {
-            sprintf(&(buf[bufplace]),"%s = %d",
-                    lambdastate,fep->init_fep_state);
+            print_lambda_vector(fep, fep->init_fep_state, TRUE, FALSE,
+                                lambda_vec_str);
+            print_lambda_vector(fep, fep->init_fep_state, TRUE, TRUE,
+                                lambda_name_str);
+            sprintf(&(buf[bufplace]),"%s %d: %s = %s",
+                    lambdastate,fep->init_fep_state,
+                    lambda_name_str, lambda_vec_str);
         }
     }
     xvgr_subtitle(fp,buf,oenv);
 
-    for (i=0;i<efptNR;i++)
+
+    nsets_dhdl=0;
+    if (fep->dhdl_derivatives == edhdlderivativesYES)
     {
-        if (fep->separate_dvdl[i]) {nsets_dhdl++;}
+        nsets_dhdl = n_lambda_terms;
     }
-
     /* count the number of delta_g states */
     nsets_de = fep->n_lambda;
 
     nsets = nsets_dhdl + nsets_de; /* dhdl + fep differences */
 
-    if (fep->n_lambda>0 && ir->bExpanded)
+    if (fep->n_lambda>0 && (expand->elmcmove > elmcmoveNO))
     {
         nsets += 1;   /*add fep state for expanded ensemble */
     }
@@ -683,13 +760,18 @@ extern FILE *open_dhdl(const char *filename,const t_inputrec *ir,
     }
 
     nsetsextend = nsets;
-    if ((ir->epc!=epcNO) && (fep->n_lambda>0))
+    if ((ir->epc!=epcNO) && (fep->n_lambda>0) && (fep->init_lambda < 0))
     {
-        nsetsextend += 1; /* for PV term, other terms possible if required for the reduced potential (only needed with foreign lambda) */
+        nsetsextend += 1; /* for PV term, other terms possible if required for
+                             the reduced potential (only needed with foreign
+                             lambda, and only output when init_lambda is not
+                             set in order to maintain compatibility of the
+                             dhdl.xvg file) */
+        write_pV = TRUE;
     }
     snew(setname,nsetsextend);
 
-    if (ir->bExpanded)
+    if (expand->elmcmove > elmcmoveNO)
     {
         /* state for the fep_vals, if we have alchemical sampling */
         sprintf(buf,"%s","Thermodynamic state");
@@ -704,12 +786,30 @@ extern FILE *open_dhdl(const char *filename,const t_inputrec *ir,
         s+=1;
     }
 
-    for (i=0;i<efptNR;i++)
+    if (fep->dhdl_derivatives == edhdlderivativesYES)
     {
-        if (fep->separate_dvdl[i]) {
-            sprintf(buf,"%s (%s)",dhdl,efpt_names[i]);
-            setname[s] = strdup(buf);
-            s+=1;
+        for (i=0;i<efptNR;i++)
+        {
+            if (fep->separate_dvdl[i]) {
+
+                if ( (fep->init_lambda >= 0)  && (n_lambda_terms == 1 ))
+                {
+                    /* compatibility output */
+                    sprintf(buf,"%s %s %.4f",dhdl,lambda, fep->init_lambda);
+                }
+                else
+                {
+                    double lam=fep->init_lambda;
+                    if (fep->init_lambda < 0)
+                    {
+                        lam=fep->all_lambda[i][fep->init_fep_state];
+                    }
+                    sprintf(buf,"%s %s = %.4f",dhdl, efpt_singular_names[i],
+                            lam);
+                }
+                setname[s] = strdup(buf);
+                s+=1;
+            }
         }
     }
 
@@ -719,7 +819,7 @@ extern FILE *open_dhdl(const char *filename,const t_inputrec *ir,
          * from this xvg legend.
          */
 
-        if (ir->bExpanded) {
+        if (expand->elmcmove > elmcmoveNO) {
             nsetsbegin = 1;  /* for including the expanded ensemble */
         } else {
             nsetsbegin = 0;
@@ -731,31 +831,33 @@ extern FILE *open_dhdl(const char *filename,const t_inputrec *ir,
         }
         nsetsbegin += nsets_dhdl;
 
-        for(s=nsetsbegin; s<nsets; s++)
+        for(i=0; i<fep->n_lambda; i++)
         {
-            nps = sprintf(buf,"%s %s (",deltag,lambda);
-            for (i=0;i<efptNR;i++)
+            print_lambda_vector(fep, i, FALSE, FALSE, lambda_vec_str);
+            if ( (fep->init_lambda >= 0)  && (n_lambda_terms == 1 ))
             {
-                if (fep->separate_dvdl[i])
-                {
-                    np = sprintf(&buf[nps],"%g,",fep->all_lambda[i][s-(nsetsbegin)]);
-                    nps += np;
-                }
+                /* for compatible dhdl.xvg files */
+                nps = sprintf(buf,"%s %s %s",deltag,lambda, lambda_vec_str);
             }
-            if (ir->bSimTemp)
+            else
             {
-                /* print the temperature for this state if doing simulated annealing */
-                sprintf(&buf[nps],"T = %g (%s))",ir->simtempvals->temperatures[s-(nsetsbegin)],unit_temp_K);
+                nps = sprintf(buf,"%s %s to %s",deltag,lambda, lambda_vec_str);
             }
-            else
+
+            if (ir->bSimTemp)
             {
-                sprintf(&buf[nps-1],")");  /* -1 to overwrite the last comma */
+                /* print the temperature for this state if doing simulated annealing */
+                sprintf(&buf[nps],"T = %g (%s)",
+                        ir->simtempvals->temperatures[s-(nsetsbegin)],
+                        unit_temp_K);
             }
             setname[s] = strdup(buf);
+            s++;
         }
-        if (ir->epc!=epcNO) {
+        if (write_pV) {
             np = sprintf(buf,"pV (%s)",unit_energy);
-            setname[nsetsextend-1] = strdup(buf);  /* the first entry after nsets */
+            setname[nsetsextend-1] = strdup(buf);  /* the first entry after
+                                                      nsets */
         }
 
         xvgr_legend(fp,nsetsextend,(const char **)setname,oenv);
@@ -805,7 +907,6 @@ void upd_mdebin(t_mdebin *md,
     real   eee[egNR];
     real   ecopy[F_NRE];
     double store_dhdl[efptNR];
-    double *dE=NULL;
     real   store_energy=0;
     real   tmp;
 
@@ -992,83 +1093,86 @@ void upd_mdebin(t_mdebin *md,
     ebin_increase_count(md->ebin,bSum);
 
     /* BAR + thermodynamic integration values */
-    if ((md->fp_dhdl || md->dhc) && bDoDHDL && (enerd->n_lambda > 0))
+    if ((md->fp_dhdl || md->dhc) && bDoDHDL)
     {
-        snew(dE,enerd->n_lambda-1);
         for(i=0; i<enerd->n_lambda-1; i++) {
-            dE[i] = enerd->enerpart_lambda[i+1]-enerd->enerpart_lambda[0];  /* zero for simulated tempering */
+            /* zero for simulated tempering */
+            md->dE[i] = enerd->enerpart_lambda[i+1]-enerd->enerpart_lambda[0];
             if (md->temperatures!=NULL)
             {
                 /* MRS: is this right, given the way we have defined the exchange probabilities? */
                 /* is this even useful to have at all? */
-                dE[i] += (md->temperatures[i]/md->temperatures[state->fep_state]-1.0)*enerd->term[F_EKIN];
+                md->dE[i] += (md->temperatures[i]/
+                          md->temperatures[state->fep_state]-1.0)*
+                            enerd->term[F_EKIN];
             }
         }
-    }
-
-    if (md->fp_dhdl && bDoDHDL)
-    {
-        fprintf(md->fp_dhdl,"%.4f",time);
-        /* the current free energy state */
 
-        /* print the current state if we are doing expanded ensemble */
-        if (expand->elmcmove > elmcmoveNO) {
-            fprintf(md->fp_dhdl," %4d",state->fep_state);
-        }
-        /* total energy (for if the temperature changes */
-        if (fep->bPrintEnergy)
+        if (md->fp_dhdl)
         {
-            store_energy = enerd->term[F_ETOT];
-            fprintf(md->fp_dhdl," %#.8g",store_energy);
-        }
+            fprintf(md->fp_dhdl,"%.4f",time);
+            /* the current free energy state */
 
-        for (i=0;i<efptNR;i++)
-        {
-            if (fep->separate_dvdl[i])
+            /* print the current state if we are doing expanded ensemble */
+            if (expand->elmcmove > elmcmoveNO) {
+                fprintf(md->fp_dhdl," %4d",state->fep_state);
+            }
+            /* total energy (for if the temperature changes */
+            if (fep->bPrintEnergy)
             {
-                fprintf(md->fp_dhdl," %#.8g",enerd->term[F_DVDL+i]); /* assumes F_DVDL is first */
+                store_energy = enerd->term[F_ETOT];
+                fprintf(md->fp_dhdl," %#.8g",store_energy);
             }
-        }
-        for(i=1; i<enerd->n_lambda; i++)
-        {
-            fprintf(md->fp_dhdl," %#.8g",dE[i-1]);
 
+            if (fep->dhdl_derivatives == edhdlderivativesYES)
+            {
+                for (i=0;i<efptNR;i++)
+                {
+                    if (fep->separate_dvdl[i])
+                    {
+                        /* assumes F_DVDL is first */
+                        fprintf(md->fp_dhdl," %#.8g",enerd->term[F_DVDL+i]);
+                    }
+                }
+            }
+            for(i=0;i<fep->n_lambda;i++)
+            {
+                fprintf(md->fp_dhdl," %#.8g",md->dE[i]);
+            }
+            if ((md->epc!=epcNO)  && 
+                (enerd->n_lambda > 0) &&
+                (fep->init_lambda<0))
+            {
+                fprintf(md->fp_dhdl," %#.8g",pv);  /* PV term only needed when
+                                                      there are alternate state
+                                                      lambda and we're not in
+                                                      compatibility mode */
+            }
+            fprintf(md->fp_dhdl,"\n");
+            /* and the binary free energy output */
         }
-        if ((md->epc!=epcNO)  && (enerd->n_lambda > 0))
-        {
-            fprintf(md->fp_dhdl," %#.8g",pv);   /* PV term only needed when there are alternate state lambda */
-        }
-        fprintf(md->fp_dhdl,"\n");
-        /* and the binary free energy output */
-    }
-    if (md->dhc && bDoDHDL)
-    {
-        int idhdl = 0;
-        for (i=0;i<efptNR;i++)
+        if (md->dhc && bDoDHDL)
         {
-            if (fep->separate_dvdl[i])
+            int idhdl = 0;
+            for (i=0;i<efptNR;i++)
             {
-                store_dhdl[idhdl] = enerd->term[F_DVDL+i]; /* assumes F_DVDL is first */
-                idhdl+=1;
+                if (fep->separate_dvdl[i])
+                {
+                    /* assumes F_DVDL is first */
+                    store_dhdl[idhdl] = enerd->term[F_DVDL+i];
+                    idhdl+=1;
+                }
             }
+            store_energy = enerd->term[F_ETOT];
+            /* store_dh is dE */
+            mde_delta_h_coll_add_dh(md->dhc,
+                                    (double)state->fep_state,
+                                    store_energy,
+                                    pv,
+                                    store_dhdl,
+                                    md->dE,
+                                    time);
         }
-        /* store_dh is dE */
-        mde_delta_h_coll_add_dh(md->dhc,
-                                (double)state->fep_state,
-                                store_energy,
-                                pv,
-                                (expand->elamstats>elamstatsNO),
-                                (fep->bPrintEnergy),
-                                (md->epc!=epcNO),
-                                idhdl,
-                                fep->n_lambda,
-                                store_dhdl,
-                                dE,
-                                time);
-    }
-    if ((md->fp_dhdl || md->dhc) && bDoDHDL && (enerd->n_lambda >0))
-    {
-        sfree(dE);
     }
 }
 
index 09897df3690a48030a014ca28893bb243d9e47e3..bc62faff1f62affadba00f4d6d1d2979c65a00e8 100644 (file)
@@ -60,10 +60,26 @@ static void mde_delta_h_reset(t_mde_delta_h *dh)
 
 /* initialize the delta_h list */
 static void mde_delta_h_init(t_mde_delta_h *dh, int nbins,
-                             double dx, unsigned int  ndhmax)
+                             double dx, unsigned int  ndhmax,
+                             int type, int derivative, int nlambda,
+                             double *lambda)
 {
     int i;
 
+    dh->type=type;
+    dh->derivative=derivative;
+    dh->lambda=lambda;
+    dh->nlambda=nlambda;
+
+    snew(dh->lambda, nlambda);
+    for(i=0;i<nlambda;i++)
+    {
+        dh->lambda[i] = lambda[i];
+    }
+
+
+    snew(dh->subblock_meta_d, dh->nlambda+1);
+
     dh->ndhmax=ndhmax+2;
     for(i=0;i<2;i++)
     {
@@ -189,32 +205,42 @@ void mde_delta_h_handle_block(t_mde_delta_h *dh, t_enxblock *blk)
     /* first check which type we should use: histogram or raw data */
     if (dh->nhist == 0)
     {
-        unsigned int i;
+        int i;
 
         /* We write raw data.
-           Raw data consists of 3 subblocks: a block with the
-           the foreign lambda, and the data itself */
+           Raw data consists of 3 subblocks: an int metadata block
+           with type and derivative index, a foreign lambda block
+           and and the data itself */
         add_subblocks_enxblock(blk, 3);
 
         blk->id=enxDH;
 
         /* subblock 1 */
-        dh->subblock_i[0]=dh->derivative ? 1 : 0; /* derivative type */
-        blk->sub[0].nr=1;
+        dh->subblock_meta_i[0]=dh->type; /* block data type */
+        dh->subblock_meta_i[1]=dh->derivative; /* derivative direction if
+                                                  applicable (in indices
+                                                  starting from first coord in
+                                                  the main delta_h_coll) */
+        blk->sub[0].nr=2;
         blk->sub[0].type=xdr_datatype_int;
-        blk->sub[0].ival=dh->subblock_i;
+        blk->sub[0].ival=dh->subblock_meta_i;
 
         /* subblock 2 */
-        dh->subblock_d[0]=dh->lambda;
-        blk->sub[1].nr=1;
+        for(i=0;i<dh->nlambda;i++)
+        {
+            dh->subblock_meta_d[i]=dh->lambda[i];
+        }
+        blk->sub[1].nr=dh->nlambda;
         blk->sub[1].type=xdr_datatype_double;
-        blk->sub[1].dval=dh->subblock_d;
+        blk->sub[1].dval=dh->subblock_meta_d;
 
         /* subblock 3 */
         /* check if there's actual data to be written. */
         /*if (dh->ndh > 1)*/
         if (dh->ndh > 0)
         {
+            unsigned int i;
+
             blk->sub[2].nr=dh->ndh;
 /* For F@H for now. */
 #undef GMX_DOUBLE
@@ -239,15 +265,17 @@ void mde_delta_h_handle_block(t_mde_delta_h *dh, t_enxblock *blk)
             blk->sub[2].fval=NULL;
 #else
             blk->sub[2].type=xdr_datatype_double;
-#endif
             blk->sub[2].dval=NULL;
+#endif
         }
     }
     else
     {
         int nhist_written=0;
         int i;
+        int k;
 
+        /* TODO histogram metadata */
         /* check if there's actual data to be written. */
         if (dh->ndh > 1)
         {
@@ -278,22 +306,37 @@ void mde_delta_h_handle_block(t_mde_delta_h *dh, t_enxblock *blk)
         add_subblocks_enxblock(blk, nhist_written+2);
         blk->id=enxDHHIST;
 
-        /* subblock 1: the foreign lambda value + the histogram spacing */
-        dh->subblock_d[0]=dh->lambda;
-        dh->subblock_d[1]=dh->dx;
-        blk->sub[0].nr=2;
+        /* subblock 1: the lambda value + the histogram spacing */
+        if (dh->nlambda == 1)
+        {
+            /* for backward compatibility */
+            dh->subblock_meta_d[0]=dh->lambda[0];
+        }
+        else
+        {
+            dh->subblock_meta_d[0]=-1;
+            for(i=0;i<dh->nlambda;i++)
+            {
+                dh->subblock_meta_d[2+i]=dh->lambda[i];
+            }
+        }
+        dh->subblock_meta_d[1]=dh->dx;
+        blk->sub[0].nr = 2+ ((dh->nlambda>1) ? dh->nlambda : 0);
         blk->sub[0].type=xdr_datatype_double;
-        blk->sub[0].dval=dh->subblock_d;
+        blk->sub[0].dval=dh->subblock_meta_d;
 
         /* subblock 2: the starting point(s) as a long integer */
-        dh->subblock_l[0]=nhist_written;
-        dh->subblock_l[1]=dh->derivative ? 1 : 0;
+        dh->subblock_meta_l[0]=nhist_written;
+        dh->subblock_meta_l[1]=dh->type; /*dh->derivative ? 1 : 0;*/
+        k=2;
         for(i=0;i<nhist_written;i++)
-            dh->subblock_l[2+i]=dh->x0[i];
+            dh->subblock_meta_l[k++]=dh->x0[i];
+        /* append the derivative data */
+        dh->subblock_meta_l[k++]=dh->derivative;
 
-        blk->sub[1].nr=nhist_written+2;
+        blk->sub[1].nr=nhist_written+3;
         blk->sub[1].type=xdr_datatype_large_int;
-        blk->sub[1].lval=dh->subblock_l;
+        blk->sub[1].lval=dh->subblock_meta_l;
 
         /* subblock 3 + 4 : the histogram data */
         for(i=0;i<nhist_written;i++)
@@ -309,53 +352,203 @@ void mde_delta_h_handle_block(t_mde_delta_h *dh, t_enxblock *blk)
 /* initialize the collection*/
 void mde_delta_h_coll_init(t_mde_delta_h_coll *dhc, const t_inputrec *ir)
 {
-    int i;
+    int i,j,n;
     double lambda;
+    double *lambda_vec;
     int ndhmax=ir->nstenergy/ir->nstcalcenergy;
+    t_lambda *fep=ir->fepvals;
 
     dhc->temperature=ir->opts.ref_t[0];  /* only store system temperature */
     dhc->start_time=0.;
     dhc->delta_time=ir->delta_t*ir->fepvals->nstdhdl;
     dhc->start_time_set=FALSE;
 
-    /* for continuous change of lambda values */
+    /* this is the compatibility lambda value. If it is >=0, it is valid,
+       and there is either an old-style lambda or a slow growth simulation. */
     dhc->start_lambda=ir->fepvals->init_lambda;
+    /* for continuous change of lambda values */
     dhc->delta_lambda=ir->fepvals->delta_lambda*ir->fepvals->nstdhdl;
 
-    /* total number of raw data points in the sample */
-    dhc->ndh = 0;
-
-    /* include one more for the specification of the state, by lambda or fep_state, store as double for now*/
-    if (ir->expandedvals->elamstats > elamstatsNO) {
-        dhc->ndh +=1;
+    if (dhc->start_lambda < 0)
+    {
+        /* create the native lambda vectors */
+        dhc->lambda_index=fep->init_fep_state;
+        dhc->n_lambda_vec=0;
+        for(i=0;i<efptNR;i++)
+        {
+            if (fep->separate_dvdl[i])
+            {
+                dhc->n_lambda_vec++;
+            }
+        }
+        snew(dhc->native_lambda_vec, dhc->n_lambda_vec);
+        snew(dhc->native_lambda_components, dhc->n_lambda_vec);
+        j=0;
+        for(i=0;i<efptNR;i++)
+        {
+            if (fep->separate_dvdl[i])
+            {
+                dhc->native_lambda_components[j]=i;
+                if (fep->init_fep_state >=0 &&
+                    fep->init_fep_state < fep->n_lambda)
+                {
+                    dhc->native_lambda_vec[j]=
+                                fep->all_lambda[i][fep->init_fep_state];
+                }
+                else
+                {
+                    dhc->native_lambda_vec[j]=-1;
+                }
+                j++;
+            }
+        }
     }
-
-    /* whether to print energies */
-    if (ir->fepvals->bPrintEnergy) {
-        dhc->ndh += 1;
+    else
+    {
+        /* don't allocate the meta-data subblocks for lambda vectors */
+        dhc->native_lambda_vec=NULL;
+        dhc->n_lambda_vec=0;
+        dhc->native_lambda_components=0;
+        dhc->lambda_index=-1;
     }
+    /* allocate metadata subblocks */
+    snew(dhc->subblock_d, 5 + dhc->n_lambda_vec);
+    snew(dhc->subblock_i, 1 + dhc->n_lambda_vec);
+
+    /* now decide which data to write out */
+    dhc->nlambda=0;
+    dhc->ndhdl=0;
+    dhc->dh_expanded=NULL;
+    dhc->dh_energy=NULL;
+    dhc->dh_pv=NULL;
+
+    /* total number of raw data point collections in the sample */
+    dhc->ndh = 0;
 
-    /* add the dhdl's */
-    for (i=0;i<efptNR;i++)
     {
-        if (ir->fepvals->separate_dvdl[i])
+        gmx_bool bExpanded=FALSE;
+        gmx_bool bEnergy=FALSE;
+        gmx_bool bPV=FALSE;
+        int n_lambda_components=0;
+
+        /* first count the number of states */
+
+        /* add the dhdl's */
+        if (fep->dhdl_derivatives == edhdlderivativesYES)
         {
-            dhc->ndh+=1;
+            for (i=0;i<efptNR;i++)
+            {
+                if (ir->fepvals->separate_dvdl[i])
+                {
+                    dhc->ndh+=1;
+                    dhc->ndhdl+=1;
+                }
+            }
         }
-    }
+        /* add the lambdas */
+        dhc->nlambda = ir->fepvals->n_lambda;
+        dhc->ndh += dhc->nlambda;
+        /* another compatibility check */
+        if (dhc->start_lambda < 0)
+        {
+            /* include one more for the specification of the state, by lambda or
+               fep_state*/
+            if (ir->expandedvals->elmcmove > elmcmoveNO) {
+                dhc->ndh +=1;
+                bExpanded=TRUE;
+            }
+            /* whether to print energies */
+            if (ir->fepvals->bPrintEnergy) {
+                dhc->ndh += 1;
+                bEnergy=TRUE;
+            }
+            if (ir->epc > epcNO) {
+                dhc->ndh += 1;  /* include pressure-volume work */
+                bPV=TRUE;
+            }
+        }
+        /* allocate them */
+        snew(dhc->dh, dhc->ndh);
+
+        /* now initialize them */
+        /* the order, for now, must match that of the dhdl.xvg file because of
+           how g_energy -odh is implemented */
+        n=0;
+        if (bExpanded)
+        {
+            dhc->dh_expanded=dhc->dh+n;
+            mde_delta_h_init(dhc->dh+n, ir->fepvals->dh_hist_size,
+                             ir->fepvals->dh_hist_spacing, ndhmax,
+                             dhbtEXPANDED, 0, 0, NULL);
+            n++;
+        }
+        if (bEnergy)
+        {
+            dhc->dh_energy=dhc->dh+n;
+            mde_delta_h_init(dhc->dh+n, ir->fepvals->dh_hist_size,
+                             ir->fepvals->dh_hist_spacing, ndhmax,
+                             dhbtEN, 0, 0, NULL);
+            n++;
+        }
+        /* add the dhdl's */
+        n_lambda_components=0;
+        if (fep->dhdl_derivatives == edhdlderivativesYES)
+        {
+            dhc->dh_dhdl = dhc->dh + n;
+            for (i=0;i<efptNR;i++)
+            {
+                if (ir->fepvals->separate_dvdl[i])
+                {
+                    /* we give it init_lambda for compatibility */
+                    mde_delta_h_init(dhc->dh+n, ir->fepvals->dh_hist_size,
+                                     ir->fepvals->dh_hist_spacing, ndhmax,
+                                     dhbtDHDL, n_lambda_components, 1,
+                                     &(fep->init_lambda));
+                    n++;
+                    n_lambda_components++;
+                }
+            }
+        }
+        else
+        {
+            for (i=0;i<efptNR;i++)
+            {
+                if (ir->fepvals->separate_dvdl[i])
+                {
+                    n_lambda_components++; /* count the components */
+                }
+            }
 
-    /* add the lambdas */
-    dhc->ndh += ir->fepvals->n_lambda;
+        }
+        /* add the lambdas */
+        dhc->dh_du = dhc->dh + n;
+        snew(lambda_vec, n_lambda_components);
+        for(i=0;i<ir->fepvals->n_lambda;i++)
+        {
+            int k=0;
 
-    if (ir->epc > epcNO) {
-        dhc->ndh += 1;  /* include pressure-volume work */
-    }
+            for(j=0;j<efptNR;j++)
+            {
+                if (ir->fepvals->separate_dvdl[j])
+                {
+                    lambda_vec[k++] = fep->all_lambda[j][i];
+                }
+            }
 
-    snew(dhc->dh, dhc->ndh);
-    for(i=0;i<dhc->ndh;i++)
-    {
-        mde_delta_h_init(dhc->dh+i, ir->fepvals->dh_hist_size,
-                         ir->fepvals->dh_hist_spacing, ndhmax);
+            mde_delta_h_init(dhc->dh+n, ir->fepvals->dh_hist_size,
+                             ir->fepvals->dh_hist_spacing, ndhmax,
+                             dhbtDH, 0, n_lambda_components, lambda_vec);
+            n++;
+        }
+        sfree(lambda_vec);
+        if (bPV)
+        {
+            dhc->dh_pv=dhc->dh+n;
+            mde_delta_h_init(dhc->dh+n, ir->fepvals->dh_hist_size,
+                             ir->fepvals->dh_hist_spacing, ndhmax,
+                             dhbtPV, 0, 0, NULL);
+            n++;
+        }
     }
 }
 
@@ -364,16 +557,11 @@ void mde_delta_h_coll_add_dh(t_mde_delta_h_coll *dhc,
                              double fep_state,
                              double energy,
                              double pV,
-                             int bExpanded,
-                             int bPrintEnergy,
-                             int bPressure,
-                             int ndhdl,
-                             int nlambda,
                              double *dhdl,
                              double *foreign_dU,
                              double time)
 {
-    int i,n;
+    int i;
 
     if (!dhc->start_time_set)
     {
@@ -381,37 +569,31 @@ void mde_delta_h_coll_add_dh(t_mde_delta_h_coll *dhc,
         dhc->start_time=time;
     }
 
-    n = 0;
-    if (bExpanded)
+    for (i=0;i<dhc->ndhdl;i++)
     {
-        mde_delta_h_add_dh(dhc->dh+n,fep_state,time);
-        n++;
+        mde_delta_h_add_dh(dhc->dh_dhdl+i, dhdl[i], time);
     }
-    if (bPrintEnergy)
+    for (i=0;i<dhc->nlambda;i++)
     {
-        mde_delta_h_add_dh(dhc->dh+n,energy,time);
-        n++;
+        mde_delta_h_add_dh(dhc->dh_du+i, foreign_dU[i], time);
     }
-    for (i=0;i<ndhdl;i++)
+    if (dhc->dh_pv != NULL)
     {
-        mde_delta_h_add_dh(dhc->dh+n, dhdl[i], time);
-        n++;
+        mde_delta_h_add_dh(dhc->dh_pv, pV, time);
     }
-    for (i=0;i<nlambda;i++)
+    if (dhc->dh_energy != NULL)
     {
-        mde_delta_h_add_dh(dhc->dh+n, foreign_dU[i], time);
-        n++;
+        mde_delta_h_add_dh(dhc->dh_energy,energy,time);
     }
-    if (bPressure)
+    if (dhc->dh_expanded != NULL)
     {
-        mde_delta_h_add_dh(dhc->dh+n, pV, time);
-        n++;
+        mde_delta_h_add_dh(dhc->dh_expanded,fep_state,time);
     }
-}
 
-/* write the data associated with all the du blocks, but not the blocks
-   themselves. Essentially, the metadata.  Or -- is this generated every time?*/
+}
 
+/* write the metadata associated with all the du blocks, and call
+   handle_block to write out all the du blocks */
 void mde_delta_h_coll_handle_block(t_mde_delta_h_coll *dhc,
                                    t_enxframe *fr, int nblock)
 {
@@ -423,18 +605,49 @@ void mde_delta_h_coll_handle_block(t_mde_delta_h_coll *dhc,
     add_blocks_enxframe(fr, nblock);
     blk=fr->block + (nblock-1);
 
-    add_subblocks_enxblock(blk, 1);
+    /* only allocate lambda vector component blocks if they must be written out
+       for backward compatibility */
+    if (dhc->native_lambda_components!=NULL)
+    {
+        add_subblocks_enxblock(blk, 2);
+    }
+    else
+    {
+        add_subblocks_enxblock(blk, 1);
+    }
 
     dhc->subblock_d[0] = dhc->temperature; /* temperature */
     dhc->subblock_d[1] = dhc->start_time; /* time of first sample */
     dhc->subblock_d[2] = dhc->delta_time; /* time difference between samples */
-    dhc->subblock_d[3] = dhc->start_lambda; /* lambda at starttime */
+    dhc->subblock_d[3] = dhc->start_lambda; /* old-style lambda at starttime */
     dhc->subblock_d[4] = dhc->delta_lambda; /* lambda diff. between samples */
+    /* set the lambda vector components if they exist */
+    if (dhc->native_lambda_components!=NULL)
+    {
+        for(i=0;i<dhc->n_lambda_vec;i++)
+        {
+            dhc->subblock_d[5+i] = dhc->native_lambda_vec[i];
+        }
+    }
     blk->id=enxDHCOLL;
-    blk->sub[0].nr=5;
+    blk->sub[0].nr=5 + dhc->n_lambda_vec;
     blk->sub[0].type=xdr_datatype_double;
     blk->sub[0].dval=dhc->subblock_d;
 
+    if (dhc->native_lambda_components != NULL)
+    {
+        dhc->subblock_i[0] = dhc->lambda_index;
+        /* set the lambda vector component IDs if they exist */
+        dhc->subblock_i[1] = dhc->n_lambda_vec;
+        for(i=0;i<dhc->n_lambda_vec;i++)
+        {
+            dhc->subblock_i[i+2] = dhc->native_lambda_components[i];
+        }
+        blk->sub[1].nr=2 + dhc->n_lambda_vec;
+        blk->sub[1].type=xdr_datatype_int;
+        blk->sub[1].ival=dhc->subblock_i;
+    }
+
     for(i=0;i<dhc->ndh;i++)
     {
         nblock++;
index c698b55082a9bd356894299897974aa129659e58..26416fa7393c3c1f74e4d903b6ab8f47fa8a5529 100644 (file)
 extern "C" {
 #endif
 
+
 /* The functions & data structures here describe writing
    energy differences (or their histogram )for use with g_bar */
 
 /* Data for one foreign lambda, or derivative. */
 typedef struct
 {
-    real *dh; /* the raw energy difference data -- actually, store more in here. */
+    real *dh; /* the raw energy data. */
     float *dhf; /* raw difference data -- in floats, for storage. */
     unsigned int ndh; /* number of data points */
     unsigned int ndhmax; /* the maximum number of points */
@@ -69,13 +70,21 @@ typedef struct
                               of the histogram */
     unsigned int maxbin[2]; /* highest bin number with data */
 
-    gmx_bool derivative; /* whether this delta_h contains derivatives */
-    double lambda; /* current lambda */
+    int type;       /* the block type according to dhbtDH, etc. */
+    int derivative; /* The derivative direction (as an index in the lambda
+                       vector) if this delta_h contains derivatives */
+    double *lambda; /* lambda vector (or NULL if not applicable) */
+    int nlambda;    /* length of the lambda vector */
     gmx_bool written;    /* whether this data has already been written out */
 
-    double subblock_d[4]; /* data for an mdebin subblock for I/O. */
-    gmx_large_int_t subblock_l[4]; /* data for an mdebin subblock for I/O.  */
-    int subblock_i[4]; /* data for an mdebin subblock for I/O.  */
+    gmx_large_int_t subblock_meta_l[5]; /* metadata for an mdebin subblock for
+                                           I/O: for histogram counts, etc.*/
+    double *subblock_meta_d; /* metadata subblock for I/O, used for
+                                communicating doubles (i.e. the lambda
+                                vector) */
+    int subblock_meta_i[4]; /* metadata subblock for I/O, used for
+                               communicating ints (i.e. derivative indices,
+                               etc.) */
 } t_mde_delta_h;
 
 /* the type definition is in mdebin_bar.h */
@@ -83,13 +92,39 @@ struct t_mde_delta_h_coll
 {
     t_mde_delta_h *dh; /* the delta h data */
     int ndh; /* the number of delta_h structures */
+
+    int nlambda; /* number of bar dU delta_h structures */
+    t_mde_delta_h *dh_du; /* the delta h data (pointer into dh) */
+
+    int ndhdl; /* number of bar dU delta_h structures */
+    t_mde_delta_h *dh_dhdl; /* the dhdl data (pointer into dh) */
+
+    t_mde_delta_h *dh_energy; /* energy output block (pointer into dh) */
+    t_mde_delta_h *dh_pv; /* pV output block (pointer into dh) */
+    t_mde_delta_h *dh_expanded; /* expanded ensemble output block (pointer 
+                                   into dh) */
+
     double start_time; /* start time of the current dh collection */
     double delta_time; /* time difference between samples */
     gmx_bool start_time_set; /* whether the start time has been set */
     double start_lambda; /* starting lambda for continuous motion of state*/
     double delta_lambda; /* delta lambda, for continuous motion of state */
     double temperature; /* the temperature of the samples*/
-    double subblock_d[5]; /* data for writing an mdebin subblock for I/O */
+
+    double *native_lambda_vec; /* The lambda vector describing the current
+                                  lambda state if it is set (NULL otherwise) */
+    int n_lambda_vec; /* the size of the native lambda vector */
+    int *native_lambda_components; /* the native lambda (and by extension,
+                                      foreign lambda) components in terms
+                                      of efptFEP, efptMASS, etc. */
+    int lambda_index; /* the lambda_fep_state */
+
+    double *subblock_d; /* for writing a metadata mdebin subblock for I/O */
+    int *subblock_i; /* for writing a metadata mdebin subblock for I/O */
+
+    double *lambda_vec_subblock; /* native lambda vector data subblock for
+                                    I/O */
+    int *lambda_index_subblock; /* lambda vector index data subblock for I/O */
 };
 
 
@@ -115,11 +150,6 @@ void mde_delta_h_coll_add_dh(t_mde_delta_h_coll *dhc,
                              double fep_state,
                              double energy,
                              double pV,
-                             int bExpanded,
-                             int bPrintEnergy,
-                             int bPressure,
-                             int ndhdl,
-                             int nlambda,
                              double *dhdl,
                              double *foreign_dU,
                              double time);
index 45cc4dcf563e93821edf3a49394a8f038fac3e44..c35dd896b8668970fef2e874cfd352b05c25638f 100644 (file)
@@ -57,6 +57,8 @@
 #include "xvgr.h"
 #include "gmx_ana.h"
 #include "maths.h"
+#include "names.h"
+#include "mdebin.h"
 
 /* Suppress Cygwin compiler warnings from using newlib version of
  * ctype.h */
 #undef isdigit
 #endif
 
-/* the dhdl.xvg data from a simulation (actually obsolete, but still
-    here for reading the dhdl.xvg file*/
+
+/* Structure for the names of lambda vector components */
+typedef struct lambda_components_t
+{
+    char **names; /* Array of strings with names for the lambda vector
+                     components */
+    int N;              /* The number of components */
+    int Nalloc;         /* The number of allocated components */
+} lambda_components_t;
+
+/* Structure for a lambda vector or a dhdl derivative direction */
+typedef struct lambda_vec_t
+{
+    double *val;    /* The lambda vector component values. Only valid if
+                       dhdl == -1 */
+    int dhdl;       /* The coordinate index for the derivative described by this
+                       structure, or -1 */
+    const lambda_components_t *lc; /* the associated lambda_components
+                                      structure */
+    int index;      /* The state number (init-lambda-state) of this lambda
+                       vector, if known. If not, it is set to -1 */
+} lambda_vec_t;
+
+/* the dhdl.xvg data from a simulation */
 typedef struct xvg_t
 {
-    char   *filename;
+    const char   *filename;
     int    ftp;     /* file type */
     int    nset;    /* number of lambdas, including dhdl */
     int *np;        /* number of data points (du or hists) per lambda */
     int  np_alloc;  /* number of points (du or hists) allocated */
     double temp;    /* temperature */
-    double *lambda; /* the lambdas (of first index for y). */
+    lambda_vec_t *lambda; /* the lambdas (of first index for y). */
     double *t;      /* the times (of second index for y) */
     double **y;     /* the dU values. y[0] holds the derivative, while
                        further ones contain the energy differences between
                        the native lambda and the 'foreign' lambdas. */
-
-    double native_lambda; /* the native lambda */
+    lambda_vec_t native_lambda; /* the native lambda */
 
     struct xvg_t *next, *prev; /*location in the global linked list of xvg_ts*/
 } xvg_t;
 
 
-
 typedef struct hist_t
 {
     unsigned int *bin[2];       /* the (forward + reverse) histogram values */
@@ -107,8 +129,8 @@ typedef struct hist_t
 /* an aggregate of samples for partial free energy calculation */
 typedef struct samples_t 
 {    
-    double native_lambda; 
-    double foreign_lambda;
+    lambda_vec_t *native_lambda;  /* pointer to native lambda vector */
+    lambda_vec_t *foreign_lambda; /* pointer to foreign lambda vector */
     double temp; /* the temperature */
     gmx_bool derivative; /* whether this sample is a derivative */
 
@@ -146,8 +168,9 @@ typedef struct sample_range_t
     foreign lambda) */
 typedef struct sample_coll_t
 {
-    double native_lambda;  /* these should be the same for all samples in the histogram?*/
-    double foreign_lambda; /* collection */
+    lambda_vec_t *native_lambda;  /* these should be the same for all samples
+                                     in the histogram */
+    lambda_vec_t *foreign_lambda; /* collection */
     double temp; /* the temperature */
 
     int nsamples; /* the number of samples */
@@ -162,20 +185,29 @@ typedef struct sample_coll_t
 } sample_coll_t;
 
 /* all the samples associated with a lambda point */
-typedef struct lambda_t
+typedef struct lambda_data_t
 {
-    double lambda; /* the native lambda (at start time if dynamic) */
+    lambda_vec_t *lambda; /* the native lambda (at start time if dynamic) */
     double temp; /* temperature */
 
     sample_coll_t *sc; /* the samples */
 
     sample_coll_t sc_head; /*the pre-allocated list head for the linked list.*/
 
-    struct lambda_t *next, *prev; /* the next and prev in the list */
-} lambda_t;
+    struct lambda_data_t *next, *prev; /* the next and prev in the list */
+} lambda_data_t;
+
+/* Top-level data structure of simulation data */
+typedef struct sim_data_t
+{
+    lambda_data_t *lb; /* a lambda data linked list */
+    lambda_data_t lb_head; /* The head element of the linked list */
 
+    lambda_components_t lc; /* the allowed components of the lambda
+                               vectors */
+} sim_data_t;
 
-/* calculated values. */
+/* Top-level data structure with calculated values. */
 typedef struct {
     sample_coll_t *a, *b; /* the simulation data */
 
@@ -195,6 +227,350 @@ typedef struct {
 } barres_t;
 
 
+/* Initialize a lambda_components structure */
+static void lambda_components_init(lambda_components_t *lc)
+{
+    lc->N=0;
+    lc->Nalloc=2;
+    snew(lc->names, lc->Nalloc);
+}
+
+/* Add a component to a lambda_components structure */
+static void lambda_components_add(lambda_components_t *lc,
+                                  const char *name, size_t name_length)
+{
+    while (lc->N + 1 > lc->Nalloc)
+    {
+        lc->Nalloc = (lc->Nalloc == 0) ? 2 : 2*lc->Nalloc;
+        srealloc( lc->names, lc->Nalloc );
+    }
+    snew(lc->names[lc->N], name_length+1);
+    strncpy(lc->names[lc->N], name, name_length);
+    lc->N++;
+}
+
+/* check whether a component with index 'index' matches the given name, or
+   is also NULL. Returns TRUE if this is the case.
+   the string name does not need to end */
+static gmx_bool lambda_components_check(const lambda_components_t *lc,
+                                        int index,
+                                        const char *name,
+                                        size_t name_length)
+{
+    size_t len;
+    if (index >= lc->N)
+        return FALSE;
+    if (name == NULL && lc->names[index] == NULL)
+        return TRUE;
+    if ((name == NULL) != (lc->names[index] == NULL))
+        return FALSE;
+    len = strlen(lc->names[index]);
+    if (len != name_length)
+        return FALSE;
+    if (strncmp(lc->names[index], name, name_length)== 0)
+        return TRUE;
+    return FALSE;
+}
+
+/* Find the index of a given lambda component name, or -1 if not found */
+static int lambda_components_find(const lambda_components_t *lc,
+                                  const char *name,
+                                  size_t name_length)
+{
+    int i;
+
+    for(i=0;i<lc->N;i++)
+    {
+        if (strncmp(lc->names[i], name, name_length) == 0)
+            return i;
+    }
+    return -1;
+}
+
+
+
+/* initialize a lambda vector */
+static void lambda_vec_init(lambda_vec_t *lv, const lambda_components_t *lc)
+{
+    snew(lv->val, lc->N);
+    lv->index = -1;
+    lv->dhdl = -1;
+    lv->lc = lc;
+}
+
+static void lambda_vec_destroy(lambda_vec_t *lv)
+{
+    sfree(lv->val);
+}
+
+static void lambda_vec_copy(lambda_vec_t *lv, const lambda_vec_t *orig)
+{
+    int i;
+
+    lambda_vec_init(lv, orig->lc);
+    lv->dhdl=orig->dhdl;
+    lv->index=orig->index;
+    for(i=0;i<lv->lc->N;i++)
+    {
+        lv->val[i] = orig->val[i];
+    }
+}
+
+/* write a lambda vec to a preallocated string */
+static void lambda_vec_print(const lambda_vec_t *lv, char *str, gmx_bool named)
+{
+    int i;
+    size_t np;
+
+    str[0]=0; /* reset the string */
+    if (lv -> dhdl < 0)
+    {
+        if (named)
+        {
+            str += sprintf(str, "delta H to ");
+        }
+        if (lv->lc->N > 1)
+        {
+            str += sprintf(str, "(");
+        }
+        for(i=0;i<lv->lc->N;i++)
+        {
+            str += sprintf(str, "%g", lv->val[i]);
+            if (i < lv->lc->N-1)
+            {
+                str += sprintf(str, ", ");
+            }
+        }
+        if (lv->lc->N > 1)
+        {
+            str += sprintf(str, ")");
+        }
+    }
+    else
+    {
+        /* this lambda vector describes a derivative */
+        str += sprintf(str, "dH/dl");
+        if (strlen(lv->lc->names[lv->dhdl]) > 0)
+        {
+            str += sprintf(str, " (%s)", lv->lc->names[lv->dhdl]);
+        }
+    }
+}
+
+/* write a shortened version of the lambda vec to a preallocated string */
+static void lambda_vec_print_short(const lambda_vec_t *lv, char *str)
+{
+    int i;
+    size_t np;
+
+    if (lv->index >= 0)
+    {
+        sprintf(str, "%6d", lv->index);
+    }
+    else
+    {
+        if (lv->dhdl < 0)
+        {
+            sprintf(str, "%6.3f", lv->val[0]);
+        }
+        else
+        {
+            sprintf(str, "dH/dl[%d]", lv->dhdl);
+        }
+    }
+}
+
+/* write an intermediate version of two lambda vecs to a preallocated string */
+static void lambda_vec_print_intermediate(const lambda_vec_t *a,
+                                          const lambda_vec_t *b, char *str)
+{
+    int i;
+    size_t np;
+
+    str[0]=0;
+    if ( (a->index >= 0) && (b->index>=0) )
+    {
+        sprintf(str, "%6.3f", ((double)a->index+(double)b->index)/2.);
+    }
+    else
+    {
+        if ( (a->dhdl < 0) && (b->dhdl < 0) )
+        {
+            sprintf(str, "%6.3f", (a->val[0]+b->val[0])/2.);
+        }
+    }
+}
+
+
+
+/* calculate the difference in lambda vectors: c = a-b.
+   c must be initialized already, and a and b must describe non-derivative
+   lambda points */
+static void lambda_vec_diff(const lambda_vec_t *a, const lambda_vec_t *b,
+                            lambda_vec_t *c)
+{
+    int i;
+
+    if ( (a->dhdl > 0)  || (b->dhdl > 0) )
+    {
+        gmx_fatal(FARGS,
+                  "Trying to calculate the difference between derivatives instead of lambda points");
+    }
+    if ((a->lc != b->lc) || (a->lc != c->lc) )
+    {
+        gmx_fatal(FARGS,
+                  "Trying to calculate the difference lambdas with differing basis set");
+    }
+    for(i=0;i<a->lc->N;i++)
+    {
+        c->val[i] = a->val[i] - b->val[i];
+    }
+}
+
+/* calculate and return the absolute difference in lambda vectors: c = |a-b|. 
+   a and b must describe non-derivative lambda points */
+static double lambda_vec_abs_diff(const lambda_vec_t *a, const lambda_vec_t *b)
+{
+    int i;
+    double ret=0.;
+
+    if ( (a->dhdl > 0)  || (b->dhdl > 0) )
+    {
+        gmx_fatal(FARGS,
+                  "Trying to calculate the difference between derivatives instead of lambda points");
+    }
+    if (a->lc != b->lc) 
+    {
+        gmx_fatal(FARGS,
+                  "Trying to calculate the difference lambdas with differing basis set");
+    }
+    for(i=0;i<a->lc->N;i++)
+    {
+        double df=a->val[i] - b->val[i];
+        ret += df*df;
+    }
+    return sqrt(ret);
+}
+
+
+/* check whether two lambda vectors are the same */
+static gmx_bool lambda_vec_same(const lambda_vec_t *a, const lambda_vec_t *b)
+{
+    int i;
+
+    if (a->lc != b->lc)
+        return FALSE;
+    if (a->dhdl < 0)
+    {
+        for(i=0;i<a->lc->N;i++)
+        {
+            if (!gmx_within_tol(a->val[i], b->val[i], 10*GMX_REAL_EPS))
+            {
+                return FALSE;
+            }
+        }
+        return TRUE;
+    }
+    else
+    {
+        /* they're derivatives, so we check whether the indices match */
+        return (a->dhdl == b->dhdl);
+    }
+}
+
+/* Compare the sort order of two foreign lambda vectors
+
+    returns 1 if a is 'bigger' than b,
+    returns 0 if they're the same,
+    returns -1 if a is 'smaller' than b.*/
+static gmx_bool lambda_vec_cmp_foreign(const lambda_vec_t *a,
+                                       const lambda_vec_t *b)
+{
+    int i;
+    double norm_a=0, norm_b=0;
+    gmx_bool different=FALSE;
+
+    if (a->lc != b->lc)
+    {
+        gmx_fatal(FARGS, "Can't compare lambdas with differing basis sets");
+    }
+    /* if either one has an index we sort based on that */
+    if ((a->index >= 0) || (b->index >= 0))
+    {
+        if (a->index == b->index)
+            return 0;
+        return (a->index > b->index) ? 1 : -1;
+    }
+    if (a->dhdl >= 0 || b->dhdl >= 0)
+    {
+        /* lambda vectors that are derivatives always sort higher than those
+           without derivatives */
+        if ((a->dhdl >= 0)  != (b->dhdl >= 0) )
+        {
+            return (a->dhdl >= 0) ? 1 : -1 ;
+        }
+        return a->dhdl > b->dhdl;
+    }
+
+    /* neither has an index, so we can only sort on the lambda components,
+       which is only valid if there is one component */
+    for(i=0;i<a->lc->N;i++)
+    {
+        if (!gmx_within_tol(a->val[i], b->val[i], 10*GMX_REAL_EPS))
+        {
+            different=TRUE;
+        }
+        norm_a += a->val[i]*a->val[i];
+        norm_b += b->val[i]*b->val[i];
+    }
+    if (!different)
+    {
+        return 0;
+    }
+    return norm_a > norm_b;
+}
+
+/* Compare the sort order of two native lambda vectors
+
+    returns 1 if a is 'bigger' than b,
+    returns 0 if they're the same,
+    returns -1 if a is 'smaller' than b.*/
+static gmx_bool lambda_vec_cmp_native(const lambda_vec_t *a,
+                                      const lambda_vec_t *b)
+{
+    int i;
+
+    if (a->lc != b->lc)
+    {
+        gmx_fatal(FARGS, "Can't compare lambdas with differing basis sets");
+    }
+    /* if either one has an index we sort based on that */
+    if ((a->index >= 0) || (b->index >= 0))
+    {
+        if (a->index == b->index)
+            return 0;
+        return (a->index > b->index) ? 1 : -1;
+    }
+    /* neither has an index, so we can only sort on the lambda components,
+       which is only valid if there is one component */
+    if (a->lc->N > 1)
+    {
+        gmx_fatal(FARGS,
+                  "Can't compare lambdas with no index and > 1 component");
+    }
+    if (a->dhdl >= 0 || b->dhdl >= 0)
+    {
+        gmx_fatal(FARGS,
+                  "Can't compare native lambdas that are derivatives");
+    }
+    if (gmx_within_tol(a->val[0], b->val[0], 10*GMX_REAL_EPS))
+    {
+        return 0;
+    }
+    return a->val[0] > b->val[0] ? 1 : -1;
+}
+
+
 
 
 static void hist_init(hist_t *h, int nhist, int *nbin)
@@ -231,8 +607,8 @@ static void xvg_init(xvg_t *ba)
     ba->y=NULL;
 }
 
-static void samples_init(samples_t *s, double native_lambda,
-                         double foreign_lambda, double temp,
+static void samples_init(samples_t *s, lambda_vec_t *native_lambda,
+                         lambda_vec_t *foreign_lambda, double temp,
                          gmx_bool derivative, const char *filename)
 {
     s->native_lambda=native_lambda;
@@ -263,8 +639,8 @@ static void sample_range_init(sample_range_t *r, samples_t *s)
     r->s=NULL;
 }
 
-static void sample_coll_init(sample_coll_t *sc, double native_lambda,
-                             double foreign_lambda, double temp)
+static void sample_coll_init(sample_coll_t *sc, lambda_vec_t *native_lambda,
+                             lambda_vec_t *foreign_lambda, double temp)
 {
     sc->native_lambda = native_lambda;
     sc->foreign_lambda = foreign_lambda;
@@ -287,7 +663,8 @@ static void sample_coll_destroy(sample_coll_t *sc)
 }
 
 
-static void lambda_init(lambda_t *l, double native_lambda, double temp)
+static void lambda_data_init(lambda_data_t *l, lambda_vec_t *native_lambda,
+                             double temp)
 {
     l->lambda=native_lambda;
     l->temp=temp;
@@ -297,7 +674,7 @@ static void lambda_init(lambda_t *l, double native_lambda, double temp)
 
     l->sc=&(l->sc_head);
 
-    sample_coll_init(l->sc, native_lambda, 0., 0.);
+    sample_coll_init(l->sc, native_lambda, NULL, 0.);
     l->sc->next=l->sc;
     l->sc->prev=l->sc;
 }
@@ -318,13 +695,6 @@ static void barres_init(barres_t *br)
 }
 
 
-
-
-static gmx_bool lambda_same(double lambda1, double lambda2)
-{
-    return gmx_within_tol(lambda1, lambda2, 10*GMX_REAL_EPS);
-}
-
 /* calculate the total number of samples in a sample collection */
 static void sample_coll_calc_ntot(sample_coll_t *sc)
 {
@@ -350,14 +720,14 @@ static void sample_coll_calc_ntot(sample_coll_t *sc)
 
 /* find the barsamples_t associated with a lambda that corresponds to
    a specific foreign lambda */
-static sample_coll_t *lambda_find_sample_coll(lambda_t *l, 
-                                              double foreign_lambda)
+static sample_coll_t *lambda_data_find_sample_coll(lambda_data_t *l,
+                                                   lambda_vec_t *foreign_lambda)
 {
     sample_coll_t *sc=l->sc->next;
 
     while(sc != l->sc)
     {
-        if (lambda_same(sc->foreign_lambda,foreign_lambda))
+        if (lambda_vec_same(sc->foreign_lambda,foreign_lambda))
         {
             return sc;
         }
@@ -368,12 +738,12 @@ static sample_coll_t *lambda_find_sample_coll(lambda_t *l,
 }
 
 /* insert li into an ordered list of lambda_colls */
-static void lambda_insert_sample_coll(lambda_t *l, sample_coll_t *sc)
+static void lambda_data_insert_sample_coll(lambda_data_t *l, sample_coll_t *sc)
 {
     sample_coll_t *scn=l->sc->next;
     while ( (scn!=l->sc) )
     {
-        if (scn->foreign_lambda > sc->foreign_lambda)
+        if (lambda_vec_cmp_foreign(scn->foreign_lambda, sc->foreign_lambda) > 0)
             break;
         scn=scn->next;
     }
@@ -385,12 +755,12 @@ static void lambda_insert_sample_coll(lambda_t *l, sample_coll_t *sc)
 }
 
 /* insert li into an ordered list of lambdas */
-static void lambda_insert_lambda(lambda_t *head, lambda_t *li)
+static void lambda_data_insert_lambda(lambda_data_t *head, lambda_data_t *li)
 {
-    lambda_t *lc=head->next;
+    lambda_data_t *lc=head->next;
     while (lc!=head) 
     {
-        if (lc->lambda > li->lambda)
+        if (lambda_vec_cmp_native(lc->lambda, li->lambda) > 0)
             break;
         lc=lc->next;
     }
@@ -412,12 +782,12 @@ static void sample_coll_insert_sample(sample_coll_t *sc, samples_t *s,
         gmx_fatal(FARGS, "Temperatures in files %s and %s are not the same!",
                    s->filename, sc->next->s[0]->filename);
     }
-    if (sc->native_lambda != s->native_lambda)
+    if (!lambda_vec_same(sc->native_lambda, s->native_lambda))
     {
         gmx_fatal(FARGS, "Native lambda in files %s and %s are not the same (and they should be)!",
                    s->filename, sc->next->s[0]->filename);
     }
-    if (sc->foreign_lambda != s->foreign_lambda)
+    if (!lambda_vec_same(sc->foreign_lambda,s->foreign_lambda))
     {
         gmx_fatal(FARGS, "Foreign lambda in files %s and %s are not the same (and they should be)!",
                    s->filename, sc->next->s[0]->filename);
@@ -439,18 +809,18 @@ static void sample_coll_insert_sample(sample_coll_t *sc, samples_t *s,
 
 /* insert a sample into a lambda_list, creating the right sample_coll if 
    neccesary */
-static void lambda_list_insert_sample(lambda_t *head, samples_t *s)
+static void lambda_data_list_insert_sample(lambda_data_t *head, samples_t *s)
 {
     gmx_bool found=FALSE;
     sample_coll_t *sc;
     sample_range_t r;
 
-    lambda_t *l=head->next;
+    lambda_data_t *l=head->next;
 
-    /* first search for the right lambda_t */
+    /* first search for the right lambda_data_t */
     while(l != head)
     {
-        if (lambda_same(l->lambda, s->native_lambda) )
+        if (lambda_vec_same(l->lambda, s->native_lambda) )
         {
             found=TRUE;
             break;
@@ -461,17 +831,17 @@ static void lambda_list_insert_sample(lambda_t *head, samples_t *s)
     if (!found)
     {
         snew(l, 1); /* allocate a new one */
-        lambda_init(l, s->native_lambda, s->temp); /* initialize it */
-        lambda_insert_lambda(head, l); /* add it to the list */
+        lambda_data_init(l, s->native_lambda, s->temp); /* initialize it */
+        lambda_data_insert_lambda(head, l); /* add it to the list */
     }
 
     /* now look for a sample collection */
-    sc=lambda_find_sample_coll(l, s->foreign_lambda);
+    sc=lambda_data_find_sample_coll(l, s->foreign_lambda);
     if (!sc)
     {
         snew(sc, 1); /* allocate a new one */
         sample_coll_init(sc, s->native_lambda, s->foreign_lambda, s->temp);
-        lambda_insert_sample_coll(l, sc);
+        lambda_data_insert_sample_coll(l, sc);
     }
 
     /* now insert the samples into the sample coll */
@@ -635,15 +1005,15 @@ static void sample_coll_make_hist(sample_coll_t *sc, int **bin,
 }
 
 /* write a collection of histograms to a file */
-void lambdas_histogram(lambda_t *bl_head, const char *filename, 
-                       int nbin_default, const output_env_t oenv)
+void sim_data_histogram(sim_data_t *sd, const char *filename,
+                        int nbin_default, const output_env_t oenv)
 {
     char label_x[STRLEN];
     const char *dhdl="dH/d\\lambda",*deltag="\\DeltaH",*lambda="\\lambda";
     const char *title="N(\\DeltaH)";
     const char *label_y="Samples";
     FILE *fp;
-    lambda_t *bl;
+    lambda_data_t *bl;
     int nsets=0;
     char **setnames=NULL;
     gmx_bool first_set=FALSE;
@@ -654,6 +1024,7 @@ void lambdas_histogram(lambda_t *bl_head, const char *filename,
     double dx=0;
     double min=0;
     int i;
+    lambda_data_t *bl_head = sd->lb;
 
     printf("\nWriting histogram to %s\n", filename);
     sprintf(label_x, "\\DeltaH (%s)", unit_energy);
@@ -670,19 +1041,23 @@ void lambdas_histogram(lambda_t *bl_head, const char *filename,
         /* iterate over all samples */
         while(sc!=bl->sc)
         {
+            char buf[STRLEN], buf2[STRLEN];
+
             nsets++;
             srenew(setnames, nsets); 
             snew(setnames[nsets-1], STRLEN);
-            if (!lambda_same(sc->foreign_lambda, sc->native_lambda))
+            if (sc->foreign_lambda->dhdl < 0)
             {
-                sprintf(setnames[nsets-1], "N(%s(%s=%g) | %s=%g)",
-                        deltag, lambda, sc->foreign_lambda, lambda,
-                        sc->native_lambda);
+                lambda_vec_print(sc->native_lambda, buf, FALSE);
+                lambda_vec_print(sc->foreign_lambda, buf2, FALSE);
+                sprintf(setnames[nsets-1], "N(%s(%s=%s) | %s=%s)",
+                        deltag, lambda, buf2, lambda, buf);
             }
             else
             {
-                sprintf(setnames[nsets-1], "N(%s | %s=%g)",
-                        dhdl, lambda, sc->native_lambda);
+                lambda_vec_print(sc->native_lambda, buf, FALSE);
+                sprintf(setnames[nsets-1], "N(%s | %s=%s)",
+                        dhdl, lambda, buf);
             }
             sc=sc->next;
         }
@@ -733,15 +1108,16 @@ void lambdas_histogram(lambda_t *bl_head, const char *filename,
 
 /* create a collection (array) of barres_t object given a ordered linked list 
    of barlamda_t sample collections */
-static barres_t *barres_list_create(lambda_t *bl_head, int *nres,
+static barres_t *barres_list_create(sim_data_t *sd, int *nres,
                                     gmx_bool use_dhdl)
 {
-    lambda_t *bl;
+    lambda_data_t *bl;
     int nlambda=0;
     barres_t *res;
     int i;
     gmx_bool dhdl=FALSE;
     gmx_bool first=TRUE;
+    lambda_data_t *bl_head=sd->lb;
 
     /* first count the lambdas */
     bl=bl_head->next;
@@ -761,8 +1137,8 @@ static barres_t *barres_list_create(lambda_t *bl_head, int *nres,
         barres_t *br=&(res[*nres]);
         /* there is always a previous one. we search for that as a foreign 
            lambda: */
-        scprev=lambda_find_sample_coll(bl->prev, bl->lambda);
-        sc=lambda_find_sample_coll(bl, bl->prev->lambda);
+        scprev=lambda_data_find_sample_coll(bl->prev, bl->lambda);
+        sc=lambda_data_find_sample_coll(bl, bl->prev->lambda);
 
         barres_init(br);
 
@@ -770,8 +1146,8 @@ static barres_t *barres_list_create(lambda_t *bl_head, int *nres,
         {
             /* we use dhdl */
 
-            scprev=lambda_find_sample_coll(bl->prev, bl->prev->lambda);
-            sc=lambda_find_sample_coll(bl, bl->lambda);
+            scprev=lambda_data_find_sample_coll(bl->prev, bl->prev->lambda);
+            sc=lambda_data_find_sample_coll(bl, bl->lambda);
 
             if (first)
             {
@@ -818,7 +1194,8 @@ static double barres_list_max_disc_err(barres_t *res, int nres)
     {
         barres_t *br=&(res[i]);
 
-        delta_lambda=fabs(br->b->native_lambda-br->a->native_lambda);
+        delta_lambda=lambda_vec_abs_diff(br->b->native_lambda,
+                                         br->a->native_lambda);
 
         for(j=0;j<br->a->nsamples;j++)
         {
@@ -905,11 +1282,12 @@ static void sample_coll_impose_times(sample_coll_t *sc, double begin_t,
     sample_coll_calc_ntot(sc);
 }
 
-static void lambdas_impose_times(lambda_t *head, double begin, double end)
+static void sim_data_impose_times(sim_data_t *sd, double begin, double end)
 {
     double first_t, last_t;
     double begin_t, end_t;
-    lambda_t *lc;
+    lambda_data_t *lc;
+    lambda_data_t *head=sd->lb;
     int j;
 
     if (begin<=0 && end<0) 
@@ -1175,6 +1553,17 @@ static void sample_coll_min_max(sample_coll_t *sc, double Wfac,
     }
 }
 
+/* Initialize a sim_data structure */
+static void sim_data_init(sim_data_t *sd)
+{
+    /* make linked list */
+    sd->lb = &(sd->lb_head);
+    sd->lb->next = sd->lb;
+    sd->lb->prev = sd->lb;
+
+    lambda_components_init(&(sd->lc));
+}
+
 
 static double calc_bar_sum(int n,const double *W,double Wfac,double sbMmDG)
 {
@@ -1250,7 +1639,8 @@ static double calc_bar_lowlevel(sample_coll_t *ca, sample_coll_t *cb,
 
     M = log(n1/n2);
 
-    if (!lambda_same(ca->native_lambda, ca->foreign_lambda))
+    /*if (!lambda_vec_same(ca->native_lambda, ca->foreign_lambda))*/
+    if (ca->foreign_lambda->dhdl<0)
     {
         /* this is the case when the delta U were calculated directly
            (i.e. we're not scaling dhdl) */
@@ -1261,7 +1651,15 @@ static double calc_bar_lowlevel(sample_coll_t *ca, sample_coll_t *cb,
     {
         /* we're using dhdl, so delta_lambda needs to be a 
            multiplication factor.  */
-        double delta_lambda=cb->native_lambda-ca->native_lambda;
+        /*double delta_lambda=cb->native_lambda-ca->native_lambda;*/
+        double delta_lambda=lambda_vec_abs_diff(cb->native_lambda,
+                                                ca->native_lambda);
+        if (cb->native_lambda->lc->N > 1)
+        {
+            gmx_fatal(FARGS,
+                      "Can't (yet) do multi-component dhdl interpolation");
+        }
+
         Wfac1 =  beta*delta_lambda;
         Wfac2 = -beta*delta_lambda;
     }
@@ -1379,7 +1777,8 @@ static void calc_rel_entropy(sample_coll_t *ca, sample_coll_t *cb,
     n2 = cb->ntot;
 
     /* to ensure the work values are the same as during the delta_G */
-    if (!lambda_same(ca->native_lambda, ca->foreign_lambda))
+    /*if (!lambda_vec_same(ca->native_lambda, ca->foreign_lambda))*/
+    if (ca->foreign_lambda->dhdl<0)
     {
         /* this is the case when the delta U were calculated directly
            (i.e. we're not scaling dhdl) */
@@ -1390,7 +1789,8 @@ static void calc_rel_entropy(sample_coll_t *ca, sample_coll_t *cb,
     {
         /* we're using dhdl, so delta_lambda needs to be a 
            multiplication factor.  */
-        double delta_lambda=cb->native_lambda-ca->native_lambda;
+        double delta_lambda=lambda_vec_abs_diff(cb->native_lambda,
+                                                ca->native_lambda);
         Wfac1 =  beta*delta_lambda;
         Wfac2 = -beta*delta_lambda;
     }
@@ -1489,7 +1889,8 @@ static void calc_dg_stddev(sample_coll_t *ca, sample_coll_t *cb,
     n2 = cb->ntot;
 
     /* to ensure the work values are the same as during the delta_G */
-    if (!lambda_same(ca->native_lambda, ca->foreign_lambda))
+    /*if (!lambda_vec_same(ca->native_lambda, ca->foreign_lambda))*/
+    if (ca->foreign_lambda->dhdl<0)
     {
         /* this is the case when the delta U were calculated directly
            (i.e. we're not scaling dhdl) */
@@ -1500,7 +1901,8 @@ static void calc_dg_stddev(sample_coll_t *ca, sample_coll_t *cb,
     {
         /* we're using dhdl, so delta_lambda needs to be a 
            multiplication factor.  */
-        double delta_lambda=cb->native_lambda-ca->native_lambda;
+        double delta_lambda=lambda_vec_abs_diff(cb->native_lambda,
+                                                ca->native_lambda);
         Wfac1 =  beta*delta_lambda;
         Wfac2 = -beta*delta_lambda;
     }
@@ -1773,43 +2175,274 @@ static double bar_err(int nbmin, int nbmax, const double *partsum)
     return sqrt(svar/(nbmax + 1 - nbmin));
 }
 
+
+/* Seek the end of an identifier (consecutive non-spaces), followed by
+   an optional number of spaces or '='-signs. Returns a pointer to the
+   first non-space value found after that. Returns NULL if the string
+   ends before that.
+   */
+static const char *find_value(const char *str)
+{
+    gmx_bool name_end_found=FALSE;
+
+    /* if the string is a NULL pointer, return a NULL pointer. */
+    if (str==NULL)
+    {
+        return NULL;
+    }
+    while(*str != '\0')
+    {
+        /* first find the end of the name */
+        if (! name_end_found)
+        {
+            if ( isspace(*str) || (*str == '=') )
+            {
+                name_end_found=TRUE;
+            }
+        }
+        else
+        {
+            if (! ( isspace(*str) || (*str == '=') ))
+            {
+                return str;
+            }
+        }
+        str++;
+    }
+    return NULL;
+}
+
+
+
+/* read a vector-notation description of a lambda vector */
+static gmx_bool read_lambda_compvec(const char *str,
+                                    lambda_vec_t *lv,
+                                    const lambda_components_t *lc_in,
+                                    lambda_components_t *lc_out,
+                                    const char **end,
+                                    const char *fn)
+{
+    gmx_bool initialize_lc = FALSE; /* whether to initialize the lambda
+                                       components, or to check them */
+    gmx_bool start_reached=FALSE; /* whether the start of component names
+                                     has been reached */
+    gmx_bool vector=FALSE; /* whether there are multiple components */
+    int n = 0; /* current component number */
+    const char *val_start=NULL; /* start of the component name, or NULL
+                                   if not in a value */
+    char *strtod_end;
+    gmx_bool OK=TRUE;
+
+    if (end)
+        *end=str;
+
+
+    if (lc_out && lc_out->N == 0)
+    {
+        initialize_lc = TRUE;
+    }
+
+    if (lc_in == NULL)
+        lc_in = lc_out;
+
+    while(1)
+    {
+        if (!start_reached)
+        {
+            if (isalnum(*str))
+            {
+                vector=FALSE;
+                start_reached=TRUE;
+                val_start=str;
+            }
+            else if (*str=='(')
+            {
+                vector=TRUE;
+                start_reached=TRUE;
+            }
+            else if (! isspace(*str))
+            {
+                gmx_fatal(FARGS, "Error in lambda components in %s", fn);
+            }
+        }
+        else
+        {
+            if (val_start)
+            {
+                if (isspace(*str) || *str==')' || *str==',' || *str=='\0')
+                {
+                    /* end of value */
+                    if (lv == NULL)
+                    {
+                        if (initialize_lc)
+                        {
+                            lambda_components_add(lc_out, val_start, 
+                                                  (str-val_start));
+                        }
+                        else
+                        {
+                            if (!lambda_components_check(lc_out, n, val_start, 
+                                                         (str-val_start)))
+                            {
+                                return FALSE;
+                            }
+                        }
+                    }
+                    else
+                    {
+                        /* add a vector component to lv */
+                        lv->val[n] = strtod(val_start, &strtod_end);
+                        if (val_start == strtod_end)
+                        {
+                            gmx_fatal(FARGS,
+                                      "Error reading lambda vector in %s", fn);
+                        }
+                    }
+                    /* reset for the next identifier */
+                    val_start=NULL;
+                    n++;
+                    if (!vector)
+                    {
+                        return OK;
+                    }
+                }
+            }
+            else if (isalnum(*str))
+            {
+                val_start=str;
+            }
+            if (*str == ')')
+            {
+                str++;
+                if (end)
+                    *end=str;
+                if (!vector)
+                {
+                    gmx_fatal(FARGS, "Error in lambda components in %s", fn);
+                }
+                else
+                {
+                    if (n == lc_in->N)
+                    {
+                        return OK;
+                    }
+                    else if (lv == NULL)
+                    {
+                        return FALSE;
+                    }
+                    else
+                    {
+                        gmx_fatal(FARGS, "Incomplete lambda vector data in %s", 
+                                  fn);
+                        return FALSE;
+                    }
+
+                }
+            }
+        }
+        if (*str == '\0')
+        {
+            break;
+        }
+        str++;
+        if (end)
+        {
+            *end=str;
+        }
+    }
+    if (vector)
+    {
+        gmx_fatal(FARGS, "Incomplete lambda components data in %s", fn);
+        return FALSE;
+    }
+    return OK;
+}
+
+/* read and check the component names from a string */
+static gmx_bool read_lambda_components(const char *str,
+                                        lambda_components_t *lc,
+                                        const char **end,
+                                        const char *fn)
+{
+    return read_lambda_compvec(str, NULL, NULL, lc, end, fn);
+}
+
+/* read an initialized lambda vector from a string */
+static gmx_bool read_lambda_vector(const char *str,
+                                   lambda_vec_t *lv,
+                                   const char **end,
+                                   const char *fn)
+{
+    return read_lambda_compvec(str, lv, lv->lc, NULL, end, fn);
+}
+
+
+
 /* deduce lambda value from legend. 
-input:
-    bdhdl = if true, value may be a derivative. 
-output:
-    bdhdl = whether the legend was for a derivative.
+    fn = the file name
+    legend = the legend string
+    ba = the xvg data
+    lam = the initialized lambda vector
+returns whether to use the data in this set.
     */
-static double legend2lambda(char *fn,const char *legend,gmx_bool *bdhdl)
+static gmx_bool legend2lambda(const char *fn,
+                              const char *legend,
+                              xvg_t *ba,
+                              lambda_vec_t *lam)
 {
     double lambda=0;
-    const char   *ptr;
+    const char   *ptr=NULL, *ptr2=NULL;
     gmx_bool ok=FALSE;
+    gmx_bool bdhdl=FALSE;
+    const char *tostr=" to ";
 
     if (legend == NULL)
     {
         gmx_fatal(FARGS,"There is no legend in file '%s', can not deduce lambda",fn);
     }
-    ptr = strrchr(legend,' ');
 
-    if (strstr(legend,"dH"))
+    /* look for the last 'to': */
+    ptr2=legend;
+    do
     {
-        if (! (*bdhdl))
+        ptr2 = strstr(ptr2, tostr);
+        if (ptr2!=NULL)
         {
-            ok=FALSE;
-        }
-        else
-        {
-            ok=TRUE;
+            ptr=ptr2;
+            ptr2++;
         }
     }
+    while(ptr2!=NULL && *ptr2!= '\0' );
+
+    if (ptr)
+    {
+        ptr += strlen(tostr)-1; /* and advance past that 'to' */
+    }
     else
     {
-        if (strchr(legend,'D') != NULL && strchr(legend,'H') != NULL)
+        /* look for the = sign */
+        ptr = strrchr(legend,'=');
+        if (!ptr)
         {
-            ok=TRUE;
-            *bdhdl=FALSE;
+            /* otherwise look for the last space */
+            ptr = strrchr(legend,' ');
         }
     }
+
+    if (strstr(legend,"dH"))
+    {
+        ok=TRUE;
+        bdhdl=TRUE;
+    }
+    else if (strchr(legend,'D') != NULL && strchr(legend,'H') != NULL)
+    {
+        ok=TRUE;
+        bdhdl=FALSE;
+    }
+    else /*if (strstr(legend, "pV"))*/
+    {
+        return FALSE;
+    }
     if (!ptr)
     {
         ok=FALSE;
@@ -1817,52 +2450,194 @@ static double legend2lambda(char *fn,const char *legend,gmx_bool *bdhdl)
 
     if (!ok)
     {
-        printf("%s\n", legend);
         gmx_fatal(FARGS,"There is no proper lambda legend in file '%s', can not deduce lambda",fn);
     }
-    if (sscanf(ptr,"%lf",&lambda) != 1)
+    if (!bdhdl)
     {
-        gmx_fatal(FARGS,"There is no proper lambda legend in file '%s', can not deduce lambda",fn);
+        ptr=find_value(ptr);
+        if (!ptr || !read_lambda_vector(ptr, lam, NULL, fn))
+        {
+            gmx_fatal(FARGS, "lambda vector '%s' %s faulty", legend, fn);
+        }
     }
+    else
+    {
+        int dhdl_index;
+        const char *end;
+        char buf[STRLEN];
 
-    return lambda;
+        ptr = strrchr(legend, '=');
+        end=ptr;
+        if (ptr)
+        {
+            /* there must be a component name */
+            ptr--;
+            if (ptr < legend)
+            {
+                gmx_fatal(FARGS, "dhdl legend '%s' %s faulty", legend, fn);
+            }
+            /* now backtrack to the start of the identifier */
+            while(isspace(*ptr))
+            {
+                end=ptr;
+                ptr--;
+                if (ptr < legend)
+                {
+                    gmx_fatal(FARGS, "dhdl legend '%s' %s faulty", legend, fn);
+                }
+            }
+            while(!isspace(*ptr))
+            {
+                ptr--;
+                if (ptr < legend)
+                {
+                    gmx_fatal(FARGS, "dhdl legend '%s' %s faulty", legend, fn);
+                }
+            }
+            ptr++;
+            strncpy(buf, ptr, (end-ptr));
+            buf[(end-ptr)]='\0';
+            dhdl_index=lambda_components_find(lam->lc, ptr, (end-ptr));
+            if (dhdl_index < 0)
+            {
+                char buf[STRLEN];
+                strncpy(buf, ptr, (end-ptr));
+                buf[(end-ptr)]='\0';
+                gmx_fatal(FARGS,
+                          "Did not find lambda component for '%s' in %s",
+                          buf, fn);
+            }
+        }
+        else
+        {
+            if (lam->lc->N > 1)
+            {
+                gmx_fatal(FARGS,
+                   "dhdl without component name with >1 lambda component in %s",
+                   fn);
+            }
+            dhdl_index=0;
+        }
+        lam->dhdl = dhdl_index;
+    }
+    return TRUE;
 }
 
-static gmx_bool subtitle2lambda(const char *subtitle,double *lambda)
+static gmx_bool subtitle2lambda(const char *subtitle, xvg_t *ba, const char *fn,
+                                lambda_components_t *lc)
 {
     gmx_bool bFound;
-    char *ptr;
+    const char *ptr;
+    char *end;
+    double native_lambda;
 
     bFound = FALSE;
 
-    /* plain text lambda string */
-    ptr = strstr(subtitle,"lambda");
-    if (ptr == NULL)
-    {
-        /* xmgrace formatted lambda string */
-        ptr = strstr(subtitle,"\\xl\\f{}");
-    }
-    if (ptr == NULL)
-    {
-        /* xmgr formatted lambda string */
-        ptr = strstr(subtitle,"\\8l\\4");
-    }
-    if (ptr != NULL)
+    /* first check for a state string */
+    ptr = strstr(subtitle, "state");
+    if (ptr)
     {
-        ptr = strstr(ptr,"=");
+        int index=-1;
+        const char *val_end;
+
+        /* the new 4.6 style lambda vectors */
+        ptr = find_value(ptr);
+        if (ptr)
+        {
+            index = strtol(ptr, &end, 10);
+            if (ptr == end)
+            {
+                gmx_fatal(FARGS,"Incomplete state data in %s", fn);
+                return FALSE;
+            }
+            ptr=end;
+        }
+        else
+        {
+            gmx_fatal(FARGS,"Incomplete state data in %s", fn);
+            return FALSE;
+        }
+        /* now find the lambda vector component names */
+        while(*ptr!='(' && ! isalnum(*ptr))
+        {
+            ptr++;
+            if (*ptr == '\0')
+            {
+                gmx_fatal(FARGS,
+                          "Incomplete lambda vector component data in %s", fn);
+                return FALSE;
+            }
+        }
+        val_end=ptr;
+        if (!read_lambda_components(ptr, lc, &val_end, fn))
+        {
+            gmx_fatal(FARGS,
+                      "lambda vector components in %s don't match those previously read", 
+                      fn);
+        }
+        ptr=find_value(val_end);
+        if (!ptr)
+        {
+            gmx_fatal(FARGS,"Incomplete state data in %s", fn);
+            return FALSE;
+        }
+        lambda_vec_init(&(ba->native_lambda), lc);
+        if (!read_lambda_vector(ptr, &(ba->native_lambda), NULL, fn))
+        {
+            gmx_fatal(FARGS, "lambda vector in %s faulty", fn);
+        }
+        ba->native_lambda.index=index;
+        bFound=TRUE;
     }
-    if (ptr != NULL)
+    else
     {
-        bFound = (sscanf(ptr+1,"%lf",lambda) == 1);
+        /* compatibility mode: check for lambda in other ways. */
+        /* plain text lambda string */
+        ptr = strstr(subtitle,"lambda");
+        if (ptr == NULL)
+        {
+            /* xmgrace formatted lambda string */
+            ptr = strstr(subtitle,"\\xl\\f{}");
+        }
+        if (ptr == NULL)
+        {
+            /* xmgr formatted lambda string */
+            ptr = strstr(subtitle,"\\8l\\4");
+        }
+        if (ptr != NULL)
+        {
+            ptr = strstr(ptr,"=");
+        }
+        if (ptr != NULL)
+        {
+            bFound = (sscanf(ptr+1,"%lf",&(native_lambda)) == 1);
+            /* add the lambda component name as an empty string */
+            if (lc->N > 0)
+            {
+                if (!lambda_components_check(lc, 0, "", 0))
+                {
+                    gmx_fatal(FARGS,
+                              "lambda vector components in %s don't match those previously read", 
+                              fn);
+                }
+            }
+            else
+            {
+                lambda_components_add(lc, "", 0);
+            }
+            lambda_vec_init(&(ba->native_lambda), lc);
+            ba->native_lambda.val[0]=native_lambda;
+        }
     }
 
     return bFound;
 }
 
-static double filename2lambda(char *fn)
+static void filename2lambda(const char *fn, xvg_t *ba)
 {
     double lambda;
-    char   *ptr,*endptr,*digitptr;
+    const char   *ptr,*digitptr;
+    char *endptr;
     int     dirsep;
     ptr = fn;
     /* go to the end of the path string and search backward to find the last 
@@ -1904,16 +2679,17 @@ static double filename2lambda(char *fn)
     {
         gmx_fatal(FARGS,"Malformed number in file path '%s'",fn);
     }
-
-    return lambda;
 }
 
-static void read_bar_xvg_lowlevel(char *fn, real *temp, xvg_t *ba)
+static void read_bar_xvg_lowlevel(const char *fn, real *temp, xvg_t *ba,
+                                  lambda_components_t *lc)
 {
     int  i;
     char *subtitle,**legend,*ptr;
     int np;
     gmx_bool native_lambda_read=FALSE;
+    char buf[STRLEN];
+    lambda_vec_t lv;
 
     xvg_init(ba);
 
@@ -1924,16 +2700,22 @@ static void read_bar_xvg_lowlevel(char *fn, real *temp, xvg_t *ba)
     {
         gmx_fatal(FARGS,"File %s contains no usable data.",fn);
     }
+    /* Reorder the data */
     ba->t  = ba->y[0];
+    for(i=1; i<ba->nset; i++)
+    {
+        ba->y[i-1] = ba->y[i];
+    }
+    ba->nset--;
 
     snew(ba->np,ba->nset);
     for(i=0;i<ba->nset;i++)
         ba->np[i]=np;
 
-
     ba->temp = -1;
     if (subtitle != NULL)
     {
+        /* try to extract temperature */
         ptr = strstr(subtitle,"T =");
         if (ptr != NULL)
         {
@@ -1960,21 +2742,21 @@ static void read_bar_xvg_lowlevel(char *fn, real *temp, xvg_t *ba)
     /* Try to deduce lambda from the subtitle */
     if (subtitle)
     {
-        if (subtitle2lambda(subtitle,&(ba->native_lambda)))
+        if (subtitle2lambda(subtitle,ba, fn, lc))
         {
             native_lambda_read=TRUE;
         }
     }
-    snew(ba->lambda,ba->nset-1);
+    snew(ba->lambda,ba->nset);
     if (legend == NULL)
     {
-        /* Check if we have a single set, no legend, nset=2 means t and dH/dl */
-        if (ba->nset == 2)
+        /* Check if we have a single set, no legend, nset=1 means t and dH/dl */
+        if (ba->nset == 1)
         {
             if (!native_lambda_read)
             {
                 /* Deduce lambda from the file name */
-                ba->native_lambda = filename2lambda(fn);
+                filename2lambda(fn, ba);
                 native_lambda_read=TRUE;
             }
             ba->lambda[0] = ba->native_lambda;
@@ -1986,16 +2768,28 @@ static void read_bar_xvg_lowlevel(char *fn, real *temp, xvg_t *ba)
     }
     else
     {
-        for(i=0; i<ba->nset-1; i++)
+        for(i=0; i<ba->nset)
         {
-            gmx_bool is_dhdl=(i==0);
+            gmx_bool use=FALSE;
             /* Read lambda from the legend */
-            ba->lambda[i] = legend2lambda(fn,legend[i], &is_dhdl);
-
-            if (is_dhdl && !native_lambda_read)
+            lambda_vec_init( &(ba->lambda[i]), lc );
+            lambda_vec_copy( &(ba->lambda[i]), &(ba->native_lambda));
+            use=legend2lambda(fn,legend[i], ba, &(ba->lambda[i]));
+            if (use)
             {
-                ba->native_lambda = ba->lambda[i];
-                native_lambda_read=TRUE;
+                lambda_vec_print(&(ba->lambda[i]), buf, FALSE);
+                i++;
+            }
+            else
+            {
+                int j;
+                printf("%s: Ignoring set '%s'.\n", fn, legend[i]);
+                for(j=i+1; j<ba->nset; j++)
+                {
+                    ba->y[j-1] = ba->y[j];
+                    legend[j-1] = legend[j];
+                }
+                ba->nset--;
             }
         }
     }
@@ -2005,11 +2799,6 @@ static void read_bar_xvg_lowlevel(char *fn, real *temp, xvg_t *ba)
         gmx_fatal(FARGS,"File %s contains multiple sets but no indication of the native lambda",fn);
     }
     
-    /* Reorder the data */
-    for(i=1; i<ba->nset; i++)
-    {
-        ba->y[i-1] = ba->y[i];
-    }
     if (legend != NULL)
     {
         for(i=0; i<ba->nset-1; i++)
@@ -2018,10 +2807,9 @@ static void read_bar_xvg_lowlevel(char *fn, real *temp, xvg_t *ba)
         }
         sfree(legend);
     }
-    ba->nset--;
 }
 
-static void read_bar_xvg(char *fn, real *temp, lambda_t *lambda_head)
+static void read_bar_xvg(char *fn, real *temp, sim_data_t *sd)
 {
     xvg_t *barsim;
     samples_t *s;
@@ -2030,7 +2818,7 @@ static void read_bar_xvg(char *fn, real *temp, lambda_t *lambda_head)
 
     snew(barsim,1);
 
-    read_bar_xvg_lowlevel(fn, temp, barsim);
+    read_bar_xvg_lowlevel(fn, temp, barsim, &(sd->lc));
 
     if (barsim->nset <1 )
     {
@@ -2047,34 +2835,41 @@ static void read_bar_xvg(char *fn, real *temp, lambda_t *lambda_head)
     snew(s, barsim->nset);
     for(i=0;i<barsim->nset;i++)
     {
-        samples_init(s+i, barsim->native_lambda, barsim->lambda[i], 
-                     barsim->temp, lambda_same(barsim->native_lambda,
-                                               barsim->lambda[i]), 
+        samples_init(s+i, &(barsim->native_lambda), &(barsim->lambda[i]),
+                     barsim->temp, lambda_vec_same(&(barsim->native_lambda),
+                                                   &(barsim->lambda[i])),
                      fn);
         s[i].du=barsim->y[i];
         s[i].ndu=barsim->np[i];
         s[i].t=barsim->t;
 
-        lambda_list_insert_sample(lambda_head, s+i);
+        lambda_data_list_insert_sample(sd->lb, s+i);
     }
-    printf("%s: %.1f - %.1f; lambda = %.3f\n    foreign lambdas:", 
-           fn, s[0].t[0], s[0].t[s[0].ndu-1], s[0].native_lambda);
-    for(i=0;i<barsim->nset;i++)
     {
-        printf(" %.3f (%d pts)", s[i].foreign_lambda, s[i].ndu);
+        char buf[STRLEN];
+
+        lambda_vec_print(s[0].native_lambda, buf, FALSE);
+        printf("%s: %.1f - %.1f; lambda = %s\n    dH/dl & foreign lambdas:\n", 
+               fn, s[0].t[0], s[0].t[s[0].ndu-1], buf);
+        for(i=0;i<barsim->nset;i++)
+        {
+            lambda_vec_print(s[i].foreign_lambda, buf, TRUE);
+            printf("        %s (%d pts)\n", buf, s[i].ndu);
+        }
     }
     printf("\n\n");
 }
 
 static void read_edr_rawdh_block(samples_t **smp, int *ndu, t_enxblock *blk, 
                                  double start_time, double delta_time,
-                                 double native_lambda, double temp,
+                                 lambda_vec_t *native_lambda, double temp,
                                  double *last_t, const char *filename)
 {
-    int j;
+    int i,j;
     gmx_bool allocated;
-    double foreign_lambda;
-    int derivative;
+    double old_foreign_lambda;
+    lambda_vec_t *foreign_lambda;
+    int type;
     samples_t *s; /* convenience pointer */
     int startj;
 
@@ -2093,16 +2888,34 @@ static void read_edr_rawdh_block(samples_t **smp, int *ndu, t_enxblock *blk,
                   "Unexpected/corrupted block data in file %s around time %g.", 
                   filename, start_time);
     }
-   
-    derivative = blk->sub[0].ival[0]; 
-    foreign_lambda = blk->sub[1].dval[0];
+
+    snew(foreign_lambda, 1);
+    lambda_vec_init(foreign_lambda, native_lambda->lc);
+    lambda_vec_copy(foreign_lambda, native_lambda);
+    type = blk->sub[0].ival[0];
+    if (type == dhbtDH)
+    {
+        for(i=0;i<native_lambda->lc->N;i++)
+        {
+            foreign_lambda->val[i] = blk->sub[1].dval[i];
+        }
+    }
+    else
+    {
+        if (blk->sub[0].nr > 1)
+        {
+            foreign_lambda->dhdl = blk->sub[0].ival[1];
+        }
+        else
+            foreign_lambda->dhdl = 0;
+    }
 
     if (! *smp)
     {
         /* initialize the samples structure if it's empty. */
         snew(*smp, 1);
         samples_init(*smp, native_lambda, foreign_lambda, temp,
-                     derivative!=0, filename);
+                     type==dhbtDHDL, filename);
         (*smp)->start_time=start_time;
         (*smp)->delta_time=delta_time;
     }
@@ -2111,13 +2924,12 @@ static void read_edr_rawdh_block(samples_t **smp, int *ndu, t_enxblock *blk,
     s=*smp;
 
     /* now double check */
-    if ( ! lambda_same(s->foreign_lambda, foreign_lambda) ||
-         (  (derivative!=0) != (s->derivative!=0) ) )
+    if ( ! lambda_vec_same(s->foreign_lambda, foreign_lambda) )
     {
-        fprintf(stderr, "Got foreign lambda=%g, expected: %g\n", 
-                foreign_lambda, s->foreign_lambda);
-        fprintf(stderr, "Got derivative=%d, expected: %d\n", 
-                derivative, s->derivative);
+        char buf[STRLEN], buf2[STRLEN];
+        lambda_vec_print(foreign_lambda, buf, FALSE);
+        lambda_vec_print(s->foreign_lambda, buf2, FALSE);
+        fprintf(stderr, "Got foreign lambda=%s, expected: %s\n", buf, buf2);
         gmx_fatal(FARGS, "Corrupted data in file %s around t=%g.", 
                   filename, start_time);
     }
@@ -2155,14 +2967,15 @@ static void read_edr_rawdh_block(samples_t **smp, int *ndu, t_enxblock *blk,
 
 static samples_t *read_edr_hist_block(int *nsamples, t_enxblock *blk,
                                       double start_time, double delta_time,
-                                      double native_lambda, double temp,
+                                      lambda_vec_t *native_lambda, double temp,
                                       double *last_t, const char *filename)
 {
     int i,j;
     samples_t *s;
     int nhist;
-    double foreign_lambda;
-    int derivative;
+    double old_foreign_lambda;
+    lambda_vec_t *foreign_lambda;
+    int type;
     int nbins[2];
 
     /* check the block types etc. */
@@ -2192,13 +3005,53 @@ static samples_t *read_edr_hist_block(int *nsamples, t_enxblock *blk,
     snew(s, 1);
     *nsamples=1;
 
-    foreign_lambda=blk->sub[0].dval[0];
-    derivative=(int)(blk->sub[1].lval[1]);
-    if (derivative)
-        foreign_lambda=native_lambda;
+    snew(foreign_lambda, 1);
+    lambda_vec_init(foreign_lambda, native_lambda->lc);
+    lambda_vec_copy(foreign_lambda, native_lambda);
+    type = (int)(blk->sub[1].lval[1]);
+    if (type == dhbtDH)
+    {
+        double old_foreign_lambda;
+
+        old_foreign_lambda=blk->sub[0].dval[0];
+        if (old_foreign_lambda >= 0)
+        {
+            foreign_lambda->val[0]=old_foreign_lambda;
+            if (foreign_lambda->lc->N > 1)
+            {
+                gmx_fatal(FARGS,
+                          "Single-component lambda in multi-component file %s", 
+                          filename);
+            }
+        }
+        else
+        {
+            for(i=0;i<native_lambda->lc->N;i++)
+            {
+                foreign_lambda->val[i] = blk->sub[0].dval[i+2];
+            }
+        }
+    }
+    else
+    {
+        if (foreign_lambda->lc->N > 1)
+        {
+            if (blk->sub[1].nr < 3 + nhist)
+            {
+                gmx_fatal(FARGS,
+                        "Missing derivative coord in multi-component file %s", 
+                        filename);
+            }
+            foreign_lambda->dhdl = blk->sub[1].lval[2 + nhist];
+        }
+        else
+        {
+            foreign_lambda->dhdl = 0;
+        }
+    }
 
-    samples_init(s, native_lambda, foreign_lambda, temp,
-                 derivative!=0, filename);
+    samples_init(s, native_lambda, foreign_lambda, temp, type==dhbtDHDL,
+                 filename);
     snew(s->hist, 1);
 
     for(i=0;i<nhist;i++)
@@ -2210,10 +3063,12 @@ static samples_t *read_edr_hist_block(int *nsamples, t_enxblock *blk,
 
     for(i=0;i<nhist;i++)
     {
-        s->hist->x0[i]=blk->sub[1].lval[2+i];
+        s->hist->x0[i] = blk->sub[1].lval[2+i];
         s->hist->dx[i] = blk->sub[0].dval[1];
         if (i==1)
+        {
             s->hist->dx[i] = - s->hist->dx[i];
+        }
     }
 
     s->hist->start_time = start_time;
@@ -2257,9 +3112,9 @@ static samples_t *read_edr_hist_block(int *nsamples, t_enxblock *blk,
 }
 
 
-static void read_barsim_edr(char *fn, real *temp, lambda_t *lambda_head)
+static void read_barsim_edr(char *fn, real *temp, sim_data_t *sd)
 {
-    int i;
+    int i,j;
     ener_file_t fp;
     t_enxframe *fr; 
     int nre;
@@ -2269,15 +3124,19 @@ static void read_barsim_edr(char *fn, real *temp, lambda_t *lambda_head)
     samples_t **samples_rawdh=NULL; /* contains samples for raw delta_h  */
     int *nhists=NULL;       /* array to keep count & print at end */
     int *npts=NULL;         /* array to keep count & print at end */
-    double *lambdas=NULL;   /* array to keep count & print at end */
-    double native_lambda=-1;
+    lambda_vec_t **lambdas=NULL;   /* array to keep count & print at end */
+    lambda_vec_t *native_lambda;
     double end_time;        /* the end time of the last batch of samples */
     int nsamples=0;
+    lambda_vec_t start_lambda;
 
     fp = open_enx(fn,"r");
     do_enxnms(fp,&nre,&enm);
     snew(fr, 1);
 
+    snew(native_lambda, 1);
+    start_lambda.lc = NULL;
+
     while(do_enx(fp, fr))
     {
         /* count the data blocks */
@@ -2286,7 +3145,7 @@ static void read_barsim_edr(char *fn, real *temp, lambda_t *lambda_head)
         int nlam=0;
         int k;
         /* DHCOLL block information: */
-        double start_time=0, delta_time=0, start_lambda=0, delta_lambda=0;
+        double start_time=0, delta_time=0, old_start_lambda=0, delta_lambda=0;
         double rtemp=0;
 
         /* count the blocks and handle collection information: */
@@ -2310,7 +3169,7 @@ static void read_barsim_edr(char *fn, real *temp, lambda_t *lambda_head)
                 rtemp =        fr->block[i].sub[0].dval[0];
                 start_time =   fr->block[i].sub[0].dval[1];
                 delta_time =   fr->block[i].sub[0].dval[2];
-                start_lambda = fr->block[i].sub[0].dval[3];
+                old_start_lambda = fr->block[i].sub[0].dval[3];
                 delta_lambda = fr->block[i].sub[0].dval[4];
 
                 if (delta_lambda>0)
@@ -2323,6 +3182,62 @@ static void read_barsim_edr(char *fn, real *temp, lambda_t *lambda_head)
                 }
                 *temp=rtemp;
 
+                if (old_start_lambda >= 0)
+                {
+                    if (sd->lc.N > 0)
+                    {
+                        if (!lambda_components_check(&(sd->lc), 0, "", 0))
+                        {
+                            gmx_fatal(FARGS,
+                                      "lambda vector components in %s don't match those previously read",
+                                      fn);
+                        }
+                    }
+                    else
+                    {
+                        lambda_components_add(&(sd->lc), "", 0);
+                    }
+                    if (!start_lambda.lc)
+                    {
+                        lambda_vec_init(&start_lambda, &(sd->lc));
+                    }
+                    start_lambda.val[0]=old_start_lambda;
+                }
+                else
+                {
+                    /* read lambda vector */
+                    int n_lambda_vec;
+                    gmx_bool check=(sd->lc.N > 0);
+                    if (fr->block[i].nsub < 2)
+                    {
+                        gmx_fatal(FARGS, 
+                                  "No lambda vector, but start_lambda=%g\n",
+                                  old_start_lambda);
+                    }
+                    n_lambda_vec=fr->block[i].sub[1].ival[1];
+                    for(j=0;j<n_lambda_vec;j++)
+                    {
+                        const char *name=
+                             efpt_singular_names[fr->block[i].sub[1].ival[1+j]];
+                        if (check)
+                        {
+                            /* check the components */
+                            lambda_components_check(&(sd->lc), j, name,
+                                                    strlen(name));
+                        }
+                        else
+                        {
+                            lambda_components_add(&(sd->lc), name, 
+                                                  strlen(name));
+                        }
+                    }
+                    lambda_vec_init(&start_lambda, &(sd->lc));
+                    start_lambda.index=fr->block[i].sub[1].ival[0];
+                    for(j=0;j<n_lambda_vec;j++)
+                    {
+                        start_lambda.val[j]=fr->block[i].sub[0].dval[5+j];
+                    }
+                }
                 if (first_t < 0)
                     first_t=start_time;
             }
@@ -2330,7 +3245,7 @@ static void read_barsim_edr(char *fn, real *temp, lambda_t *lambda_head)
 
         if (nlam != 1)
         {
-            gmx_fatal(FARGS, "Did not find a delta h information in file %s" , fn);
+            gmx_fatal(FARGS, "Did not find delta H information in file %s", fn);
         }
         if (nblocks_raw > 0 && nblocks_hist > 0 )
         {
@@ -2340,7 +3255,7 @@ static void read_barsim_edr(char *fn, real *temp, lambda_t *lambda_head)
         if (nsamples > 0)
         {
             /* check the native lambda */
-            if (!lambda_same(start_lambda, native_lambda) )
+            if (!lambda_vec_same(&start_lambda, native_lambda) )
             {
                 gmx_fatal(FARGS, "Native lambda not constant in file %s: started at %g, and becomes %g at time %g", 
                           fn, native_lambda, start_lambda, start_time);
@@ -2361,8 +3276,8 @@ static void read_barsim_edr(char *fn, real *temp, lambda_t *lambda_head)
                     if (samples_rawdh[i])
                     {
                         /* insert it into the existing list */
-                        lambda_list_insert_sample(lambda_head, 
-                                                  samples_rawdh[i]);
+                        lambda_data_list_insert_sample(sd->lb,
+                                                       samples_rawdh[i]);
                         /* and make sure we'll allocate a new one this time
                            around */
                         samples_rawdh[i]=NULL;
@@ -2374,7 +3289,9 @@ static void read_barsim_edr(char *fn, real *temp, lambda_t *lambda_head)
         {
             /* this is the first round; allocate the associated data 
                structures */
-            native_lambda=start_lambda;
+            /*native_lambda=start_lambda;*/
+            lambda_vec_init(native_lambda, &(sd->lc));
+            lambda_vec_copy(native_lambda, &start_lambda);
             nsamples=nblocks_raw+nblocks_hist;
             snew(nhists, nsamples);
             snew(npts, nsamples);
@@ -2384,7 +3301,7 @@ static void read_barsim_edr(char *fn, real *temp, lambda_t *lambda_head)
             {
                 nhists[i]=0;
                 npts[i]=0;
-                lambdas[i]=-1;
+                lambdas[i]=NULL;
                 samples_rawdh[i]=NULL; /* init to NULL so we know which
                                           ones contain values */
             }
@@ -2394,41 +3311,49 @@ static void read_barsim_edr(char *fn, real *temp, lambda_t *lambda_head)
         k=0; /* counter for the lambdas, etc. arrays */
         for(i=0;i<fr->nblock;i++)
         {
-            if (fr->block[i].id == enxDH)
-            {
-                int ndu;
-                read_edr_rawdh_block(&(samples_rawdh[k]),
-                                     &ndu,
-                                     &(fr->block[i]), 
-                                     start_time, delta_time, 
-                                     start_lambda, rtemp, 
-                                     &last_t, fn);
-                npts[k] += ndu;
-                if (samples_rawdh[k])
+            if (fr->block[i].id == enxDH) 
+            {
+                int type=(fr->block[i].sub[0].ival[0]);
+                if (type == dhbtDH || type == dhbtDHDL)
                 {
-                    lambdas[k]=samples_rawdh[k]->foreign_lambda;
+                    int ndu;
+                    read_edr_rawdh_block(&(samples_rawdh[k]),
+                                         &ndu,
+                                         &(fr->block[i]),
+                                         start_time, delta_time,
+                                         native_lambda, rtemp,
+                                         &last_t, fn);
+                    npts[k] += ndu;
+                    if (samples_rawdh[k])
+                    {
+                        lambdas[k]=samples_rawdh[k]->foreign_lambda;
+                    }
+                    k++;
                 }
-                k++;
             }
             else if (fr->block[i].id == enxDHHIST)
             {
-                int j;
-                int nb=0;
-                samples_t *s; /* this is where the data will go */
-                s=read_edr_hist_block(&nb, &(fr->block[i]), 
-                                      start_time, delta_time, 
-                                      start_lambda, rtemp, 
-                                      &last_t, fn);
-                nhists[k] += nb;
-                if (nb>0)
+                int type=(int)(fr->block[i].sub[1].lval[1]);
+                if (type == dhbtDH || type == dhbtDHDL)
                 {
-                    lambdas[k]= s->foreign_lambda;
-                }
-                k++;
-                /* and insert the new sample immediately */
-                for(j=0;j<nb;j++)
-                {
-                    lambda_list_insert_sample(lambda_head, s+j);
+                    int j;
+                    int nb=0;
+                    samples_t *s; /* this is where the data will go */
+                    s=read_edr_hist_block(&nb, &(fr->block[i]),
+                                          start_time, delta_time,
+                                          native_lambda, rtemp,
+                                          &last_t, fn);
+                    nhists[k] += nb;
+                    if (nb>0)
+                    {
+                        lambdas[k]=s->foreign_lambda;
+                    }
+                    k++;
+                    /* and insert the new sample immediately */
+                    for(j=0;j<nb;j++)
+                    {
+                        lambda_data_list_insert_sample(sd->lb, s+j);
+                    }
                 }
             }
         }
@@ -2439,23 +3364,31 @@ static void read_barsim_edr(char *fn, real *temp, lambda_t *lambda_head)
         if (samples_rawdh[i])
         {
             /* insert it into the existing list */
-            lambda_list_insert_sample(lambda_head, samples_rawdh[i]);
+            lambda_data_list_insert_sample(sd->lb, samples_rawdh[i]);
         }
     }
 
 
-    fprintf(stderr, "\n");
-    printf("%s: %.1f - %.1f; lambda = %.3f\n    foreign lambdas:", 
-           fn, first_t, last_t, native_lambda);
-    for(i=0;i<nsamples;i++)
     {
-        if (nhists[i] > 0)
-        {
-            printf(" %.3f (%d hists)", lambdas[i], nhists[i]);
-        }
-        else
+        char buf[STRLEN];
+        printf("\n");
+        lambda_vec_print(native_lambda, buf, FALSE);
+        printf("%s: %.1f - %.1f; lambda = %s\n    foreign lambdas:\n",
+               fn, first_t, last_t, buf);
+        for(i=0;i<nsamples;i++)
         {
-            printf(" %.3f (%d pts)", lambdas[i], npts[i]);
+            if (lambdas[i])
+            {
+                lambda_vec_print(lambdas[i], buf, TRUE);
+                if (nhists[i] > 0)
+                {
+                    printf("        %s (%d hists)\n", buf, nhists[i]);
+                }
+                else
+                {
+                    printf("        %s (%d pts)\n", buf, npts[i]);
+                }
+            }
         }
     }
     printf("\n\n");
@@ -2601,16 +3534,15 @@ int gmx_bar(int argc,char *argv[])
     int      nedrfile=0;
     char     **fxvgnms;
     char     **fedrnms;
-    lambda_t *lb;    /* the pre-processed lambda data (linked list head) */
-    lambda_t lambda_head; /* the head element */
+    sim_data_t sim_data; /* the simulation data */
     barres_t *results;  /* the results */
     int    nresults;  /* number of results in results array */
 
     double   *partsum;
     double   prec,dg_tot,dg,sig, dg_tot_max, dg_tot_min;
     FILE     *fpb,*fpi;
-    char     lamformat[20];
-    char     dgformat[20],xvg2format[STRLEN],xvg3format[STRLEN],buf[STRLEN];
+    char     dgformat[20],xvg2format[STRLEN],xvg3format[STRLEN];
+    char     buf[STRLEN], buf2[STRLEN];
     char     ktformat[STRLEN], sktformat[STRLEN];
     char     kteformat[STRLEN], skteformat[STRLEN];
     output_env_t oenv;
@@ -2637,11 +3569,14 @@ int gmx_bar(int argc,char *argv[])
         nedrfile = opt2fns(&fedrnms,"-g",NFILE,fnm);
     }
 
+    sim_data_init(&sim_data);
+#if 0
     /* make linked list */
     lb=&lambda_head;
-    lambda_init(lb, 0, 0);
+    lambda_data_init(lb, 0, 0);
     lb->next=lb;
     lb->prev=lb;
+#endif
 
 
     nfile_tot = nxvgfile + nedrfile;
@@ -2663,26 +3598,26 @@ int gmx_bar(int argc,char *argv[])
     /* read in all files. First xvg files */
     for(f=0; f<nxvgfile; f++)
     {
-        read_bar_xvg(fxvgnms[f],&temp,lb);
+        read_bar_xvg(fxvgnms[f],&temp,&sim_data);
         nf++;
     }
     /* then .edr files */
     for(f=0; f<nedrfile; f++)
     {
-        read_barsim_edr(fedrnms[f],&temp,lb);;
+        read_barsim_edr(fedrnms[f],&temp,&sim_data);;
         nf++;
     }
 
     /* fix the times to allow for equilibration */
-    lambdas_impose_times(lb, begin, end);
+    sim_data_impose_times(&sim_data, begin, end);
 
     if (opt2bSet("-oh",NFILE,fnm))
     {
-        lambdas_histogram(lb, opt2fn("-oh",NFILE,fnm), nbin, oenv);
+        sim_data_histogram(&sim_data, opt2fn("-oh",NFILE,fnm), nbin, oenv);
     }
    
     /* assemble the output structures from the lambdas */
-    results=barres_list_create(lb, &nresults, use_dhdl);
+    results=barres_list_create(&sim_data, &nresults, use_dhdl);
 
     sum_disc_err=barres_list_max_disc_err(results, nresults);
 
@@ -2700,7 +3635,7 @@ int gmx_bar(int argc,char *argv[])
     }
 
 
-    sprintf(lamformat,"%%6.3f");
+    /*sprintf(lamformat,"%%6.3f");*/
     sprintf( dgformat,"%%%d.%df",3+nd,nd);
     /* the format strings of the results in kT */
     sprintf( ktformat,"%%%d.%df",5+nd,nd);
@@ -2708,8 +3643,8 @@ int gmx_bar(int argc,char *argv[])
     /* the format strings of the errors in kT */
     sprintf( kteformat,"%%%d.%df",3+nd,nd);
     sprintf( skteformat,"%%%ds",4+nd);
-    sprintf(xvg2format,"%s %s\n","%g",dgformat);
-    sprintf(xvg3format,"%s %s %s\n","%g",dgformat,dgformat);
+    sprintf(xvg2format,"%s %s\n","%s",dgformat);
+    sprintf(xvg3format,"%s %s %s\n","%s",dgformat,dgformat);
 
 
 
@@ -2779,10 +3714,10 @@ int gmx_bar(int argc,char *argv[])
     printf("\n");
     for(f=0; f<nresults; f++)
     {
-        printf(lamformat, results[f].a->native_lambda);
-        printf(" ");
-        printf(lamformat, results[f].b->native_lambda);
-        printf(" ");
+        lambda_vec_print_short(results[f].a->native_lambda, buf);
+        printf("%s ", buf);
+        lambda_vec_print_short(results[f].b->native_lambda, buf);
+        printf("%s ", buf);
         printf(ktformat,  results[f].dg);
         printf(" ");
         if (bEE)
@@ -2848,24 +3783,24 @@ int gmx_bar(int argc,char *argv[])
         
         if (fpi != NULL)
         {
-            fprintf(fpi, xvg2format, results[f].a->native_lambda, dg_tot);
+            lambda_vec_print_short(results[f].a->native_lambda, buf);
+            fprintf(fpi, xvg2format, buf, dg_tot);
         }
 
 
         if (fpb != NULL)
         {
-            fprintf(fpb, xvg3format,
-                    0.5*(results[f].a->native_lambda + 
-                         results[f].b->native_lambda),
-                    results[f].dg,results[f].dg_err);
+            lambda_vec_print_intermediate(results[f].a->native_lambda,
+                                          results[f].b->native_lambda,
+                                          buf);
+
+            fprintf(fpb, xvg3format, buf, results[f].dg,results[f].dg_err);
         }
 
-        /*printf("lambda %4.2f - %4.2f, DG ", results[f].lambda_a,
-                                              results[f].lambda_b);*/
-        printf("lambda ");
-        printf(lamformat, results[f].a->native_lambda);
-        printf(" - ");
-        printf(lamformat, results[f].b->native_lambda);
+        printf("point ");
+        lambda_vec_print_short(results[f].a->native_lambda, buf);
+        lambda_vec_print_short(results[f].b->native_lambda, buf2);
+        printf("%s - %s", buf, buf2);
         printf(",   DG ");
 
         printf(dgformat,results[f].dg*kT);
@@ -2886,10 +3821,10 @@ int gmx_bar(int argc,char *argv[])
         dg_tot += results[f].dg;
     }
     printf("\n");
-    printf("total  ");
-    printf(lamformat, results[0].a->native_lambda);
-    printf(" - ");
-    printf(lamformat, results[nresults-1].b->native_lambda);
+    printf("total ");
+    lambda_vec_print_short(results[0].a->native_lambda, buf);
+    lambda_vec_print_short(results[nresults-1].b->native_lambda, buf2);
+    printf("%s - %s", buf, buf2);
     printf(",   DG ");
 
     printf(dgformat,dg_tot*kT);
@@ -2924,8 +3859,8 @@ int gmx_bar(int argc,char *argv[])
 
     if (fpi != NULL)
     {
-        fprintf(fpi, xvg2format,
-                results[nresults-1].b->native_lambda, dg_tot);
+        lambda_vec_print_short(results[nresults-1].b->native_lambda, buf);
+        fprintf(fpi, xvg2format, buf, dg_tot);
         ffclose(fpi);
     }
 
index 25b8b8b2232d2ba7f533c6007d6a3b44fd84d6e5..3154840ed834362935d3747fddf1f0aaebc4eedb 100644 (file)
@@ -1368,8 +1368,10 @@ static void fec(const char *ene2fn, const char *runavgfn,
 }
 
 
-static void do_dhdl(t_enxframe *fr, t_inputrec *ir, FILE **fp_dhdl, const char *filename, gmx_bool bDp,
-                    int *blocks, int *hists, int *samples, int *nlambdas, const output_env_t oenv)
+static void do_dhdl(t_enxframe *fr, t_inputrec *ir, FILE **fp_dhdl,
+                    const char *filename, gmx_bool bDp,
+                    int *blocks, int *hists, int *samples, int *nlambdas,
+                    const output_env_t oenv)
 {
     const char *dhdl="dH/d\\lambda",*deltag="\\DeltaH",*lambda="\\lambda";
     char title[STRLEN],label_x[STRLEN],label_y[STRLEN], legend[STRLEN];
@@ -1380,7 +1382,11 @@ static void do_dhdl(t_enxframe *fr, t_inputrec *ir, FILE **fp_dhdl, const char *
     /* coll data */
     double temp=0, start_time=0, delta_time=0, start_lambda=0, delta_lambda=0;
     static int setnr=0;
+    double *native_lambda_vec=NULL;
+    const char **lambda_components=NULL;
+    int n_lambda_vec=0;
     gmx_bool changing_lambda=FALSE;
+    int lambda_fep_state;
 
     /* now count the blocks & handle the global dh data */
     for(i=0;i<fr->nblock;i++)
@@ -1410,6 +1416,32 @@ static void do_dhdl(t_enxframe *fr, t_inputrec *ir, FILE **fp_dhdl, const char *
             start_lambda = fr->block[i].sub[0].dval[3];
             delta_lambda = fr->block[i].sub[0].dval[4];
             changing_lambda = (delta_lambda != 0);
+            if (fr->block[i].nsub > 1)
+            {
+                lambda_fep_state=fr->block[i].sub[1].ival[0];
+                if (n_lambda_vec==0)
+                {
+                    n_lambda_vec=fr->block[i].sub[1].ival[1];
+                }
+                else
+                {
+                    if (n_lambda_vec!=fr->block[i].sub[1].ival[1])
+                    {
+                        gmx_fatal(FARGS,
+                                  "Unexpected change of basis set in lambda");
+                    }
+                }
+                if (lambda_components == NULL)
+                    snew(lambda_components, n_lambda_vec);
+                if (native_lambda_vec == NULL)
+                    snew(native_lambda_vec, n_lambda_vec);
+                for(j=0;j<n_lambda_vec;j++)
+                {
+                    native_lambda_vec[j] = fr->block[i].sub[0].dval[5+j];
+                    lambda_components[j] =
+                           efpt_singular_names[fr->block[i].sub[1].ival[2+j]];
+                }
+            }
         }
     }
 
@@ -1426,7 +1458,12 @@ static void do_dhdl(t_enxframe *fr, t_inputrec *ir, FILE **fp_dhdl, const char *
     {
         if (nblock_dh>0)
         {
-            /* we have standard, non-histogram data -- call open_dhdl to open the file */
+            /* we have standard, non-histogram data --
+               call open_dhdl to open the file */
+            /* TODO this is an ugly hack that needs to be fixed: this will only
+               work if the order of data is always the same and if we're
+               only using the g_energy compiled with the mdrun that produced
+               the ener.edr. */
             *fp_dhdl=open_dhdl(filename,ir,oenv);
         }
         else