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

Refactoring of MC moves #21

Merged
merged 47 commits into from
Feb 27, 2024
Merged
Show file tree
Hide file tree
Changes from 38 commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
b313867
Fix u_kn shape in test_multistate_run
wiederm Jan 18, 2024
a9d9f16
Refactor MCMCMove subclasses and update method names
wiederm Jan 18, 2024
1ab9b75
Merge branch 'multistage' into mc
wiederm Jan 21, 2024
15ba2e8
Merge branch 'multistage' into mc
chrisiacovella Jan 22, 2024
4af534a
Started MC refactor, merging from multistage branch that includes new…
chrisiacovella Jan 22, 2024
70b3997
Merge remote-tracking branch 'main_origin/mc' into mc
chrisiacovella Jan 22, 2024
53f0616
Small refactor to Langevin integrator. Small changes: run command now…
chrisiacovella Jan 23, 2024
0117630
created initialize_velocities funtion in utilities; langevin code now…
chrisiacovella Jan 23, 2024
5e52df2
Finished fixing up langevin integrator/move
chrisiacovella Jan 25, 2024
469071d
Implemented Metropolis displacement move in new scheme.
chrisiacovella Jan 25, 2024
a3ea44d
Added refactored barostat move and ideal gas test case.
chrisiacovella Jan 25, 2024
4ad677e
Abstractmethod for _reporting wrapper (this will make it easier for e…
chrisiacovella Jan 25, 2024
9c4d47b
Added _reporter to the displacement moves and the ability to only mov…
chrisiacovella Jan 26, 2024
b6d88cc
Added in stepsize updater to allow move_parameters to be updated on t…
chrisiacovella Jan 29, 2024
c5bce7a
Continued reworking to ensure expected behavior of loggers and stepsi…
chrisiacovella Jan 30, 2024
df93003
Fixed test_integrator and atom_subsetting in displacement move.
chrisiacovella Jan 30, 2024
acfe5e2
Added in flag to initialize velocities the first time langevin is cal…
chrisiacovella Jan 30, 2024
beb0bdc
test_multistate still is failing an assertion, but I think that is fi…
chrisiacovella Jan 30, 2024
0b57c2b
Modified the routines to ensure that passing the nbr_list fits in mor…
chrisiacovella Jan 31, 2024
85a9ae4
Added ideal gas test written in the other PR. updated logic in lange…
chrisiacovella Jan 31, 2024
67a9831
Added ideal gas test written in the other PR. updated logic in lange…
chrisiacovella Jan 31, 2024
ea1695a
Added in additional tests for barostat
chrisiacovella Jan 31, 2024
c992b04
Fixed convergence test syntax: these were missed on my part because t…
chrisiacovella Feb 1, 2024
0f049b2
Fixed convergence test syntax: these were missed on my part because t…
chrisiacovella Feb 1, 2024
21ff068
Updated CI.yaml to enabled CI to run for branches commiting to the mu…
chrisiacovella Feb 2, 2024
2074665
Added in skip to the multistate testing, as this still needs to be wo…
chrisiacovella Feb 2, 2024
e339e44
match/case statements don't exist in python 3.9. I commented this ou…
chrisiacovella Feb 2, 2024
231f830
fixture was missing in test_testsystems
chrisiacovella Feb 2, 2024
9085a43
fixture was missing in test_testsystems
chrisiacovella Feb 2, 2024
88ee9ba
weirdly wrong syntax in the test ideal gas test.
chrisiacovella Feb 2, 2024
030435c
Updating ideal gas example.
chrisiacovella Feb 9, 2024
3ec2aa0
Update Examples/Idealgas.py with descriptive assert statement
chrisiacovella Feb 9, 2024
0987cf2
Update Examples/Idealgas.py with descriptive assert statement
chrisiacovella Feb 9, 2024
fc9ede3
Updating ideal gas example.
chrisiacovella Feb 9, 2024
1daab8a
Updating ideal gas example.
chrisiacovella Feb 9, 2024
e419169
removed velocity initialization flag in langevin
chrisiacovella Feb 9, 2024
9ad8a23
updated various functions in reponse to marcus' comments.
chrisiacovella Feb 9, 2024
9e000db
Merge branch 'multistage' into mc
chrisiacovella Feb 9, 2024
60e0814
Merge failed to correctly merge, and broke MCMCSampler; fixed now. m…
chrisiacovella Feb 10, 2024
e78992d
Working through comments from Jchodera.
chrisiacovella Feb 12, 2024
07fe368
Fixed error with multistate systems. Various other updates based on c…
chrisiacovella Feb 20, 2024
6a2733b
Changed `coordinates` to `positions` in neighbor/pairlist routines to…
chrisiacovella Feb 20, 2024
6337ce5
Changed PairListNsqrd to allow setting the cutoff to None which will …
chrisiacovella Feb 21, 2024
12d3b10
mised an instance where the reporter called space.box_vectors. fixed.
chrisiacovella Feb 22, 2024
47ab2cc
name refactoring in MCMC
chrisiacovella Feb 22, 2024
192604e
further addressing comments.
chrisiacovella Feb 23, 2024
ba3817d
Added accumulated for number_of_attemps_made instead of elapsed_steps…
chrisiacovella Feb 27, 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
1 change: 1 addition & 0 deletions .github/workflows/CI.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ on:
pull_request:
branches:
- "main"
- "multistage"
wiederm marked this conversation as resolved.
Show resolved Hide resolved
schedule:
# Weekly tests run on main by default:
# Scheduled workflows run on the latest commit on the default or base branch.
Expand Down
148 changes: 148 additions & 0 deletions Examples/Idealgas.py
chrisiacovella marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
from openmmtools.testsystems import IdealGas
from openmm import unit

"""
This example explore an ideal gas system, where the particles are non-interacting.
This will use the MonteCarloBarostatMove to sample the volume of the system and
MetropolisDisplacementMove to sample the particle positions.

This utilizes the IdealGas example from openmmtools to initialize particle positions and topology.

"""

# Use the IdealGas example from openmmtools to initialize particle positions and topology
# For this example, the topology provides the masses for the particles

n_particles = 216
wiederm marked this conversation as resolved.
Show resolved Hide resolved
temperature = 298 * unit.kelvin
pressure = 1 * unit.atmosphere

ideal_gas = IdealGas(nparticles=n_particles, temperature=temperature, pressure=pressure)


from chiron.potential import IdealGasPotential
wiederm marked this conversation as resolved.
Show resolved Hide resolved
from chiron.utils import PRNG, get_list_of_mass
import jax.numpy as jnp

# particles are non interacting
cutoff = 0.0 * unit.nanometer
ideal_gas_potential = IdealGasPotential(ideal_gas.topology)

from chiron.states import SamplerState, ThermodynamicState

# define the thermodynamic state
thermodynamic_state = ThermodynamicState(
potential=ideal_gas_potential,
temperature=temperature,
pressure=pressure,
)

PRNG.set_seed(1234)


# define the sampler state
sampler_state = SamplerState(
x0=ideal_gas.positions,
current_PRNG_key=PRNG.get_random_key(),
box_vectors=ideal_gas.system.getDefaultPeriodicBoxVectors(),
)

from chiron.neighbors import PairList, OrthogonalPeriodicSpace

# define the pair list for an orthogonal periodic space
# since particles are non-interacting, this will not really do much
# but will be used to appropriately wrap particles in space
nbr_list = PairList(OrthogonalPeriodicSpace(), cutoff=cutoff)
nbr_list.build_from_state(sampler_state)

from chiron.reporters import MCReporter

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

if os.path.isfile(filename):
os.remove(filename)
reporter = MCReporter(filename, 100)


from chiron.mcmc import (
MetropolisDisplacementMove,
MonteCarloBarostatMove,
MoveSchedule,
MCMCSampler,
)

# initialize the displacement move
mc_barostat_move = MonteCarloBarostatMove(
volume_max_scale=0.2,
nr_of_moves=10,
Copy link
Member

Choose a reason for hiding this comment

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

Could we standardize abbreviations or avoid them altogether, such as n_moves or number_of_moves? Or maybe we can even say n_attempts_per_step?

Copy link
Member

@chrisiacovella chrisiacovella Feb 12, 2024

Choose a reason for hiding this comment

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

But what if we run out of bytes with such long names?! I think that is a great point; n_attempts_per_step would probably be very clear.

Edit: Since this will also apply to the langevin integrator move, we might not want to use the word "attempts" in the variable. number_of_moves might be more generally applicable.

Copy link
Member

Choose a reason for hiding this comment

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

The Langevin move is a special case of un-Metropolized move, where every step is accepted.
These moves are also distinct in that the Langevin move has "number of steps to take per move application" and a Monte Carlo barostat has a "number of volume adjustments to attempt per move application". In either case, something like n_steps and n_attempts is probably more appropriate for those move types.

reporter=reporter,
update_stepsize=True,
Copy link
Member

Choose a reason for hiding this comment

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

Instead of update, we might want to refer to this as autotune.
frequency would refer to number of events per step, but interval or period refers to the number of steps between events.

Suggest:

  • update_stepsize=True -> autotune=True
  • update_stepsize_frequency=100 -> autotune_interval=100

Copy link
Member

Choose a reason for hiding this comment

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

This will be much clearer. I was struggling with coming up with a clear/general name for this.

Copy link
Member

Choose a reason for hiding this comment

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

I also updated the name of the internal class function to be self._autotune

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's fine to make self.autotune be public, since it is reasonable to allow a user to change the autotune behavior on the fly.

update_stepsize_frequency=100,
)

# initialize the barostat move and the move schedule
metropolis_displacement_move = MetropolisDisplacementMove(
displacement_sigma=0.1 * unit.nanometer,
nr_of_moves=100,
update_stepsize=True,
update_stepsize_frequency=100,
)

# define the move schedule
move_set = MoveSchedule(
[
("MetropolisDisplacementMove", metropolis_displacement_move),
("MonteCarloBarostatMove", mc_barostat_move),
]
)

sampler = MCMCSampler(move_set, sampler_state, thermodynamic_state)
sampler.run(n_iterations=10, nbr_list=nbr_list) # how many times to repeat

# get the volume from the reporter
volume = reporter.get_property("volume")
step = reporter.get_property("elapsed_step")


import matplotlib.pyplot as plt

plt.plot(step, volume)
plt.show()

# get expectations
ideal_volume = ideal_gas.get_volume_expectation(thermodynamic_state)
chrisiacovella marked this conversation as resolved.
Show resolved Hide resolved
ideal_volume_std = ideal_gas.get_volume_standard_deviation(thermodynamic_state)

print("ideal volume and standard deviation: ", ideal_volume, ideal_volume_std)


volume_mean = jnp.mean(jnp.array(volume)) * unit.nanometer**3
volume_std = jnp.std(jnp.array(volume)) * unit.nanometer**3


print("measured volume and standard deviation: ", volume_mean, volume_std)

# get the masses of particles from the topology
masses = get_list_of_mass(ideal_gas.topology)
chrisiacovella marked this conversation as resolved.
Show resolved Hide resolved

sum_of_masses = jnp.sum(jnp.array(masses.value_in_unit(unit.amu))) * unit.amu

ideal_density = sum_of_masses / unit.AVOGADRO_CONSTANT_NA / ideal_volume
chrisiacovella marked this conversation as resolved.
Show resolved Hide resolved
measured_density = sum_of_masses / unit.AVOGADRO_CONSTANT_NA / volume_mean

assert jnp.isclose(
ideal_density.value_in_unit(unit.kilogram / unit.meter**3),
measured_density.value_in_unit(unit.kilogram / unit.meter**3),
atol=1e-1,
)
# see if within 5% of ideal volume
assert (
abs(ideal_volume - volume_mean) / ideal_volume < 0.05
), f"Warning: {abs(ideal_volume - volume_mean) / ideal_volume} exceeds the 5% threshold"

# see if within 10% of the ideal standard deviation of the volume
assert (
abs(ideal_volume_std - volume_std) / ideal_volume_std < 0.1
), f"Warning: {abs(ideal_volume_std - volume_std) / ideal_volume_std} exceeds the 10% threshold"
159 changes: 159 additions & 0 deletions Examples/LJ_MCMC.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
from openmm import unit
chrisiacovella marked this conversation as resolved.
Show resolved Hide resolved
from openmm import app

"""
This example explore a Lennard-Jones system, where a single bead represents a united atom methane molecule,
modeled with the UA-TraPPE force field.


"""
n_particles = 1100
temperature = 140 * unit.kelvin
pressure = 13.00765 * unit.atmosphere
mass = unit.Quantity(16.04, unit.gram / unit.mole)

# create the topology
lj_topology = app.Topology()
element = app.Element(1000, "CH4", "CH4", mass)
chain = lj_topology.addChain()
for i in range(n_particles):
residue = lj_topology.addResidue("CH4", chain)
lj_topology.addAtom("CH4", element, residue)

import jax.numpy as jnp

# these were generated in Mbuild using fill_box which wraps packmol
# a minimum spacing of 0.4 nm was used during construction.

from chiron.utils import get_full_path

positions = jnp.load(get_full_path("Examples/methane_coords.npy")) * unit.nanometer

box_vectors = jnp.array(
Copy link
Member

Choose a reason for hiding this comment

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

Move unit.nanometer into the definition of box_vectors. It's inconsistent to have some variables like positions contain units and box_vectors not contain units.

[
[4.275021399280942, 0.0, 0.0],
[0.0, 4.275021399280942, 0.0],
[0.0, 0.0, 4.275021399280942],
]
)

from chiron.potential import LJPotential
from chiron.utils import PRNG
import jax.numpy as jnp

#

# initialize the LennardJones potential for UA-TraPPE methane
#
sigma = 0.373 * unit.nanometer
epsilon = 0.2941 * unit.kilocalories_per_mole
cutoff = 1.4 * unit.nanometer

lj_potential = LJPotential(lj_topology, sigma=sigma, epsilon=epsilon, cutoff=cutoff)

from chiron.states import SamplerState, ThermodynamicState

# define the thermodynamic state
thermodynamic_state = ThermodynamicState(
potential=lj_potential,
temperature=temperature,
pressure=pressure,
)

PRNG.set_seed(1234)


# define the sampler state
sampler_state = SamplerState(
x0=positions,
current_PRNG_key=PRNG.get_random_key(),
box_vectors=box_vectors * unit.nanometer,
Copy link
Member

Choose a reason for hiding this comment

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

Move unit.nanometer into the definition of box_vectors. It's inconsistent to have some variables like positions contain units and box_vectors not contain units.

)


from chiron.neighbors import PairList, OrthogonalPeriodicSpace

# define the pair list for an orthogonal periodic space
# since particles are non-interacting, this will not really do much
# but will appropriately wrap particles in space
nbr_list = PairList(OrthogonalPeriodicSpace(), cutoff=cutoff)
nbr_list.build_from_state(sampler_state)

# CRI: minimizer is not working correctly on my mac
# from chiron.minimze import minimize_energy
#
# results = minimize_energy(
# sampler_state.x0, lj_potential.compute_energy, nbr_list, maxiter=100
# )
#
# min_x = results.params
#
# sampler_state.x0 = min_x

Copy link
Member Author

@wiederm wiederm Feb 8, 2024

Choose a reason for hiding this comment

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

Can you open an issue and explain the error you are seeing on your machine? I just tested this on Linux, and it runs without error.

Copy link
Member

Choose a reason for hiding this comment

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

Yes. I was going to do some more investigation separately.

from chiron.reporters import MCReporter, LangevinDynamicsReporter

# initialize a reporter to save the simulation data
filename_barostat = "test_mc_lj_barostat.h5"
Copy link
Member

Choose a reason for hiding this comment

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

Instead of a separate reporter for each MCMove, can we use the same reporter for all MCMove classes and just have each MCMove object create its own HDF5 group to store its data? That drastically reduces the complexity of this example.

Copy link
Member

@chrisiacovella chrisiacovella Feb 12, 2024

Choose a reason for hiding this comment

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

@wiederm What are your thoughts? I feel that is definitely doable (probably some small changes to the API)...not sure if worth it though, since the idea was using these more for debugging (but that could make it even more relevant, simplify the interface to ensure we have good debug logging with minimal effort).

filename_displacement = "test_mc_lj_disp.h5"
filename_langevin = "test_mc_lj_langevin.h5"

import os

if os.path.isfile(filename_barostat):
os.remove(filename_barostat)
reporter_barostat = MCReporter(filename_barostat, 1)

if os.path.isfile(filename_displacement):
os.remove(filename_displacement)
reporter_displacement = MCReporter(filename_displacement, 10)

if os.path.isfile(filename_langevin):
os.remove(filename_langevin)
reporter_langevin = LangevinDynamicsReporter(filename_langevin, 10)

from chiron.mcmc import (
Copy link
Member

Choose a reason for hiding this comment

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

I prefer importing the object just before you use it, since it makes it easier for people to copy-and-paste parts of examples this way. So instead, I suggest:

from chiron.mcmc import MonteCarloBarostatMove
mc_barostat_move = MonteCarloBarostatMove(...)

from chiron.mcmc import LangevinDynamicMove
langevin_dynamics_move = LangevinDynamicsMove(...)

...

MetropolisDisplacementMove,
MonteCarloBarostatMove,
LangevinDynamicsMove,
MoveSchedule,
MCMCSampler,
)

mc_displacement_move = MetropolisDisplacementMove(
displacement_sigma=0.001 * unit.nanometer,
nr_of_moves=100,
reporter=reporter_displacement,
report_frequency=10,
update_stepsize=True,
update_stepsize_frequency=100,
)

mc_barostat_move = MonteCarloBarostatMove(
volume_max_scale=0.1,
nr_of_moves=10,
reporter=reporter_barostat,
report_frequency=1,
update_stepsize=True,
update_stepsize_frequency=50,
)

langevin_dynamics_move = LangevinDynamicsMove(
stepsize=1.0 * unit.femtoseconds,
collision_rate=1.0 / unit.picoseconds,
nr_of_steps=100,
reporter=reporter_langevin,
report_frequency=10,
initialize_velocities=True,
)


move_set = MoveSchedule(
[
("LangevinDynamicsMove", langevin_dynamics_move),
# ("MetropolisDisplacementMove", mc_displacement_move),
("MonteCarloBarostatMove", mc_barostat_move),
]
)

sampler = MCMCSampler(move_set, sampler_state, thermodynamic_state)
sampler.run(n_iterations=100, nbr_list=nbr_list) # how many times to repeat
Loading
Loading