Skip to content

Commit 8ede30b

Browse files
author
Ludwig Schneider
authored
Merge pull request #149 from InnocentBug/parallel2
Reimplement parallelization API
2 parents ea1dd0e + 505fb1c commit 8ede30b

16 files changed

Lines changed: 441 additions & 125 deletions

File tree

.github/workflows/docker-ci.yml

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,30 @@ jobs:
8484
docker load --input /tmp/pysages.tar
8585
docker run -t pysages bash -c "cd PySAGES/examples/openmm/metad/ && python3 ./alanine-dipeptide.py --time-steps=25"
8686
87+
alanine-dipeptide-openmm-mpi:
88+
runs-on: ubuntu-latest
89+
needs: build
90+
steps:
91+
- name: Set up Docker Buildx
92+
uses: docker/setup-buildx-action@v1
93+
- name: Download artifact
94+
uses: actions/download-artifact@v2
95+
with:
96+
name: pysages
97+
path: /tmp
98+
- name: Set up script
99+
id: example
100+
run: |
101+
SCRIPT="cd PySAGES/examples/openmm/umbrella_integration &&"
102+
SCRIPT="${SCRIPT} mpirun --allow-run-as-root --oversubscribe -n 6"
103+
SCRIPT="${SCRIPT} python3 -m mpi4py.futures"
104+
SCRIPT="${SCRIPT} integration.py --replicas=5 --time-steps=1000 --mpi"
105+
echo "::set-output name=script::$SCRIPT"
106+
- name: Load and run test
107+
run: |
108+
docker load --input /tmp/pysages.tar
109+
docker run -t pysages bash -c "${{ steps.example.outputs.script }}"
110+
87111
metad-hoomd:
88112
runs-on: ubuntu-latest
89113
needs: build
@@ -143,3 +167,35 @@ jobs:
143167
name: umbrella-integration-plots
144168
path: /tmp/plots
145169
retention-days: 1
170+
171+
umbrella-integration-hoomd-mpi:
172+
runs-on: ubuntu-latest
173+
needs: build
174+
steps:
175+
- name: Set up Docker Buildx
176+
uses: docker/setup-buildx-action@v1
177+
- name: Download artifact
178+
uses: actions/download-artifact@v2
179+
with:
180+
name: pysages
181+
path: /tmp
182+
- name: Set up script
183+
id: setup
184+
run: |
185+
SCRIPT="cd PySAGES/examples/hoomd-blue/umbrella_integration &&"
186+
SCRIPT="${SCRIPT} python3 ./gen_gsd.py &&"
187+
SCRIPT="${SCRIPT} mpirun --allow-run-as-root --oversubscribe -n 6"
188+
SCRIPT="${SCRIPT} python3 -m mpi4py.futures"
189+
SCRIPT="${SCRIPT} integration.py --replicas=5 --time-steps=1000 --mpi &&"
190+
SCRIPT="${SCRIPT} mkdir /tmp/plots && mv *.pdf /tmp/plots/"
191+
echo "::set-output name=script::$SCRIPT"
192+
- name: Load and run test
193+
run: |
194+
docker load --input /tmp/pysages.tar
195+
docker run -v /tmp:/tmp -t pysages bash -c "${{ steps.setup.outputs.script }}"
196+
- name: Upload artifacts
197+
uses: actions/upload-artifact@v2
198+
with:
199+
name: umbrella-integration-mpi-plots
200+
path: /tmp/plots
201+
retention-days: 1

.gitignore

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
__pycache__/
2-
pysages/_version.py
2+
**.pdf
33
build/
44
dist/
55
docs/build
6+
examples/**/*.dat
7+
examples/**/*.gsd
8+
examples/**/.ipynb_checkpoints/
9+
pysages/_version.py
610
pysages.egg-info/
7-
examples/*/*pdf
8-
examples/*/*dat
9-
examples/*/*gsd

examples/hoomd-blue/umbrella_integration/integration.py

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#!/usr/bin/env python3
22
import sys
33
import argparse
4+
import importlib
45
import numpy as np
56
import matplotlib.pyplot as plt
67

@@ -10,14 +11,19 @@
1011

1112
import pysages
1213
from pysages.colvars import Component
13-
from pysages.methods import UmbrellaIntegration
14+
from pysages.methods import UmbrellaIntegration, SerialExecutor
1415

1516

1617
params = {"A": 0.5, "w": 0.2, "p": 2}
1718

1819

1920
def generate_context(**kwargs):
20-
hoomd.context.initialize("")
21+
if kwargs.get("mpi_enabled"):
22+
MPI = importlib.import_module("mpi4py.MPI")
23+
init_kwargs = {"mpi_comm": MPI.COMM_SELF}
24+
else:
25+
init_kwargs = {}
26+
hoomd.context.initialize("--single-mpi", **init_kwargs)
2127
context = hoomd.context.SimulationContext()
2228

2329
with context:
@@ -106,6 +112,7 @@ def get_args(argv):
106112
parser = argparse.ArgumentParser(description="Example script to run umbrella integration")
107113
for (name, short, T, val, doc) in available_args:
108114
parser.add_argument("--" + name, "-" + short, type=T, default=T(val), help=doc)
115+
parser.add_argument("--mpi", action="store_true", help="Use MPI executor")
109116
args = parser.parse_args(argv)
110117
return args
111118

@@ -119,6 +126,13 @@ def post_run_action(**kwargs):
119126
)
120127

121128

129+
def get_executor(args):
130+
if args.mpi:
131+
futures = importlib.import_module("mpi4py.futures")
132+
return futures.MPIPoolExecutor()
133+
return SerialExecutor()
134+
135+
122136
def main(argv):
123137
args = get_args(argv)
124138

@@ -127,8 +141,15 @@ def main(argv):
127141
centers = list(np.linspace(args.start_path, args.end_path, args.replicas))
128142
method = UmbrellaIntegration(cvs, args.k_spring, centers, args.log_period, args.log_delay)
129143

144+
context_args = {"mpi_enabled": args.mpi}
145+
130146
raw_result = pysages.run(
131-
method, generate_context, args.time_steps, post_run_action=post_run_action
147+
method,
148+
generate_context,
149+
args.time_steps,
150+
context_args=context_args,
151+
post_run_action=post_run_action,
152+
executor=get_executor(args),
132153
)
133154
result = pysages.analyze(raw_result)
134155

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#!/bin/sh
2+
3+
#SBATCH --job-name=umbrella
4+
#SBATCH --partition=gpu
5+
#SBATCH --nodes=1
6+
#SBATCH --ntasks-per-node=5
7+
#SBATCH --time=01:00:00
8+
#SBATCH --output=out.txt
9+
#SBATCH --gres=gpu:4
10+
#SBATCH --mem=20G
11+
12+
#python harmonic_bias.py
13+
mpirun -n 5 python -m mpi4py.futures integration.py

examples/openmm/Harmonic_Bias.ipynb

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -437,17 +437,26 @@
437437
"provenance": []
438438
},
439439
"jupytext": {
440-
"formats": "ipynb,md",
441-
"main_language": "python"
440+
"formats": "ipynb,md"
442441
},
443442
"kernelspec": {
444443
"display_name": "Python 3",
444+
"language": "python",
445445
"name": "python3"
446446
},
447447
"language_info": {
448-
"name": "python"
448+
"codemirror_mode": {
449+
"name": "ipython",
450+
"version": 3
451+
},
452+
"file_extension": ".py",
453+
"mimetype": "text/x-python",
454+
"name": "python",
455+
"nbconvert_exporter": "python",
456+
"pygments_lexer": "ipython3",
457+
"version": "3.9.2"
449458
}
450459
},
451460
"nbformat": 4,
452-
"nbformat_minor": 0
461+
"nbformat_minor": 1
453462
}

examples/openmm/Harmonic_Bias.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@
22
jupyter:
33
jupytext:
44
formats: ipynb,md
5-
main_language: python
65
text_representation:
76
extension: .md
87
format_name: markdown
98
format_version: '1.3'
109
jupytext_version: 1.14.0
1110
kernelspec:
1211
display_name: Python 3
12+
language: python
1313
name: python3
1414
---
1515

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../../inputs/alanine-dipeptide/adp-explicit.pdb
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
#!/usr/bin/env python3
2+
3+
import sys
4+
import argparse
5+
import importlib
6+
import numpy as np
7+
8+
from pysages.colvars import DihedralAngle
9+
from pysages.methods import UmbrellaIntegration, SerialExecutor
10+
from pysages.utils import try_import
11+
12+
import pysages
13+
14+
openmm = try_import("openmm", "simtk.openmm")
15+
unit = try_import("openmm.unit", "simtk.unit")
16+
app = try_import("openmm.app", "simtk.openmm.app")
17+
18+
19+
def generate_simulation(**kwargs):
20+
"""
21+
Generates a simulation context, we pass this function to `pysages.run`.
22+
"""
23+
pdb_filename = "adp-explicit.pdb"
24+
T = 298.15 * unit.kelvin
25+
dt = 2.0 * unit.femtoseconds
26+
pdb = app.PDBFile(pdb_filename)
27+
28+
ff = app.ForceField("amber99sb.xml", "tip3p.xml")
29+
cutoff_distance = 1.0 * unit.nanometer
30+
topology = pdb.topology
31+
system = ff.createSystem(
32+
topology,
33+
constraints=app.HBonds,
34+
nonbondedMethod=app.NoCutoff,
35+
nonbondedCutoff=cutoff_distance,
36+
)
37+
38+
positions = pdb.getPositions(asNumpy=True)
39+
40+
integrator = openmm.LangevinIntegrator(T, 1 / unit.picosecond, dt)
41+
42+
simulation = app.Simulation(topology, system, integrator)
43+
simulation.context.setPositions(positions)
44+
simulation.minimizeEnergy()
45+
46+
return simulation
47+
48+
49+
def get_args(argv):
50+
available_args = [
51+
("k-spring", "k", float, 50, "Spring constant for each replica"),
52+
("replicas", "N", int, 25, "Number of replicas along the path"),
53+
("time-steps", "t", int, 1e4, "Number of simulation steps for each replica"),
54+
("log-period", "l", int, 100, "Frequency of logging the CVs into each histogram"),
55+
("log-delay", "d", int, 0, "Number of timesteps to discard before logging"),
56+
]
57+
parser = argparse.ArgumentParser(description="Example script to run umbrella integration")
58+
for (name, short, T, val, doc) in available_args:
59+
parser.add_argument("--" + name, "-" + short, type=T, default=T(val), help=doc)
60+
parser.add_argument("--mpi", action="store_true", help="Use MPI executor")
61+
args = parser.parse_args(argv)
62+
return args
63+
64+
65+
def get_executor(args):
66+
if args.mpi:
67+
futures = importlib.import_module("mpi4py.futures")
68+
return futures.MPIPoolExecutor()
69+
return SerialExecutor()
70+
71+
72+
def main(argv):
73+
args = get_args(argv)
74+
75+
cvs = (DihedralAngle((4, 6, 8, 14)), DihedralAngle((6, 8, 14, 16)))
76+
centers = []
77+
center_pos = np.linspace(+0.45 * np.pi, -0.45 * np.pi, args.replicas)
78+
for pos in center_pos:
79+
centers.append((pos, pos))
80+
method = UmbrellaIntegration(cvs, args.k_spring, centers, args.log_period, args.log_delay)
81+
82+
raw_result = pysages.run(
83+
method,
84+
generate_simulation,
85+
args.time_steps,
86+
# post_run_action=post_run_action,
87+
executor=get_executor(args),
88+
)
89+
result = pysages.analyze(raw_result)
90+
print(result)
91+
92+
return result
93+
94+
95+
if __name__ == "__main__":
96+
main(sys.argv[1:])

0 commit comments

Comments
 (0)