Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
149 commits
Select commit Hold shift + click to select a range
b4af0e2
Weighted Poisson integration (squashed changes)
jhenin Apr 30, 2025
a2fbcd6
cache the laplacian coefficients and integrate calculate weight sum i…
levo-str Jun 12, 2025
5e9dcc9
precompute laplacian coefficient and parallelize code
levo-str Jun 13, 2025
d53eda2
implement data extrapolation
levo-str Jun 23, 2025
130a424
fix print of convergence
levo-str Jun 23, 2025
7f4c1dc
uses the conjugate gradient optimizer for unweighted poisson resolution
levo-str Jul 17, 2025
6746be7
implement as minimization of error on data's estimation of the gradient
levo-str Sep 26, 2025
3d6c43c
fix unweighted boundary conditions handling and change laplacian calc…
levo-str Oct 9, 2025
0acb376
fix segmentation error in div calculation when handling periodic boun…
levo-str Oct 9, 2025
45e77d2
clean code
levo-str Oct 10, 2025
4c1ecfd
use optimal parameter
levo-str Oct 10, 2025
8dc0af2
Don't include omp header
jhenin Oct 23, 2025
ba06021
delete unused code
levo-str Oct 27, 2025
46bfbeb
parallel for loop over signed int and force thresholds as floats
levo-str Oct 27, 2025
41b7bbe
fix type incoherence
levo-str Oct 27, 2025
55535f9
remove excess variable
levo-str Oct 27, 2025
5daf52d
reformat code as normal
levo-str Oct 28, 2025
19a122a
[MUST BE CHECKED] add flag for weighted integration
levo-str Oct 28, 2025
f09fcaa
comment code
levo-str Oct 28, 2025
de81c18
chore: remove unused code in poisson_integrator
jhenin Nov 19, 2025
6e8f634
chore: remove unused code
jhenin Nov 19, 2025
113e06a
chore: undo formatting changes
jhenin Nov 19, 2025
bcc6dc7
chore: more cleanup of weighted Poisson
jhenin Nov 19, 2025
644ab03
fix: Adapt weighted Poisson for ABF
jhenin Nov 19, 2025
e500501
test: Update NAMD regression tests output
jhenin Nov 19, 2025
44f5601
allow eABF weighted Poisson integration
levo-str Nov 21, 2025
3ea71df
fix reading for eABF
levo-str Nov 24, 2025
70a3cc3
fix: smart pointer to fix memory leak
jhenin Nov 24, 2025
9490b67
test: add functional test for 2D eABF
jhenin Nov 24, 2025
8422b93
:fix: fix pointer issue, computation size issue and divergence stenci…
levo-str Nov 25, 2025
9cc4ab9
fix the memory leakage by correcting border gradients calculation for…
levo-str Nov 27, 2025
b103a79
fix the memory leakage by correcting border gradients calculation for…
levo-str Nov 27, 2025
818b935
add deletion of computation grid pointer
levo-str Nov 27, 2025
393eca2
fix extrapolation (again)
levo-str Nov 27, 2025
a96e08b
fix: add missing test inputs
jhenin Nov 28, 2025
0429115
delete unused logs
levo-str Nov 28, 2025
5e60693
update test and delete unused testing variable
levo-str Nov 28, 2025
8247a4c
add new test eabf test with weighted integration
levo-str Nov 28, 2025
e4c2750
add new test abf2d_euler_weighted.in
levo-str Nov 28, 2025
020afac
:fix: correct test's colvar file name
levo-str Dec 1, 2025
a14064b
update tests [Needs to be checked]
levo-str Dec 1, 2025
69a0e82
fix: test for empty count grid before integrating
jhenin Dec 2, 2025
a297e94
test: reduce grid size in ext abf2d test
jhenin Dec 2, 2025
9e510a6
add documentation
levo-str Dec 3, 2025
c016bcc
add missing argument
levo-str Dec 3, 2025
0e0588c
allow sample grid to be added later
levo-str Dec 3, 2025
cc86fcc
add 2D eABF with weighted integration test
levo-str Dec 3, 2025
c17e340
allow sample grid to be added later
levo-str Dec 3, 2025
8a093e4
format code
levo-str Dec 3, 2025
999d2af
check if this passes the test
levo-str Dec 3, 2025
97966c6
delete unused import
levo-str Dec 3, 2025
e9e1eb9
error catching to handle 1D cases
levo-str Dec 3, 2025
3ebdf04
fix: more robust colvargrid_gradient constructor
jhenin Dec 9, 2025
330a15d
fix: grid handling in ABF, limit header scope
jhenin Dec 9, 2025
d698498
test: fix output for custom grid metadata in ABF
jhenin Dec 9, 2025
2553424
put back integrate weighted argument
levo-str Dec 9, 2025
4ffdefe
improve test with custom grid of different size to the original one
levo-str Dec 9, 2025
efe3a77
update eABF weighted test reference value
levo-str Dec 9, 2025
fb76d62
delete solved comments and unused log
levo-str Dec 9, 2025
e74d20d
create ABF with weighted integration NAMD test
levo-str Dec 9, 2025
5fac1de
create NAMD test for eABF
levo-str Dec 10, 2025
779aad2
update tests
levo-str Dec 10, 2025
17d6bf5
Make total_forces_same_step consistent
jhenin Dec 15, 2025
f9916a1
Remove redundant gradient tests
jhenin Dec 15, 2025
c2164ff
Improve poisson_integrator by adding CLI flags
jhenin Dec 17, 2025
de8fdc8
Cast integers to cvm::real before division
jhenin Dec 17, 2025
ac63ac5
delete useless imports
levo-str Dec 18, 2025
f56e4e4
add more details to the documentation
levo-str Dec 18, 2025
eb1a65a
Cleanup after PR review
jhenin Jan 12, 2026
5a9e729
prevent div calculation before preparations
levo-str Jan 12, 2026
ec90c1b
restore use import of sys/stat.h
levo-str Jan 12, 2026
5436ca9
optimize code thanks to HanatoK's recommendations
levo-str Jan 12, 2026
83937db
fix: change the type of nb_averaged_gradients from size_t to int
HanatoK Jan 12, 2026
c1e5057
optimize code thanks to HanatoK's recommendations
levo-str Jan 22, 2026
547f476
update tests
levo-str Dec 10, 2025
88f1aea
delete unused member 'samples'
levo-str Oct 24, 2025
7b7ea11
delete duplicate code by creating a colvar_grid_scalar_function
levo-str Oct 27, 2025
d81ffab
try to pass the CI
levo-str Oct 27, 2025
1d9fbd7
[WIP] implement kernel grid
levo-str Nov 12, 2025
fbae03d
update file with new implementation
levo-str Nov 14, 2025
cff0510
change argument value so it doesn't shadow class attribute
levo-str Nov 14, 2025
6e9d74f
[WIP] continue implementing kernel grid
levo-str Nov 19, 2025
11d8ea5
[WIP] continue implementing kernel grid
levo-str Dec 10, 2025
b361170
[WIP] :fix: add positions size assignment
levo-str Dec 11, 2025
b7efbe4
[WIP] use bins for acc_abf_force
levo-str Dec 11, 2025
7fd3b19
[WIP] :fix: fix acc_value
levo-str Dec 11, 2025
129efb8
[WIP] add log
levo-str Dec 11, 2025
be2f3c3
fix initialization for czar gradients with weights kernel grid
levo-str Dec 11, 2025
38c162e
fix conversion to int of scalar weights and add smooth abf possibilities
levo-str Dec 11, 2025
f1561b9
clean debugging lines
levo-str Dec 16, 2025
b295825
handle use of scalar weights
levo-str Dec 16, 2025
ac82d99
make sure pointers out are initialized
levo-str Dec 16, 2025
42a736d
make sure pointers out are initialized
levo-str Dec 16, 2025
2e449b5
[WIP] comment debugging log that may be useful until implementation
levo-str Dec 16, 2025
902ca8f
[WIP] comment debugging log that may be useful until implementation
levo-str Dec 16, 2025
e373bcb
fix: cut and paste error
jhenin Dec 16, 2025
ea9f722
delete useless logs
levo-str Dec 16, 2025
a3a36a1
allow weighted integration with scalar weights
levo-str Dec 16, 2025
d7a6ac0
disable divergence calculation without having done preparation first
levo-str Dec 16, 2025
f5f865f
finish implementation of kernel grid and clean unused logs
levo-str Dec 17, 2025
e9f66e3
fix miscalculation of kernel support
levo-str Dec 17, 2025
1d88639
add unit test
levo-str Dec 17, 2025
7978d33
fix implementation
levo-str Dec 17, 2025
f55052f
use the whole kernel support
levo-str Dec 17, 2025
be0ed00
try fixing floating point exceptions test error
levo-str Dec 17, 2025
7548875
try fixing floating point exceptions test error
levo-str Dec 17, 2025
c01881b
add smoothing argument in file
levo-str Dec 17, 2025
986deb3
add namd tests for kernel grid abf
levo-str Dec 17, 2025
f433d29
add helper function to loop properly on the kernel support
levo-str Dec 18, 2025
ede52a9
update tests
levo-str Dec 18, 2025
b8dbba5
precompute kernel parameters
levo-str Dec 18, 2025
c22b472
precompute kernel parameters
levo-str Dec 18, 2025
98bcbf8
finish kernel grid implementation
levo-str Dec 18, 2025
9f19f92
add smoothing to the test
levo-str Dec 18, 2025
75dfca3
update test
levo-str Dec 18, 2025
96c8a0f
delete unused helper function
levo-str Dec 18, 2025
065d8c4
fix copy and paste error
levo-str Dec 18, 2025
ee8e80e
update test
levo-str Dec 19, 2025
df129e7
make sure pointers are not null
levo-str Dec 19, 2025
f557c59
delete unused logs
levo-str Dec 19, 2025
a6c5a52
implement adaptative kernel size
levo-str Jan 22, 2026
2a6093c
implement adaptative kernel size
levo-str Jan 22, 2026
9f1e328
improve code performance by using the kernel's separability
levo-str Jan 26, 2026
986f8d2
comment unused dim_sum
levo-str Jan 26, 2026
7059aad
make the weight of each data point mean force estimate equal to 1
levo-str Jan 26, 2026
1a17cb8
make kernel support more symmetric
levo-str Jan 26, 2026
5bc8db6
make kernel more adaptive
levo-str Jan 26, 2026
8ef3215
clean useless comment
levo-str Jan 26, 2026
ab72f6d
fix appearance of kernels of size 0
levo-str Feb 3, 2026
908e53b
fix appearance of kernels of size 0
levo-str Feb 3, 2026
ddaf775
adaptive kernel
levo-str Feb 4, 2026
593ddee
[To uncommit] dirty implement save history as DX option
levo-str Feb 4, 2026
1941019
:fix: fix some code so it actually acts like before
levo-str Feb 5, 2026
4fba8c7
improve history saved as Dx
levo-str Feb 5, 2026
f0c02f5
[WIP] improve
levo-str Feb 5, 2026
8366990
improve DX history saving
levo-str Feb 6, 2026
456d531
add variance based kernel
levo-str Feb 10, 2026
c8d9ee9
fixed gradients grid updating for abf extended
levo-str Feb 10, 2026
4fa9fbb
implement directional variance based kernel
levo-str Feb 11, 2026
7c57970
update tests
levo-str Feb 11, 2026
e307e17
add doc
levo-str Feb 12, 2026
e861891
fix typo
levo-str Feb 12, 2026
3a87712
implement kernel smoothing depending on each dimension variance
levo-str Feb 24, 2026
96708f5
change int to size_t
levo-str Feb 24, 2026
d8f8f07
delete unused logs and fix rebasing issue
levo-str Feb 24, 2026
7597284
reformat code
levo-str Feb 26, 2026
025b35a
correct kernel grid implementation so the czar_gradients are associat…
levo-str Mar 30, 2026
e4bf7eb
make poisson_integrator.cpp more precise
levo-str Mar 30, 2026
08e9346
[WIP] enable reading of DX file
levo-str Apr 9, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11,527 changes: 11,527 additions & 0 deletions colvartools/CLI11.hpp

Large diffs are not rendered by default.

114 changes: 88 additions & 26 deletions colvartools/poisson_integrator.cpp
Original file line number Diff line number Diff line change
@@ -1,42 +1,104 @@
#include <iostream>
#include <fstream>
#include <sys/stat.h>

#include "colvargrid.h"
#include "colvargrid_integrate.h"
#include "colvarproxy.h"
#include "CLI11.hpp"

int main(int argc, char *argv[])
{
bool weighted = false;
bool debug = false;
int itmax = 1000;
cvm::real err;
cvm::real tol = 1e-3;

int main (int argc, char *argv[]) {
std::string gradfile;
std::string countfile;

if (argc < 2) {
std::cerr << "\n\nOne argument needed: gradient multicol file name.\n";
return 1;
}
CLI::App app("Colvars gradient grid integrator");

colvarproxy *proxy = new colvarproxy();
proxy->colvars = new colvarmodule(proxy); // This could be omitted if we used the colvarproxy_stub class
app.add_option("gradfile", gradfile, "Gradient multicol file name")
->required()
->check(CLI::ExistingFile);

std::string gradfile (argv[1]);
std::shared_ptr<colvar_grid_gradient> grad_ptr = std::make_shared<colvar_grid_gradient>(gradfile);
if (cvm::get_error()) { return -1; }
app.add_option("countfile", countfile, "Count file name");
app.add_flag("--weighted,!--unweighted", weighted, "Enable or disable weighting")
->default_val(weighted);

int itmax = 10000;
cvm::real err;
cvm::real tol = 1e-8;
app.add_option("--tol", tol, "Tolerance value");
app.add_option("--itmax", itmax, "Maximum iterations");
app.add_flag("--debug", debug, "Write debugging output and files");

colvargrid_integrate fes(grad_ptr);
fes.set_div();
fes.integrate(itmax, tol, err);
fes.set_zero_minimum();
CLI11_PARSE(app, argc, argv);

if (fes.num_variables() < 3) {
std::cout << "\nWriting integrated free energy in multicol format to " + gradfile + ".int\n";
fes.write_multicol(std::string(gradfile + ".int"), "integrated free energy");
} else { // Write 3D grids to more convenient DX format
std::cout << "\nWriting integrated free energy in OpenDX format to " + gradfile + ".int.dx\n";
fes.write_opendx(std::string(gradfile + ".int.dx"), "integrated free energy");
}
// (Moved after parsing so we don't allocate memory if help/error is printed)
colvarproxy *proxy = new colvarproxy();
proxy->colvars = new colvarmodule(proxy);
std::shared_ptr<colvar_grid_scalar> count_ptr;

delete proxy;
return 0;
// Deduce countfile if not provided by user
if (countfile.empty())
{
size_t pos = gradfile.rfind(std::string(".czar.grad"));
if (pos != std::string::npos)
{
countfile = gradfile.substr(0, pos) + ".zcount";
}
else
{
pos = gradfile.rfind(std::string(".grad"));
if (pos != std::string::npos)
{
countfile = gradfile.substr(0, pos) + ".count";
}
}
}

if (countfile.size())
{
struct stat buffer;
if (stat(countfile.c_str(), &buffer) == 0)
{
std::cout << "Found associated count file " << countfile << ", reading...\n";
count_ptr.reset(new colvar_grid_scalar(countfile));
if (!count_ptr || count_ptr->nd == 0)
{
// catch constructor failure
cvm::error("Error reading count grid.");
return cvm::get_error();
}
if (debug) count_ptr->write_multicol("counts.dat");
}
}

std::cout << "Reading gradient file " << gradfile << std::endl;
std::shared_ptr<colvar_grid_gradient> grad_ptr = std::make_shared<colvar_grid_gradient>(gradfile, count_ptr);

if (!grad_ptr || grad_ptr->nd == 0)
{
// catch constructor failure
cvm::error("Error reading gradient grid.");
return cvm::get_error();
}

if (debug) grad_ptr->write_multicol("gradient_in.dat");

colvargrid_integrate fes(grad_ptr, weighted);
fes.integrate(itmax, tol, err, true);
fes.set_zero_minimum();
if (fes.num_variables() < 3)
{
std::cout << "\nWriting integrated fes in multicol format to " + gradfile + ".int\n";
fes.write_multicol(std::string(gradfile + ".int"), "integrated fes");
} else {
// Write 3D grids to more convenient DX format
std::cout << "\nWriting integrated free energy in OpenDX format to " + gradfile + ".int.dx\n";
fes.write_opendx(std::string(gradfile + ".int.dx"), "integrated free energy");
}

delete proxy;
return 0;
}
5 changes: 3 additions & 2 deletions colvartools/poisson_integrator_conv.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,15 @@ int main (int argc, char *argv[]) {
proxy->colvars = new colvarmodule(proxy); // This could be omitted if we used the colvarproxy_stub class

std::string gradfile (argv[1]);
std::shared_ptr<colvar_grid_gradient> grad_ptr = std::make_shared<colvar_grid_gradient>(gradfile);
std::shared_ptr<colvar_grid_count> const nullpointer = nullptr;
std::shared_ptr<colvar_grid_gradient> grad_ptr = std::make_shared<colvar_grid_gradient>(gradfile, nullpointer);
if (cvm::get_error()) { return -1; }

cvm::real err = 1.;
cvm::real tol = 1e-10;

colvargrid_integrate fes(grad_ptr);
fes.set_div();
fes.set_unweighted_div();

// Load reference
colvar_grid_scalar ref(gradfile + ".ref");
Expand Down
7 changes: 7 additions & 0 deletions doc/colvars-code-refs.bib
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,13 @@ @Article{Henin2021
url = {https://doi.org/10.1021/acs.jctc.1c00593},
}

% Poisson equation resolution using discrete difference scheme
@misc{ChenFDM101,
author = {Long Chen},
title = {Finite difference methods for Poisson equation},
url = {https://www.math.uci.edu/~chenlong/226/FDM.pdf},
}

% VMD engine
@article{Humphrey1996,
title = {{VMD}: visual molecular dynamics},
Expand Down
45 changes: 41 additions & 4 deletions doc/colvars-refman-main.tex
Original file line number Diff line number Diff line change
Expand Up @@ -5862,6 +5862,27 @@
}
\end{itemize}

\cvsubsubsec{Kernel grid ABF}
ABF stores energy gradient sums and counts on two separate grids in order to estimate the free energy gradient. If the
grids are big, whether for accuracy or because of the dimensionality curse filling them will be long. In order to reduce
this time we use kernel grid to leverage the free energy function regularity. At each time step $t$ : the system is in
the state $(x(t), \xi(t), \nabla U(x(t))$, with $x(t)$ the atomic coordinate, $\xi(t)$ the value of the collective
variables, $\nabla U(x(t))$ the value of the energy of the system. With kernel grid, instead of updating only the cell of
each grid corresponding to $\xi(t)$ we'll also update the surrounding cells with a weight based on their distance (kernel)
to $\xi(t)$. Now in order to estimate the free energy gradient at a bin, the bin averaging will actually corresponds to a
weighted average of the energy gradients of nearby datapoints seen during simulation.

The weights are calculated using a kernel of the form
\begin{equation}
K(\tilde{\xi}, \tilde{\xi_{cell}}) = \frac{1}{Z}e^{\frac{||\tilde{\xi} - \tilde{\xi_{cell}}||^2}{\sigma}}
\end{equation}
Where \tilde{\xi} is the coordinate of the collective variable in the grid. i.e.
\begin{equation}
\tilde{\xi^i} = \frac{\xi^i - boundary_{lower}^i}{boundary_{upper}^i - boundary_{lower}^i}
\end{equation}
In order to activate kernel grid \refkey{smoothing}{abf|smoothing} to set the value of
the kernel's bandwith $\sigma$ to a value, if the value is > 0 kernel grid is in use.

\cvsubsubsec{Multiple-walker ABF}{sec:colvarbias_abf_shared}
\label{sec:mw-ABF}

Expand Down Expand Up @@ -5947,19 +5968,19 @@
{\texttt{yes}}
{
This option, active by default when the dimension of the colvar space is 3 or less, enables the calculation of an integrated free energy surface every time ABF output files are written.
In dimension 2 or 3, integration is performed by solving a Poisson equation:~\cite{Henin2021}
In dimension 2 or 3, by default integration is performed by solving an unweighted Poisson equation:~\cite{Henin2021}
\begin{equation}
\nabla^2 A_t = \nabla \cdot G_t
\end{equation}
wehere $G_t$ is the estimated gradient at time $t$, and $A_t$ is corresponding free energy surface.
where $G_t$ is the estimated gradient at time $t$, and $A_t$ is corresponding free energy surface.
The free energy surface is written under the file name \texttt{<outputName>.pmf},
in a plain text format (see \ref{sec:colvar_multicolumn_grid}) that can be read by most data plotting
and analysis programs (e.g.{} Gnuplot).
Periodic boundary conditions are applied to periodic coordinates, and Neumann boundary
conditions otherwise (imposed free energy gradient at the boundary of the domain).
The grid used for free energy discretization is extended by one point along
non-periodic coordinates, but not along periodic coordinates. See ref.~\cite{Henin2021}
for details.
non-periodic coordinates, but not along periodic coordinates. See ref.~\cite{Henin2021}, ~\cite{ChenFDM101}
for implementation details.
}

\item \keydef{integrateTol}{\texttt{abf}}{%
Expand All @@ -5981,6 +6002,22 @@
is stopped when the number of iterations reaches \texttt{integrateMaxIterations},
unless the RMS error has reached \texttt{integrateTol} before.
}
\item \keydef{integrateWeighted}{\texttt{abf}}{%
Use weighted method to integrate free energy surface from ABF gradients}
{boolean}
{\texttt{no}}{
This option is disabled by default. If activated, the calculation of the free energy will be performed using weighted
integration. This means solving the following weighted Poisson equation:
\begin{equation}
\nabla\cdot \theta \nabla A_t = \nabla \cdot G_t.
\end{equation}
With $\theta$, the weights, corresponding to how much an area has been visited during the simulation. The weights values
are all strictly superior to 0 so the problem is well-posed. For any cell with zero visits, its weight is assigned to
the value corresponding to the 10th percentile of the visit-count distribution. Moreover, visit counts are capped at
the 50th percentile measured from the upper tail (i.e., the 50th percentile from the lower tail). The choice of the
percentile is hard code. The goal is that the most visited cells which corresponds to the cells where the gradient is
the best known have more importance in the free energy calculation.
}
\end{itemize}


Expand Down
2 changes: 1 addition & 1 deletion misc_interfaces/stubs/colvarproxy_stub.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ bool colvarproxy_stub::total_forces_enabled() const

bool colvarproxy_stub::total_forces_same_step() const
{
return total_force_requested;
return true;
}


Expand Down
Loading
Loading