Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding Simulation and MultistateSimulation reporter #18

Merged
merged 41 commits into from
Jan 18, 2024
Merged
Show file tree
Hide file tree
Changes from 32 commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
c3bd752
Add MBAREstimator class and MultistateReporter class
wiederm Jan 9, 2024
84a85be
Add MBAREstimator class for performing mbar analysis
wiederm Jan 9, 2024
6ce2d70
Remove online analysis and add offline estimator
wiederm Jan 9, 2024
1006be5
Fix initialization of MBAREstimator and update offline free energy es…
wiederm Jan 9, 2024
0d7c944
Add MBAR class and update free energy estimators
wiederm Jan 10, 2024
f3340a3
Refactor MBAREstimator class in analysis.py
wiederm Jan 10, 2024
060947a
Update reporters in integrators and mcmc modules
wiederm Jan 10, 2024
4a42807
Update LangevinIntegrator class in integrators.py
wiederm Jan 11, 2024
faa027e
Refactor LangevinDynamicsReporter constructor signature
wiederm Jan 11, 2024
36507ab
Remove save_traj_in_memory parameter from LangevinIntegrator constructor
wiederm Jan 11, 2024
2128015
Add MCReporter class for Monte Carlo simulations
wiederm Jan 11, 2024
bf1d3c9
Update MCMCMove and LangevinDynamicsMove constructors
wiederm Jan 11, 2024
46c116f
Update chiron.reporters import
wiederm Jan 11, 2024
cdc664c
Fix MBAREstimator initialization in MultiStateSampler
wiederm Jan 11, 2024
5f760c5
Refactor reporters and tests
wiederm Jan 11, 2024
398c869
Add new reporters and update tests
wiederm Jan 11, 2024
083ace2
Add MultistateReporter class and update MultiStateSampler constructor
wiederm Jan 11, 2024
6c83aed
Refactor reporters in test files
wiederm Jan 11, 2024
46e4cc3
Add MultistateReporter to MultiStateSampler
wiederm Jan 12, 2024
0f61230
Refactor energy reporting and add debug logs
wiederm Jan 12, 2024
e472867
Wrap and rebuild neighborlist in LangevinIntegrator
wiederm Jan 12, 2024
b3b1ca1
Refactor MultiStateSampler and reset reporter file
wiederm Jan 12, 2024
d53fbc2
Refactor code to transpose u_kn array in MultiStateSampler and _Simul…
wiederm Jan 12, 2024
b02317c
Refactor _SimulationReporter class to improve code readability and ma…
wiederm Jan 13, 2024
918395c
Remove unused variable 'a' in MultiStateSampler class
wiederm Jan 13, 2024
4c6993c
Refactor position reporting in MultiStateSampler
wiederm Jan 13, 2024
9ebac30
Add save_traj_in_memory flag to LangevinIntegrator
wiederm Jan 14, 2024
4eb4599
Remove unused seed variable and update LangevinDynamicsMove constructor
wiederm Jan 14, 2024
7d593eb
Refactor code and add random seed functionality
wiederm Jan 14, 2024
eef82d1
Remove debug print statements and logging
wiederm Jan 14, 2024
8152b9f
Remove seed parameter from MetropolizedMove and MetropolisDisplacemen…
wiederm Jan 15, 2024
f6e2bae
Add PRNG seed initialization to test files
wiederm Jan 15, 2024
5455143
Add PRNG seed initialization to test files
wiederm Jan 17, 2024
bd3cc7c
Merge branch 'rep' of github.com:choderalab/chiron into rep
wiederm Jan 17, 2024
79634ff
refactoring
wiederm Jan 17, 2024
f203aca
Refactor reporter logging in integrators and mcmc
wiederm Jan 17, 2024
c2c0a14
Update PRNG key handling in chiron/mcmc.py, chiron/tests/test_testsys…
wiederm Jan 17, 2024
20fabb6
Refactor MultiStateSampler and reporters
wiederm Jan 17, 2024
337eec1
Add PRNG class with seed functionality
wiederm Jan 18, 2024
99ee26b
Fix LangevinIntegrator bug and update test_multistate.py
wiederm Jan 18, 2024
ebc27d3
Fix reporter visibility and add test for multistate reporter
wiederm Jan 18, 2024
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
4 changes: 2 additions & 2 deletions Examples/LJ_langevin.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,15 +43,15 @@
# build the neighbor list from the sampler state
nbr_list.build_from_state(sampler_state)

from chiron.reporters import SimulationReporter
from chiron.reporters import _SimulationReporter

# initialize a reporter to save the simulation data
filename = "test_lj.h5"
import os

if os.path.isfile(filename):
os.remove(filename)
reporter = SimulationReporter("test_lj.h5", lj_fluid.topology, 1)
reporter = _SimulationReporter("test_lj.h5", lj_fluid.topology, 1)

from chiron.integrators import LangevinIntegrator

Expand Down
6 changes: 3 additions & 3 deletions Examples/LJ_mcmove.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,23 +45,23 @@
# build the neighbor list from the sampler state
nbr_list.build_from_state(sampler_state)

from chiron.reporters import SimulationReporter
from chiron.reporters import _SimulationReporter

# initialize a reporter to save the simulation data
filename = "test_lj.h5"
import os

if os.path.isfile(filename):
os.remove(filename)
reporter = SimulationReporter("test_mc_lj.h5", lj_fluid.topology, 1)
reporter = _SimulationReporter("test_mc_lj.h5", lj_fluid.topology, 1)

from chiron.mcmc import MetropolisDisplacementMove

mc_move = MetropolisDisplacementMove(
seed=1234,
displacement_sigma=0.01 * unit.nanometer,
nr_of_moves=1000,
simulation_reporter=reporter,
reporter=reporter,
)

mc_move.run(sampler_state, thermodynamic_state, nbr_list, True)
60 changes: 60 additions & 0 deletions chiron/analysis.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import numpy as np


class MBAREstimator:
def __init__(self) -> None:
"""
Initialize the MBAR analysis class.

Returns:
- None
"""
self.mbar_f_k = None
self.mbar = None

def initialize(self, u_kn: np.ndarray, N_k: np.ndarray):
"""
Initialize the analysis object.

Parameters
----------
u_kn: np.ndarray
Array of dimensionless reduced potentials for each state.
N_k: np.ndarray
Array of number of samples for each state.

"""
from pymbar import MBAR
from loguru import logger as log

log.debug(f"{N_k=}")
self.mbar = MBAR(u_kn=u_kn, N_k=N_k)

@property
def f_k(self):
"""
Free energy for each state.

Returns
-------
mbar.f_k.
"""

from loguru import logger as log

log.debug(self.mbar.f_k)
return self.mbar.f_k

def get_free_energy_difference(self):
"""
Calculate the free energy difference between the endstates.

Returns
-------
float
"""
from loguru import logger as log

log.debug(self.mbar.f_k[-1])
self.f_k = self.mbar.f_k
return self.mbar_f_k[-1]
File renamed without changes.
117 changes: 78 additions & 39 deletions chiron/integrators.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@
from jax import random
from openmm import unit
from .states import SamplerState, ThermodynamicState
from .reporters import SimulationReporter
from .reporters import LangevinDynamicsReporter
from typing import Optional
from .potential import NeuralNetworkPotential
from .neighbors import PairsBase


class LangevinIntegrator:
Expand All @@ -25,7 +27,7 @@ def __init__(
stepsize=1.0 * unit.femtoseconds,
collision_rate=1.0 / unit.picoseconds,
save_frequency: int = 100,
reporter: Optional[SimulationReporter] = None,
reporter: Optional[LangevinDynamicsReporter] = None,
save_traj_in_memory: bool = False,
) -> None:
"""
Expand All @@ -41,8 +43,9 @@ def __init__(
Frequency of saving the simulation data. Default is 100.
reporter : SimulationReporter, optional
Reporter object for saving the simulation data. Default is None.
save_traj_in_memory : bool
Whether to save the trajectory in memory. For debugging purposes only.
save_traj_in_memory: bool
Flag indicating whether to save the trajectory in memory.
Default is False. NOTE: Only for debugging purposes.
"""
from loguru import logger as log

Expand All @@ -53,13 +56,13 @@ def __init__(

self.stepsize = stepsize
self.collision_rate = collision_rate
if reporter is not None:
log.info(f"Using reporter {reporter} saving to {reporter.filename}")
if reporter:
log.info(f"Using reporter {reporter} saving to {reporter.file_path}")
self.reporter = reporter
self.save_frequency = save_frequency
self.velocities = None
self.save_traj_in_memory = save_traj_in_memory
self.traj = []
self.velocities = None

def set_velocities(self, vel: unit.Quantity) -> None:
"""
Expand All @@ -77,8 +80,7 @@ def run(
sampler_state: SamplerState,
thermodynamic_state: ThermodynamicState,
n_steps: int = 5_000,
key=random.PRNGKey(0),
nbr_list=None,
nbr_list: Optional[PairsBase] = None,
progress_bar=False,
):
"""
Expand All @@ -92,9 +94,7 @@ def run(
The thermodynamic state of the system, including temperature and potential.
n_steps : int, optional
Number of simulation steps to perform.
key : jax.random.PRNGKey, optional
Random key for generating random numbers.
nbr_list : NeighborListNsqrd, optional
nbr_list : PairBase, optional
Neighbor list for the system.
progress_bar : bool, optional
Flag indicating whether to display a progress bar during integration.
Expand All @@ -116,8 +116,11 @@ def run(
log.debug("Running Langevin dynamics")
log.debug(f"n_steps = {n_steps}")
log.debug(f"temperature = {temperature}")
log.debug(f"Using seed: {key}")

# Initialize the random number generator
key = sampler_state.random_seed

# Convert to dimensionless quantities
kbT_unitless = (self.kB * temperature).value_in_unit_system(unit.md_unit_system)
mass_unitless = jnp.array(mass.value_in_unit_system(unit.md_unit_system))[
:, None
Expand All @@ -127,22 +130,24 @@ def run(
collision_rate_unitless = self.collision_rate.value_in_unit_system(
unit.md_unit_system
)
a = jnp.exp((-collision_rate_unitless * stepsize_unitless))
b = jnp.sqrt(1 - jnp.exp(-2 * collision_rate_unitless * stepsize_unitless))

# Initialize velocities
if self.velocities is None:
v0 = sigma_v * random.normal(key, x0.shape)
else:
v0 = self.velocities.value_in_unit_system(unit.md_unit_system)
# Convert to dimensionless quantities
a = jnp.exp((-collision_rate_unitless * stepsize_unitless))
b = jnp.sqrt(1 - jnp.exp(-2 * collision_rate_unitless * stepsize_unitless))

x = x0
v = v0

if nbr_list is not None:
nbr_list.build_from_state(sampler_state)

F = potential.compute_force(x, nbr_list)

# propagation loop
for step in tqdm(range(n_steps)) if self.progress_bar else range(n_steps):
key, subkey = random.split(key)
# v
Expand All @@ -151,46 +156,80 @@ def run(
x += (stepsize_unitless * 0.5) * v

if nbr_list is not None:
x = nbr_list.space.wrap(x)
# check if we need to rebuild the neighborlist after moving the particles
if nbr_list.check(x):
nbr_list.build(x, self.box_vectors)
x = self._wrap_and_rebuild_neighborlist(x, nbr_list)
# o
random_noise_v = random.normal(subkey, x.shape)
v = (a * v) + (b * sigma_v * random_noise_v)

x += (stepsize_unitless * 0.5) * v
if nbr_list is not None:
x = nbr_list.space.wrap(x)
# check if we need to rebuild the neighborlist after moving the particles
if nbr_list.check(x):
nbr_list.build(x, self.box_vectors)
x = self._wrap_and_rebuild_neighborlist(x, nbr_list)

F = potential.compute_force(x, nbr_list)
# v
v += (stepsize_unitless * 0.5) * F / mass_unitless

if step % self.save_frequency == 0:
# log.debug(f"Saving at step {step}")
# check if reporter is attribute of the class
# log.debug(f"step {step} energy {potential.compute_energy(x, nbr_list)}")
# log.debug(f"step {step} force {F}")

if hasattr(self, "reporter") and self.reporter is not None:
d = {
"traj": x,
"energy": potential.compute_energy(x, nbr_list),
"step": step,
}
if nbr_list is not None:
d["box_vectors"] = nbr_list.space.box_vectors

# log.debug(d)
self.reporter.report(d)
self._report(x, potential, nbr_list, step)

if self.save_traj_in_memory:
self.traj.append(x)

log.debug("Finished running Langevin dynamics")
# save the final state of the simulation in the sampler_state object
sampler_state.x0 = x
sampler_state.v0 = v
# self.reporter.close()

def _wrap_and_rebuild_neighborlist(self, x: jnp.array, nbr_list: PairsBase):
"""
Wrap the coordinates and rebuild the neighborlist if necessary.

Parameters
----------
x: jnp.array
The coordinates of the particles.
nbr_list: PairsBsse
The neighborlist object.
"""

x = nbr_list.space.wrap(x)
# check if we need to rebuild the neighborlist after moving the particles
if nbr_list.check(x):
nbr_list.build(x, self.box_vectors)
return x

def _report(
self,
x: jnp.array,
potential: NeuralNetworkPotential,
nbr_list: PairsBase,
step: int,
):
"""
Reports the trajectory, energy, step, and box vectors (if available) to the reporter.

Parameters
----------
x : jnp.array
current coordinate set
potential: NeuralNetworkPotential
potential used to compute the energy and force
nbr_list: PairsBase
The neighbor list
step: int
The current time step.

Returns:
None
"""
d = {
"traj": x,
"energy": potential.compute_energy(x, nbr_list),
"step": step,
}
if nbr_list is not None:
d["box_vectors"] = nbr_list.space.box_vectors

self.reporter.report(d)
Binary file added chiron/langevin_reporter_test_md.h5
Binary file not shown.
Loading