Skip to content

Implementation of TABF - #998

Draft
fhh2626 wants to merge 12 commits into
masterfrom
tabf
Draft

fhh2626 wants to merge 12 commits into
masterfrom
tabf

Conversation

@fhh2626

@fhh2626 fhh2626 commented Nov 12, 2026 •

Copy link
Copy Markdown
Contributor

PR migration

This draft PR supersedes #949, using the tabf branch in the main Colvars repository as suggested by @jhenin and @giacomofiorin, so that backend CI can run without the fork-related configuration restrictions.

The implementation is unchanged by this PR migration. The original discussion and review comments remain available in #949; I will address the outstanding items here before requesting another review.

Outstanding review items

  • Replace the reviewed cvm::error_static calls in the NAMD TABF proxy with cvmodule->error, preserving the unsupported-build error semantics, and check the analogous TABF methods for consistency.
  • Address the std::isfinite / -ffast-math compatibility concern in the reflecting-boundary implementation.
  • Explain the UI-estimator input-loading changes in d618943 and provide appropriate regression coverage.
  • Reproduce and address the reported boundary-initialization ordering issue when hard-boundary flags are enabled without explicit numeric boundaries.
  • Investigate the GROMACS backend CI failure and check backend CI results after the fixes.

Original implementation description

This PR implements the use of the temperature scaling factor α as a CV (https://doi.org/10.1021/acs.accounts.5c00666), achieving REMD-like sampling with smaller temperature intervals. A new version of NAMD is required (https://gitlab.com/tcbgUIUC/namd/-/merge_requests/537).

example:

colvar {
  name alpha
  width 0.01
  lowerBoundary 0.97
  upperBoundary 2.03
  hardLowerBoundary on
  hardUpperBoundary on
  reflectingLowerBoundary on
  reflectingUpperBoundary on

  extendedLagrangian on
  extendedMass 1500000
  extendedLangevinDamping 1000

  tabfAlpha {}
}

In NAMD config:

tabf on
tabfAlphaStart 1.0
tabfAlphaMin   0.97
tabfAlphaMax  2.03
tabfOutFreq     100

Register backend alpha limits during component initialization, before grid parameters and hard-boundary dependencies are parsed. Propagate initialization errors and preserve explicit boundary conflict checks.

Add regression coverage for omitted and matching boundaries, conflict diagnostics, hard-boundary defaults and overrides, transformed components, non-TABF bounds, and backend query failures.
Use module-owned errors for unsupported NAMD TABF operations. Preserve non-finite checks under fast-math and guard UI input records against invalid allocated-bin indices.

Add reflection and UI regression tests, including production fast-math coverage, and match reflection test compilation settings to optional library features such as Lepton.
Accept NAMD fullElectFrequency 0 as an inactive schedule while retaining the every-step requirement for active full electrostatics. Clarify the diagnostic without changing NAMD core scheduling checks.
@fhh2626

fhh2626 commented Nov 12, 2026

Copy link
Copy Markdown
Contributor Author

@jhenin @giacomofiorin @HanatoK, could you please review the updated TABF changes and help resolve the CI failures with Apptainer installation and the ARM64 container format? Thank you!

@giacomofiorin

Copy link
Copy Markdown
Member

@jhenin @giacomofiorin @HanatoK, could you please review the updated TABF changes and help resolve the CI failures with Apptainer installation and the ARM64 container format? Thank you!

You are missing updates from the latest master. Can you resolve the merge conflicts and rebase your branch onto master, or merge master into it, whichever is easier?

@giacomofiorin

Copy link
Copy Markdown
Member

Separately, @fhh2626, can you adapt (at least in part) the PR template and fill the relevant information, e.g. regarding backends affected (just NAMD or others?) and the AI usage?

@HanatoK

HanatoK commented Nov 12, 2026

Copy link
Copy Markdown
Member

Looks good to me overall. However, both this PR and https://gitlab.com/tcbgUIUC/namd/-/merge_requests/537 do not document the new options and CV tabfAlpha. It's better to document them and cite your paper (also update colvarmodule_refs.h). Will you submit the documentation updates in new NAMD/Colvars PRs?

@fhh2626

fhh2626 commented Nov 13, 2026

Copy link
Copy Markdown
Contributor Author

Summary

This PR adds tabfAlpha, a collective variable representing the TABF temperature-scaling parameter, together with its NAMD interface.

The follow-up changes address the review comments from #949:

  • Replace the TABF proxy's static error calls with module-owned error reporting.
  • Make non-finite checks in the reflection implementation work under fast-math.
  • Initialize intrinsic TABF boundaries before parsing grid and hard-boundary settings.
  • Add regression tests for TABF boundaries, reflection, and UI-estimator input loading.
  • Allow an inactive full-electrostatics schedule in the NAMD proxy while retaining the every-step requirement for active schedules.

An existing loop-index bug in the UI estimator was also fixed, with explanatory comments added to the code.

TABF alpha remains unsupported by the UI and CZAR estimators. Requests for these combinations are explicitly rejected.

Related issue(s)

This PR supersedes #949. The original review discussion remains available there.

The corresponding NAMD implementation is:
https://gitlab.com/tcbgUIUC/namd/-/merge_requests/537

Affected engine(s)

  • NAMD: The new TABF functionality requires a NAMD build providing the TABF backend interface.
  • Shared Colvars code: The reflection and UI input-loading changes also affect other engines when those shared features are used.
  • Other backends: This PR does not implement TABF backend support for LAMMPS, GROMACS, VMD, or other engines.

The latest master has been merged into this branch, including its updated CI workflows.

Author(s)

Licensing

The code contributed in this PR follows the Colvars project's license, the GNU Lesser General Public License version 3 (LGPLv3).

Artificial Intelligence (AI)

All code contributed in this PR was written using AI tools. It was reviewed separately by models from at least two different AI providers, including a GPT-series model.

Further information / links

Related paper:
https://doi.org/10.1021/acs.accounts.5c00666

Documentation

I will submit a separate PR to update the Colvars documentation for tabfAlpha and add the paper citation and associated citation metadata.

@giacomofiorin giacomofiorin left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

From a general point of view, this looks similar to how the alchLambda feature is already implemented, which made review a bit easier. However, this is highly dependent on the NAMD MR, so I think it should be revisited when that is merged.

Besides a few specific comments, I think these are significant items missing:

  • A regression test using NAMD, preferably under namd/tests/interface
  • LaTeX documentation; the reference to your paper could be added to doc/colvars-code-refs.bib, annotated in the same way as the existing references so that the internal Colvars citation reporter can pick it up
  • A clear error message when folks try (and fail) to use the feature outside NAMD (especially in VMD, which is a companion code to NAMD)

int colvarproxy_system::get_tabf_alpha(cvm::real * /* alpha */)
{
return cvm::error_static("Error in get_tabf_alpha: TABF alpha dynamics is not supported by this build.",
COLVARS_NOT_IMPLEMENTED);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This and other functions are using cvm::error_static() only because their class does not own a cvmodule pointer. But these functions are also only called from the colvar class, which does have such a pointer. So you could simply return the integer value COLVARS_NOT_IMPLEMENTED and call cvmodule->error() from the colvar class.

provide(f_cvc_Jacobian); // Zero

x.type(colvarvalue::type_scalar);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

One can use init_scalar_boundaries() to set predefined values of the boundaries (which we also forgot to use it in alchLambda).

Comment thread src/colvar.h
return cvcs;
}

/// Whether this colvar has a component of the given function type

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

"at least one" component?

Comment thread src/colvar.cpp

// Detect if we have a single component that is an alchemical lambda
const bool is_tabf_alpha =
is_enabled(f_cv_single_cvc) && cvcs[0]->function_type() == "tabfAlpha";

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This should really be a separate function that init() would call if the condition is true. We had historically very long member functions, but we have broken them up quite a bit recently, and the AI agent should look into that as well, or at least balance good practices with consistency with the existing code.

Comment thread src/colvar.cpp
cvm::real tabf_lower = 0.0;
cvm::real tabf_upper = 0.0;
error_code |= cvmodule->proxy->get_tabf_alpha_boundaries(&tabf_lower, &tabf_upper);
const bool lower_was_set = key_lookup(conf, "lowerBoundary");

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

There is a feature called f_cv_lower_boundary to test for this: if (is_enabled(f_cv_lower_boundary)) { ... }. Is there a reason for not using that instead of parsing the input again?

Comment thread src/colvarbias_abf.cpp

if (uses_tabf_alpha) {
bool requested_UI_estimator = false;
get_keyval(conf, "UIestimator", requested_UI_estimator, false, colvarparse::parse_silent);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Why parse_silent?

Comment thread src/colvar.cpp
Comment on lines +2073 to +2098
namespace {
// Floating-point classification may be folded to true under -ffast-math.
// Inspect the IEEE binary64 exponent instead, without type-punning. Supported
// targets use the same byte order for double and uint64_t (including x86/ARM);
// mixed-endian floating-point representations are not supported by this helper.
bool reflection_is_finite(cvm::real value)
{
#if defined(__FLOAT_WORD_ORDER__) && defined(__BYTE_ORDER__)
static_assert(__FLOAT_WORD_ORDER__ == __BYTE_ORDER__,
"Reflection does not support mixed-endian floating point");
#endif
static_assert(sizeof(cvm::real) == sizeof(std::uint64_t) &&
std::numeric_limits<cvm::real>::is_iec559 &&
std::numeric_limits<cvm::real>::radix == 2 &&
std::numeric_limits<cvm::real>::digits == 53 &&
std::numeric_limits<cvm::real>::max_exponent == 1024,
"Reflection requires IEEE 754 binary64 cvm::real");
std::uint64_t bits;
std::memcpy(&bits, &value, sizeof(bits));
// LLVM can recognize even memcpy + exponent masking as floating-point
// classification and fold it under finite-math assumptions. Observe the
// representation through a volatile integer to prevent that transformation.
volatile std::uint64_t observed_bits = bits;
return (observed_bits & UINT64_C(0x7ff0000000000000)) != UINT64_C(0x7ff0000000000000);
}
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Can the AI agent clarify if this would work on all compilers including MSVC?

Comment thread src/colvarcomp.h
virtual void calc_force_invgrads();
virtual void calc_Jacobian_derivative();
virtual void apply_force(colvarvalue const &force);
virtual int set_external_value(bool now=false);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Probably best to skip the virtual if this is a specialized function, with an optional parameter on top of that

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants