diff --git a/doc/src/PDF/colvars-refman-lammps.pdf b/doc/src/PDF/colvars-refman-lammps.pdf
index 5b776defca36759dadaaabebcc6df409000d52cd..37201275fe4b4efa0da77afbb72e6c55a127c042 100644
Binary files a/doc/src/PDF/colvars-refman-lammps.pdf and b/doc/src/PDF/colvars-refman-lammps.pdf differ
diff --git a/lib/colvars/colvar.cpp b/lib/colvars/colvar.cpp
index d60a9e90f774179fb1fa2bcf6d8b61452786048f..e8c7e88324a9e88299c4c7c1834ce4c7b21e8e90 100644
--- a/lib/colvars/colvar.cpp
+++ b/lib/colvars/colvar.cpp
@@ -1,5 +1,5 @@
-// -*- c++ -*-
 
+// -*- c++ -*-
 // This file is part of the Collective Variables module (Colvars).
 // The original version of Colvars and its updates are located at:
 // https://github.com/colvars/colvars
@@ -7,6 +7,7 @@
 // If you wish to distribute your changes, please submit them to the
 // Colvars repository at GitHub.
 
+
 #include "colvarmodule.h"
 #include "colvarvalue.h"
 #include "colvarparse.h"
@@ -23,20 +24,31 @@ bool compare(colvar::cvc *i, colvar::cvc *j) {
 }
 
 
-colvar::colvar(std::string const &conf)
-  : colvarparse(conf)
+colvar::colvar()
+{
+  // Initialize static array once and for all
+  init_cv_requires();
+}
+
+
+int colvar::init(std::string const &conf)
 {
   cvm::log("Initializing a new collective variable.\n");
+  colvarparse::init(conf);
+
   int error_code = COLVARS_OK;
 
+  colvarmodule *cv = cvm::main();
+
   get_keyval(conf, "name", this->name,
-             (std::string("colvar")+cvm::to_str(cvm::colvars.size()+1)));
+             (std::string("colvar")+cvm::to_str(cv->variables()->size()+1)));
 
-  if (cvm::colvar_by_name(this->name) != NULL) {
+  if ((cvm::colvar_by_name(this->name) != NULL) &&
+      (cvm::colvar_by_name(this->name) != this)) {
     cvm::error("Error: this colvar cannot have the same name, \""+this->name+
                       "\", as another colvar.\n",
                INPUT_ERROR);
-    return;
+    return INPUT_ERROR;
   }
 
   // Initialize dependency members
@@ -44,14 +56,13 @@ colvar::colvar(std::string const &conf)
 
   this->description = "colvar " + this->name;
 
-  // Initialize static array once and for all
-  init_cv_requires();
-
   kinetic_energy = 0.0;
   potential_energy = 0.0;
 
   error_code |= init_components(conf);
-  if (error_code != COLVARS_OK) return;
+  if (error_code != COLVARS_OK) {
+    return cvm::get_error();
+  }
 
   size_t i;
 
@@ -59,8 +70,6 @@ colvar::colvar(std::string const &conf)
   if (get_keyval(conf, "scriptedFunction", scripted_function,
     "", colvarparse::parse_silent)) {
 
-    // Make feature available only on user request
-    provide(f_cv_scripted);
     enable(f_cv_scripted);
     cvm::log("This colvar uses scripted function \"" + scripted_function + "\".");
 
@@ -76,8 +85,8 @@ colvar::colvar(std::string const &conf)
       }
     }
     if (x.type() == colvarvalue::type_notset) {
-      cvm::error("Could not parse scripted colvar type.");
-      return;
+      cvm::error("Could not parse scripted colvar type.", INPUT_ERROR);
+      return INPUT_ERROR;
     }
 
     cvm::log(std::string("Expecting colvar value of type ")
@@ -86,8 +95,9 @@ colvar::colvar(std::string const &conf)
     if (x.type() == colvarvalue::type_vector) {
       int size;
       if (!get_keyval(conf, "scriptedFunctionVectorSize", size)) {
-        cvm::error("Error: no size specified for vector scripted function.");
-        return;
+        cvm::error("Error: no size specified for vector scripted function.",
+                   INPUT_ERROR);
+        return INPUT_ERROR;
       }
       x.vector1d_value.resize(size);
     }
@@ -154,7 +164,7 @@ colvar::colvar(std::string const &conf)
         }
       }
     }
-    feature_states[f_cv_linear]->enabled = lin;
+    set_enabled(f_cv_linear, lin);
   }
 
   // Colvar is homogeneous if:
@@ -168,7 +178,7 @@ colvar::colvar(std::string const &conf)
         homogeneous = false;
       }
     }
-    feature_states[f_cv_homogeneous]->enabled = homogeneous;
+    set_enabled(f_cv_homogeneous, homogeneous);
   }
 
   // Colvar is deemed periodic if:
@@ -188,7 +198,7 @@ colvar::colvar(std::string const &conf)
                  "Make sure that you know what you are doing!");
       }
     }
-    feature_states[f_cv_periodic]->enabled = b_periodic;
+    set_enabled(f_cv_periodic, b_periodic);
   }
 
   // check that cvcs are compatible
@@ -202,7 +212,7 @@ colvar::colvar(std::string const &conf)
                  "by using components of different types. "
                  "You must use the same type in order to "
                  "sum them together.\n", INPUT_ERROR);
-      return;
+      return INPUT_ERROR;
     }
   }
 
@@ -215,44 +225,110 @@ colvar::colvar(std::string const &conf)
   f.type(value());
   f_accumulated.type(value());
 
+  x_old.type(value());
+  v_fdiff.type(value());
+  v_reported.type(value());
+  fj.type(value());
+  ft.type(value());
+  ft_reported.type(value());
+  f_old.type(value());
+  f_old.reset();
+
+  x_restart.type(value());
+  after_restart = false;
+
   reset_bias_force();
 
+  // TODO use here information from the CVCs' own natural boundaries
+  error_code |= init_grid_parameters(conf);
+
+  get_keyval(conf, "timeStepFactor", time_step_factor, 1);
+
+  error_code |= init_extended_Lagrangian(conf);
+  error_code |= init_output_flags(conf);
+
+  // Start in active state by default
+  enable(f_cv_active);
+  // Make sure dependency side-effects are correct
+  refresh_deps();
+
+  if (cvm::b_analysis)
+    parse_analysis(conf);
+
+  if (cvm::debug())
+    cvm::log("Done initializing collective variable \""+this->name+"\".\n");
+
+  return error_code;
+}
+
+
+int colvar::init_grid_parameters(std::string const &conf)
+{
+  colvarmodule *cv = cvm::main();
+
   get_keyval(conf, "width", width, 1.0);
   if (width <= 0.0) {
     cvm::error("Error: \"width\" must be positive.\n", INPUT_ERROR);
-    return;
+    return INPUT_ERROR;
   }
 
   lower_boundary.type(value());
-  lower_wall.type(value());
 
   upper_boundary.type(value());
   upper_wall.type(value());
 
-  feature_states[f_cv_scalar]->enabled = (value().type() == colvarvalue::type_scalar);
+  set_enabled(f_cv_scalar, (value().type() == colvarvalue::type_scalar));
 
   if (is_enabled(f_cv_scalar)) {
 
     if (get_keyval(conf, "lowerBoundary", lower_boundary, lower_boundary)) {
-      provide(f_cv_lower_boundary);
       enable(f_cv_lower_boundary);
     }
+    std::string lw_conf, uw_conf;
 
-    get_keyval(conf, "lowerWallConstant", lower_wall_k, 0.0);
-    if (lower_wall_k > 0.0) {
+    if (get_keyval(conf, "lowerWallConstant", lower_wall_k, 0.0, parse_silent)) {
+      cvm::log("Warning: lowerWallConstant and lowerWall are deprecated, "
+               "please define a harmonicWalls bias instead.\n");
+      lower_wall.type(value());
       get_keyval(conf, "lowerWall", lower_wall, lower_boundary);
-      enable(f_cv_lower_wall);
+      lw_conf = std::string("\n\
+    lowerWallConstant "+cvm::to_str(lower_wall_k*width*width)+"\n\
+    lowerWalls "+cvm::to_str(lower_wall)+"\n");
     }
 
     if (get_keyval(conf, "upperBoundary", upper_boundary, upper_boundary)) {
-      provide(f_cv_upper_boundary);
       enable(f_cv_upper_boundary);
     }
 
-    get_keyval(conf, "upperWallConstant", upper_wall_k, 0.0);
-    if (upper_wall_k > 0.0) {
+    if (get_keyval(conf, "upperWallConstant", upper_wall_k, 0.0, parse_silent)) {
+      cvm::log("Warning: upperWallConstant and upperWall are deprecated, "
+               "please define a harmonicWalls bias instead.\n");
+      upper_wall.type(value());
       get_keyval(conf, "upperWall", upper_wall, upper_boundary);
-      enable(f_cv_upper_wall);
+      uw_conf = std::string("\n\
+    upperWallConstant "+cvm::to_str(upper_wall_k*width*width)+"\n\
+    upperWalls "+cvm::to_str(upper_wall)+"\n");
+    }
+
+    if (lw_conf.size() && uw_conf.size()) {
+      if (lower_wall >= upper_wall) {
+        cvm::error("Error: the upper wall, "+
+                   cvm::to_str(upper_wall)+
+                   ", is not higher than the lower wall, "+
+                   cvm::to_str(lower_wall)+".\n",
+                   INPUT_ERROR);
+        return INPUT_ERROR;
+      }
+    }
+
+    if (lw_conf.size() || uw_conf.size()) {
+      cvm::log("Generating a new harmonicWalls bias for compatibility purposes.\n");
+      std::string const walls_conf("\n\
+harmonicWalls {\n\
+    name "+this->name+"w\n\
+    colvars "+this->name+"\n"+lw_conf+uw_conf+
+                             "}\n");
+      cv->append_new_config(walls_conf);
     }
   }
 
@@ -271,16 +347,7 @@ colvar::colvar(std::string const &conf)
                         ", is not higher than the lower boundary, "+
                         cvm::to_str(lower_boundary)+".\n",
                 INPUT_ERROR);
-    }
-  }
-
-  if (is_enabled(f_cv_lower_wall) && is_enabled(f_cv_upper_wall)) {
-    if (lower_wall >= upper_wall) {
-      cvm::error("Error: the upper wall, "+
-                 cvm::to_str(upper_wall)+
-                 ", is not higher than the lower wall, "+
-                 cvm::to_str(lower_wall)+".\n",
-                 INPUT_ERROR);
+      return INPUT_ERROR;
     }
   }
 
@@ -289,83 +356,90 @@ colvar::colvar(std::string const &conf)
     cvm::error("Error: trying to expand boundaries that already "
                "cover a whole period of a periodic colvar.\n",
                INPUT_ERROR);
+    return INPUT_ERROR;
   }
   if (expand_boundaries && hard_lower_boundary && hard_upper_boundary) {
     cvm::error("Error: inconsistent configuration "
                "(trying to expand boundaries with both "
                "hardLowerBoundary and hardUpperBoundary enabled).\n",
                INPUT_ERROR);
+    return INPUT_ERROR;
   }
 
-  get_keyval(conf, "timeStepFactor", time_step_factor, 1);
+  return COLVARS_OK;
+}
 
-  {
-    bool b_extended_Lagrangian;
-    get_keyval(conf, "extendedLagrangian", b_extended_Lagrangian, false);
 
-    if (b_extended_Lagrangian) {
-      cvm::real temp, tolerance, period;
+int colvar::init_extended_Lagrangian(std::string const &conf)
+{
+  bool b_extended_Lagrangian;
+  get_keyval(conf, "extendedLagrangian", b_extended_Lagrangian, false);
 
-      cvm::log("Enabling the extended Lagrangian term for colvar \""+
-                this->name+"\".\n");
+  if (b_extended_Lagrangian) {
+    cvm::real temp, tolerance, period;
 
-      // Make feature available only on user request
-      provide(f_cv_extended_Lagrangian);
-      enable(f_cv_extended_Lagrangian);
-      provide(f_cv_Langevin);
-
-      // The extended mass will apply forces
-      enable(f_cv_gradient);
-
-      xr.type(value());
-      vr.type(value());
-      fr.type(value());
-
-      const bool found = get_keyval(conf, "extendedTemp", temp, cvm::temperature());
-      if (temp <= 0.0) {
-        if (found)
-          cvm::error("Error: \"extendedTemp\" must be positive.\n", INPUT_ERROR);
-        else
-          cvm::error("Error: a positive temperature must be provided, either "
-                     "by enabling a thermostat, or through \"extendedTemp\".\n",
-                     INPUT_ERROR);
-      }
+    cvm::log("Enabling the extended Lagrangian term for colvar \""+
+             this->name+"\".\n");
 
-      get_keyval(conf, "extendedFluctuation", tolerance);
-      if (tolerance <= 0.0) {
-        cvm::error("Error: \"extendedFluctuation\" must be positive.\n", INPUT_ERROR);
-      }
-      ext_force_k = cvm::boltzmann() * temp / (tolerance * tolerance);
-      cvm::log("Computed extended system force constant: " + cvm::to_str(ext_force_k) + " kcal/mol/U^2");
+    enable(f_cv_extended_Lagrangian);
 
-      get_keyval(conf, "extendedTimeConstant", period, 200.0);
-      if (period <= 0.0) {
-        cvm::error("Error: \"extendedTimeConstant\" must be positive.\n", INPUT_ERROR);
-      }
-      ext_mass = (cvm::boltzmann() * temp * period * period)
-                 / (4.0 * PI * PI * tolerance * tolerance);
-      cvm::log("Computed fictitious mass: " + cvm::to_str(ext_mass) + " kcal/mol/(U/fs)^2   (U: colvar unit)");
-
-      {
-        bool b_output_energy;
-        get_keyval(conf, "outputEnergy", b_output_energy, false);
-        if (b_output_energy) {
-          enable(f_cv_output_energy);
-        }
-      }
+    xr.type(value());
+    vr.type(value());
+    fr.type(value());
 
-      get_keyval(conf, "extendedLangevinDamping", ext_gamma, 1.0);
-      if (ext_gamma < 0.0) {
-        cvm::error("Error: \"extendedLangevinDamping\" may not be negative.\n", INPUT_ERROR);
-      }
-      if (ext_gamma != 0.0) {
-        enable(f_cv_Langevin);
-        ext_gamma *= 1.0e-3; // convert from ps-1 to fs-1
-        ext_sigma = std::sqrt(2.0 * cvm::boltzmann() * temp * ext_gamma * ext_mass / cvm::dt());
+    const bool found = get_keyval(conf, "extendedTemp", temp, cvm::temperature());
+    if (temp <= 0.0) {
+      if (found)
+        cvm::error("Error: \"extendedTemp\" must be positive.\n", INPUT_ERROR);
+      else
+        cvm::error("Error: a positive temperature must be provided, either "
+                   "by enabling a thermostat, or through \"extendedTemp\".\n",
+                   INPUT_ERROR);
+      return INPUT_ERROR;
+    }
+
+    get_keyval(conf, "extendedFluctuation", tolerance);
+    if (tolerance <= 0.0) {
+      cvm::error("Error: \"extendedFluctuation\" must be positive.\n", INPUT_ERROR);
+      return INPUT_ERROR;
+    }
+    ext_force_k = cvm::boltzmann() * temp / (tolerance * tolerance);
+    cvm::log("Computed extended system force constant: " + cvm::to_str(ext_force_k) + " kcal/mol/U^2");
+
+    get_keyval(conf, "extendedTimeConstant", period, 200.0);
+    if (period <= 0.0) {
+      cvm::error("Error: \"extendedTimeConstant\" must be positive.\n", INPUT_ERROR);
+    }
+    ext_mass = (cvm::boltzmann() * temp * period * period)
+      / (4.0 * PI * PI * tolerance * tolerance);
+    cvm::log("Computed fictitious mass: " + cvm::to_str(ext_mass) + " kcal/mol/(U/fs)^2   (U: colvar unit)");
+
+    {
+      bool b_output_energy;
+      get_keyval(conf, "outputEnergy", b_output_energy, false);
+      if (b_output_energy) {
+        enable(f_cv_output_energy);
       }
     }
+
+    get_keyval(conf, "extendedLangevinDamping", ext_gamma, 1.0);
+    if (ext_gamma < 0.0) {
+      cvm::error("Error: \"extendedLangevinDamping\" may not be negative.\n", INPUT_ERROR);
+      return INPUT_ERROR;
+    }
+    if (ext_gamma != 0.0) {
+      enable(f_cv_Langevin);
+      ext_gamma *= 1.0e-3; // convert from ps-1 to fs-1
+      ext_sigma = std::sqrt(2.0 * cvm::boltzmann() * temp * ext_gamma * ext_mass / cvm::dt());
+    }
   }
 
+  return COLVARS_OK;
+}
+
+
+int colvar::init_output_flags(std::string const &conf)
+{
   {
     bool b_output_value;
     get_keyval(conf, "outputValue", b_output_value, true);
@@ -387,7 +461,7 @@ colvar::colvar(std::string const &conf)
     if (get_keyval(conf, "outputSystemForce", temp, false, colvarparse::parse_silent)) {
       cvm::error("Option outputSystemForce is deprecated: only outputTotalForce is supported instead.\n"
                  "The two are NOT identical: see http://colvars.github.io/totalforce.html.\n", INPUT_ERROR);
-      return;
+      return INPUT_ERROR;
     }
   }
 
@@ -395,28 +469,7 @@ colvar::colvar(std::string const &conf)
   get_keyval_feature(this, conf, "outputAppliedForce", f_cv_output_applied_force, false);
   get_keyval_feature(this, conf, "subtractAppliedForce", f_cv_subtract_applied_force, false);
 
-  // Start in active state by default
-  enable(f_cv_active);
-  // Make sure dependency side-effects are correct
-  refresh_deps();
-
-  x_old.type(value());
-  v_fdiff.type(value());
-  v_reported.type(value());
-  fj.type(value());
-  ft.type(value());
-  ft_reported.type(value());
-  f_old.type(value());
-  f_old.reset();
-
-  x_restart.type(value());
-  after_restart = false;
-
-  if (cvm::b_analysis)
-    parse_analysis(conf);
-
-  if (cvm::debug())
-    cvm::log("Done initializing collective variable \""+this->name+"\".\n");
+  return COLVARS_OK;
 }
 
 
@@ -637,7 +690,7 @@ int colvar::parse_analysis(std::string const &conf)
 
     std::string runave_outfile;
     get_keyval(conf, "runAveOutputFile", runave_outfile,
-                std::string(cvm::output_prefix+"."+
+                std::string(cvm::output_prefix()+"."+
                              this->name+".runave.traj"));
 
     size_t const this_cv_width = x.output_width(cvm::cv_width);
@@ -693,7 +746,7 @@ int colvar::parse_analysis(std::string const &conf)
 
     get_keyval(conf, "corrFuncNormalize", acf_normalize, true);
     get_keyval(conf, "corrFuncOutputFile", acf_outfile,
-                std::string(cvm::output_prefix+"."+this->name+
+                std::string(cvm::output_prefix()+"."+this->name+
                              ".corrfunc.dat"));
   }
   return (cvm::get_error() ? COLVARS_ERROR : COLVARS_OK);
@@ -730,11 +783,12 @@ colvar::~colvar()
   }
 
   // remove reference to this colvar from the CVM
-  for (std::vector<colvar *>::iterator cvi = cvm::colvars.begin();
-       cvi != cvm::colvars.end();
+  colvarmodule *cv = cvm::main();
+  for (std::vector<colvar *>::iterator cvi = cv->variables()->begin();
+       cvi != cv->variables()->end();
        ++cvi) {
     if ( *cvi == this) {
-      cvm::colvars.erase(cvi);
+      cv->variables()->erase(cvi);
       break;
     }
   }
@@ -892,7 +946,6 @@ int colvar::collect_cvc_values()
               cvm::to_str(x, cvm::cv_width, cvm::cv_prec)+".\n");
 
   if (after_restart) {
-    after_restart = false;
     if (cvm::proxy->simulation_running()) {
       cvm::real const jump2 = dist2(x, x_restart) / (width*width);
       if (jump2 > 0.25) {
@@ -1122,8 +1175,7 @@ int colvar::calc_colvar_properties()
 
     // initialize the restraint center in the first step to the value
     // just calculated from the cvcs
-    // TODO: put it in the restart information
-    if (cvm::step_relative() == 0) {
+    if (cvm::step_relative() == 0 && !after_restart) {
       xr = x;
       vr.reset(); // (already 0; added for clarity)
     }
@@ -1148,6 +1200,8 @@ int colvar::calc_colvar_properties()
     ft_reported = ft;
   }
 
+  // At the end of the first update after a restart, we can reset the flag
+  after_restart = false;
   return COLVARS_OK;
 }
 
@@ -1173,51 +1227,17 @@ cvm::real colvar::update_forces_energy()
       f -= fj;
   }
 
-  // Wall force
-  colvarvalue fw(x);
-  fw.reset();
-
-  if (is_enabled(f_cv_lower_wall) || is_enabled(f_cv_upper_wall)) {
-
-    if (cvm::debug())
-      cvm::log("Calculating wall forces for colvar \""+this->name+"\".\n");
-
-    // For a periodic colvar, both walls may be applicable at the same time
-    // in which case we pick the closer one
-    if ( (!is_enabled(f_cv_upper_wall)) ||
-         (this->dist2(x, lower_wall) < this->dist2(x, upper_wall)) ) {
-
-      cvm::real const grad = this->dist2_lgrad(x, lower_wall);
-      if (grad < 0.0) {
-        fw = -0.5 * lower_wall_k * grad;
-        if (cvm::debug())
-          cvm::log("Applying a lower wall force("+
-                    cvm::to_str(fw)+") to \""+this->name+"\".\n");
-      }
-
-    } else {
-
-      cvm::real const grad = this->dist2_lgrad(x, upper_wall);
-      if (grad > 0.0) {
-        fw = -0.5 * upper_wall_k * grad;
-        if (cvm::debug())
-          cvm::log("Applying an upper wall force("+
-                    cvm::to_str(fw)+") to \""+this->name+"\".\n");
-      }
-    }
-  }
-
   // At this point f is the force f from external biases that will be applied to the
   // extended variable if there is one
 
   if (is_enabled(f_cv_extended_Lagrangian)) {
 
     if (cvm::debug()) {
-      cvm::log("Updating extended-Lagrangian degrees of freedom.\n");
+      cvm::log("Updating extended-Lagrangian degree of freedom.\n");
     }
 
     cvm::real dt = cvm::dt();
-    colvarvalue f_ext(fr.type());
+    colvarvalue f_ext(fr.type()); // force acting on the extended variable
     f_ext.reset();
 
     // the total force is applied to the fictitious mass, while the
@@ -1226,7 +1246,7 @@ cvm::real colvar::update_forces_energy()
     // f_ext: total force on extended variable (including harmonic spring)
     // f: - initially, external biasing force
     //    - after this code block, colvar force to be applied to atomic coordinates
-    //      ie. spring force + wall force
+    //      ie. spring force (fb_actual will be added just below)
     fr    = f;
     f_ext = f + (-0.5 * ext_force_k) * this->dist2_lgrad(xr, x);
     f     =     (-0.5 * ext_force_k) * this->dist2_rgrad(xr, x);
@@ -1259,8 +1279,6 @@ cvm::real colvar::update_forces_energy()
     if (this->is_enabled(f_cv_periodic)) this->wrap(xr);
   }
 
-  // TODO remove the wall force
-  f += fw;
   // Now adding the force on the actual colvar (for those biases who
   // bypass the extended Lagrangian mass)
   f += fb_actual;
@@ -1286,8 +1304,10 @@ cvm::real colvar::update_forces_energy()
 void colvar::communicate_forces()
 {
   size_t i;
-  if (cvm::debug())
+  if (cvm::debug()) {
     cvm::log("Communicating forces from colvar \""+this->name+"\".\n");
+    cvm::log("Force to be applied: " + cvm::to_str(f_accumulated) + "\n");
+  }
 
   if (is_enabled(f_cv_scripted)) {
     std::vector<cvm::matrix2d<cvm::real> > func_grads;
@@ -1364,7 +1384,7 @@ int colvar::update_cvc_flags()
     active_cvc_square_norm = 0.;
 
     for (size_t i = 0; i < cvcs.size(); i++) {
-      cvcs[i]->feature_states[f_cvc_active]->enabled = cvc_flags[i];
+      cvcs[i]->set_enabled(f_cvc_active, cvc_flags[i]);
       if (cvcs[i]->is_enabled()) {
         n_active_cvcs++;
         active_cvc_square_norm += cvcs[i]->sup_coeff * cvcs[i]->sup_coeff;
diff --git a/lib/colvars/colvar.h b/lib/colvars/colvar.h
index 2cf3d2dac512b7492452940393067e8d6f7088ef..0cbda450b8af878ba611f1689cc8d85628e8e0e5 100644
--- a/lib/colvars/colvar.h
+++ b/lib/colvars/colvar.h
@@ -227,11 +227,23 @@ public:
 
 
   /// Constructor
-  colvar(std::string const &conf);
+  colvar();
+
+  /// Main init function
+  int init(std::string const &conf);
 
   /// Parse the CVC configuration and allocate their data
   int init_components(std::string const &conf);
 
+  /// Init defaults for grid options
+  int init_grid_parameters(std::string const &conf);
+
+  /// Init extended Lagrangian parameters
+  int init_extended_Lagrangian(std::string const &conf);
+
+  /// Init output flags
+  int init_output_flags(std::string const &conf);
+
 private:
   /// Parse the CVC configuration for all components of a certain type
   template<typename def_class_name> int init_components_type(std::string const &conf,
diff --git a/lib/colvars/colvaratoms.cpp b/lib/colvars/colvaratoms.cpp
index 48c16e887af95c9e623aa3b8b525b54594e10ca2..32cfadf3b6c84aae0af422fb63f6bcad0d870158 100644
--- a/lib/colvars/colvaratoms.cpp
+++ b/lib/colvars/colvaratoms.cpp
@@ -98,7 +98,7 @@ cvm::atom_group::atom_group()
 
 cvm::atom_group::~atom_group()
 {
-  if (is_enabled(f_ag_scalable)) {
+  if (is_enabled(f_ag_scalable) && !b_dummy) {
     (cvm::proxy)->clear_atom_group(index);
     index = -1;
   }
@@ -418,7 +418,7 @@ int cvm::atom_group::parse(std::string const &conf)
   // We need to know the fitting options to decide whether the group is scalable
   parse_error |= parse_fitting_options(group_conf);
 
-  if (is_available(f_ag_scalable_com) && !b_rotate) {
+  if (is_available(f_ag_scalable_com) && !b_rotate && !b_center) {
     enable(f_ag_scalable_com);
     enable(f_ag_scalable);
   }
@@ -500,14 +500,16 @@ int cvm::atom_group::add_atom_numbers(std::string const &numbers_conf)
 
 int cvm::atom_group::add_index_group(std::string const &index_group_name)
 {
-  std::list<std::string>::iterator names_i = cvm::index_group_names.begin();
-  std::list<std::vector<int> >::iterator index_groups_i = cvm::index_groups.begin();
-  for ( ; names_i != cvm::index_group_names.end() ; ++names_i, ++index_groups_i) {
+  colvarmodule *cv = cvm::main();
+
+  std::list<std::string>::iterator names_i = cv->index_group_names.begin();
+  std::list<std::vector<int> >::iterator index_groups_i = cv->index_groups.begin();
+  for ( ; names_i != cv->index_group_names.end() ; ++names_i, ++index_groups_i) {
     if (*names_i == index_group_name)
       break;
   }
 
-  if (names_i == cvm::index_group_names.end()) {
+  if (names_i == cv->index_group_names.end()) {
     cvm::error("Error: could not find index group "+
                index_group_name+" among those provided by the index file.\n",
                INPUT_ERROR);
diff --git a/lib/colvars/colvarbias.cpp b/lib/colvars/colvarbias.cpp
index fdd2b6254ca12dd8b450244f72c75f8b570f8a2f..3779c82aa300b86814a8f3ab23eaf1b161c34946 100644
--- a/lib/colvars/colvarbias.cpp
+++ b/lib/colvars/colvarbias.cpp
@@ -19,20 +19,6 @@ colvarbias::colvarbias(char const *key)
 
   rank = 1;
 
-  if (bias_type == std::string("abf")) {
-    rank = cvm::n_abf_biases+1;
-  }
-  if (bias_type == std::string("harmonic") ||
-      bias_type == std::string("linear")) {
-    rank = cvm::n_rest_biases+1;
-  }
-  if (bias_type == std::string("histogram")) {
-    rank = cvm::n_histo_biases+1;
-  }
-  if (bias_type == std::string("metadynamics")) {
-    rank = cvm::n_meta_biases+1;
-  }
-
   has_data = false;
   b_output_energy = false;
   reset();
@@ -48,7 +34,11 @@ int colvarbias::init(std::string const &conf)
   colvarparse::init(conf);
 
   if (name.size() == 0) {
+
+    // first initialization
+
     cvm::log("Initializing a new \""+bias_type+"\" instance.\n");
+    rank = cvm::num_biases_type(bias_type);
     get_keyval(conf, "name", name, bias_type+cvm::to_str(rank));
 
     {
@@ -69,7 +59,7 @@ int colvarbias::init(std::string const &conf)
       // lookup the associated colvars
       std::vector<std::string> colvar_names;
       if (get_keyval(conf, "colvars", colvar_names)) {
-        if (colvars.size()) {
+        if (num_variables()) {
           cvm::error("Error: cannot redefine the colvars that a bias was already defined on.\n",
                      INPUT_ERROR);
           return INPUT_ERROR;
@@ -80,7 +70,7 @@ int colvarbias::init(std::string const &conf)
       }
     }
 
-    if (!colvars.size()) {
+    if (!num_variables()) {
       cvm::error("Error: no collective variables specified.\n", INPUT_ERROR);
       return INPUT_ERROR;
     }
@@ -89,6 +79,8 @@ int colvarbias::init(std::string const &conf)
     cvm::log("Reinitializing bias \""+name+"\".\n");
   }
 
+  output_prefix = cvm::output_prefix();
+
   get_keyval(conf, "outputEnergy", b_output_energy, b_output_energy);
 
   return COLVARS_OK;
@@ -98,7 +90,7 @@ int colvarbias::init(std::string const &conf)
 int colvarbias::reset()
 {
   bias_energy = 0.0;
-  for (size_t i = 0; i < colvars.size(); i++) {
+  for (size_t i = 0; i < num_variables(); i++) {
     colvar_forces[i].reset();
   }
   return COLVARS_OK;
@@ -132,12 +124,13 @@ int colvarbias::clear()
     }
   }
 
+  colvarmodule *cv = cvm::main();
   // ...and from the colvars module
-  for (std::vector<colvarbias *>::iterator bi = cvm::biases.begin();
-       bi != cvm::biases.end();
+  for (std::vector<colvarbias *>::iterator bi = cv->biases.begin();
+       bi != cv->biases.end();
        ++bi) {
     if ( *bi == this) {
-      cvm::biases.erase(bi);
+      cv->biases.erase(bi);
       break;
     }
   }
@@ -185,21 +178,29 @@ int colvarbias::add_colvar(std::string const &cv_name)
 
 int colvarbias::update()
 {
-  // Note: if anything is added here, it should be added also in the SMP block of calc_biases()
-  // TODO move here debug msg of bias update
+  if (cvm::debug()) {
+    cvm::log("Updating the "+bias_type+" bias \""+this->name+"\".\n");
+  }
+
   has_data = true;
+
+  bias_energy = 0.0;
+  for (size_t ir = 0; ir < num_variables(); ir++) {
+    colvar_forces[ir].reset();
+  }
+
   return COLVARS_OK;
 }
 
 
 void colvarbias::communicate_forces()
 {
-  for (size_t i = 0; i < colvars.size(); i++) {
+  for (size_t i = 0; i < num_variables(); i++) {
     if (cvm::debug()) {
       cvm::log("Communicating a force to colvar \""+
-               colvars[i]->name+"\".\n");
+               variables(i)->name+"\".\n");
     }
-    colvars[i]->add_bias_force(colvar_forces[i]);
+    variables(i)->add_bias_force(colvar_forces[i]);
   }
 }
 
diff --git a/lib/colvars/colvarbias.h b/lib/colvars/colvarbias.h
index 12397dcb8fa0ba8aa2356fd776d1a8ccb16b4546..6d5776d3db87047c78ff15c637d6703ceeb77300 100644
--- a/lib/colvars/colvarbias.h
+++ b/lib/colvars/colvarbias.h
@@ -32,17 +32,39 @@ public:
   /// Add a new collective variable to this bias
   int add_colvar(std::string const &cv_name);
 
-  /// Add a new collective variable to this bias
-  size_t number_of_colvars() const
+  /// How many variables are defined for this bias
+  inline size_t num_variables() const
   {
     return colvars.size();
   }
 
+  /// Access the variables vector
+  inline std::vector<colvar *> *variables()
+  {
+    return &colvars;
+  }
+
+  /// Access the i-th variable
+  inline colvar * variables(int i) const
+  {
+    return colvars[i];
+  }
+
   /// Retrieve colvar values and calculate their biasing forces
   /// Return bias energy
   virtual int update();
 
-  // TODO: move update_bias here (share with metadynamics)
+  /// \brief Compute the energy of the bias with alternative values of the
+  /// collective variables (suitable for bias exchange)
+  virtual int calc_energy(std::vector<colvarvalue> const &values = 
+                          std::vector<colvarvalue>(0))
+  {
+    cvm::error("Error: calc_energy() not implemented.\n", COLVARS_NOT_IMPLEMENTED);
+    return COLVARS_NOT_IMPLEMENTED;
+  }
+
+  /// Send forces to the collective variables
+  virtual void communicate_forces();
 
   /// Load new configuration - force constant and/or centers only
   virtual int change_configuration(std::string const &conf);
@@ -51,10 +73,13 @@ public:
   virtual cvm::real energy_difference(std::string const &conf);
 
   /// Give the total number of bins for a given bias.
+  // FIXME this is currently 1D only
   virtual int bin_num();
   /// Calculate the bin index for a given bias.
+  // FIXME this is currently 1D only
   virtual int current_bin();
   //// Give the count at a given bin index.
+  // FIXME this is currently 1D only
   virtual int bin_count(int bin_index);
   //// Share information between replicas, whatever it may be.
   virtual int replica_share();
@@ -62,9 +87,6 @@ public:
   /// Perform analysis tasks
   virtual void analyze() {}
 
-  /// Send forces to the collective variables
-  virtual void communicate_forces();
-
   /// \brief Constructor
   colvarbias(char const *key);
 
@@ -135,6 +157,9 @@ public:
     return COLVARS_OK;
   }
 
+  /// Use this prefix for all output files
+  std::string output_prefix;
+
   /// If this bias is communicating with other replicas through files, send it to them
   virtual int write_state_to_replicas()
   {
@@ -162,7 +187,7 @@ protected:
   /// through each colvar object
   std::vector<colvar *>    colvars;
 
-  /// \brief Current forces from this bias to the colvars
+  /// \brief Current forces from this bias to the variables
   std::vector<colvarvalue> colvar_forces;
 
   /// \brief Current energy of this bias (colvar_forces should be obtained by deriving this)
diff --git a/lib/colvars/colvarbias_abf.cpp b/lib/colvars/colvarbias_abf.cpp
index ccfa401697693bb97a2d403c6cc06da3f2f32d9e..d039004f09d47f3b216c62a3e5d5304e9cb89678 100644
--- a/lib/colvars/colvarbias_abf.cpp
+++ b/lib/colvars/colvarbias_abf.cpp
@@ -30,9 +30,8 @@ int colvarbias_abf::init(std::string const &conf)
 {
   colvarbias::init(conf);
 
-  provide(f_cvb_scalar_variables);
   enable(f_cvb_scalar_variables);
-  provide(f_cvb_history_dependent);
+  enable(f_cvb_calc_pmf);
 
   // TODO relax this in case of VMD plugin
   if (cvm::temperature() == 0.0)
@@ -221,9 +220,6 @@ colvarbias_abf::~colvarbias_abf()
     delete [] system_force;
     system_force = NULL;
   }
-
-  if (cvm::n_abf_biases > 0)
-    cvm::n_abf_biases -= 1;
 }
 
 
@@ -319,11 +315,11 @@ int colvarbias_abf::update()
   }
 
   // update the output prefix; TODO: move later to setup_output() function
-  if ( cvm::n_abf_biases == 1 && cvm::n_meta_biases == 0 ) {
-    // This is the only ABF bias
-    output_prefix = cvm::output_prefix;
+  if (cvm::num_biases_feature(colvardeps::f_cvb_calc_pmf) == 1) {
+    // This is the only bias computing PMFs
+    output_prefix = cvm::output_prefix();
   } else {
-    output_prefix = cvm::output_prefix + "." + this->name;
+    output_prefix = cvm::output_prefix() + "." + this->name;
   }
 
   if (output_freq && (cvm::step_absolute() % output_freq) == 0) {
diff --git a/lib/colvars/colvarbias_alb.cpp b/lib/colvars/colvarbias_alb.cpp
index 9e414d4db49637087dc954ff8239a5465287664e..d096ac3daf15a508e076b38e2dc1e18368f321b7 100644
--- a/lib/colvars/colvarbias_alb.cpp
+++ b/lib/colvars/colvarbias_alb.cpp
@@ -40,11 +40,8 @@ int colvarbias_alb::init(std::string const &conf)
 {
   colvarbias::init(conf);
 
-  provide(f_cvb_scalar_variables);
   enable(f_cvb_scalar_variables);
 
-  provide(f_cvb_history_dependent);
-
   size_t i;
 
   // get the initial restraint centers
@@ -138,8 +135,6 @@ int colvarbias_alb::init(std::string const &conf)
 
 colvarbias_alb::~colvarbias_alb()
 {
-  if (cvm::n_rest_biases > 0)
-    cvm::n_rest_biases -= 1;
 }
 
 
diff --git a/lib/colvars/colvarbias_histogram.cpp b/lib/colvars/colvarbias_histogram.cpp
index 900ad213d51d6d0301063d6edba45561e285a0e7..502a7455b1ed4ab99ec9c6d553e621ec4a4ff7d7 100644
--- a/lib/colvars/colvarbias_histogram.cpp
+++ b/lib/colvars/colvarbias_histogram.cpp
@@ -24,10 +24,7 @@ int colvarbias_histogram::init(std::string const &conf)
 {
   colvarbias::init(conf);
 
-  provide(f_cvb_scalar_variables);
   enable(f_cvb_scalar_variables);
-
-  provide(f_cvb_history_dependent);
   enable(f_cvb_history_dependent);
 
   size_t i;
@@ -104,9 +101,6 @@ colvarbias_histogram::~colvarbias_histogram()
     delete grid;
     grid = NULL;
   }
-
-  if (cvm::n_histo_biases > 0)
-    cvm::n_histo_biases -= 1;
 }
 
 
@@ -127,14 +121,14 @@ int colvarbias_histogram::update()
     // At the first timestep, we need to assign out_name since
     // output_prefix is unset during the constructor
     if (cvm::step_relative() == 0) {
-      out_name = cvm::output_prefix + "." + this->name + ".dat";
+      out_name = cvm::output_prefix() + "." + this->name + ".dat";
       cvm::log("Histogram " + this->name + " will be written to file \"" + out_name + "\"");
     }
   }
 
   if (out_name_dx.size() == 0) {
     if (cvm::step_relative() == 0) {
-      out_name_dx = cvm::output_prefix + "." + this->name + ".dx";
+      out_name_dx = cvm::output_prefix() + "." + this->name + ".dx";
       cvm::log("Histogram " + this->name + " will be written to file \"" + out_name_dx + "\"");
     }
   }
diff --git a/lib/colvars/colvarbias_meta.cpp b/lib/colvars/colvarbias_meta.cpp
index 01a04d1a016dbb116f707296cfd54bb99ad86f83..b0acfe974a188a179de18da36d1c537e70f77995 100644
--- a/lib/colvars/colvarbias_meta.cpp
+++ b/lib/colvars/colvarbias_meta.cpp
@@ -43,7 +43,7 @@ int colvarbias_meta::init(std::string const &conf)
 {
   colvarbias::init(conf);
 
-  provide(f_cvb_history_dependent);
+  enable(f_cvb_calc_pmf);
 
   get_keyval(conf, "hillWeight", hill_weight, 0.0);
   if (hill_weight > 0.0) {
@@ -59,9 +59,9 @@ int colvarbias_meta::init(std::string const &conf)
 
   get_keyval(conf, "hillWidth", hill_width, std::sqrt(2.0 * PI) / 2.0);
   cvm::log("Half-widths of the Gaussian hills (sigma's):\n");
-  for (size_t i = 0; i < colvars.size(); i++) {
-    cvm::log(colvars[i]->name+std::string(": ")+
-             cvm::to_str(0.5 * colvars[i]->width * hill_width));
+  for (size_t i = 0; i < num_variables(); i++) {
+    cvm::log(variables(i)->name+std::string(": ")+
+             cvm::to_str(0.5 * variables(i)->width * hill_width));
   }
 
   {
@@ -73,8 +73,10 @@ int colvarbias_meta::init(std::string const &conf)
       comm = single_replica;
   }
 
-  // This implies gradients for all colvars
-  enable(f_cvb_apply_force);
+  // in all cases, the first replica is this bias itself
+  if (replicas.size() == 0) {
+    replicas.push_back(this);
+  }
 
   get_keyval(conf, "useGrids", use_grids, true);
 
@@ -84,14 +86,14 @@ int colvarbias_meta::init(std::string const &conf)
 
     expand_grids = false;
     size_t i;
-    for (i = 0; i < colvars.size(); i++) {
-      colvars[i]->enable(f_cv_grid);
-      if (colvars[i]->expand_boundaries) {
+    for (i = 0; i < num_variables(); i++) {
+      variables(i)->enable(f_cv_grid);
+      if (variables(i)->expand_boundaries) {
         expand_grids = true;
         cvm::log("Metadynamics bias \""+this->name+"\""+
                  ((comm != single_replica) ? ", replica \""+replica_id+"\"" : "")+
                  ": Will expand grids when the colvar \""+
-                 colvars[i]->name+"\" approaches its boundaries.\n");
+                 variables(i)->name+"\" approaches its boundaries.\n");
       }
     }
 
@@ -100,7 +102,7 @@ int colvarbias_meta::init(std::string const &conf)
       get_keyval(conf, "dumpFreeEnergyFile", dump_fes, true, colvarparse::parse_silent);
     if (get_keyval(conf, "saveFreeEnergyFile", dump_fes_save, false, colvarparse::parse_silent)) {
       cvm::log("Option \"saveFreeEnergyFile\" is deprecated, "
-               "please use \"keepFreeEnergyFiles\" instead.");
+               "please use \"keepFreeEnergyFile\" instead.");
     }
     get_keyval(conf, "keepFreeEnergyFiles", dump_fes_save, dump_fes_save);
 
@@ -154,6 +156,20 @@ int colvarbias_meta::init(std::string const &conf)
 
   get_keyval(conf, "writeHillsTrajectory", b_hills_traj, false);
 
+  init_well_tempered_params(conf);
+  init_ebmeta_params(conf);
+
+  if (cvm::debug())
+    cvm::log("Done initializing the metadynamics bias \""+this->name+"\""+
+             ((comm != single_replica) ? ", replica \""+replica_id+"\"" : "")+".\n");
+
+  save_delimiters = false;
+  return COLVARS_OK;
+}
+
+
+int colvarbias_meta::init_well_tempered_params(std::string const &conf)
+{
   // for well-tempered metadynamics
   get_keyval(conf, "wellTempered", well_tempered, false);
   get_keyval(conf, "biasTemperature", bias_temperature, -1.0);
@@ -164,8 +180,12 @@ int colvarbias_meta::init(std::string const &conf)
     cvm::log("Well-tempered metadynamics is used.\n");
     cvm::log("The bias temperature is "+cvm::to_str(bias_temperature)+".\n");
   }
+  return COLVARS_OK;
+}
 
 
+int colvarbias_meta::init_ebmeta_params(std::string const &conf)
+{
   // for ebmeta
   target_dist = NULL;
   get_keyval(conf, "ebMeta", ebmeta, false);
@@ -203,11 +223,6 @@ int colvarbias_meta::init(std::string const &conf)
     get_keyval(conf, "ebMetaEquilSteps", ebmeta_equil_steps, 0);
   }
 
-  if (cvm::debug())
-    cvm::log("Done initializing the metadynamics bias \""+this->name+"\""+
-             ((comm != single_replica) ? ", replica \""+replica_id+"\"" : "")+".\n");
-
-  save_delimiters = false;
   return COLVARS_OK;
 }
 
@@ -234,9 +249,6 @@ colvarbias_meta::~colvarbias_meta()
     delete target_dist;
     target_dist = NULL;
   }
-
-  if (cvm::n_meta_biases > 0)
-    cvm::n_meta_biases -= 1;
 }
 
 
@@ -314,23 +326,45 @@ colvarbias_meta::delete_hill(hill_iter &h)
 
 int colvarbias_meta::update()
 {
-  if (cvm::debug())
-    cvm::log("Updating the metadynamics bias \""+this->name+"\""+
-             ((comm != single_replica) ? ", replica \""+replica_id+"\"" : "")+".\n");
+  int error_code = COLVARS_OK;
+
+  // update base class
+  error_code |= colvarbias::update();
+
+  // update grid definition, if needed
+  error_code |= update_grid_params();
+  // add new biasing energy/forces
+  error_code |= update_bias();
+  // update grid content to reflect new bias
+  error_code |= update_grid_data();
+
+  if (comm != single_replica &&
+      (cvm::step_absolute() % replica_update_freq) == 0) {
+    // sync with the other replicas (if needed)
+    error_code |= replica_share();
+  }
+
+  error_code |= calc_energy();
+  error_code |= calc_forces();
 
+  return error_code;
+}
+
+
+int colvarbias_meta::update_grid_params()
+{
   if (use_grids) {
 
     std::vector<int> curr_bin = hills_energy->get_colvars_index();
+    if (cvm::debug()) {
+      cvm::log("Metadynamics bias \""+this->name+"\""+
+               ((comm != single_replica) ? ", replica \""+replica_id+"\"" : "")+
+               ": current coordinates on the grid: "+
+               cvm::to_str(curr_bin)+".\n");
+    }
 
     if (expand_grids) {
-
       // first of all, expand the grids, if specified
-      if (cvm::debug())
-        cvm::log("Metadynamics bias \""+this->name+"\""+
-                 ((comm != single_replica) ? ", replica \""+replica_id+"\"" : "")+
-                 ": current coordinates on the grid: "+
-                 cvm::to_str(curr_bin)+".\n");
-
       bool changed_grids = false;
       int const min_buffer =
         (3 * (size_t) std::floor(hill_width)) + 1;
@@ -339,9 +373,9 @@ int colvarbias_meta::update()
       std::vector<colvarvalue> new_lower_boundaries(hills_energy->lower_boundaries);
       std::vector<colvarvalue> new_upper_boundaries(hills_energy->upper_boundaries);
 
-      for (size_t i = 0; i < colvars.size(); i++) {
+      for (size_t i = 0; i < num_variables(); i++) {
 
-        if (! colvars[i]->expand_boundaries)
+        if (! variables(i)->expand_boundaries)
           continue;
 
         cvm::real &new_lb   = new_lower_boundaries[i].real_value;
@@ -349,10 +383,10 @@ int colvarbias_meta::update()
         int       &new_size = new_sizes[i];
         bool changed_lb = false, changed_ub = false;
 
-        if (!colvars[i]->hard_lower_boundary)
+        if (!variables(i)->hard_lower_boundary)
           if (curr_bin[i] < min_buffer) {
             int const extra_points = (min_buffer - curr_bin[i]);
-            new_lb -= extra_points * colvars[i]->width;
+            new_lb -= extra_points * variables(i)->width;
             new_size += extra_points;
             // changed offset in this direction => the pointer needs to
             // be changed, too
@@ -362,21 +396,21 @@ int colvarbias_meta::update()
             cvm::log("Metadynamics bias \""+this->name+"\""+
                      ((comm != single_replica) ? ", replica \""+replica_id+"\"" : "")+
                      ": new lower boundary for colvar \""+
-                     colvars[i]->name+"\", at "+
+                     variables(i)->name+"\", at "+
                      cvm::to_str(new_lower_boundaries[i])+".\n");
           }
 
-        if (!colvars[i]->hard_upper_boundary)
+        if (!variables(i)->hard_upper_boundary)
           if (curr_bin[i] > new_size - min_buffer - 1) {
             int const extra_points = (curr_bin[i] - (new_size - 1) + min_buffer);
-            new_ub += extra_points * colvars[i]->width;
+            new_ub += extra_points * variables(i)->width;
             new_size += extra_points;
 
             changed_ub = true;
             cvm::log("Metadynamics bias \""+this->name+"\""+
                      ((comm != single_replica) ? ", replica \""+replica_id+"\"" : "")+
                      ": new upper boundary for colvar \""+
-                     colvars[i]->name+"\", at "+
+                     variables(i)->name+"\", at "+
                      cvm::to_str(new_upper_boundaries[i])+".\n");
           }
 
@@ -401,7 +435,7 @@ int colvarbias_meta::update()
 
         new_hills_energy_gradients->lower_boundaries = new_lower_boundaries;
         new_hills_energy_gradients->upper_boundaries = new_upper_boundaries;
-        new_hills_energy_gradients->setup(new_sizes, 0.0, colvars.size());
+        new_hills_energy_gradients->setup(new_sizes, 0.0, num_variables());
 
         new_hills_energy->map_grid(*hills_energy);
         new_hills_energy_gradients->map_grid(*hills_energy_gradients);
@@ -418,25 +452,32 @@ int colvarbias_meta::update()
       }
     }
   }
+  return COLVARS_OK;
+}
 
+
+int colvarbias_meta::update_bias()
+{
   // add a new hill if the required time interval has passed
-  if ((cvm::step_absolute() % new_hill_freq) == 0) {
+  if ((cvm::step_absolute() % new_hill_freq) == 0 &&
+      is_enabled(f_cvb_history_dependent)) {
 
-    if (cvm::debug())
+    if (cvm::debug()) {
       cvm::log("Metadynamics bias \""+this->name+"\""+
                ((comm != single_replica) ? ", replica \""+replica_id+"\"" : "")+
                ": adding a new hill at step "+cvm::to_str(cvm::step_absolute())+".\n");
+    }
 
     cvm::real hills_scale=1.0;
 
     if (ebmeta) {
-       hills_scale *= 1.0/target_dist->value(target_dist->get_colvars_index());
-       if(cvm::step_absolute() <= long(ebmeta_equil_steps)) {
-         cvm::real const hills_lambda =
-           (cvm::real(long(ebmeta_equil_steps) - cvm::step_absolute())) /
-           (cvm::real(ebmeta_equil_steps));
-         hills_scale = hills_lambda + (1-hills_lambda)*hills_scale;
-       }
+      hills_scale *= 1.0/target_dist->value(target_dist->get_colvars_index());
+      if(cvm::step_absolute() <= long(ebmeta_equil_steps)) {
+        cvm::real const hills_lambda =
+          (cvm::real(long(ebmeta_equil_steps) - cvm::step_absolute())) /
+          (cvm::real(ebmeta_equil_steps));
+        hills_scale = hills_lambda + (1-hills_lambda)*hills_scale;
+      }
     }
 
     if (well_tempered) {
@@ -471,160 +512,165 @@ int colvarbias_meta::update()
     }
   }
 
-  // sync with the other replicas (if needed)
-  if (comm != single_replica) {
+  return COLVARS_OK;
+}
 
-    // reread the replicas registry
-    if ((cvm::step_absolute() % replica_update_freq) == 0) {
-      update_replicas_registry();
-      // empty the output buffer
-      if (replica_hills_os.is_open())
-        replica_hills_os.flush();
 
-      read_replica_files();
+int colvarbias_meta::update_grid_data()
+{
+  if ((cvm::step_absolute() % grids_freq) == 0) {
+    // map the most recent gaussians to the grids
+    project_hills(new_hills_begin, hills.end(),
+                  hills_energy,    hills_energy_gradients);
+    new_hills_begin = hills.end();
+
+    // TODO: we may want to condense all into one replicas array,
+    // including "this" as the first element
+    if (comm == multiple_replicas) {
+      for (size_t ir = 0; ir < replicas.size(); ir++) {
+        replicas[ir]->project_hills(replicas[ir]->new_hills_begin,
+                                    replicas[ir]->hills.end(),
+                                    replicas[ir]->hills_energy,
+                                    replicas[ir]->hills_energy_gradients);
+        replicas[ir]->new_hills_begin = replicas[ir]->hills.end();
+      }
     }
   }
 
-  // calculate the biasing energy and forces
-  bias_energy = 0.0;
-  for (size_t ir = 0; ir < colvars.size(); ir++) {
-    colvar_forces[ir].reset();
+  return COLVARS_OK;
+}
+
+
+int colvarbias_meta::calc_energy(std::vector<colvarvalue> const &values)
+{
+  size_t ir = 0;
+
+  for (ir = 0; ir < replicas.size(); ir++) {
+    replicas[ir]->bias_energy = 0.0;
   }
-  if (comm == multiple_replicas)
-    for (size_t ir = 0; ir < replicas.size(); ir++) {
-      replicas[ir]->bias_energy = 0.0;
-      for (size_t ic = 0; ic < colvars.size(); ic++) {
-        replicas[ir]->colvar_forces[ic].reset();
-      }
-    }
 
-  if (use_grids) {
+  std::vector<int> const curr_bin = values.size() ?
+    hills_energy->get_colvars_index(values) :
+    hills_energy->get_colvars_index();
 
-    // get the forces from the grid
-
-    if ((cvm::step_absolute() % grids_freq) == 0) {
-      // map the most recent gaussians to the grids
-      project_hills(new_hills_begin, hills.end(),
-                    hills_energy,    hills_energy_gradients);
-      new_hills_begin = hills.end();
-
-      // TODO: we may want to condense all into one replicas array,
-      // including "this" as the first element
-      if (comm == multiple_replicas) {
-        for (size_t ir = 0; ir < replicas.size(); ir++) {
-          replicas[ir]->project_hills(replicas[ir]->new_hills_begin,
-                                      replicas[ir]->hills.end(),
-                                      replicas[ir]->hills_energy,
-                                      replicas[ir]->hills_energy_gradients);
-          replicas[ir]->new_hills_begin = replicas[ir]->hills.end();
-        }
+  if (hills_energy->index_ok(curr_bin)) {
+    // index is within the grid: get the energy from there
+    for (ir = 0; ir < replicas.size(); ir++) {
+
+      bias_energy += replicas[ir]->hills_energy->value(curr_bin);
+      if (cvm::debug()) {
+        cvm::log("Metadynamics bias \""+this->name+"\""+
+                 ((comm != single_replica) ? ", replica \""+replica_id+"\"" : "")+
+                 ": current coordinates on the grid: "+
+                 cvm::to_str(curr_bin)+".\n");
+        cvm::log("Grid energy = "+cvm::to_str(bias_energy)+".\n");
       }
     }
+  } else {
+    // off the grid: compute analytically only the hills at the grid's edges
+    for (ir = 0; ir < replicas.size(); ir++) {
+      calc_hills(replicas[ir]->hills_off_grid.begin(),
+                 replicas[ir]->hills_off_grid.end(),
+                 bias_energy,
+                 values);
+    }
+  }
 
-    std::vector<int> curr_bin = hills_energy->get_colvars_index();
-    if (cvm::debug())
-      cvm::log("Metadynamics bias \""+this->name+"\""+
-               ((comm != single_replica) ? ", replica \""+replica_id+"\"" : "")+
-               ": current coordinates on the grid: "+
-               cvm::to_str(curr_bin)+".\n");
+  // now include the hills that have not been binned yet (starting
+  // from new_hills_begin)
 
-    if (hills_energy->index_ok(curr_bin)) {
+  for (ir = 0; ir < replicas.size(); ir++) {
+    calc_hills(replicas[ir]->new_hills_begin,
+               replicas[ir]->hills.end(),
+               bias_energy);
+    if (cvm::debug()) {
+      cvm::log("Hills energy = "+cvm::to_str(bias_energy)+".\n");
+    }
+  }
 
-      // within the grid: add the energy and the forces from there
+  return COLVARS_OK;
+}
 
-      bias_energy += hills_energy->value(curr_bin);
-      for (size_t ic = 0; ic < colvars.size(); ic++) {
-        cvm::real const *f = &(hills_energy_gradients->value(curr_bin));
-        colvar_forces[ic].real_value += -1.0 * f[ic];
-        // the gradients are stored, not the forces
-      }
-      if (comm == multiple_replicas)
-        for (size_t ir = 0; ir < replicas.size(); ir++) {
-          bias_energy += replicas[ir]->hills_energy->value(curr_bin);
-          cvm::real const *f = &(replicas[ir]->hills_energy_gradients->value(curr_bin));
-          for (size_t ic = 0; ic < colvars.size(); ic++) {
-            colvar_forces[ic].real_value += -1.0 * f[ic];
-          }
-        }
 
-    } else {
+int colvarbias_meta::calc_forces(std::vector<colvarvalue> const &values)
+{
+  size_t ir = 0, ic = 0;
+  for (ir = 0; ir < replicas.size(); ir++) {
+    for (ic = 0; ic < num_variables(); ic++) {
+      replicas[ir]->colvar_forces[ic].reset();
+    }
+  }
 
-      // off the grid: compute analytically only the hills at the grid's edges
+  std::vector<int> const curr_bin = values.size() ?
+    hills_energy->get_colvars_index(values) :
+    hills_energy->get_colvars_index();
 
-      calc_hills(hills_off_grid.begin(), hills_off_grid.end(), bias_energy);
-      for (size_t ic = 0; ic < colvars.size(); ic++) {
-        calc_hills_force(ic, hills_off_grid.begin(), hills_off_grid.end(), colvar_forces);
+  if (hills_energy->index_ok(curr_bin)) {
+    for (ir = 0; ir < replicas.size(); ir++) {
+      cvm::real const *f = &(replicas[ir]->hills_energy_gradients->value(curr_bin));
+      for (ic = 0; ic < num_variables(); ic++) {
+        // the gradients are stored, not the forces
+        colvar_forces[ic].real_value += -1.0 * f[ic];
+      }
+    }
+  } else {
+    // off the grid: compute analytically only the hills at the grid's edges
+    for (ir = 0; ir < replicas.size(); ir++) {
+      for (ic = 0; ic < num_variables(); ic++) {
+        calc_hills_force(ic,
+                         replicas[ir]->hills_off_grid.begin(),
+                         replicas[ir]->hills_off_grid.end(),
+                         colvar_forces,
+                         values);
       }
-
-      if (comm == multiple_replicas)
-        for (size_t ir = 0; ir < replicas.size(); ir++) {
-          calc_hills(replicas[ir]->hills_off_grid.begin(),
-                     replicas[ir]->hills_off_grid.end(),
-                     bias_energy);
-          for (size_t ic = 0; ic < colvars.size(); ic++) {
-            calc_hills_force(ic,
-                             replicas[ir]->hills_off_grid.begin(),
-                             replicas[ir]->hills_off_grid.end(),
-                             colvar_forces);
-          }
-        }
     }
   }
 
   // now include the hills that have not been binned yet (starting
   // from new_hills_begin)
 
-  calc_hills(new_hills_begin, hills.end(), bias_energy);
-  for (size_t ic = 0; ic < colvars.size(); ic++) {
-    calc_hills_force(ic, new_hills_begin, hills.end(), colvar_forces);
-  }
-
-  if (cvm::debug())
-    cvm::log("Hills energy = "+cvm::to_str(bias_energy)+
-             ", hills forces = "+cvm::to_str(colvar_forces)+".\n");
-
-  if (cvm::debug())
+  if (cvm::debug()) {
     cvm::log("Metadynamics bias \""+this->name+"\""+
              ((comm != single_replica) ? ", replica \""+replica_id+"\"" : "")+
              ": adding the forces from the other replicas.\n");
+  }
 
-  if (comm == multiple_replicas)
-    for (size_t ir = 0; ir < replicas.size(); ir++) {
-      calc_hills(replicas[ir]->new_hills_begin,
-                 replicas[ir]->hills.end(),
-                 bias_energy);
-      for (size_t ic = 0; ic < colvars.size(); ic++) {
-        calc_hills_force(ic,
-                         replicas[ir]->new_hills_begin,
-                         replicas[ir]->hills.end(),
-                         colvar_forces);
+  for (ir = 0; ir < replicas.size(); ir++) {
+    for (ic = 0; ic < num_variables(); ic++) {
+      calc_hills_force(ic,
+                       replicas[ir]->new_hills_begin,
+                       replicas[ir]->hills.end(),
+                       colvar_forces,
+                       values);
+      if (cvm::debug()) {
+        cvm::log("Hills forces = "+cvm::to_str(colvar_forces)+".\n");
       }
-      if (cvm::debug())
-        cvm::log("Hills energy = "+cvm::to_str(bias_energy)+
-                 ", hills forces = "+cvm::to_str(colvar_forces)+".\n");
     }
+  }
 
   return COLVARS_OK;
 }
 
 
+
 void colvarbias_meta::calc_hills(colvarbias_meta::hill_iter      h_first,
                                  colvarbias_meta::hill_iter      h_last,
                                  cvm::real                      &energy,
                                  std::vector<colvarvalue> const &colvar_values)
 {
-  std::vector<colvarvalue> curr_values(colvars.size());
-  for (size_t i = 0; i < colvars.size(); i++) {
-    curr_values[i].type(colvars[i]->value());
+  size_t i = 0;
+  std::vector<colvarvalue> curr_values(num_variables());
+  for (i = 0; i < num_variables(); i++) {
+    curr_values[i].type(variables(i)->value());
   }
 
   if (colvar_values.size()) {
-    for (size_t i = 0; i < colvars.size(); i++) {
+    for (i = 0; i < num_variables(); i++) {
       curr_values[i] = colvar_values[i];
     }
   } else {
-    for (size_t i = 0; i < colvars.size(); i++) {
-      curr_values[i] = colvars[i]->value();
+    for (i = 0; i < num_variables(); i++) {
+      curr_values[i] = variables(i)->value();
     }
   }
 
@@ -632,11 +678,11 @@ void colvarbias_meta::calc_hills(colvarbias_meta::hill_iter      h_first,
 
     // compute the gaussian exponent
     cvm::real cv_sqdev = 0.0;
-    for (size_t i = 0; i < colvars.size(); i++) {
+    for (i = 0; i < num_variables(); i++) {
       colvarvalue const &x  = curr_values[i];
       colvarvalue const &center = h->centers[i];
       cvm::real const    half_width = 0.5 * h->widths[i];
-      cv_sqdev += (colvars[i]->dist2(x, center)) / (half_width*half_width);
+      cv_sqdev += (variables(i)->dist2(x, center)) / (half_width*half_width);
     }
 
     // compute the gaussian
@@ -658,14 +704,14 @@ void colvarbias_meta::calc_hills_force(size_t const &i,
                                        std::vector<colvarvalue> const &values)
 {
   // Retrieve the value of the colvar
-  colvarvalue const x(values.size() ? values[i] : colvars[i]->value());
+  colvarvalue const x(values.size() ? values[i] : variables(i)->value());
 
   // do the type check only once (all colvarvalues in the hills series
   // were already saved with their types matching those in the
   // colvars)
 
   hill_iter h;
-  switch (colvars[i]->value().type()) {
+  switch (variables(i)->value().type()) {
 
   case colvarvalue::type_scalar:
     for (h = h_first; h != h_last; h++) {
@@ -674,7 +720,7 @@ void colvarbias_meta::calc_hills_force(size_t const &i,
       cvm::real const    half_width = 0.5 * h->widths[i];
       forces[i].real_value +=
         ( h->weight() * h->value() * (0.5 / (half_width*half_width)) *
-          (colvars[i]->dist2_lgrad(x, center)).real_value );
+          (variables(i)->dist2_lgrad(x, center)).real_value );
     }
     break;
 
@@ -687,7 +733,7 @@ void colvarbias_meta::calc_hills_force(size_t const &i,
       cvm::real const    half_width = 0.5 * h->widths[i];
       forces[i].rvector_value +=
         ( h->weight() * h->value() * (0.5 / (half_width*half_width)) *
-          (colvars[i]->dist2_lgrad(x, center)).rvector_value );
+          (variables(i)->dist2_lgrad(x, center)).rvector_value );
     }
     break;
 
@@ -699,7 +745,7 @@ void colvarbias_meta::calc_hills_force(size_t const &i,
       cvm::real const    half_width = 0.5 * h->widths[i];
       forces[i].quaternion_value +=
         ( h->weight() * h->value() * (0.5 / (half_width*half_width)) *
-          (colvars[i]->dist2_lgrad(x, center)).quaternion_value );
+          (variables(i)->dist2_lgrad(x, center)).quaternion_value );
     }
     break;
 
@@ -710,7 +756,7 @@ void colvarbias_meta::calc_hills_force(size_t const &i,
       cvm::real const    half_width = 0.5 * h->widths[i];
       forces[i].vector1d_value +=
         ( h->weight() * h->value() * (0.5 / (half_width*half_width)) *
-          (colvars[i]->dist2_lgrad(x, center)).vector1d_value );
+          (variables(i)->dist2_lgrad(x, center)).vector1d_value );
     }
     break;
 
@@ -739,13 +785,13 @@ void colvarbias_meta::project_hills(colvarbias_meta::hill_iter  h_first,
 
   // TODO: improve it by looping over a small subgrid instead of the whole grid
 
-  std::vector<colvarvalue> colvar_values(colvars.size());
-  std::vector<cvm::real> colvar_forces_scalar(colvars.size());
+  std::vector<colvarvalue> colvar_values(num_variables());
+  std::vector<cvm::real> colvar_forces_scalar(num_variables());
 
   std::vector<int> he_ix = he->new_index();
   std::vector<int> hg_ix = (hg != NULL) ? hg->new_index() : std::vector<int> (0);
   cvm::real hills_energy_here = 0.0;
-  std::vector<colvarvalue> hills_forces_here(colvars.size(), 0.0);
+  std::vector<colvarvalue> hills_forces_here(num_variables(), 0.0);
 
   size_t count = 0;
   size_t const print_frequency = ((hills.size() >= 1000000) ? 1 : (1000000/(hills.size()+1)));
@@ -757,7 +803,7 @@ void colvarbias_meta::project_hills(colvarbias_meta::hill_iter  h_first,
           (he->index_ok(he_ix)) && (hg->index_ok(hg_ix));
           count++) {
       size_t i;
-      for (i = 0; i < colvars.size(); i++) {
+      for (i = 0; i < num_variables(); i++) {
         colvar_values[i] = hills_energy->bin_to_value_scalar(he_ix[i], i);
       }
 
@@ -766,7 +812,7 @@ void colvarbias_meta::project_hills(colvarbias_meta::hill_iter  h_first,
       calc_hills(h_first, h_last, hills_energy_here, colvar_values);
       he->acc_value(he_ix, hills_energy_here);
 
-      for (i = 0; i < colvars.size(); i++) {
+      for (i = 0; i < num_variables(); i++) {
         hills_forces_here[i].reset();
         calc_hills_force(i, h_first, h_last, hills_forces_here, colvar_values);
         colvar_forces_scalar[i] = hills_forces_here[i].real_value;
@@ -795,7 +841,7 @@ void colvarbias_meta::project_hills(colvarbias_meta::hill_iter  h_first,
 
     for ( ; (he->index_ok(he_ix)); ) {
 
-      for (size_t i = 0; i < colvars.size(); i++) {
+      for (size_t i = 0; i < num_variables(); i++) {
         colvar_values[i] = hills_energy->bin_to_value_scalar(he_ix[i], i);
       }
 
@@ -851,6 +897,21 @@ void colvarbias_meta::recount_hills_off_grid(colvarbias_meta::hill_iter  h_first
 // **********************************************************************
 
 
+int colvarbias_meta::replica_share()
+{
+  // sync with the other replicas (if needed)
+  if (comm == multiple_replicas) {
+    // reread the replicas registry
+    update_replicas_registry();
+    // empty the output buffer
+    if (replica_hills_os.is_open())
+      replica_hills_os.flush();
+    read_replica_files();
+  }
+  return COLVARS_OK;
+}
+
+
 void colvarbias_meta::update_replicas_registry()
 {
   if (cvm::debug())
@@ -975,7 +1036,6 @@ void colvarbias_meta::update_replicas_registry()
     }
   }
 
-
   if (cvm::debug())
     cvm::log("Metadynamics bias \""+this->name+"\": the list of replicas contains "+
              cvm::to_str(replicas.size())+" elements.\n");
@@ -984,7 +1044,8 @@ void colvarbias_meta::update_replicas_registry()
 
 void colvarbias_meta::read_replica_files()
 {
-  for (size_t ir = 0; ir < replicas.size(); ir++) {
+  // Note: we start from the 2nd replica.
+  for (size_t ir = 1; ir < replicas.size(); ir++) {
 
     if (! (replicas[ir])->replica_state_file_in_sync) {
       // if a new state file is being read, the hills file is also new
@@ -1352,9 +1413,9 @@ std::istream & colvarbias_meta::read_hill(std::istream &is)
   cvm::real h_weight;
   get_keyval(data, "weight", h_weight, hill_weight, parse_silent);
 
-  std::vector<colvarvalue> h_centers(colvars.size());
-  for (size_t i = 0; i < colvars.size(); i++) {
-    h_centers[i].type(colvars[i]->value());
+  std::vector<colvarvalue> h_centers(num_variables());
+  for (size_t i = 0; i < num_variables(); i++) {
+    h_centers[i].type(variables(i)->value());
   }
   {
     // it is safer to read colvarvalue objects one at a time;
@@ -1362,14 +1423,14 @@ std::istream & colvarbias_meta::read_hill(std::istream &is)
     std::string centers_input;
     key_lookup(data, "centers", centers_input);
     std::istringstream centers_is(centers_input);
-    for (size_t i = 0; i < colvars.size(); i++) {
+    for (size_t i = 0; i < num_variables(); i++) {
       centers_is >> h_centers[i];
     }
   }
 
-  std::vector<cvm::real> h_widths(colvars.size());
+  std::vector<cvm::real> h_widths(num_variables());
   get_keyval(data, "widths", h_widths,
-             std::vector<cvm::real> (colvars.size(), (std::sqrt(2.0 * PI) / 2.0)),
+             std::vector<cvm::real>(num_variables(), (std::sqrt(2.0 * PI) / 2.0)),
              parse_silent);
 
   std::string h_replica = "";
@@ -1406,6 +1467,13 @@ std::istream & colvarbias_meta::read_hill(std::istream &is)
 
 int colvarbias_meta::setup_output()
 {
+  output_prefix = cvm::output_prefix();
+  if (cvm::num_biases_feature(colvardeps::f_cvb_calc_pmf) > 1) {
+    // if this is not the only free energy integrator, append
+    // this bias's name, to distinguish it from the output of the other
+    // biases producing a .pmf file
+    output_prefix += ("."+this->name);
+  }
 
   if (comm == multiple_replicas) {
 
@@ -1421,10 +1489,10 @@ int colvarbias_meta::setup_output()
     // those by another replica
     replica_hills_file =
       (std::string(pwd)+std::string(PATHSEP)+
-       cvm::output_prefix+".colvars."+this->name+"."+replica_id+".hills");
+       cvm::output_prefix()+".colvars."+this->name+"."+replica_id+".hills");
     replica_state_file =
       (std::string(pwd)+std::string(PATHSEP)+
-       cvm::output_prefix+".colvars."+this->name+"."+replica_id+".state");
+       cvm::output_prefix()+".colvars."+this->name+"."+replica_id+".state");
     delete[] pwd;
 
     // now register this replica
@@ -1492,13 +1560,14 @@ int colvarbias_meta::setup_output()
   }
 
   if (b_hills_traj) {
-    std::string const traj_file_name(cvm::output_prefix+
+    std::string const traj_file_name(cvm::output_prefix()+
                                      ".colvars."+this->name+
                                      ( (comm != single_replica) ?
                                        ("."+replica_id) :
                                        ("") )+
                                      ".hills.traj");
     if (!hills_traj_os.is_open()) {
+      cvm::backup_file(traj_file_name.c_str());
       hills_traj_os.open(traj_file_name.c_str());
     }
     if (!hills_traj_os.is_open())
@@ -1585,16 +1654,6 @@ void colvarbias_meta::write_pmf()
   colvar_grid_scalar *pmf = new colvar_grid_scalar(*hills_energy);
   pmf->setup();
 
-  std::string fes_file_name_prefix(cvm::output_prefix);
-
-  if ((cvm::n_meta_biases > 1) || (cvm::n_abf_biases > 0)) {
-    // if this is not the only free energy integrator, append
-    // this bias's name, to distinguish it from the output of the other
-    // biases producing a .pmf file
-    // TODO: fix for ABF with updateBias == no
-    fes_file_name_prefix += ("."+this->name);
-  }
-
   if ((comm == single_replica) || (dump_replica_fes)) {
     // output the PMF from this instance or replica
     pmf->reset();
@@ -1607,7 +1666,7 @@ void colvarbias_meta::write_pmf()
       pmf->multiply_constant(well_temper_scale);
     }
     {
-      std::string const fes_file_name(fes_file_name_prefix +
+      std::string const fes_file_name(this->output_prefix +
                                       ((comm != single_replica) ? ".partial" : "") +
                                       (dump_fes_save ?
                                        "."+cvm::to_str(cvm::step_absolute()) : "") +
@@ -1632,7 +1691,7 @@ void colvarbias_meta::write_pmf()
       cvm::real const well_temper_scale = (bias_temperature + cvm::temperature()) / bias_temperature;
       pmf->multiply_constant(well_temper_scale);
     }
-    std::string const fes_file_name(fes_file_name_prefix +
+    std::string const fes_file_name(this->output_prefix +
                                     (dump_fes_save ?
                                      "."+cvm::to_str(cvm::step_absolute()) : "") +
                                     ".pmf");
diff --git a/lib/colvars/colvarbias_meta.h b/lib/colvars/colvarbias_meta.h
index a88a34ba00a7749895390cd81e5179687ebb5599..01921eaf640855f40fabcd8147b4815e7579cd89 100644
--- a/lib/colvars/colvarbias_meta.h
+++ b/lib/colvars/colvarbias_meta.h
@@ -36,8 +36,20 @@ public:
 
   colvarbias_meta(char const *key);
   virtual int init(std::string const &conf);
+  virtual int init_well_tempered_params(std::string const &conf);
+  virtual int init_ebmeta_params(std::string const &conf);
   virtual ~colvarbias_meta();
+
   virtual int update();
+  virtual int update_grid_params();
+  virtual int update_bias();
+  virtual int update_grid_data();
+  virtual int replica_share();
+
+  virtual int calc_energy(std::vector<colvarvalue> const &values =
+                          std::vector<colvarvalue>(0));
+  virtual int calc_forces(std::vector<colvarvalue> const &values =
+                          std::vector<colvarvalue>(0));
 
   virtual std::string const get_state_params() const;
   virtual int set_state_params(std::string const &state_conf);
@@ -102,18 +114,18 @@ protected:
   /// \brief Calculate the values of the hills, incrementing
   /// bias_energy
   virtual void calc_hills(hill_iter  h_first,
-                           hill_iter  h_last,
-                           cvm::real &energy,
-                           std::vector<colvarvalue> const &values = std::vector<colvarvalue> (0));
+                          hill_iter  h_last,
+                          cvm::real &energy,
+                          std::vector<colvarvalue> const &values = std::vector<colvarvalue>(0));
 
   /// \brief Calculate the forces acting on the i-th colvar,
   /// incrementing colvar_forces[i]; must be called after calc_hills
   /// each time the values of the colvars are changed
   virtual void calc_hills_force(size_t const &i,
-                                 hill_iter h_first,
-                                 hill_iter h_last,
-                                 std::vector<colvarvalue> &forces,
-                                 std::vector<colvarvalue> const &values = std::vector<colvarvalue> (0));
+                                hill_iter h_first,
+                                hill_iter h_last,
+                                std::vector<colvarvalue> &forces,
+                                std::vector<colvarvalue> const &values = std::vector<colvarvalue>(0));
 
 
   /// Height of new hills
diff --git a/lib/colvars/colvarbias_restraint.cpp b/lib/colvars/colvarbias_restraint.cpp
index 84630984e5085fb9b07e96ca9ad8da9480608b62..159d9eae6451bbfc9a307d8507d64888188c5bc7 100644
--- a/lib/colvars/colvarbias_restraint.cpp
+++ b/lib/colvars/colvarbias_restraint.cpp
@@ -33,17 +33,15 @@ int colvarbias_restraint::init(std::string const &conf)
 
 int colvarbias_restraint::update()
 {
-  bias_energy = 0.0;
-
-  if (cvm::debug())
-    cvm::log("Updating the restraint bias \""+this->name+"\".\n");
+  // Update base class (bias_energy and colvar_forces are zeroed there)
+  colvarbias::update();
 
   // Force and energy calculation
-  for (size_t i = 0; i < colvars.size(); i++) {
-    colvar_forces[i].type(colvars[i]->value());
+  for (size_t i = 0; i < num_variables(); i++) {
+    bias_energy += restraint_potential(i);
+    colvar_forces[i].type(variables(i)->value());
     colvar_forces[i].is_derivative();
     colvar_forces[i] = restraint_force(i);
-    bias_energy += restraint_potential(i);
   }
 
   if (cvm::debug())
@@ -59,8 +57,6 @@ int colvarbias_restraint::update()
 
 colvarbias_restraint::~colvarbias_restraint()
 {
-  if (cvm::n_rest_biases > 0)
-    cvm::n_rest_biases -= 1;
 }
 
 
@@ -102,18 +98,18 @@ int colvarbias_restraint_centers::init(std::string const &conf)
   bool null_centers = (colvar_centers.size() == 0);
   if (null_centers) {
     // try to initialize the restraint centers for the first time
-    colvar_centers.resize(colvars.size());
-    colvar_centers_raw.resize(colvars.size());
-    for (i = 0; i < colvars.size(); i++) {
-      colvar_centers[i].type(colvars[i]->value());
+    colvar_centers.resize(num_variables());
+    colvar_centers_raw.resize(num_variables());
+    for (i = 0; i < num_variables(); i++) {
+      colvar_centers[i].type(variables(i)->value());
       colvar_centers[i].reset();
-      colvar_centers_raw[i].type(colvars[i]->value());
+      colvar_centers_raw[i].type(variables(i)->value());
       colvar_centers_raw[i].reset();
     }
   }
 
   if (get_keyval(conf, "centers", colvar_centers, colvar_centers)) {
-    for (i = 0; i < colvars.size(); i++) {
+    for (i = 0; i < num_variables(); i++) {
       if (cvm::debug()) {
         cvm::log("colvarbias_restraint: parsing initial centers, i = "+cvm::to_str(i)+".\n");
       }
@@ -129,7 +125,7 @@ int colvarbias_restraint_centers::init(std::string const &conf)
     return INPUT_ERROR;
   }
 
-  if (colvar_centers.size() != colvars.size()) {
+  if (colvar_centers.size() != num_variables()) {
     cvm::error("Error: number of centers does not match "
                "that of collective variables.\n", INPUT_ERROR);
     return INPUT_ERROR;
@@ -142,10 +138,10 @@ int colvarbias_restraint_centers::init(std::string const &conf)
 int colvarbias_restraint_centers::change_configuration(std::string const &conf)
 {
   if (get_keyval(conf, "centers", colvar_centers, colvar_centers)) {
-    for (size_t i = 0; i < colvars.size(); i++) {
-      colvar_centers[i].type(colvars[i]->value());
+    for (size_t i = 0; i < num_variables(); i++) {
+      colvar_centers[i].type(variables(i)->value());
       colvar_centers[i].apply_constraints();
-      colvar_centers_raw[i].type(colvars[i]->value());
+      colvar_centers_raw[i].type(variables(i)->value());
       colvar_centers_raw[i] = colvar_centers[i];
     }
   }
@@ -269,7 +265,7 @@ int colvarbias_restraint_centers_moving::init(std::string const &conf)
 
   size_t i;
   if (get_keyval(conf, "targetCenters", target_centers, colvar_centers)) {
-    if (colvar_centers.size() != colvars.size()) {
+    if (colvar_centers.size() != num_variables()) {
       cvm::error("Error: number of target centers does not match "
                  "that of collective variables.\n");
     }
@@ -308,9 +304,9 @@ int colvarbias_restraint_centers_moving::update()
       // at each simulation step (or stage, if applicable)
       // (take current stage into account: it can be non-zero
       //  if we are restarting a staged calculation)
-      centers_incr.resize(colvars.size());
-      for (size_t i = 0; i < colvars.size(); i++) {
-        centers_incr[i].type(colvars[i]->value());
+      centers_incr.resize(num_variables());
+      for (size_t i = 0; i < num_variables(); i++) {
+        centers_incr[i].type(variables(i)->value());
         centers_incr[i] = (target_centers[i] - colvar_centers_raw[i]) /
           cvm::real( target_nstages ? (target_nstages - stage) :
                      (target_nsteps - cvm::step_absolute()));
@@ -326,10 +322,10 @@ int colvarbias_restraint_centers_moving::update()
           && (cvm::step_absolute() % target_nsteps) == 0
           && stage < target_nstages) {
 
-        for (size_t i = 0; i < colvars.size(); i++) {
+        for (size_t i = 0; i < num_variables(); i++) {
           colvar_centers_raw[i] += centers_incr[i];
           colvar_centers[i] = colvar_centers_raw[i];
-          colvars[i]->wrap(colvar_centers[i]);
+          variables(i)->wrap(colvar_centers[i]);
           colvar_centers[i].apply_constraints();
         }
         stage++;
@@ -341,10 +337,10 @@ int colvarbias_restraint_centers_moving::update()
     } else if ((cvm::step_relative() > 0) && (cvm::step_absolute() <= target_nsteps)) {
       // move the restraint centers in the direction of the targets
       // (slow growth)
-      for (size_t i = 0; i < colvars.size(); i++) {
+      for (size_t i = 0; i < num_variables(); i++) {
         colvar_centers_raw[i] += centers_incr[i];
         colvar_centers[i] = colvar_centers_raw[i];
-        colvars[i]->wrap(colvar_centers[i]);
+        variables(i)->wrap(colvar_centers[i]);
         colvar_centers[i].apply_constraints();
       }
     }
@@ -363,7 +359,7 @@ int colvarbias_restraint_centers_moving::update_acc_work()
 {
   if (b_output_acc_work) {
     if ((cvm::step_relative() > 0) || (cvm::step_absolute() == 0)) {
-      for (size_t i = 0; i < colvars.size(); i++) {
+      for (size_t i = 0; i < num_variables(); i++) {
         // project forces on the calculated increments at this step
         acc_work += colvar_forces[i] * centers_incr[i];
       }
@@ -381,14 +377,14 @@ std::string const colvarbias_restraint_centers_moving::get_state_params() const
   if (b_chg_centers) {
     size_t i;
     os << "centers ";
-    for (i = 0; i < colvars.size(); i++) {
+    for (i = 0; i < num_variables(); i++) {
       os << " "
          << std::setprecision(cvm::cv_prec) << std::setw(cvm::cv_width)
          << colvar_centers[i];
     }
     os << "\n";
     os << "centers_raw ";
-    for (i = 0; i < colvars.size(); i++) {
+    for (i = 0; i < num_variables(); i++) {
       os << " "
          << std::setprecision(cvm::cv_prec) << std::setw(cvm::cv_width)
          << colvar_centers_raw[i];
@@ -429,10 +425,10 @@ int colvarbias_restraint_centers_moving::set_state_params(std::string const &con
 std::ostream & colvarbias_restraint_centers_moving::write_traj_label(std::ostream &os)
 {
   if (b_output_centers) {
-    for (size_t i = 0; i < colvars.size(); i++) {
-      size_t const this_cv_width = (colvars[i]->value()).output_width(cvm::cv_width);
+    for (size_t i = 0; i < num_variables(); i++) {
+      size_t const this_cv_width = (variables(i)->value()).output_width(cvm::cv_width);
       os << " x0_"
-         << cvm::wrap_string(colvars[i]->name, this_cv_width-3);
+         << cvm::wrap_string(variables(i)->name, this_cv_width-3);
     }
   }
 
@@ -448,7 +444,7 @@ std::ostream & colvarbias_restraint_centers_moving::write_traj_label(std::ostrea
 std::ostream & colvarbias_restraint_centers_moving::write_traj(std::ostream &os)
 {
   if (b_output_centers) {
-    for (size_t i = 0; i < colvars.size(); i++) {
+    for (size_t i = 0; i < num_variables(); i++) {
       os << " "
          << std::setprecision(cvm::cv_prec) << std::setw(cvm::cv_width)
          << colvar_centers[i];
@@ -539,9 +535,9 @@ int colvarbias_restraint_k_moving::update()
         }
         force_k = starting_force_k + (target_force_k - starting_force_k)
           * std::pow(lambda, force_k_exp);
-        cvm::log("Restraint " + this->name + ", stage " +
-                 cvm::to_str(stage) + " : lambda = " + cvm::to_str(lambda));
-        cvm::log("Setting force constant to " + cvm::to_str(force_k));
+          cvm::log("Restraint " + this->name + ", stage " + cvm::to_str(stage)
+                  + " : lambda = " + cvm::to_str(lambda)
+                  + ", k = " + cvm::to_str(force_k));
       }
 
       // TI calculation: estimate free energy derivative
@@ -557,7 +553,7 @@ int colvarbias_restraint_k_moving::update()
 
         // Square distance normalized by square colvar width
         cvm::real dist_sq = 0.0;
-        for (size_t i = 0; i < colvars.size(); i++) {
+        for (size_t i = 0; i < num_variables(); i++) {
           dist_sq += d_restraint_potential_dk(i);
         }
 
@@ -569,7 +565,8 @@ int colvarbias_restraint_k_moving::update()
       if (cvm::step_absolute() % target_nsteps == 0 &&
           cvm::step_absolute() > 0) {
 
-        cvm::log("Lambda= " + cvm::to_str(lambda) + " dA/dLambda= "
+        cvm::log("Restraint " + this->name + " Lambda= "
+                 + cvm::to_str(lambda) + " dA/dLambda= "
                  + cvm::to_str(restraint_FE / cvm::real(target_nsteps - target_equil_steps)));
 
         //  ...and move on to the next one
@@ -584,9 +581,9 @@ int colvarbias_restraint_k_moving::update()
           }
           force_k = starting_force_k + (target_force_k - starting_force_k)
             * std::pow(lambda, force_k_exp);
-          cvm::log("Restraint " + this->name + ", stage " +
-                   cvm::to_str(stage) + " : lambda = " + cvm::to_str(lambda));
-          cvm::log("Setting force constant to " + cvm::to_str(force_k));
+          cvm::log("Restraint " + this->name + ", stage " + cvm::to_str(stage)
+                  + " : lambda = " + cvm::to_str(lambda)
+                  + ", k = " + cvm::to_str(force_k));
         }
       }
 
@@ -721,11 +718,11 @@ int colvarbias_restraint_harmonic::init(std::string const &conf)
   colvarbias_restraint_centers_moving::init(conf);
   colvarbias_restraint_k_moving::init(conf);
 
-  for (size_t i = 0; i < colvars.size(); i++) {
-    if (colvars[i]->width != 1.0)
-      cvm::log("The force constant for colvar \""+colvars[i]->name+
+  for (size_t i = 0; i < num_variables(); i++) {
+    if (variables(i)->width != 1.0)
+      cvm::log("The force constant for colvar \""+variables(i)->name+
                "\" will be rescaled to "+
-               cvm::to_str(force_k / (colvars[i]->width * colvars[i]->width))+
+               cvm::to_str(force_k / (variables(i)->width * variables(i)->width))+
                " according to the specified width.\n");
   }
 
@@ -751,22 +748,22 @@ int colvarbias_restraint_harmonic::update()
 
 cvm::real colvarbias_restraint_harmonic::restraint_potential(size_t i) const
 {
-  return 0.5 * force_k / (colvars[i]->width * colvars[i]->width) *
-    colvars[i]->dist2(colvars[i]->value(), colvar_centers[i]);
+  return 0.5 * force_k / (variables(i)->width * variables(i)->width) *
+    variables(i)->dist2(variables(i)->value(), colvar_centers[i]);
 }
 
 
 colvarvalue const colvarbias_restraint_harmonic::restraint_force(size_t i) const
 {
-  return -0.5 * force_k / (colvars[i]->width * colvars[i]->width) *
-    colvars[i]->dist2_lgrad(colvars[i]->value(), colvar_centers[i]);
+  return -0.5 * force_k / (variables(i)->width * variables(i)->width) *
+    variables(i)->dist2_lgrad(variables(i)->value(), colvar_centers[i]);
 }
 
 
 cvm::real colvarbias_restraint_harmonic::d_restraint_potential_dk(size_t i) const
 {
-  return 0.5 / (colvars[i]->width * colvars[i]->width) *
-    colvars[i]->dist2(colvars[i]->value(), colvar_centers[i]);
+  return 0.5 / (variables(i)->width * variables(i)->width) *
+    variables(i)->dist2(variables(i)->value(), colvar_centers[i]);
 }
 
 
@@ -840,6 +837,8 @@ colvarbias_restraint_harmonic_walls::colvarbias_restraint_harmonic_walls(char co
     colvarbias_restraint_moving(key),
     colvarbias_restraint_k_moving(key)
 {
+  lower_wall_k = 0.0;
+  upper_wall_k = 0.0;
 }
 
 
@@ -849,7 +848,11 @@ int colvarbias_restraint_harmonic_walls::init(std::string const &conf)
   colvarbias_restraint_moving::init(conf);
   colvarbias_restraint_k_moving::init(conf);
 
-  provide(f_cvb_scalar_variables);
+  get_keyval(conf, "lowerWallConstant", lower_wall_k,
+             (lower_wall_k > 0.0) ? lower_wall_k : force_k);
+  get_keyval(conf, "upperWallConstant", upper_wall_k,
+             (upper_wall_k > 0.0) ? upper_wall_k : force_k);
+
   enable(f_cvb_scalar_variables);
 
   size_t i;
@@ -857,9 +860,9 @@ int colvarbias_restraint_harmonic_walls::init(std::string const &conf)
   bool b_null_lower_walls = false;
   if (lower_walls.size() == 0) {
     b_null_lower_walls = true;
-    lower_walls.resize(number_of_colvars());
-    for (i = 0; i < colvars.size(); i++) {
-      lower_walls[i].type(colvars[i]->value());
+    lower_walls.resize(num_variables());
+    for (i = 0; i < num_variables(); i++) {
+      lower_walls[i].type(variables(i)->value());
       lower_walls[i].reset();
     }
   }
@@ -872,9 +875,9 @@ int colvarbias_restraint_harmonic_walls::init(std::string const &conf)
   bool b_null_upper_walls = false;
   if (upper_walls.size() == 0) {
     b_null_upper_walls = true;
-    upper_walls.resize(number_of_colvars());
-    for (i = 0; i < colvars.size(); i++) {
-      upper_walls[i].type(colvars[i]->value());
+    upper_walls.resize(num_variables());
+    for (i = 0; i < num_variables(); i++) {
+      upper_walls[i].type(variables(i)->value());
       upper_walls[i].reset();
     }
   }
@@ -890,17 +893,17 @@ int colvarbias_restraint_harmonic_walls::init(std::string const &conf)
   }
 
   if ((lower_walls.size() == 0) || (upper_walls.size() == 0)) {
-    for (i = 0; i < colvars.size(); i++) {
-      if (colvars[i]->is_enabled(f_cv_periodic)) {
+    for (i = 0; i < num_variables(); i++) {
+      if (variables(i)->is_enabled(f_cv_periodic)) {
         cvm::error("Error: at least one variable is periodic, "
-                   "both walls must be provided .\n", INPUT_ERROR);
+                   "both walls must be provided.\n", INPUT_ERROR);
         return INPUT_ERROR;
       }
     }
   }
 
   if ((lower_walls.size() > 0) && (upper_walls.size() > 0)) {
-    for (i = 0; i < colvars.size(); i++) {
+    for (i = 0; i < num_variables(); i++) {
       if (lower_walls[i] >= upper_walls[i]) {
         cvm::error("Error: one upper wall, "+
                    cvm::to_str(upper_walls[i])+
@@ -909,13 +912,24 @@ int colvarbias_restraint_harmonic_walls::init(std::string const &conf)
                    INPUT_ERROR);
       }
     }
+    if (lower_wall_k * upper_wall_k == 0.0) {
+      cvm::error("Error: lowerWallConstant and upperWallConstant, "
+                 "when defined, must both be positive.\n",
+                 INPUT_ERROR);
+      return INPUT_ERROR;
+    }
+    force_k = lower_wall_k * upper_wall_k;
+    // transform the two constants to relative values
+    // (allow changing both via force_k)
+    lower_wall_k /= force_k;
+    upper_wall_k /= force_k;
   }
 
-  for (i = 0; i < colvars.size(); i++) {
-    if (colvars[i]->width != 1.0)
-      cvm::log("The force constant for colvar \""+colvars[i]->name+
+  for (i = 0; i < num_variables(); i++) {
+    if (variables(i)->width != 1.0)
+      cvm::log("The force constant for colvar \""+variables(i)->name+
                "\" will be rescaled to "+
-               cvm::to_str(force_k / (colvars[i]->width * colvars[i]->width))+
+               cvm::to_str(force_k / (variables(i)->width * variables(i)->width))+
                " according to the specified width.\n");
   }
 
@@ -935,20 +949,20 @@ int colvarbias_restraint_harmonic_walls::update()
 
 void colvarbias_restraint_harmonic_walls::communicate_forces()
 {
-  for (size_t i = 0; i < colvars.size(); i++) {
+  for (size_t i = 0; i < num_variables(); i++) {
     if (cvm::debug()) {
       cvm::log("Communicating a force to colvar \""+
-               colvars[i]->name+"\".\n");
+               variables(i)->name+"\".\n");
     }
-    colvars[i]->add_bias_force_actual_value(colvar_forces[i]);
+    variables(i)->add_bias_force_actual_value(colvar_forces[i]);
   }
 }
 
 
 cvm::real colvarbias_restraint_harmonic_walls::colvar_distance(size_t i) const
 {
-  colvar *cv = colvars[i];
-  colvarvalue const &cvv = colvars[i]->actual_value();
+  colvar *cv = variables(i);
+  colvarvalue const &cvv = variables(i)->actual_value();
 
   // For a periodic colvar, both walls may be applicable at the same time
   // in which case we pick the closer one
@@ -958,21 +972,21 @@ cvm::real colvarbias_restraint_harmonic_walls::colvar_distance(size_t i) const
     cvm::real const upper_wall_dist2 = cv->dist2(cvv, upper_walls[i]);
     if (lower_wall_dist2 < upper_wall_dist2) {
       cvm::real const grad = cv->dist2_lgrad(cvv, lower_walls[i]);
-      if (grad < 0.0) { return grad; }
+      if (grad < 0.0) { return 0.5 * grad; }
     } else {
       cvm::real const grad = cv->dist2_lgrad(cvv, upper_walls[i]);
-      if (grad > 0.0) { return grad; }
+      if (grad > 0.0) { return 0.5 * grad; }
     }
     return 0.0;
   }
 
   if (lower_walls.size() > 0) {
     cvm::real const grad = cv->dist2_lgrad(cvv, lower_walls[i]);
-    if (grad < 0.0) { return grad; }
+    if (grad < 0.0) { return 0.5 * grad; }
   }
   if (upper_walls.size() > 0) {
     cvm::real const grad = cv->dist2_lgrad(cvv, upper_walls[i]);
-    if (grad > 0.0) { return grad; }
+    if (grad > 0.0) { return 0.5 * grad; }
   }
   return 0.0;
 }
@@ -981,7 +995,8 @@ cvm::real colvarbias_restraint_harmonic_walls::colvar_distance(size_t i) const
 cvm::real colvarbias_restraint_harmonic_walls::restraint_potential(size_t i) const
 {
   cvm::real const dist = colvar_distance(i);
-  return 0.5 * force_k / (colvars[i]->width * colvars[i]->width) *
+  cvm::real const scale = dist > 0.0 ? upper_wall_k : lower_wall_k;
+  return 0.5 * force_k * scale / (variables(i)->width * variables(i)->width) *
     dist * dist;
 }
 
@@ -989,15 +1004,16 @@ cvm::real colvarbias_restraint_harmonic_walls::restraint_potential(size_t i) con
 colvarvalue const colvarbias_restraint_harmonic_walls::restraint_force(size_t i) const
 {
   cvm::real const dist = colvar_distance(i);
-  return -0.5 * force_k / (colvars[i]->width * colvars[i]->width) *
-    dist;
+  cvm::real const scale = dist > 0.0 ? upper_wall_k : lower_wall_k;
+  return - force_k * scale / (variables(i)->width * variables(i)->width) * dist;
 }
 
 
 cvm::real colvarbias_restraint_harmonic_walls::d_restraint_potential_dk(size_t i) const
 {
   cvm::real const dist = colvar_distance(i);
-  return 0.5 / (colvars[i]->width * colvars[i]->width) *
+  cvm::real const scale = dist > 0.0 ? upper_wall_k : lower_wall_k;
+  return 0.5 * scale / (variables(i)->width * variables(i)->width) *
     dist * dist;
 }
 
@@ -1054,16 +1070,16 @@ int colvarbias_restraint_linear::init(std::string const &conf)
   colvarbias_restraint_centers_moving::init(conf);
   colvarbias_restraint_k_moving::init(conf);
 
-  for (size_t i = 0; i < colvars.size(); i++) {
-    if (colvars[i]->is_enabled(f_cv_periodic)) {
+  for (size_t i = 0; i < num_variables(); i++) {
+    if (variables(i)->is_enabled(f_cv_periodic)) {
       cvm::error("Error: linear biases cannot be applied to periodic variables.\n",
                  INPUT_ERROR);
       return INPUT_ERROR;
     }
-    if (colvars[i]->width != 1.0)
-      cvm::log("The force constant for colvar \""+colvars[i]->name+
+    if (variables(i)->width != 1.0)
+      cvm::log("The force constant for colvar \""+variables(i)->name+
                "\" will be rescaled to "+
-               cvm::to_str(force_k / colvars[i]->width)+
+               cvm::to_str(force_k / variables(i)->width)+
                " according to the specified width.\n");
   }
 
@@ -1113,19 +1129,19 @@ cvm::real colvarbias_restraint_linear::energy_difference(std::string const &conf
 
 cvm::real colvarbias_restraint_linear::restraint_potential(size_t i) const
 {
-  return force_k / colvars[i]->width * (colvars[i]->value() - colvar_centers[i]);
+  return force_k / variables(i)->width * (variables(i)->value() - colvar_centers[i]);
 }
 
 
 colvarvalue const colvarbias_restraint_linear::restraint_force(size_t i) const
 {
-  return -1.0 * force_k / colvars[i]->width;
+  return -1.0 * force_k / variables(i)->width;
 }
 
 
 cvm::real colvarbias_restraint_linear::d_restraint_potential_dk(size_t i) const
 {
-  return 1.0 / colvars[i]->width * (colvars[i]->value() - colvar_centers[i]);
+  return 1.0 / variables(i)->width * (variables(i)->value() - colvar_centers[i]);
 }
 
 
@@ -1279,16 +1295,16 @@ int colvarbias_restraint_histogram::update()
 
   size_t vector_size = 0;
   size_t icv;
-  for (icv = 0; icv < colvars.size(); icv++) {
-    vector_size += colvars[icv]->value().size();
+  for (icv = 0; icv < num_variables(); icv++) {
+    vector_size += variables(icv)->value().size();
   }
 
   cvm::real const norm = 1.0/(std::sqrt(2.0*PI)*gaussian_width*vector_size);
 
   // calculate the histogram
   p.reset();
-  for (icv = 0; icv < colvars.size(); icv++) {
-    colvarvalue const &cv = colvars[icv]->value();
+  for (icv = 0; icv < num_variables(); icv++) {
+    colvarvalue const &cv = variables(icv)->value();
     if (cv.type() == colvarvalue::type_scalar) {
       cvm::real const cv_value = cv.real_value;
       size_t igrid;
@@ -1309,7 +1325,9 @@ int colvarbias_restraint_histogram::update()
         }
       }
     } else {
-      // TODO
+      cvm::error("Error: unsupported type for variable "+variables(icv)->name+".\n",
+                 COLVARS_NOT_IMPLEMENTED);
+      return COLVARS_NOT_IMPLEMENTED;
     }
   }
 
@@ -1320,8 +1338,8 @@ int colvarbias_restraint_histogram::update()
   bias_energy = 0.5 * force_k_cv * p_diff * p_diff;
 
   // calculate the forces
-  for (icv = 0; icv < colvars.size(); icv++) {
-    colvarvalue const &cv = colvars[icv]->value();
+  for (icv = 0; icv < num_variables(); icv++) {
+    colvarvalue const &cv = variables(icv)->value();
     colvarvalue &cv_force = colvar_forces[icv];
     cv_force.type(cv);
     cv_force.reset();
@@ -1363,10 +1381,10 @@ int colvarbias_restraint_histogram::update()
 std::ostream & colvarbias_restraint_histogram::write_restart(std::ostream &os)
 {
   if (b_write_histogram) {
-    std::string file_name(cvm::output_prefix+"."+this->name+".hist.dat");
+    std::string file_name(cvm::output_prefix()+"."+this->name+".hist.dat");
     std::ofstream os(file_name.c_str());
-    os << "# " << cvm::wrap_string(colvars[0]->name, cvm::cv_width)
-       << "  " << "p(" << cvm::wrap_string(colvars[0]->name, cvm::cv_width-3)
+    os << "# " << cvm::wrap_string(variables(0)->name, cvm::cv_width)
+       << "  " << "p(" << cvm::wrap_string(variables(0)->name, cvm::cv_width-3)
        << ")\n";
     size_t igrid;
     for (igrid = 0; igrid < p.size(); igrid++) {
diff --git a/lib/colvars/colvarbias_restraint.h b/lib/colvars/colvarbias_restraint.h
index cbdd9c44d163a3c4684eafafaeb07cfa0a9b0495..98b967abdb1ba6a50754e2f26c3d9600e742557a 100644
--- a/lib/colvars/colvarbias_restraint.h
+++ b/lib/colvars/colvarbias_restraint.h
@@ -260,6 +260,12 @@ protected:
   /// \brief Location of the upper walls
   std::vector<colvarvalue> upper_walls;
 
+  /// \brief If both walls are defined, use this k for the lower
+  cvm::real lower_wall_k;
+
+  /// \brief If both walls are defined, use this k for the upper
+  cvm::real upper_wall_k;
+
   virtual cvm::real colvar_distance(size_t i) const;
   virtual cvm::real restraint_potential(size_t i) const;
   virtual colvarvalue const restraint_force(size_t i) const;
diff --git a/lib/colvars/colvarcomp.cpp b/lib/colvars/colvarcomp.cpp
index d99da56386f05970d4d90f30d1cee27811390aa0..786bc032d2ab11e5eb466c1ef3b43dda01809560 100644
--- a/lib/colvars/colvarcomp.cpp
+++ b/lib/colvars/colvarcomp.cpp
@@ -48,8 +48,6 @@ colvar::cvc::cvc(std::string const &conf)
   get_keyval(conf, "period", period, 0.0);
   get_keyval(conf, "wrapAround", wrap_center, 0.0);
 
-  // All cvcs implement this
-  provide(f_cvc_debug_gradient);
   get_keyval_feature((colvarparse *)this, conf, "debugGradients",
                      f_cvc_debug_gradient, false, parse_silent);
 
@@ -63,6 +61,8 @@ colvar::cvc::cvc(std::string const &conf)
 
 int colvar::cvc::init_total_force_params(std::string const &conf)
 {
+  if (cvm::get_error()) return COLVARS_ERROR;
+
   if (get_keyval_feature(this, conf, "oneSiteSystemForce",
                          f_cvc_one_site_total_force, is_enabled(f_cvc_one_site_total_force))) {
     cvm::log("Warning: keyword \"oneSiteSystemForce\" is deprecated: "
@@ -72,6 +72,19 @@ int colvar::cvc::init_total_force_params(std::string const &conf)
                          f_cvc_one_site_total_force, is_enabled(f_cvc_one_site_total_force))) {
     cvm::log("Computing total force on group 1 only");
   }
+
+  if (! is_enabled(f_cvc_one_site_total_force)) {
+    // check whether any of the other atom groups is dummy
+    std::vector<cvm::atom_group *>::iterator agi = atom_groups.begin();
+    agi++;
+    for ( ; agi != atom_groups.end(); agi++) {
+      if ((*agi)->b_dummy) {
+        provide(f_cvc_inv_gradient, false);
+        provide(f_cvc_Jacobian, false);
+      }
+    }
+  }
+
   return COLVARS_OK;
 }
 
@@ -87,8 +100,7 @@ cvm::atom_group *colvar::cvc::parse_group(std::string const &conf,
     group->key = group_key;
 
     if (b_try_scalable) {
-      // TODO rewrite this logic in terms of dependencies
-      if (is_available(f_cvc_scalable_com) && is_available(f_cvc_com_based)) {
+      if (is_available(f_cvc_scalable_com) && is_enabled(f_cvc_com_based)) {
         enable(f_cvc_scalable_com);
         enable(f_cvc_scalable);
         // The CVC makes the feature available;
diff --git a/lib/colvars/colvarcomp.h b/lib/colvars/colvarcomp.h
index a230cae8a9a04bf9b42e4b769f8e693062b8926d..ec215cbad107485d84f534d6c0eeeaff7b552e96 100644
--- a/lib/colvars/colvarcomp.h
+++ b/lib/colvars/colvarcomp.h
@@ -343,6 +343,15 @@ public:
   virtual void calc_value();
   virtual void calc_gradients();
   virtual void apply_force(colvarvalue const &force);
+  /// Redefined to override the distance ones
+  virtual cvm::real dist2(colvarvalue const &x1,
+                          colvarvalue const &x2) const;
+  /// Redefined to override the distance ones
+  virtual colvarvalue dist2_lgrad(colvarvalue const &x1,
+                                  colvarvalue const &x2) const;
+  /// Redefined to override the distance ones
+  virtual colvarvalue dist2_rgrad(colvarvalue const &x1,
+                                  colvarvalue const &x2) const;
 };
 
 
diff --git a/lib/colvars/colvarcomp_angles.cpp b/lib/colvars/colvarcomp_angles.cpp
index 4c3390a8bd1de9bf0fb37288d114d6cb8177daca..0204f3b4b1b50d83c58fe4d27be05c015cae1f10 100644
--- a/lib/colvars/colvarcomp_angles.cpp
+++ b/lib/colvars/colvarcomp_angles.cpp
@@ -21,7 +21,7 @@ colvar::angle::angle(std::string const &conf)
   function_type = "angle";
   provide(f_cvc_inv_gradient);
   provide(f_cvc_Jacobian);
-  provide(f_cvc_com_based);
+  enable(f_cvc_com_based);
 
   group1 = parse_group(conf, "group1");
   group2 = parse_group(conf, "group2");
@@ -40,7 +40,7 @@ colvar::angle::angle(cvm::atom const &a1,
   function_type = "angle";
   provide(f_cvc_inv_gradient);
   provide(f_cvc_Jacobian);
-  provide(f_cvc_com_based);
+  enable(f_cvc_com_based);
 
   group1 = new cvm::atom_group(std::vector<cvm::atom>(1, a1));
   group2 = new cvm::atom_group(std::vector<cvm::atom>(1, a2));
@@ -259,7 +259,7 @@ colvar::dihedral::dihedral(std::string const &conf)
   b_periodic = true;
   provide(f_cvc_inv_gradient);
   provide(f_cvc_Jacobian);
-  provide(f_cvc_com_based);
+  enable(f_cvc_com_based);
 
   group1 = parse_group(conf, "group1");
   group2 = parse_group(conf, "group2");
@@ -285,7 +285,7 @@ colvar::dihedral::dihedral(cvm::atom const &a1,
   b_periodic = true;
   provide(f_cvc_inv_gradient);
   provide(f_cvc_Jacobian);
-  provide(f_cvc_com_based);
+  enable(f_cvc_com_based);
 
   b_1site_force = false;
 
diff --git a/lib/colvars/colvarcomp_distances.cpp b/lib/colvars/colvarcomp_distances.cpp
index 7481dd360ba170e327a31977d21c33d47f4507a1..f46270246f5abbfd304568c3d7a0dca6317efbd3 100644
--- a/lib/colvars/colvarcomp_distances.cpp
+++ b/lib/colvars/colvarcomp_distances.cpp
@@ -23,7 +23,7 @@ colvar::distance::distance(std::string const &conf)
   function_type = "distance";
   provide(f_cvc_inv_gradient);
   provide(f_cvc_Jacobian);
-  provide(f_cvc_com_based);
+  enable(f_cvc_com_based);
 
   group1 = parse_group(conf, "group1");
   group2 = parse_group(conf, "group2");
@@ -44,7 +44,7 @@ colvar::distance::distance()
   function_type = "distance";
   provide(f_cvc_inv_gradient);
   provide(f_cvc_Jacobian);
-  provide(f_cvc_com_based);
+  enable(f_cvc_com_based);
   b_no_PBC = false;
   x.type(colvarvalue::type_scalar);
 }
@@ -106,7 +106,7 @@ colvar::distance_vec::distance_vec(std::string const &conf)
   : distance(conf)
 {
   function_type = "distance_vec";
-  provide(f_cvc_com_based);
+  enable(f_cvc_com_based);
   x.type(colvarvalue::type_3vector);
 }
 
@@ -115,7 +115,7 @@ colvar::distance_vec::distance_vec()
   : distance()
 {
   function_type = "distance_vec";
-  provide(f_cvc_com_based);
+  enable(f_cvc_com_based);
   x.type(colvarvalue::type_3vector);
 }
 
@@ -176,7 +176,7 @@ colvar::distance_z::distance_z(std::string const &conf)
   function_type = "distance_z";
   provide(f_cvc_inv_gradient);
   provide(f_cvc_Jacobian);
-  provide(f_cvc_com_based);
+  enable(f_cvc_com_based);
   x.type(colvarvalue::type_scalar);
 
   // TODO detect PBC from MD engine (in simple cases)
@@ -228,7 +228,7 @@ colvar::distance_z::distance_z()
   function_type = "distance_z";
   provide(f_cvc_inv_gradient);
   provide(f_cvc_Jacobian);
-  provide(f_cvc_com_based);
+  enable(f_cvc_com_based);
   x.type(colvarvalue::type_scalar);
 }
 
@@ -372,7 +372,7 @@ colvar::distance_xy::distance_xy(std::string const &conf)
   function_type = "distance_xy";
   provide(f_cvc_inv_gradient);
   provide(f_cvc_Jacobian);
-  provide(f_cvc_com_based);
+  enable(f_cvc_com_based);
   x.type(colvarvalue::type_scalar);
 }
 
@@ -383,7 +383,7 @@ colvar::distance_xy::distance_xy()
   function_type = "distance_xy";
   provide(f_cvc_inv_gradient);
   provide(f_cvc_Jacobian);
-  provide(f_cvc_com_based);
+  enable(f_cvc_com_based);
   x.type(colvarvalue::type_scalar);
 }
 
@@ -479,7 +479,7 @@ colvar::distance_dir::distance_dir(std::string const &conf)
   : distance(conf)
 {
   function_type = "distance_dir";
-  provide(f_cvc_com_based);
+  enable(f_cvc_com_based);
   x.type(colvarvalue::type_unit3vector);
 }
 
@@ -488,7 +488,7 @@ colvar::distance_dir::distance_dir()
   : distance()
 {
   function_type = "distance_dir";
-  provide(f_cvc_com_based);
+  enable(f_cvc_com_based);
   x.type(colvarvalue::type_unit3vector);
 }
 
@@ -529,6 +529,27 @@ void colvar::distance_dir::apply_force(colvarvalue const &force)
 }
 
 
+cvm::real colvar::distance_dir::dist2(colvarvalue const &x1,
+                                      colvarvalue const &x2) const
+{
+  return (x1.rvector_value - x2.rvector_value).norm2();
+}
+
+
+colvarvalue colvar::distance_dir::dist2_lgrad(colvarvalue const &x1,
+                                              colvarvalue const &x2) const
+{
+  return colvarvalue((x1.rvector_value - x2.rvector_value), colvarvalue::type_unit3vector);
+}
+
+
+colvarvalue colvar::distance_dir::dist2_rgrad(colvarvalue const &x1,
+                                              colvarvalue const &x2) const
+{
+  return colvarvalue((x2.rvector_value - x1.rvector_value), colvarvalue::type_unit3vector);
+}
+
+
 
 colvar::distance_inv::distance_inv(std::string const &conf)
   : distance(conf)
diff --git a/lib/colvars/colvardeps.cpp b/lib/colvars/colvardeps.cpp
index e3ccd4ecf5815fcabc0bf7b337a06bf32340c1e7..8252f77e6270d309fdf0db3bad08644ab1c1745b 100644
--- a/lib/colvars/colvardeps.cpp
+++ b/lib/colvars/colvardeps.cpp
@@ -14,9 +14,6 @@
 colvardeps::~colvardeps() {
   size_t i;
 
-  for (i=0; i<feature_states.size(); i++) {
-    if (feature_states[i] != NULL) delete feature_states[i];
-  }
       // Do not delete features if it's static
 //     for (i=0; i<features.size(); i++) {
 //       if (features[i] != NULL) delete features[i];
@@ -34,16 +31,34 @@ colvardeps::~colvardeps() {
 }
 
 
-void colvardeps::provide(int feature_id) {
-  feature_states[feature_id]->available = true;
+void colvardeps::provide(int feature_id, bool truefalse) {
+  feature_states[feature_id].available = truefalse;
+}
+
+
+void colvardeps::set_enabled(int feature_id, bool truefalse) {
+//   if (!is_static(feature_id)) {
+//     cvm::error("Cannot set feature " + features()[feature_id]->description + " statically in " + description + ".\n");
+//     return;
+//   }
+  if (truefalse) {
+    // Resolve dependencies too
+    enable(feature_id);
+  } else {
+    feature_states[feature_id].enabled = false;
+  }
 }
 
 
 bool colvardeps::get_keyval_feature(colvarparse *cvp,
-                        std::string const &conf, char const *key,
-                        int feature_id, bool const &def_value,
-                        colvarparse::Parse_Mode const parse_mode)
+                                    std::string const &conf, char const *key,
+                                    int feature_id, bool const &def_value,
+                                    colvarparse::Parse_Mode const parse_mode)
 {
+  if (!is_user(feature_id)) {
+    cvm::error("Cannot set feature " + features()[feature_id]->description + " from user input in " + description + ".\n");
+    return false;
+  }
   bool value;
   bool const found = cvp->get_keyval(conf, key, value, def_value, parse_mode);
   if (value) enable(feature_id);
@@ -52,19 +67,19 @@ bool colvardeps::get_keyval_feature(colvarparse *cvp,
 
 
 int colvardeps::enable(int feature_id,
-                      bool dry_run /* default: false */,
-                      // dry_run: fail silently, do not enable if available
-                      // flag is passed recursively to deps of this feature
-                      bool toplevel /* default: true */)
-  // toplevel: false if this is called as part of a chain of dependency resolution
-  // this is used to diagnose failed dependencies by displaying the full stack
-  // only the toplevel dependency will throw a fatal error
+                       bool dry_run /* default: false */,
+                       // dry_run: fail silently, do not enable if available
+                       // flag is passed recursively to deps of this feature
+                       bool toplevel /* default: true */)
+// toplevel: false if this is called as part of a chain of dependency resolution
+// this is used to diagnose failed dependencies by displaying the full stack
+// only the toplevel dependency will throw a fatal error
 {
   int res;
   size_t i, j;
   bool ok;
   feature *f = features()[feature_id];
-  feature_state *fs = feature_states[feature_id];
+  feature_state *fs = &feature_states[feature_id];
 
   if (cvm::debug()) {
     cvm::log("DEPS: " + description +
@@ -88,6 +103,14 @@ int colvardeps::enable(int feature_id,
     return COLVARS_ERROR;
   }
 
+  if (!toplevel && !is_dynamic(feature_id)) {
+    if (!dry_run) {
+      cvm::log("Non-dynamic feature : \"" + f->description
+        + "\" in " + description + " may not be enabled as a dependency.\n");
+    }
+    return COLVARS_ERROR;
+  }
+
   // 1) enforce exclusions
   for (i=0; i<f->requires_exclude.size(); i++) {
     feature *g = features()[f->requires_exclude[i]];
@@ -168,9 +191,9 @@ int colvardeps::enable(int feature_id,
       if (res != COLVARS_OK) {
         if (!dry_run) {
           cvm::log("...required by \"" + f->description + "\" in " + description);
-        }
-        if (toplevel) {
-          cvm::error("Error: Failed dependency in " + description + ".");
+          if (toplevel) {
+            cvm::error("Error: Failed dependency in " + description + ".");
+          }
         }
         return res;
       }
@@ -194,9 +217,12 @@ int colvardeps::enable(int feature_id,
 //       // we need refs to parents to walk up the deps tree!
 //       // or refresh
 //     }
+void colvardeps::init_feature(int feature_id, const char *description, feature_type type) {
+  features()[feature_id]->description = description;
+  features()[feature_id]->type = type;
+}
 
-   // Shorthand macros for describing dependencies
-#define f_description(f, d) features()[f]->description = d
+// Shorthand macros for describing dependencies
 #define f_req_self(f, g) features()[f]->requires_self.push_back(g)
 // This macro ensures that exclusions are symmetric
 #define f_req_exclude(f, g) features()[f]->requires_exclude.push_back(g); \
@@ -216,35 +242,31 @@ void colvardeps::init_cvb_requires() {
     for (i = 0; i < f_cvb_ntot; i++) {
       features().push_back(new feature);
     }
-  }
 
-  f_description(f_cvb_active, "active");
-  f_req_children(f_cvb_active, f_cv_active);
+    init_feature(f_cvb_active, "active", f_type_dynamic);
+    f_req_children(f_cvb_active, f_cv_active);
+
+    init_feature(f_cvb_apply_force, "apply force", f_type_user);
+    f_req_children(f_cvb_apply_force, f_cv_gradient);
 
-  f_description(f_cvb_apply_force, "apply force");
-  f_req_children(f_cvb_apply_force, f_cv_gradient);
+    init_feature(f_cvb_get_total_force, "obtain total force");
+    f_req_children(f_cvb_get_total_force, f_cv_total_force);
 
-  f_description(f_cvb_get_total_force, "obtain total force");
-  f_req_children(f_cvb_get_total_force, f_cv_total_force);
+    init_feature(f_cvb_history_dependent, "history-dependent", f_type_static);
 
-  f_description(f_cvb_history_dependent, "history-dependent");
+    init_feature(f_cvb_scalar_variables, "require scalar variables", f_type_static);
+    f_req_children(f_cvb_scalar_variables, f_cv_scalar);
 
-  f_description(f_cvb_scalar_variables, "require scalar variables");
-  f_req_children(f_cvb_scalar_variables, f_cv_scalar);
+    init_feature(f_cvb_calc_pmf, "calculate a PMF", f_type_static);
+  }
 
   // Initialize feature_states for each instance
   feature_states.reserve(f_cvb_ntot);
   for (i = 0; i < f_cvb_ntot; i++) {
-    feature_states.push_back(new feature_state(true, false));
+    feature_states.push_back(feature_state(true, false));
     // Most features are available, so we set them so
     // and list exceptions below
   }
-
-  // some biases are not history-dependent
-  feature_states[f_cvb_history_dependent]->available = false;
-
-  // by default, biases should work with vector variables, too
-  feature_states[f_cvb_scalar_variables]->available = false;
 }
 
 
@@ -255,117 +277,111 @@ void colvardeps::init_cv_requires() {
       features().push_back(new feature);
     }
 
-    f_description(f_cv_active, "active");
+    init_feature(f_cv_active, "active", f_type_dynamic);
     f_req_children(f_cv_active, f_cvc_active);
     // Colvars must be either a linear combination, or scalar (and polynomial) or scripted
     f_req_alt3(f_cv_active, f_cv_scalar, f_cv_linear, f_cv_scripted);
 
-    f_description(f_cv_gradient, "gradient");
+    init_feature(f_cv_gradient, "gradient", f_type_dynamic);
     f_req_children(f_cv_gradient, f_cvc_gradient);
 
-    f_description(f_cv_collect_gradient, "collect gradient");
+    init_feature(f_cv_collect_gradient, "collect gradient", f_type_dynamic);
     f_req_self(f_cv_collect_gradient, f_cv_gradient);
     f_req_self(f_cv_collect_gradient, f_cv_scalar);
 
-    f_description(f_cv_fdiff_velocity, "fdiff_velocity");
+    init_feature(f_cv_fdiff_velocity, "fdiff_velocity", f_type_dynamic);
 
     // System force: either trivial (spring force); through extended Lagrangian, or calculated explicitly
-    f_description(f_cv_total_force, "total force");
+    init_feature(f_cv_total_force, "total force", f_type_dynamic);
     f_req_alt2(f_cv_total_force, f_cv_extended_Lagrangian, f_cv_total_force_calc);
 
     // Deps for explicit total force calculation
-    f_description(f_cv_total_force_calc, "total force calculation");
+    init_feature(f_cv_total_force_calc, "total force calculation", f_type_dynamic);
     f_req_self(f_cv_total_force_calc, f_cv_scalar);
     f_req_self(f_cv_total_force_calc, f_cv_linear);
     f_req_children(f_cv_total_force_calc, f_cvc_inv_gradient);
     f_req_self(f_cv_total_force_calc, f_cv_Jacobian);
 
-    f_description(f_cv_Jacobian, "Jacobian derivative");
+    init_feature(f_cv_Jacobian, "Jacobian derivative", f_type_dynamic);
     f_req_self(f_cv_Jacobian, f_cv_scalar);
     f_req_self(f_cv_Jacobian, f_cv_linear);
     f_req_children(f_cv_Jacobian, f_cvc_Jacobian);
 
-    f_description(f_cv_hide_Jacobian, "hide Jacobian force");
+    init_feature(f_cv_hide_Jacobian, "hide Jacobian force", f_type_user);
     f_req_self(f_cv_hide_Jacobian, f_cv_Jacobian); // can only hide if calculated
 
-    f_description(f_cv_extended_Lagrangian, "extended Lagrangian");
+    init_feature(f_cv_extended_Lagrangian, "extended Lagrangian", f_type_user);
+    f_req_self(f_cv_extended_Lagrangian, f_cv_scalar);
+    f_req_self(f_cv_extended_Lagrangian, f_cv_gradient);
 
-    f_description(f_cv_Langevin, "Langevin dynamics");
+    init_feature(f_cv_Langevin, "Langevin dynamics", f_type_user);
     f_req_self(f_cv_Langevin, f_cv_extended_Lagrangian);
 
-    f_description(f_cv_linear, "linear");
+    init_feature(f_cv_linear, "linear", f_type_static);
 
-    f_description(f_cv_scalar, "scalar");
+    init_feature(f_cv_scalar, "scalar", f_type_static);
 
-    f_description(f_cv_output_energy, "output energy");
+    init_feature(f_cv_output_energy, "output energy", f_type_user);
 
-    f_description(f_cv_output_value, "output value");
+    init_feature(f_cv_output_value, "output value", f_type_user);
 
-    f_description(f_cv_output_velocity, "output velocity");
+    init_feature(f_cv_output_velocity, "output velocity", f_type_user);
     f_req_self(f_cv_output_velocity, f_cv_fdiff_velocity);
 
-    f_description(f_cv_output_applied_force, "output applied force");
+    init_feature(f_cv_output_applied_force, "output applied force", f_type_user);
 
-    f_description(f_cv_output_total_force, "output total force");
+    init_feature(f_cv_output_total_force, "output total force", f_type_user);
     f_req_self(f_cv_output_total_force, f_cv_total_force);
 
-    f_description(f_cv_subtract_applied_force, "subtract applied force from total force");
+    init_feature(f_cv_subtract_applied_force, "subtract applied force from total force", f_type_user);
     f_req_self(f_cv_subtract_applied_force, f_cv_total_force);
 
-    f_description(f_cv_lower_boundary, "lower boundary");
+    init_feature(f_cv_lower_boundary, "lower boundary", f_type_user);
     f_req_self(f_cv_lower_boundary, f_cv_scalar);
 
-    f_description(f_cv_upper_boundary, "upper boundary");
+    init_feature(f_cv_upper_boundary, "upper boundary", f_type_user);
     f_req_self(f_cv_upper_boundary, f_cv_scalar);
 
-    f_description(f_cv_grid, "grid");
+    init_feature(f_cv_grid, "grid", f_type_user);
     f_req_self(f_cv_grid, f_cv_lower_boundary);
     f_req_self(f_cv_grid, f_cv_upper_boundary);
 
-    f_description(f_cv_lower_wall, "lower wall");
-    f_req_self(f_cv_lower_wall, f_cv_lower_boundary);
-    f_req_self(f_cv_lower_wall, f_cv_gradient);
-
-    f_description(f_cv_upper_wall, "upper wall");
-    f_req_self(f_cv_upper_wall, f_cv_upper_boundary);
-    f_req_self(f_cv_upper_wall, f_cv_gradient);
+    init_feature(f_cv_runave, "running average", f_type_user);
 
-    f_description(f_cv_runave, "running average");
+    init_feature(f_cv_corrfunc, "correlation function", f_type_user);
 
-    f_description(f_cv_corrfunc, "correlation function");
-
-    // The features below are set programmatically
-    f_description(f_cv_scripted, "scripted");
-    f_description(f_cv_periodic, "periodic");
+    init_feature(f_cv_scripted, "scripted", f_type_static);
+    init_feature(f_cv_periodic, "periodic", f_type_static);
     f_req_self(f_cv_periodic, f_cv_homogeneous);
-    f_description(f_cv_scalar, "scalar");
-    f_description(f_cv_linear, "linear");
-    f_description(f_cv_homogeneous, "homogeneous");
+    init_feature(f_cv_scalar, "scalar", f_type_static);
+    init_feature(f_cv_linear, "linear", f_type_static);
+    init_feature(f_cv_homogeneous, "homogeneous", f_type_static);
   }
 
   // Initialize feature_states for each instance
   feature_states.reserve(f_cv_ntot);
   for (i = 0; i < f_cv_ntot; i++) {
-    feature_states.push_back(new feature_state(true, false));
+    feature_states.push_back(feature_state(true, false));
     // Most features are available, so we set them so
     // and list exceptions below
    }
 
-  // properties that may NOT be enabled as a dependency
-  int unavailable_deps[] = {
-    f_cv_lower_boundary,
-    f_cv_upper_boundary,
-    f_cv_extended_Lagrangian,
-    f_cv_Langevin,
-    f_cv_scripted,
-    f_cv_periodic,
-    f_cv_scalar,
-    f_cv_linear,
-    f_cv_homogeneous
-  };
-  for (i = 0; i < sizeof(unavailable_deps) / sizeof(unavailable_deps[0]); i++) {
-    feature_states[unavailable_deps[i]]->available = false;
-  }
+//   // properties that may NOT be enabled as a dependency
+//   // This will be deprecated by feature types
+//   int unavailable_deps[] = {
+//     f_cv_lower_boundary,
+//     f_cv_upper_boundary,
+//     f_cv_extended_Lagrangian,
+//     f_cv_Langevin,
+//     f_cv_scripted,
+//     f_cv_periodic,
+//     f_cv_scalar,
+//     f_cv_linear,
+//     f_cv_homogeneous
+//   };
+//   for (i = 0; i < sizeof(unavailable_deps) / sizeof(unavailable_deps[0]); i++) {
+//     feature_states[unavailable_deps[i]].available = false;
+//   }
 }
 
 
@@ -377,34 +393,34 @@ void colvardeps::init_cvc_requires() {
       features().push_back(new feature);
     }
 
-    f_description(f_cvc_active, "active");
+    init_feature(f_cvc_active, "active", f_type_dynamic);
 //     The dependency below may become useful if we use dynamic atom groups
 //     f_req_children(f_cvc_active, f_ag_active);
 
-    f_description(f_cvc_scalar, "scalar");
+    init_feature(f_cvc_scalar, "scalar", f_type_static);
 
-    f_description(f_cvc_gradient, "gradient");
+    init_feature(f_cvc_gradient, "gradient", f_type_dynamic);
 
-    f_description(f_cvc_inv_gradient, "inverse gradient");
+    init_feature(f_cvc_inv_gradient, "inverse gradient", f_type_dynamic);
     f_req_self(f_cvc_inv_gradient, f_cvc_gradient);
 
-    f_description(f_cvc_debug_gradient, "debug gradient");
+    init_feature(f_cvc_debug_gradient, "debug gradient", f_type_user);
     f_req_self(f_cvc_debug_gradient, f_cvc_gradient);
 
-    f_description(f_cvc_Jacobian, "Jacobian derivative");
+    init_feature(f_cvc_Jacobian, "Jacobian derivative", f_type_dynamic);
     f_req_self(f_cvc_Jacobian, f_cvc_inv_gradient);
 
-    f_description(f_cvc_com_based, "depends on group centers of mass");
+    init_feature(f_cvc_com_based, "depends on group centers of mass", f_type_static);
 
     // Compute total force on first site only to avoid unwanted
     // coupling to other colvars (see e.g. Ciccotti et al., 2005)
-    f_description(f_cvc_one_site_total_force, "compute total collective force only from one group center");
+    init_feature(f_cvc_one_site_total_force, "compute total collective force only from one group center", f_type_user);
     f_req_self(f_cvc_one_site_total_force, f_cvc_com_based);
 
-    f_description(f_cvc_scalable, "scalable calculation");
+    init_feature(f_cvc_scalable, "scalable calculation", f_type_static);
     f_req_self(f_cvc_scalable, f_cvc_scalable_com);
 
-    f_description(f_cvc_scalable_com, "scalable calculation of centers of mass");
+    init_feature(f_cvc_scalable_com, "scalable calculation of centers of mass", f_type_static);
     f_req_self(f_cvc_scalable_com, f_cvc_com_based);
 
 
@@ -414,23 +430,25 @@ void colvardeps::init_cvc_requires() {
   }
 
   // Initialize feature_states for each instance
-  // default as unavailable, not enabled
+  // default as available, not enabled
+  // except dynamic features which default as unavailable
   feature_states.reserve(f_cvc_ntot);
   for (i = 0; i < colvardeps::f_cvc_ntot; i++) {
-    feature_states.push_back(new feature_state(false, false));
+    bool avail = is_dynamic(i) ? false : true;
+    feature_states.push_back(feature_state(avail, false));
   }
 
   // Features that are implemented by all cvcs by default
   // Each cvc specifies what other features are available
-  feature_states[f_cvc_active]->available = true;
-  feature_states[f_cvc_gradient]->available = true;
+  feature_states[f_cvc_active].available = true;
+  feature_states[f_cvc_gradient].available = true;
 
   // Features that are implemented by default if their requirements are
-  feature_states[f_cvc_one_site_total_force]->available = true;
+  feature_states[f_cvc_one_site_total_force].available = true;
 
   // Features That are implemented only for certain simulation engine configurations
-  feature_states[f_cvc_scalable_com]->available = (cvm::proxy->scalable_group_coms() == COLVARS_OK);
-  feature_states[f_cvc_scalable]->available = feature_states[f_cvc_scalable_com]->available;
+  feature_states[f_cvc_scalable_com].available = (cvm::proxy->scalable_group_coms() == COLVARS_OK);
+  feature_states[f_cvc_scalable].available = feature_states[f_cvc_scalable_com].available;
 }
 
 
@@ -442,21 +460,21 @@ void colvardeps::init_ag_requires() {
       features().push_back(new feature);
     }
 
-    f_description(f_ag_active, "active");
-    f_description(f_ag_center, "translational fit");
-    f_description(f_ag_rotate, "rotational fit");
-    f_description(f_ag_fitting_group, "reference positions group");
-    f_description(f_ag_fit_gradient_group, "fit gradient for main group");
-    f_description(f_ag_fit_gradient_ref, "fit gradient for reference group");
-    f_description(f_ag_atom_forces, "atomic forces");
+    init_feature(f_ag_active, "active", f_type_dynamic);
+    init_feature(f_ag_center, "translational fit", f_type_static);
+    init_feature(f_ag_rotate, "rotational fit", f_type_static);
+    init_feature(f_ag_fitting_group, "reference positions group", f_type_static);
+    init_feature(f_ag_fit_gradient_group, "fit gradient for main group", f_type_static);
+    init_feature(f_ag_fit_gradient_ref, "fit gradient for reference group", f_type_static);
+    init_feature(f_ag_atom_forces, "atomic forces", f_type_dynamic);
 
     // parallel calculation implies that we have at least a scalable center of mass,
     // but f_ag_scalable is kept as a separate feature to deal with future dependencies
-    f_description(f_ag_scalable, "scalable group calculation");
-    f_description(f_ag_scalable_com, "scalable group center of mass calculation");
+    init_feature(f_ag_scalable, "scalable group calculation", f_type_static);
+    init_feature(f_ag_scalable_com, "scalable group center of mass calculation", f_type_static);
     f_req_self(f_ag_scalable, f_ag_scalable_com);
 
-//     f_description(f_ag_min_msd_fit, "minimum MSD fit")
+//     init_feature(f_ag_min_msd_fit, "minimum MSD fit")
 //     f_req_self(f_ag_min_msd_fit, f_ag_center)
 //     f_req_self(f_ag_min_msd_fit, f_ag_rotate)
 //     f_req_exclude(f_ag_min_msd_fit, f_ag_fitting_group)
@@ -466,15 +484,15 @@ void colvardeps::init_ag_requires() {
   // default as unavailable, not enabled
   feature_states.reserve(f_ag_ntot);
   for (i = 0; i < colvardeps::f_ag_ntot; i++) {
-    feature_states.push_back(new feature_state(false, false));
+    feature_states.push_back(feature_state(false, false));
   }
 
   // Features that are implemented (or not) by all atom groups
-  feature_states[f_ag_active]->available = true;
+  feature_states[f_ag_active].available = true;
   // f_ag_scalable_com is provided by the CVC iff it is COM-based
-  feature_states[f_ag_scalable_com]->available = false;
+  feature_states[f_ag_scalable_com].available = false;
   // TODO make f_ag_scalable depend on f_ag_scalable_com (or something else)
-  feature_states[f_ag_scalable]->available = true;
+  feature_states[f_ag_scalable].available = true;
 }
 
 
@@ -482,7 +500,7 @@ void colvardeps::print_state() {
   size_t i;
   cvm::log("Enabled features of " + description);
   for (i = 0; i < feature_states.size(); i++) {
-    if (feature_states[i]->enabled)
+    if (feature_states[i].enabled)
       cvm::log("- " + features()[i]->description);
   }
   for (i=0; i<children.size(); i++) {
diff --git a/lib/colvars/colvardeps.h b/lib/colvars/colvardeps.h
index 4ef27ded85a5b2a1bbcaaad54933a9294e11c51a..fd07cb645715eac8a8e367d654b6fbd06735a633 100644
--- a/lib/colvars/colvardeps.h
+++ b/lib/colvars/colvardeps.h
@@ -13,17 +13,16 @@
 #include "colvarmodule.h"
 #include "colvarparse.h"
 
-/// Parent class for a member object of a bias, cv or cvc etc. containing dependencies
-/// (features) and handling dependency resolution
-
-// Some features like colvar::f_linear have no dependencies, require() doesn't enable anything but fails if unavailable
-// Policy: those features are unavailable at all times
-// Other features are under user control
-// They are unavailable unless requested by the user, then they may be enabled subject to
-// satisfied dependencies
-
-// It seems important to have available default to false (for safety) and enabled to false (for efficiency)
-
+/// \brief Parent class for a member object of a bias, cv or cvc etc. containing features and
+/// their dependencies, and handling dependency resolution
+///
+/// There are 3 kinds of features:
+/// 1. Dynamic features are under the control of the dependency resolution
+/// system. They may be enabled or disabled depending on dependencies.
+/// 2. User features may be enabled based on user input (they may trigger a failure upon dependency resolution, though)
+/// 3. Static features are static properties of the object, determined
+///   programatically at initialization time.
+///
 class colvardeps {
 public:
 
@@ -39,12 +38,7 @@ public:
     feature_state(bool a, bool e)
     : available(a), enabled(e) {}
 
-    /// Available means: supported, subject to dependencies as listed,
-    /// MAY BE ENABLED AS A RESULT OF DEPENDENCY SOLVING
-    /// Remains false for passive flags that are set based on other object properties,
-    /// eg. f_cv_linear
-    /// Is set to true upon user request for features that are implemented by the user
-    /// or under his/her direct control, e.g. f_cv_scripted or f_cv_extended_Lagrangian
+    /// Feature may be enabled, subject to possible dependencies
     bool available;
     /// Currently enabled - this flag is subject to change dynamically
     /// TODO consider implications for dependency solving: anyone who disables
@@ -53,8 +47,22 @@ public:
     // bool enabledOnce; // this should trigger an update when object is evaluated
   };
 
-  /// List of the state of all features
-  std::vector<feature_state *> feature_states;
+
+private:
+  /// List of the states of all features
+  std::vector<feature_state> feature_states;
+
+  /// Enum of possible feature types
+  enum feature_type {
+    f_type_not_set,
+    f_type_dynamic,
+    f_type_user,
+    f_type_static
+  };
+
+public:
+  /// Pair a numerical feature ID with a description and type
+  void init_feature(int feature_id, const char *description, feature_type type = f_type_not_set);
 
   /// Describes a feature and its dependecies
   /// used in a static array within each subclass
@@ -83,8 +91,18 @@ public:
 
     // features that this feature requires in children
     std::vector<int> requires_children;
+
+    inline bool is_dynamic() { return type == f_type_dynamic; }
+    inline bool is_static() { return type == f_type_static; }
+    inline bool is_user() { return type == f_type_user; }
+    /// Type of this feature, from the enum feature_type
+    feature_type type;
   };
 
+  inline bool is_dynamic(int id) { return features()[id]->type == f_type_dynamic; }
+  inline bool is_static(int id) { return features()[id]->type == f_type_static; }
+  inline bool is_user(int id) { return features()[id]->type == f_type_user; }
+
   // Accessor to array of all features with deps, static in most derived classes
   // Subclasses with dynamic dependency trees may override this
   // with a non-static array
@@ -100,9 +118,8 @@ public:
   /// (useful for cvcs because their children are member objects)
   void remove_all_children();
 
-
-
 private:
+
   // pointers to objects this object depends on
   // list should be maintained by any code that modifies the object
   // this could be secured by making lists of colvars / cvcs / atom groups private and modified through accessor functions
@@ -130,16 +147,28 @@ public:
   // Checks whether given feature is enabled
   // Defaults to querying f_*_active
   inline bool is_enabled(int f = f_cv_active) const {
-    return feature_states[f]->enabled;
+    return feature_states[f].enabled;
   }
 
   // Checks whether given feature is available
   // Defaults to querying f_*_active
   inline bool is_available(int f = f_cv_active) const {
-    return feature_states[f]->available;
+    return feature_states[f].available;
   }
 
-  void provide(int feature_id); // set the feature's flag to available in local object
+  /// Set the feature's available flag, without checking
+  /// To be used for dynamic properties
+  /// dependencies will be checked by enable()
+  void provide(int feature_id, bool truefalse = true);
+
+  /// Set the feature's enabled flag, without dependency check or resolution
+  /// To be used for static properties only
+  /// Checking for availability is up to the caller
+  void set_enabled(int feature_id, bool truefalse = true);
+
+protected:
+
+
 
   /// Parse a keyword and enable a feature accordingly
   bool get_keyval_feature(colvarparse *cvp,
@@ -147,6 +176,8 @@ public:
                           int feature_id, bool const &def_value,
                           colvarparse::Parse_Mode const parse_mode = colvarparse::parse_normal);
 
+public:
+
   int enable(int f, bool dry_run = false, bool toplevel = true);  // enable a feature and recursively solve its dependencies
   // dry_run is set to true to recursively test if a feature is available, without enabling it
 //     int disable(int f);
@@ -165,6 +196,7 @@ public:
     f_cvb_get_total_force, // requires total forces
     f_cvb_history_dependent, // depends on simulation history
     f_cvb_scalar_variables, // requires scalar colvars
+    f_cvb_calc_pmf, // whether this bias will compute a PMF
     f_cvb_ntot
   };
 
@@ -216,12 +248,6 @@ public:
     /// be used by the biases or in analysis (needs lower and upper
     /// boundary)
     f_cv_grid,
-    /// \brief Apply a restraining potential (|x-xb|^2) to the colvar
-    /// when it goes below the lower wall
-    f_cv_lower_wall,
-    /// \brief Apply a restraining potential (|x-xb|^2) to the colvar
-    /// when it goes above the upper wall
-    f_cv_upper_wall,
     /// \brief Compute running average
     f_cv_runave,
     /// \brief Compute time correlation function
diff --git a/lib/colvars/colvarmodule.cpp b/lib/colvars/colvarmodule.cpp
index b9a435152b2cc56b50987b08adb7cf2bd75e4362..10cd3c0e4737905dabb664bcaa0d159608fd16f7 100644
--- a/lib/colvars/colvarmodule.cpp
+++ b/lib/colvars/colvarmodule.cpp
@@ -36,6 +36,9 @@ colvarmodule::colvarmodule(colvarproxy *proxy_in)
                "variable module twice.\n");
     return;
   }
+
+  depth_s = 0;
+
   cvm::log(cvm::line_marker);
   cvm::log("Initializing the collective variables module, version "+
            cvm::to_str(COLVARS_VERSION)+".\n");
@@ -71,6 +74,48 @@ colvarmodule::colvarmodule(colvarproxy *proxy_in)
 }
 
 
+colvarmodule * colvarmodule::main()
+{
+  return proxy->colvars;
+}
+
+
+std::vector<colvar *> *colvarmodule::variables()
+{
+  return &colvars;
+}
+
+
+std::vector<colvar *> *colvarmodule::variables_active()
+{
+  return &colvars_active;
+}
+
+
+std::vector<colvar *> *colvarmodule::variables_active_smp()
+{
+  return &colvars_smp;
+}
+
+
+std::vector<int> *colvarmodule::variables_active_smp_items()
+{
+  return &colvars_smp_items;
+}
+
+
+std::vector<colvarbias *> *colvarmodule::biases_active()
+{
+  return &(biases_active_);
+}
+
+
+size_t colvarmodule::size() const
+{
+  return colvars.size() + biases.size();
+}
+
+
 int colvarmodule::read_config_file(char const  *config_filename)
 {
   cvm::log(cvm::line_marker);
@@ -118,8 +163,29 @@ int colvarmodule::read_config_string(std::string const &config_str)
 }
 
 
+std::istream & colvarmodule::getline(std::istream &is, std::string &line)
+{
+  std::string l;
+  if (std::getline(is, l)) {
+    size_t const sz = l.size();
+    if (sz > 0) {
+      if (l[sz-1] == '\r' ) {
+        line = l.substr(0, sz-1);
+      } else {
+        line = l;
+      }
+    } else {
+      line.clear();
+    }
+  }
+  return is;
+}
+
+
 int colvarmodule::parse_config(std::string &conf)
 {
+  extra_conf.clear();
+
   // parse global options
   if (catch_input_errors(parse_global_params(conf))) {
     return get_error();
@@ -140,6 +206,15 @@ int colvarmodule::parse_config(std::string &conf)
     return get_error();
   }
 
+  if (extra_conf.size()) {
+    catch_input_errors(parse_global_params(extra_conf));
+    catch_input_errors(parse_colvars(extra_conf));
+    catch_input_errors(parse_biases(extra_conf));
+    parse->check_keywords(extra_conf, "colvarmodule");
+    extra_conf.clear();
+    if (get_error() != COLVARS_OK) return get_error();
+  }
+
   cvm::log(cvm::line_marker);
   cvm::log("Collective variables module (re)initialized.\n");
   cvm::log(cvm::line_marker);
@@ -156,11 +231,20 @@ int colvarmodule::parse_config(std::string &conf)
 }
 
 
+int colvarmodule::append_new_config(std::string const &new_conf)
+{
+  extra_conf += new_conf;
+  return COLVARS_OK;
+}
+
+
 int colvarmodule::parse_global_params(std::string const &conf)
 {
+  colvarmodule *cvm = cvm::main();
+
   std::string index_file_name;
   if (parse->get_keyval(conf, "indexFile", index_file_name)) {
-    read_index_file(index_file_name.c_str());
+    cvm->read_index_file(index_file_name.c_str());
   }
 
   if (parse->get_keyval(conf, "smp", proxy->b_smp_active, proxy->b_smp_active)) {
@@ -216,8 +300,8 @@ int colvarmodule::parse_colvars(std::string const &conf)
     if (colvar_conf.size()) {
       cvm::log(cvm::line_marker);
       cvm::increase_depth();
-      colvars.push_back(new colvar(colvar_conf));
-      if (cvm::get_error() ||
+      colvars.push_back(new colvar());
+      if (((colvars.back())->init(colvar_conf) != COLVARS_OK) ||
           ((colvars.back())->check_keywords(colvar_conf, "colvar") != COLVARS_OK)) {
         cvm::log("Error while constructing colvar number " +
                  cvm::to_str(colvars.size()) + " : deleting.");
@@ -262,8 +346,7 @@ bool colvarmodule::check_new_bias(std::string &conf, char const *key)
 
 template <class bias_type>
 int colvarmodule::parse_biases_type(std::string const &conf,
-                                    char const *keyword,
-                                    size_t &bias_count)
+                                    char const *keyword)
 {
   std::string bias_conf = "";
   size_t conf_saved_pos = 0;
@@ -277,7 +360,6 @@ int colvarmodule::parse_biases_type(std::string const &conf,
         return COLVARS_ERROR;
       }
       cvm::decrease_depth();
-      bias_count++;
     } else {
       cvm::error("Error: keyword \""+std::string(keyword)+"\" found without configuration.\n",
                  INPUT_ERROR);
@@ -295,28 +377,28 @@ int colvarmodule::parse_biases(std::string const &conf)
     cvm::log("Initializing the collective variables biases.\n");
 
   /// initialize ABF instances
-  parse_biases_type<colvarbias_abf>(conf, "abf", n_abf_biases);
+  parse_biases_type<colvarbias_abf>(conf, "abf");
 
   /// initialize adaptive linear biases
-  parse_biases_type<colvarbias_alb>(conf, "ALB", n_rest_biases);
+  parse_biases_type<colvarbias_alb>(conf, "ALB");
 
   /// initialize harmonic restraints
-  parse_biases_type<colvarbias_restraint_harmonic>(conf, "harmonic", n_rest_biases);
+  parse_biases_type<colvarbias_restraint_harmonic>(conf, "harmonic");
 
   /// initialize harmonic walls restraints
-  parse_biases_type<colvarbias_restraint_harmonic_walls>(conf, "harmonicWalls", n_rest_biases);
+  parse_biases_type<colvarbias_restraint_harmonic_walls>(conf, "harmonicWalls");
 
   /// initialize histograms
-  parse_biases_type<colvarbias_histogram>(conf, "histogram", n_histo_biases);
+  parse_biases_type<colvarbias_histogram>(conf, "histogram");
 
   /// initialize histogram restraints
-  parse_biases_type<colvarbias_restraint_histogram>(conf, "histogramRestraint", n_rest_biases);
+  parse_biases_type<colvarbias_restraint_histogram>(conf, "histogramRestraint");
 
   /// initialize linear restraints
-  parse_biases_type<colvarbias_restraint_linear>(conf, "linear", n_rest_biases);
+  parse_biases_type<colvarbias_restraint_linear>(conf, "linear");
 
   /// initialize metadynamics instances
-  parse_biases_type<colvarbias_meta>(conf, "metadynamics", n_meta_biases);
+  parse_biases_type<colvarbias_meta>(conf, "metadynamics");
 
   if (use_scripted_forces) {
     cvm::log(cvm::line_marker);
@@ -363,6 +445,36 @@ int colvarmodule::parse_biases(std::string const &conf)
 }
 
 
+int colvarmodule::num_biases_feature(int feature_id)
+{
+  colvarmodule *cv = cvm::main();
+  size_t n = 0;
+  for (std::vector<colvarbias *>::iterator bi = cv->biases.begin();
+       bi != cv->biases.end();
+       bi++) {
+    if ((*bi)->is_enabled(feature_id)) {
+      n++;
+    }
+  }
+  return n;
+}
+
+
+int colvarmodule::num_biases_type(std::string const &type)
+{
+  colvarmodule *cv = cvm::main();
+  size_t n = 0;
+  for (std::vector<colvarbias *>::iterator bi = cv->biases.begin();
+       bi != cv->biases.end();
+       bi++) {
+    if ((*bi)->bias_type == type) {
+      n++;
+    }
+  }
+  return n;
+}
+
+
 int colvarmodule::catch_input_errors(int result)
 {
   if (result != COLVARS_OK || get_error()) {
@@ -376,8 +488,9 @@ int colvarmodule::catch_input_errors(int result)
 
 
 colvarbias * colvarmodule::bias_by_name(std::string const &name) {
-  for (std::vector<colvarbias *>::iterator bi = biases.begin();
-       bi != biases.end();
+  colvarmodule *cv = cvm::main();
+  for (std::vector<colvarbias *>::iterator bi = cv->biases.begin();
+       bi != cv->biases.end();
        bi++) {
     if ((*bi)->name == name) {
       return (*bi);
@@ -388,8 +501,9 @@ colvarbias * colvarmodule::bias_by_name(std::string const &name) {
 
 
 colvar *colvarmodule::colvar_by_name(std::string const &name) {
-  for (std::vector<colvar *>::iterator cvi = colvars.begin();
-       cvi != colvars.end();
+  colvarmodule *cv = cvm::main();
+  for (std::vector<colvar *>::iterator cvi = cv->colvars.begin();
+       cvi != cv->colvars.end();
        cvi++) {
     if ((*cvi)->name == name) {
       return (*cvi);
@@ -555,9 +669,15 @@ int colvarmodule::calc_colvars()
   int error_code = COLVARS_OK;
   std::vector<colvar *>::iterator cvi;
 
-  // Determine which colvars are active at this time step
-  for (cvi = colvars.begin(); cvi != colvars.end(); cvi++) {
-    (*cvi)->feature_states[colvardeps::f_cv_active]->enabled = (step_absolute() % (*cvi)->get_time_step_factor() == 0);
+  // Determine which colvars are active at this iteration
+  variables_active()->resize(0);
+  variables_active()->reserve(variables()->size());
+  for (cvi = variables()->begin(); cvi != variables()->end(); cvi++) {
+    // This is a dynamic feature - the next call should be to enable()
+    // or disable() when dynamic dependency resolution is fully implemented
+    (*cvi)->set_enabled(colvardeps::f_cv_active,
+      step_absolute() % (*cvi)->get_time_step_factor() == 0);
+    variables_active()->push_back(*cvi);
   }
 
   // if SMP support is available, split up the work
@@ -565,25 +685,24 @@ int colvarmodule::calc_colvars()
 
     // first, calculate how much work (currently, how many active CVCs) each colvar has
 
-    colvars_smp.resize(0);
-    colvars_smp_items.resize(0);
+    variables_active_smp()->resize(0);
+    variables_active_smp_items()->resize(0);
 
-    colvars_smp.reserve(colvars.size());
-    colvars_smp_items.reserve(colvars.size());
+    variables_active_smp()->reserve(variables_active()->size());
+    variables_active_smp_items()->reserve(variables_active()->size());
 
     // set up a vector containing all components
     cvm::increase_depth();
-    for (cvi = colvars.begin(); cvi != colvars.end(); cvi++) {
+    for (cvi = variables_active()->begin(); cvi != variables_active()->end(); cvi++) {
 
-      if (!(*cvi)->is_enabled()) continue;
       error_code |= (*cvi)->update_cvc_flags();
 
       size_t num_items = (*cvi)->num_active_cvcs();
-      colvars_smp.reserve(colvars_smp.size() + num_items);
-      colvars_smp_items.reserve(colvars_smp_items.size() + num_items);
+      variables_active_smp()->reserve(variables_active_smp()->size() + num_items);
+      variables_active_smp_items()->reserve(variables_active_smp_items()->size() + num_items);
       for (size_t icvc = 0; icvc < num_items; icvc++) {
-        colvars_smp.push_back(*cvi);
-        colvars_smp_items.push_back(icvc);
+        variables_active_smp()->push_back(*cvi);
+        variables_active_smp_items()->push_back(icvc);
       }
     }
     cvm::decrease_depth();
@@ -592,8 +711,7 @@ int colvarmodule::calc_colvars()
     error_code |= proxy->smp_colvars_loop();
 
     cvm::increase_depth();
-    for (cvi = colvars.begin(); cvi != colvars.end(); cvi++) {
-      if (!(*cvi)->is_enabled()) continue;
+    for (cvi = variables_active()->begin(); cvi != variables_active()->end(); cvi++) {
       error_code |= (*cvi)->collect_cvc_data();
     }
     cvm::decrease_depth();
@@ -602,8 +720,7 @@ int colvarmodule::calc_colvars()
 
     // calculate colvars one at a time
     cvm::increase_depth();
-    for (cvi = colvars.begin(); cvi != colvars.end(); cvi++) {
-      if (!(*cvi)->is_enabled()) continue;
+    for (cvi = variables_active()->begin(); cvi != variables_active()->end(); cvi++) {
       error_code |= (*cvi)->calc();
       if (cvm::get_error()) {
         return COLVARS_ERROR;
@@ -627,6 +744,18 @@ int colvarmodule::calc_biases()
   std::vector<colvarbias *>::iterator bi;
   int error_code = COLVARS_OK;
 
+  // Total bias energy is reset before calling scripted biases
+  total_bias_energy = 0.0;
+
+  // update the list of active biases
+  biases_active()->resize(0);
+  biases_active()->reserve(biases.size());
+  for (bi = biases.begin(); bi != biases.end(); bi++) {
+    if ((*bi)->is_enabled()) {
+      biases_active()->push_back(*bi);
+    }
+  }
+
   // if SMP support is available, split up the work
   if (proxy->smp_enabled() == COLVARS_OK) {
 
@@ -645,7 +774,7 @@ int colvarmodule::calc_biases()
     }
 
     cvm::increase_depth();
-    for (bi = biases.begin(); bi != biases.end(); bi++) {
+    for (bi = biases_active()->begin(); bi != biases_active()->end(); bi++) {
       error_code |= (*bi)->update();
       if (cvm::get_error()) {
         return error_code;
@@ -654,12 +783,10 @@ int colvarmodule::calc_biases()
     cvm::decrease_depth();
   }
 
-  cvm::real total_bias_energy = 0.0;
-  for (bi = biases.begin(); bi != biases.end(); bi++) {
+  for (bi = biases_active()->begin(); bi != biases_active()->end(); bi++) {
     total_bias_energy += (*bi)->get_energy();
   }
 
-  proxy->add_energy(total_bias_energy);
   return (cvm::get_error() ? COLVARS_ERROR : COLVARS_OK);
 }
 
@@ -675,7 +802,7 @@ int colvarmodule::update_colvar_forces()
   if (cvm::debug() && biases.size())
     cvm::log("Collecting forces from all biases.\n");
   cvm::increase_depth();
-  for (bi = biases.begin(); bi != biases.end(); bi++) {
+  for (bi = biases_active()->begin(); bi != biases_active()->end(); bi++) {
     (*bi)->communicate_forces();
     if (cvm::get_error()) {
       return COLVARS_ERROR;
@@ -687,6 +814,11 @@ int colvarmodule::update_colvar_forces()
     error_code |= calc_scripted_forces();
   }
 
+  // Now we have collected energies from both built-in and scripted biases
+  if (cvm::debug())
+    cvm::log("Adding total bias energy: " + cvm::to_str(total_bias_energy) + "\n");
+  proxy->add_energy(total_bias_energy);
+
   cvm::real total_colvar_energy = 0.0;
   // sum up the forces for each colvar, including wall forces
   // and integrate any internal
@@ -695,7 +827,7 @@ int colvarmodule::update_colvar_forces()
     cvm::log("Updating the internal degrees of freedom "
              "of colvars (if they have any).\n");
   cvm::increase_depth();
-  for (cvi = colvars.begin(); cvi != colvars.end(); cvi++) {
+  for (cvi = variables()->begin(); cvi != variables()->end(); cvi++) {
     // Here we call even inactive colvars, so they accumulate biasing forces
     // as well as update their extended-system dynamics
     total_colvar_energy += (*cvi)->update_forces_energy();
@@ -704,6 +836,8 @@ int colvarmodule::update_colvar_forces()
     }
   }
   cvm::decrease_depth();
+  if (cvm::debug())
+    cvm::log("Adding total colvar energy: " + cvm::to_str(total_colvar_energy) + "\n");
   proxy->add_energy(total_colvar_energy);
 
   // make collective variables communicate their forces to their
@@ -711,9 +845,8 @@ int colvarmodule::update_colvar_forces()
   if (cvm::debug())
     cvm::log("Communicating forces from the colvars to the atoms.\n");
   cvm::increase_depth();
-  for (cvi = colvars.begin(); cvi != colvars.end(); cvi++) {
+  for (cvi = variables_active()->begin(); cvi != variables_active()->end(); cvi++) {
     if ((*cvi)->is_enabled(colvardeps::f_cv_gradient)) {
-      if (!(*cvi)->is_enabled()) continue;
       (*cvi)->communicate_forces();
       if (cvm::get_error()) {
         return COLVARS_ERROR;
@@ -800,8 +933,8 @@ int colvarmodule::analyze()
     cvm::log("Performing analysis.\n");
 
   // perform colvar-specific analysis
-  for (std::vector<colvar *>::iterator cvi = colvars.begin();
-       cvi != colvars.end();
+  for (std::vector<colvar *>::iterator cvi = variables_active()->begin();
+       cvi != variables_active()->end();
        cvi++) {
     cvm::increase_depth();
     (*cvi)->analyze();
@@ -825,8 +958,8 @@ int colvarmodule::setup()
 {
   if (this->size() == 0) return cvm::get_error();
   // loop over all components of all colvars to reset masses of all groups
-  for (std::vector<colvar *>::iterator cvi = colvars.begin();
-       cvi != colvars.end();  cvi++) {
+  for (std::vector<colvar *>::iterator cvi = variables()->begin();
+       cvi != variables()->end();  cvi++) {
     (*cvi)->setup();
   }
   return (cvm::get_error() ? COLVARS_ERROR : COLVARS_OK);
@@ -934,18 +1067,18 @@ int colvarmodule::setup_output()
     cvm::log("The restart output state file will be \""+restart_out_name+"\".\n");
   }
 
-  output_prefix = proxy->output_prefix();
-  if (output_prefix.size()) {
+  output_prefix() = proxy->output_prefix();
+  if (output_prefix().size()) {
     cvm::log("The final output state file will be \""+
-             (output_prefix.size() ?
-              std::string(output_prefix+".colvars.state") :
+             (output_prefix().size() ?
+              std::string(output_prefix()+".colvars.state") :
               std::string("colvars.state"))+"\".\n");
     // cvm::log (cvm::line_marker);
   }
 
   cv_traj_name =
-    (output_prefix.size() ?
-     std::string(output_prefix+".colvars.traj") :
+    (output_prefix().size() ?
+     std::string(output_prefix()+".colvars.traj") :
      std::string(""));
 
   if (cv_traj_freq && cv_traj_name.size()) {
@@ -1077,14 +1210,14 @@ continue the previous simulation.\n\n");
     cvm::log(cvm::line_marker);
 
     // update this ahead of time in this special case
-    output_prefix = proxy->input_prefix();
-    cvm::log("All output files will now be saved with the prefix \""+output_prefix+".tmp.*\".\n");
+    output_prefix() = proxy->input_prefix();
+    cvm::log("All output files will now be saved with the prefix \""+output_prefix()+".tmp.*\".\n");
     cvm::log(cvm::line_marker);
     cvm::log("Please review the important warning above. After that, you may rename:\n\
-\""+output_prefix+".tmp.colvars.state\"\n\
+\""+output_prefix()+".tmp.colvars.state\"\n\
 to:\n\
 \""+ proxy->input_prefix()+".colvars.state\"\n");
-    output_prefix = output_prefix+".tmp";
+    output_prefix() = output_prefix()+".tmp";
     write_output_files();
     cvm::error("Exiting with error until issue is addressed.\n", FATAL_ERROR);
   }
@@ -1104,8 +1237,8 @@ int colvarmodule::write_output_files()
   // if this is a simulation run (i.e. not a postprocessing), output data
   // must be written to be able to restart the simulation
   std::string const out_name =
-    (output_prefix.size() ?
-     std::string(output_prefix+".colvars.state") :
+    (output_prefix().size() ?
+     std::string(output_prefix()+".colvars.state") :
      std::string("colvars.state"));
   cvm::log("Saving collective variables state to \""+out_name+"\".\n");
 
@@ -1340,7 +1473,8 @@ std::ostream & colvarmodule::write_traj(std::ostream &os)
 
 void cvm::log(std::string const &message)
 {
-  size_t const d = depth();
+  // allow logging when the module is not fully initialized
+  size_t const d = (cvm::main() != NULL) ? depth() : 0;
   if (d > 0)
     proxy->log((std::string(2*d, ' '))+message);
   else
@@ -1365,19 +1499,20 @@ void cvm::decrease_depth()
 size_t & cvm::depth()
 {
   // NOTE: do not call log() or error() here, to avoid recursion
-  size_t const nt = proxy->smp_num_threads();
+  colvarmodule *cv = cvm::main();
   if (proxy->smp_enabled() == COLVARS_OK) {
-    if (depth_v.size() != nt) {
-      // update array of depths
+    int const nt = proxy->smp_num_threads();
+    if (int(cv->depth_v.size()) != nt) {
       proxy->smp_lock();
-      if (depth_v.size() > 0) { depth_s = depth_v[0]; }
-      depth_v.clear();
-      depth_v.assign(nt, depth_s);
+      // update array of depths
+      if (cv->depth_v.size() > 0) { cv->depth_s = cv->depth_v[0]; }
+      cv->depth_v.clear();
+      cv->depth_v.assign(nt, cv->depth_s);
       proxy->smp_unlock();
     }
-    return depth_v[proxy->smp_thread_id()];
+    return cv->depth_v[proxy->smp_thread_id()];
   }
-  return depth_s;
+  return cv->depth_s;
 }
 
 
@@ -1451,8 +1586,8 @@ int cvm::read_index_file(char const *filename)
                      FILE_ERROR);
         }
       }
-      cvm::index_group_names.push_back(group_name);
-      cvm::index_groups.push_back(std::vector<int> ());
+      index_group_names.push_back(group_name);
+      index_groups.push_back(std::vector<int>());
     } else {
       cvm::error("Error: in parsing index file \""+
                  std::string(filename)+"\".\n",
@@ -1462,7 +1597,7 @@ int cvm::read_index_file(char const *filename)
     int atom_number = 1;
     size_t pos = is.tellg();
     while ( (is >> atom_number) && (atom_number > 0) ) {
-      (cvm::index_groups.back()).push_back(atom_number);
+      (index_groups.back()).push_back(atom_number);
       pos = is.tellg();
     }
     is.clear();
@@ -1532,8 +1667,8 @@ int cvm::load_coords_xyz(char const *filename,
                + std::string(filename) + ".\n", INPUT_ERROR);
   }
   // skip comment line
-  std::getline(xyz_is, line);
-  std::getline(xyz_is, line);
+  cvm::getline(xyz_is, line);
+  cvm::getline(xyz_is, line);
   xyz_is.width(255);
   std::vector<atom_pos>::iterator pos_i = pos.begin();
 
@@ -1543,7 +1678,7 @@ int cvm::load_coords_xyz(char const *filename,
     for ( ; pos_i != pos.end() ; pos_i++, index++) {
 
       while ( next < *index ) {
-        std::getline(xyz_is, line);
+        cvm::getline(xyz_is, line);
         next++;
       }
       xyz_is >> symbol;
@@ -1558,15 +1693,9 @@ int cvm::load_coords_xyz(char const *filename,
   return (cvm::get_error() ? COLVARS_ERROR : COLVARS_OK);
 }
 
-// static pointers
-std::vector<colvar *>     colvarmodule::colvars;
-std::vector<colvarbias *> colvarmodule::biases;
-size_t                    colvarmodule::n_abf_biases = 0;
-size_t                    colvarmodule::n_rest_biases = 0;
-size_t                    colvarmodule::n_histo_biases = 0;
-size_t                    colvarmodule::n_meta_biases = 0;
-colvarproxy              *colvarmodule::proxy = NULL;
 
+// shared pointer to the proxy object
+colvarproxy *colvarmodule::proxy = NULL;
 
 // static runtime data
 cvm::real colvarmodule::debug_gradients_step_size = 1.0e-07;
@@ -1575,14 +1704,9 @@ long      colvarmodule::it = 0;
 long      colvarmodule::it_restart = 0;
 size_t    colvarmodule::restart_out_freq = 0;
 size_t    colvarmodule::cv_traj_freq = 0;
-size_t    colvarmodule::depth_s = 0;
-std::vector<size_t> colvarmodule::depth_v(0);
 bool      colvarmodule::b_analysis = false;
-std::list<std::string> colvarmodule::index_group_names;
-std::list<std::vector<int> > colvarmodule::index_groups;
-bool     colvarmodule::use_scripted_forces = false;
-bool     colvarmodule::scripting_after_biases = true;
-std::string colvarmodule::output_prefix = "";
+bool      colvarmodule::use_scripted_forces = false;
+bool      colvarmodule::scripting_after_biases = true;
 
 // i/o constants
 size_t const colvarmodule::it_width = 12;
diff --git a/lib/colvars/colvarmodule.h b/lib/colvars/colvarmodule.h
index 0e98709adb79c7e746d76ed3fa331a65c2809e65..b4f80e0b5e47bcd0dd4475ebe9a22ddc90981ed5 100644
--- a/lib/colvars/colvarmodule.h
+++ b/lib/colvars/colvarmodule.h
@@ -11,7 +11,7 @@
 #define COLVARMODULE_H
 
 #ifndef COLVARS_VERSION
-#define COLVARS_VERSION "2016-12-27"
+#define COLVARS_VERSION "2017-03-09"
 #endif
 
 #ifndef COLVARS_DEBUG
@@ -161,12 +161,37 @@ public:
   /// dt)
   static real debug_gradients_step_size;
 
+private:
+
   /// Prefix for all output files for this run
-  static std::string output_prefix;
+  std::string cvm_output_prefix;
+
+public:
+  /// Accessor for the above
+  static inline std::string &output_prefix()
+  {
+    colvarmodule *cv = colvarmodule::main();
+    return cv->cvm_output_prefix;
+  }
 
+private:
+
+  /// Array of collective variables
+  std::vector<colvar *> colvars;
 
   /// Array of collective variables
-  static std::vector<colvar *>     colvars;
+  std::vector<colvar *> colvars_active;
+
+  /// Collective variables to be calculated on different threads;
+  /// colvars with multple items (e.g. multiple active CVCs) are duplicated
+  std::vector<colvar *> colvars_smp;
+  /// Indexes of the items to calculate for each colvar
+  std::vector<int> colvars_smp_items;
+
+public:
+
+  /// Array of collective variables
+  std::vector<colvar *> *variables();
 
   /* TODO: implement named CVCs
   /// Array of named (reusable) collective variable components
@@ -177,26 +202,31 @@ public:
   }
   */
 
+  /// Collective variables with the active flag on
+  std::vector<colvar *> *variables_active();
+
   /// Collective variables to be calculated on different threads;
   /// colvars with multple items (e.g. multiple active CVCs) are duplicated
-  std::vector<colvar *> colvars_smp;
+  std::vector<colvar *> *variables_active_smp();
+
   /// Indexes of the items to calculate for each colvar
-  std::vector<int> colvars_smp_items;
+  std::vector<int> *variables_active_smp_items();
 
   /// Array of collective variable biases
-  static std::vector<colvarbias *> biases;
-  /// \brief Number of ABF biases initialized (in normal conditions
-  /// should be 1)
-  static size_t n_abf_biases;
-  /// \brief Number of metadynamics biases initialized (in normal
-  /// conditions should be 1)
-  static size_t n_meta_biases;
-  /// \brief Number of restraint biases initialized (no limit on the
-  /// number)
-  static size_t n_rest_biases;
-  /// \brief Number of histograms initialized (no limit on the
-  /// number)
-  static size_t n_histo_biases;
+  std::vector<colvarbias *> biases;
+
+  /// Energy of built-in and scripted biases, summed per time-step
+  real total_bias_energy;
+
+private:
+
+  /// Array of active collective variable biases
+  std::vector<colvarbias *> biases_active_;
+
+public:
+
+  /// Array of active collective variable biases
+  std::vector<colvarbias *> *biases_active();
 
   /// \brief Whether debug output should be enabled (compile-time option)
   static inline bool debug()
@@ -205,10 +235,7 @@ public:
   }
 
   /// \brief How many objects are configured yet?
-  inline size_t size() const
-  {
-    return colvars.size() + biases.size();
-  }
+  size_t size() const;
 
   /// \brief Constructor \param config_name Configuration file name
   /// \param restart_name (optional) Restart file name
@@ -230,9 +257,12 @@ public:
   /// \brief Parse a "clean" config string (no comments)
   int parse_config(std::string &conf);
 
-
   // Parse functions (setup internal data based on a string)
 
+  /// Allow reading from Windows text files using using std::getline
+  /// (which can still be used when the text is produced by Colvars itself)
+  static std::istream & getline(std::istream &is, std::string &line);
+
   /// Parse the few module's global parameters
   int parse_global_params(std::string const &conf);
 
@@ -242,14 +272,33 @@ public:
   /// Parse and initialize collective variable biases
   int parse_biases(std::string const &conf);
 
+  /// \brief Add new configuration during parsing (e.g. to implement
+  /// back-compatibility); cannot be nested, i.e. conf should not contain
+  /// anything that triggers another call
+  int append_new_config(std::string const &conf);
+
+private:
+
+  /// Auto-generated configuration during parsing (e.g. to implement
+  /// back-compatibility)
+  std::string extra_conf;
+
   /// Parse and initialize collective variable biases of a specific type
   template <class bias_type>
-  int parse_biases_type(std::string const &conf, char const *keyword, size_t &bias_count);
+  int parse_biases_type(std::string const &conf, char const *keyword);
 
   /// Test error condition and keyword parsing
   /// on error, delete new bias
   bool check_new_bias(std::string &conf, char const *key);
 
+public:
+
+  /// Return how many biases have this feature enabled
+  static int num_biases_feature(int feature_id);
+
+  /// Return how many biases are defined with this type
+  static int num_biases_type(std::string const &type);
+
 private:
   /// Useful wrapper to interrupt parsing if any error occurs
   int catch_input_errors(int result);
@@ -449,13 +498,13 @@ public:
 
 
   /// \brief Names of groups from a Gromacs .ndx file to be read at startup
-  static std::list<std::string> index_group_names;
+  std::list<std::string> index_group_names;
 
   /// \brief Groups from a Gromacs .ndx file read at startup
-  static std::list<std::vector<int> > index_groups;
+  std::list<std::vector<int> > index_groups;
 
   /// \brief Read a Gromacs .ndx file
-  static int read_index_file(char const *filename);
+  int read_index_file(char const *filename);
 
 
   /// \brief Create atoms from a file \param filename name of the file
@@ -515,13 +564,13 @@ protected:
   /// Output restart file
   colvarmodule::ofstream restart_out_os;
 
-protected:
+private:
 
   /// Counter for the current depth in the object hierarchy (useg e.g. in output)
-  static size_t depth_s;
+  size_t depth_s;
 
   /// Thread-specific depth
-  static std::vector<size_t> depth_v;
+  std::vector<size_t> depth_v;
 
 public:
 
@@ -552,6 +601,10 @@ public:
   /// from the hosting program; it is static in order to be accessible
   /// from static functions in the colvarmodule class
   static colvarproxy *proxy;
+
+  /// \brief Accessor for the above
+  static colvarmodule *main();
+
 };
 
 
diff --git a/lib/colvars/colvarparse.cpp b/lib/colvars/colvarparse.cpp
index 4d2e17f098dbb55457e5b1d8bc44f2cab79fdf70..8055d925dbbd3a403649c091563eb6e4e5e1cd0e 100644
--- a/lib/colvars/colvarparse.cpp
+++ b/lib/colvars/colvarparse.cpp
@@ -503,7 +503,7 @@ int colvarparse::check_keywords(std::string &conf, char const *key)
 
   std::string line;
   std::istringstream is(conf);
-  while (std::getline(is, line)) {
+  while (cvm::getline(is, line)) {
     if (line.size() == 0)
       continue;
     if (line.find_first_not_of(white_space) ==
@@ -539,10 +539,9 @@ int colvarparse::check_keywords(std::string &conf, char const *key)
 
 
 std::istream & colvarparse::getline_nocomments(std::istream &is,
-                                               std::string &line,
-                                               char const delim)
+                                               std::string &line)
 {
-  std::getline(is, line, delim);
+  cvm::getline(is, line);
   size_t const comment = line.find('#');
   if (comment != std::string::npos) {
     line.erase(comment);
diff --git a/lib/colvars/colvarparse.h b/lib/colvars/colvarparse.h
index acdc46ddc9ee42d39f5889f2ebf4ab01c1781156..9f116caafdf8a5b96ec19ccc21469d2abc60741c 100644
--- a/lib/colvars/colvarparse.h
+++ b/lib/colvars/colvarparse.h
@@ -304,8 +304,7 @@ public:
   /// \brief Works as std::getline() but also removes everything
   /// between a comment character and the following newline
   static std::istream & getline_nocomments(std::istream &is,
-                                           std::string &s,
-                                           char const delim = '\n');
+                                           std::string &s);
 
   /// Check if the content of the file has matching braces
   bool brace_check(std::string const &conf,
diff --git a/lib/colvars/colvarproxy.h b/lib/colvars/colvarproxy.h
index bdf65c0b1f0a617a09427769e151a6db688dd154..5b216c9d41528f390f3f99dfd86184cd847e1186 100644
--- a/lib/colvars/colvarproxy.h
+++ b/lib/colvars/colvarproxy.h
@@ -336,8 +336,6 @@ public:
   /// Are total forces being used?
   virtual bool total_forces_enabled() const
   {
-    cvm::error("Error: total forces are currently not implemented.\n",
-               COLVARS_NOT_IMPLEMENTED);
     return false;
   }
 
diff --git a/lib/colvars/colvarscript.cpp b/lib/colvars/colvarscript.cpp
index ffb83e97801df0d4bb448b75747925e729fdb4ff..f192dcb7c05e377ba2dc44e8cd7ebf6e98481f73 100644
--- a/lib/colvars/colvarscript.cpp
+++ b/lib/colvars/colvarscript.cpp
@@ -12,6 +12,7 @@
 #include <string.h>
 
 #include "colvarscript.h"
+#include "colvardeps.h"
 
 
 colvarscript::colvarscript(colvarproxy *p)
@@ -221,6 +222,16 @@ int colvarscript::run(int argc, char const *argv[]) {
     }
   }
 
+  if (cmd == "addenergy") {
+    if (argc == 3) {
+      colvars->total_bias_energy += strtod(argv[2], NULL);
+      return COLVARS_OK;
+    } else {
+      result = "Wrong arguments to command \"addenergy\"\n" + help_string();
+      return COLVARSCRIPT_ERROR;
+    }
+  }
+
   result = "Syntax error\n" + help_string();
   return COLVARSCRIPT_ERROR;
 }
@@ -263,10 +274,11 @@ int colvarscript::proc_colvar(int argc, char const *argv[]) {
   }
 
   if (subcmd == "delete") {
-    if (cv->biases.size() > 0) {
-      result = "Cannot delete a colvar currently used by biases, delete those biases first";
-      return COLVARSCRIPT_ERROR;
+    size_t i;
+    for (i = 0; i < cv->biases.size(); i++) {
+      delete cv->biases[i];
     }
+    cv->biases.resize(0);
     // colvar destructor is tasked with the cleanup
     delete cv;
     // TODO this could be done by the destructors
@@ -338,9 +350,8 @@ int colvarscript::proc_colvar(int argc, char const *argv[]) {
     return COLVARS_OK;
   }
 
-  if (subcmd == "state") {
-    cv->print_state();
-    return COLVARS_OK;
+  if ((subcmd == "get") || (subcmd == "set") || (subcmd == "state")) {
+    return proc_features(cv, argc, argv);
   }
 
   result = "Syntax error\n" + help_string();
@@ -379,11 +390,6 @@ int colvarscript::proc_bias(int argc, char const *argv[]) {
     return COLVARS_OK;
   }
 
-  if (subcmd == "state") {
-    b->print_state();
-    return COLVARS_OK;
-  }
-
   // Subcommands for MW ABF
   if (subcmd == "bin") {
     int r = b->current_bin();
@@ -420,6 +426,10 @@ int colvarscript::proc_bias(int argc, char const *argv[]) {
     return COLVARS_OK;
   }
 
+  if ((subcmd == "get") || (subcmd == "set") || (subcmd == "state")) {
+    return proc_features(b, argc, argv);
+  }
+
   if (argc >= 4) {
     std::string param = argv[3];
     if (subcmd == "count") {
@@ -441,6 +451,83 @@ int colvarscript::proc_bias(int argc, char const *argv[]) {
 }
 
 
+int colvarscript::proc_features(colvardeps *obj,
+                                int argc, char const *argv[]) {
+  // size was already checked before calling
+  std::string subcmd = argv[2];
+
+  if (argc == 3) {
+    if (subcmd == "state") {
+      // TODO make this returned as result?
+      obj->print_state();
+      return COLVARS_OK;
+    }
+
+    // get and set commands require more arguments
+    result = "Syntax error\n" + help_string();
+    return COLVARSCRIPT_ERROR;
+  }
+
+  if ((subcmd == "get") || (subcmd == "set")) {
+    std::vector<colvardeps::feature *> &features = obj->features();
+    std::string const req_feature(argv[3]);
+    colvardeps::feature *f = NULL;
+    int fid = 0;
+    for (fid = 0; fid < int(features.size()); fid++) {
+      if (features[fid]->description ==
+          colvarparse::to_lower_cppstr(req_feature)) {
+        f = features[fid];
+        break;
+      }
+    }
+
+    if (f == NULL) {
+
+      result = "Error: feature \""+req_feature+"\" does not exist.\n";
+      return COLVARSCRIPT_ERROR;
+
+    } else {
+
+      if (! obj->is_available(fid)) {
+        result = "Error: feature \""+req_feature+"\" is unavailable.\n";
+        return COLVARSCRIPT_ERROR;
+      }
+
+      if (subcmd == "get") {
+        result = cvm::to_str(obj->is_enabled(fid) ? 1 : 0);
+        return COLVARS_OK;
+      }
+
+      if (subcmd == "set") {
+        if (argc == 5) {
+          std::string const yesno =
+            colvarparse::to_lower_cppstr(std::string(argv[4]));
+          if ((yesno == std::string("yes")) ||
+              (yesno == std::string("on")) ||
+              (yesno == std::string("1"))) {
+            obj->enable(fid);
+            return COLVARS_OK;
+          } else if ((yesno == std::string("no")) ||
+              (yesno == std::string("off")) ||
+              (yesno == std::string("0"))) {
+            // TODO disable() function does not exist yet,
+            // dependencies will not be resolved
+            // obj->disable(fid);
+            obj->set_enabled(fid, false);
+            return COLVARS_OK;
+          }
+        }
+        result = "Syntax error\n" + help_string();
+        return COLVARSCRIPT_ERROR;
+      }
+    }
+  }
+
+  result = "Syntax error\n" + help_string();
+  return COLVARSCRIPT_ERROR;
+}
+
+
 std::string colvarscript::help_string()
 {
   std::string buf;
@@ -459,6 +546,7 @@ Input and output:\n\
   load <file name>            -- load a state file (requires configuration)\n\
   save <file name>            -- save a state file (requires configuration)\n\
   update                      -- recalculate colvars and biases\n\
+  addenergy <E>               -- add <E> to the total bias energy\n\
   printframe                  -- return a summary of the current frame\n\
   printframelabels            -- return labels to annotate printframe's output\n";
 
@@ -478,12 +566,17 @@ Accessing collective variables:\n\
   colvar <name> addforce <F>  -- apply given force on colvar <name>\n\
   colvar <name> getconfig     -- return config string of colvar <name>\n\
   colvar <name> cvcflags <fl> -- enable or disable cvcs according to 0/1 flags\n\
+  colvar <name> get <f>       -- get the value of the colvar feature <f>\n\
+  colvar <name> set <f> <val> -- set the value of the colvar feature <f>\n\
 \n\
 Accessing biases:\n\
   bias <name> energy          -- return the current energy of bias <name>\n\
   bias <name> update          -- recalculate bias <name>\n\
   bias <name> delete          -- delete bias <name>\n\
-  bias <name> getconfig       -- return config string of bias <name>\n";
+  bias <name> getconfig       -- return config string of bias <name>\n\
+  bias <name> get <f>         -- get the value of the bias feature <f>\n\
+  bias <name> set <f> <val>   -- set the value of the bias feature <f>\n\
+";
 
   return buf;
 }
diff --git a/lib/colvars/colvarscript.h b/lib/colvars/colvarscript.h
index bfb8381e3b6c72bec02d3b6fe41e5037ab969bed..46b1ddd203afdcf72beb5da2e1ff2ffc2f01c65b 100644
--- a/lib/colvars/colvarscript.h
+++ b/lib/colvars/colvarscript.h
@@ -51,6 +51,10 @@ private:
   /// Run subcommands on bias
   int proc_bias(int argc, char const *argv[]);
 
+  /// Run subcommands on base colvardeps object (colvar, bias, ...)
+  int proc_features(colvardeps *obj,
+                    int argc, char const *argv[]);
+
   /// Builds and return a short help
   std::string help_string(void);
 };
diff --git a/src/USER-COLVARS/colvarproxy_lammps.cpp b/src/USER-COLVARS/colvarproxy_lammps.cpp
index 7320263ba7b27f3d6a79caf40002934405df1e91..89453ded9be77b795ea1b005e2262927442a1088 100644
--- a/src/USER-COLVARS/colvarproxy_lammps.cpp
+++ b/src/USER-COLVARS/colvarproxy_lammps.cpp
@@ -352,12 +352,17 @@ int colvarproxy_lammps::smp_enabled()
 int colvarproxy_lammps::smp_colvars_loop()
 {
   colvarmodule *cv = this->colvars;
+  colvarproxy_lammps *proxy = (colvarproxy_lammps *) cv->proxy;
 #pragma omp parallel for
-  for (size_t i = 0; i < cv->colvars_smp.size(); i++) {
+  for (size_t i = 0; i < cv->variables_active_smp()->size(); i++) {
+    colvar *x = (*(cv->variables_active_smp()))[i];
+    int x_item = (*(cv->variables_active_smp_items()))[i];
     if (cvm::debug()) {
-      cvm::log("Calculating colvar \""+cv->colvars_smp[i]->name+"\" on thread "+cvm::to_str(smp_thread_id())+"\n");
+      cvm::log("["+cvm::to_str(proxy->smp_thread_id())+"/"+cvm::to_str(proxy->smp_num_threads())+
+               "]: calc_colvars_items_smp(), i = "+cvm::to_str(i)+", cv = "+
+               x->name+", cvc = "+cvm::to_str(x_item)+"\n");
     }
-    cv->colvars_smp[i]->calc_cvcs(cv->colvars_smp_items[i], 1);
+    x->calc_cvcs(x_item, 1);
   }
   return cvm::get_error();
 }
@@ -367,11 +372,13 @@ int colvarproxy_lammps::smp_biases_loop()
 {
   colvarmodule *cv = this->colvars;
 #pragma omp parallel for
-  for (size_t i = 0; i < cv->biases.size(); i++) {
+  for (size_t i = 0; i < cv->biases_active()->size(); i++) {
+    colvarbias *b = (*(cv->biases_active()))[i];
     if (cvm::debug()) {
-      cvm::log("Calculating bias \""+cv->biases[i]->name+"\" on thread "+cvm::to_str(smp_thread_id())+"\n");
+      cvm::log("Calculating bias \""+b->name+"\" on thread "+
+               cvm::to_str(smp_thread_id())+"\n");
     }
-    cv->biases[i]->update();
+    b->update();
   }
   return cvm::get_error();
 }
diff --git a/src/USER-COLVARS/colvarproxy_lammps.h b/src/USER-COLVARS/colvarproxy_lammps.h
index c0b4ea20fecb37dfad428b0e7058b4fa885652bc..ad63eb2f9eb6b740131d38aaac641b6f25df23e5 100644
--- a/src/USER-COLVARS/colvarproxy_lammps.h
+++ b/src/USER-COLVARS/colvarproxy_lammps.h
@@ -29,7 +29,7 @@
 #endif
 
 #ifndef COLVARPROXY_VERSION
-#define COLVARPROXY_VERSION "2016-12-27"
+#define COLVARPROXY_VERSION "2017-01-09"
 #endif
 
 /* struct for packed data communication of coordinates and forces. */