Skip to content

Conversation

@fhh2626
Copy link
Contributor

@fhh2626 fhh2626 commented Oct 27, 2025

As mentioned in #649 , this PR makes it possible to use a harmonic restraint as a collective variable (CV). Enhanced sampling algorithms can then be used to calculate the free-energy change associated with creating or removing the harmonic restraint(s).

Examples:

colvar {
  name d
  distance {
    group1 {
      atomnumbers   4759
    }
    group2 {
      dummyAtom (-2.543, 1.510, 0.364)    
    }
  }
}
harmonic {
  name       harm
  colvars       d
  centers       0.0
  forceConstant 10.0
}

colvar {
  name k_cv

  extendedLagrangian on
  extendedMass 1500000
  extendedLangevinDamping 5000

  lowerBoundary      0.0
  upperBoundary      1.0
  width 0.01
  
  reflectinglowerboundary  on
  reflectingupperboundary  on

  harmonicForceConstant {
    harmonicName harm
    kExponent    4.0
  }
}

 abf {
   colvars k_cv
   fullSamples 1000
   outputFreq  500000
   historyFreq 500000
}
metadynamics {
    colvars             k_cv
    hillWeight          0.05
    hillWidth           3.0
    wellTempered        on
    biasTemperature     4000
    keepFreeEnergyFiles on
    outputFreq   500000
}

Next steps:

  • Extensive tests

@jhenin
Copy link
Member

jhenin commented Oct 30, 2025

Thanks @fhh2626 ! I don't have time for an in-depth review right now, but as a reminder to myself I'll write here some points I'd like to watch for:

  • maximum code reuse with the alchemical coordinate
  • compatibility with the recently overhauled moving restraint code
  • extensibility to other parameters e.g. harmonic wall position

src/colvar.h Outdated
Comment on lines 116 to 122
/// \brief Link this colvar's components to any required biases
int link_biases(colvarmodule *cvm);

/// \brief Get a pointer to the i-th component (CVC)
cvc* get_cvc_ptr(size_t index);
cvc const* get_cvc_ptr(size_t index) const; // Const version too

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it should be possible to avoid these intrusive changes to the colvar class, if the linkage is driven by the bias instead. That is, at init time, the bias could be given a colvar to link to, look it up by name (that is already implemented) and set the mutual pointers.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your comment. I've removed the link_biases function and done the reverse lookup, but I am not sure how to bypass get_cvc_ptr.

Comment on lines 755 to 756
// Loop over all associated collective variables
for (size_t i = 0; i < num_variables(); i++) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unless I'm misunderstanding this, the force constant update should be done once, not once per colvar, so it should not be inside this loop.

More generally, this reimplements colvarbias_restraint::update() with a lot of duplication. Instead, this should be implemented as a variant of the moving restraint code: this is another way of updating the force constant. It would be very clear if the update was done in a similar way, calling the linked cv instead of computing the new force constant internally.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have reused the current implementation of update().

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please have a look whether this version addresses your comment.

@jhenin
Copy link
Member

jhenin commented Nov 17, 2025

Whenever possible we rebase branches onto master rather than merging master. I've done that here.

// update restraint energy and forces
error_code |= colvarbias_restraint::update();
// The base class update() zeros out energy and forces, so it must be called first.
colvarbias::update();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe I was not clear in my first review. Since this is a moving force constant scheme, I think it should integrate into colvarbias_restraint_k_moving and the update can be done in the well-named function update_k().

for (size_t i = 0; i < cvm->num_variables(); ++i) {
colvar* cv = (*(cvm->variables()))[i];
for (size_t j = 0; j < cv->num_cvcs(); ++j) {
cvc_harmonicforceconstant* hfc_cvc = dynamic_cast<cvc_harmonicforceconstant*>(cv->get_cvc_ptr(j));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What I meant before is, the name of the CV to reference could be provided as a new parameter in the bias configuration, so no reverse lookup would be needed, just a regular lookup by CV name.

I recommend a slightly different separation of tasks between the CV (and its constituent CVC) and the bias.
Since the CV's value is lambda, it does not need to know about the exponent n. The bias needs to know about n, so it should compute the force on lambda for extended Lagrangian integration and apply it to the CV using apply_bias. Effectively, this bias acts on the restrained CVs, but also on the lambda CV, because its energy depends on CV values as well as lambda.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks Jerome! So what does your expected Colvars config file look like? Could you please provide an example to prevent misunderstanding? I can revise the code to adapt it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The user interface could be something like:

colvar {
  name lambda_cv

  extendedLagrangian on
  extendedMass 1500000
  extendedLangevinDamping 5000
  reflectinglowerboundary  on
  reflectingupperboundary  on

  harmonicForceConstant # Similar to alchLambda, no parameters needed
}

harmonic {
    colvars d
    forceConstant 100.  # k_max
    dynamicForceConstantLambda lambda_cv
    dynamicForceConstantExponent 4.0
}

When initializing the harmonic restraint, it can look up the cv by name and do the linking, that is, set a pointer to itself in the CVC. The CVC would complain if asked to calculate itself when the bias pointer is not set.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants