Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
1be1b2d
start to SimStore support
dwhswenson Oct 23, 2020
e773c1f
fix tests
dwhswenson Oct 23, 2020
67c1b22
Tests for SimStore support
dwhswenson Nov 27, 2020
22ca390
monkey patching and snapshots for SimStore
dwhswenson Dec 6, 2020
132f23e
improve error message when missing objects
dwhswenson Dec 6, 2020
df83b71
Only monkeypatch SimStore once; better error msg
dwhswenson Dec 8, 2020
695ed8b
Add support for --table option in `contents`
dwhswenson Dec 8, 2020
f983f35
Steps toward allowing multiple --init_conds
dwhswenson Dec 8, 2020
fbe73da
Add md command
dwhswenson Dec 8, 2020
7948e37
OPSStorageLoadMultiple for INIT_CONDS
dwhswenson Dec 8, 2020
3229fba
Add test for getting multiple init conds
dwhswenson Dec 8, 2020
47e41af
Tests for --table arg to contents
dwhswenson Dec 8, 2020
98f3868
Add tests for md
dwhswenson Dec 9, 2020
830ef03
cleanup
dwhswenson Dec 9, 2020
6125554
tests for md command
dwhswenson Dec 9, 2020
00b83cc
more test coverage
dwhswenson Dec 9, 2020
dd4ec60
Some cleanup in contents; complete testing
dwhswenson Dec 9, 2020
586f1a7
docstrings
dwhswenson Dec 9, 2020
586e6e4
add test for tags table
dwhswenson Dec 9, 2020
8df4a54
Add progress reporting to md command
dwhswenson Dec 9, 2020
6531c49
Merge pull request #34 from dwhswenson/multiple-traj-init-conds
dwhswenson Dec 10, 2020
fb91628
Merge remote-tracking branch 'upstream/master' into md
dwhswenson Dec 10, 2020
10dcb1b
Merge pull request #33 from dwhswenson/content-table
dwhswenson Dec 11, 2020
38e01ba
Merge remote-tracking branch 'upstream/master' into md
dwhswenson Dec 11, 2020
bdace7f
Merge remote-tracking branch 'upstream/master' into simstore
dwhswenson Dec 11, 2020
7726d8c
Tell InterfaceSet to use SimStore when needed
dwhswenson Dec 11, 2020
8e9fa46
more tests; docstrings
dwhswenson Dec 15, 2020
29cd0d0
Merge pull request #35 from dwhswenson/md
dwhswenson Dec 17, 2020
3aa79ba
Merge remote-tracking branch 'upstream/master' into simstore
dwhswenson Dec 17, 2020
5650b49
Bump to 0.2.0.dev0; update autorelease
dwhswenson Dec 22, 2020
0aa3aa2
Merge pull request #36 from dwhswenson/bump-0.2.0.dev0
dwhswenson Dec 22, 2020
941e6ff
Merge remote-tracking branch 'upstream/master' into simstore
dwhswenson Dec 24, 2020
8695676
add reqs for SimStore
dwhswenson Dec 25, 2020
107f1db
Undo SimStore monkey patches after tests
dwhswenson Jan 1, 2021
17a0319
add a couple tests to complete coverage
dwhswenson Jan 1, 2021
40a503b
Merge pull request #29 from dwhswenson/simstore
dwhswenson Jan 1, 2021
3daed14
Release 0.2.0
dwhswenson Jan 1, 2021
aa58da0
use custom test-testpypi to add simstore reqs
dwhswenson Jan 1, 2021
b7b22e7
one-liner of test-testpypi
dwhswenson Jan 1, 2021
7fdf2cf
will && work?
dwhswenson Jan 1, 2021
3931c99
Fix bash variable
dwhswenson Jan 2, 2021
ecc32f2
Try updating autorelease vendoring
dwhswenson Jan 2, 2021
e36d201
don't forget to add the file!
dwhswenson Jan 2, 2021
a2a10aa
fix missing dots
dwhswenson Jan 2, 2021
b77dac4
install extras in autorelease install
dwhswenson Jan 4, 2021
3fb2f49
remove comment
dwhswenson Jan 4, 2021
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
2 changes: 2 additions & 0 deletions .autorelease/test-testpypi.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
python -m pip install sqlalchemy dill pytest
py.test --pyargs paths_cli
2 changes: 1 addition & 1 deletion .github/workflows/autorelease-default-env.sh
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
INSTALL_AUTORELEASE="python -m pip install autorelease==0.2.3"
INSTALL_AUTORELEASE="python -m pip install autorelease==0.2.6"
if [ -f autorelease-env.sh ]; then
source autorelease-env.sh
fi
Expand Down
6 changes: 4 additions & 2 deletions .github/workflows/autorelease-deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ jobs:
python-version: "3.x"
- run: | # TODO: move this to an action
source ./.github/workflows/autorelease-default-env.sh
cat autorelease-env.sh >> $GITHUB_ENV
if [ -f "autorelease-env.sh" ]; then
cat autorelease-env.sh >> $GITHUB_ENV
fi
eval $INSTALL_AUTORELEASE
name: "Install autorelease"
- run: |
Expand All @@ -27,5 +29,5 @@ jobs:
- uses: pypa/gh-action-pypi-publish@master
with:
password: ${{ secrets.pypi_password }}
name: "Deploy to testpypi"
name: "Deploy to pypi"

4 changes: 3 additions & 1 deletion .github/workflows/autorelease-gh-rel.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ jobs:
python-version: "3.7"
- run: | # TODO: move this to an action
source ./.github/workflows/autorelease-default-env.sh
cat autorelease-env.sh >> $GITHUB_ENV
if [ -f "autorelease-env.sh" ]; then
cat autorelease-env.sh >> $GITHUB_ENV
fi
eval $INSTALL_AUTORELEASE
name: "Install autorelease"
- run: |
Expand Down
8 changes: 6 additions & 2 deletions .github/workflows/autorelease-prep.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ jobs:
python-version: "3.x"
- run: | # TODO: move this to an action
source ./.github/workflows/autorelease-default-env.sh
cat autorelease-env.sh >> $GITHUB_ENV
if [ -f "autorelease-env.sh" ]; then
cat autorelease-env.sh >> $GITHUB_ENV
fi
eval $INSTALL_AUTORELEASE
name: "Install autorelease"
- run: |
Expand Down Expand Up @@ -49,7 +51,9 @@ jobs:
python-version: "3.x"
- run: | # TODO: move this to an action
source ./.github/workflows/autorelease-default-env.sh
cat autorelease-env.sh >> $GITHUB_ENV
if [ -f "autorelease-env.sh" ]; then
cat autorelease-env.sh >> $GITHUB_ENV
fi
eval $INSTALL_AUTORELEASE
name: "Install autorelease"
- run: test-testpypi
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ miscellaneous operations on OPS output files.
**Simulation Commands:**

* `visit-all`: Run MD to generate initial trajectories
* `md`: Run MD for fixed time or until a given ensemble is satisfied
* `equilibrate`: Run equilibration for path sampling
* `pathsampling`: Run any path sampling simulation, including TIS variants

Expand Down
2 changes: 1 addition & 1 deletion autorelease-env.sh
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
INSTALL_AUTORELEASE="python -m pip install autorelease==0.2.3 nose"
INSTALL_AUTORELEASE="python -m pip install autorelease==0.2.3 nose sqlalchemy dill"
PACKAGE_IMPORT_NAME=paths_cli
3 changes: 3 additions & 0 deletions devtools/tests_require.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,6 @@ nose
pytest
pytest-cov
coveralls
# following are for SimStore integration
dill
sqlalchemy
61 changes: 55 additions & 6 deletions paths_cli/commands/contents.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,64 @@
import click
from paths_cli.parameters import INPUT_FILE

UNNAMED_SECTIONS = ['steps', 'movechanges', 'samplesets', 'trajectories',
'snapshots']

NAME_TO_ATTR = {
'CVs': 'cvs',
'Volumes': 'volumes',
'Engines': 'engines',
'Networks': 'networks',
'Move Schemes': 'schemes',
'Simulations': 'pathsimulators',
'Tags': 'tags',
'Steps': 'steps',
'Move Changes': 'movechanges',
'SampleSets': 'samplesets',
'Trajectories': 'trajectories',
'Snapshots': 'snapshots'
}

@click.command(
'contents',
short_help="list named objects from an OPS .nc file",
)
@INPUT_FILE.clicked(required=True)
def contents(input_file):
@click.option('--table', type=str, required=False,
help="table to show results from")
def contents(input_file, table):
"""List the names of named objects in an OPS .nc file.

This is particularly useful when getting ready to use one of simulation
scripts (i.e., to identify exactly how a state or engine is named.)
"""
storage = INPUT_FILE.get(input_file)
print(storage)
if table is None:
report_all_tables(storage)
else:
table_attr = table.lower()
try:
store = getattr(storage, table_attr)
except AttributeError:
raise click.UsageError("Unknown table: '" + table_attr + "'")
else:
print(get_section_string(table_attr, store))


def get_section_string(label, store):
attr = NAME_TO_ATTR.get(label, label.lower())
if attr in UNNAMED_SECTIONS:
string = get_unnamed_section_string(label, store)
elif attr in ['tag', 'tags']:
string = get_section_string_nameable(label, store, _get_named_tags)
else:
string = get_section_string_nameable(label, store,
_get_named_namedobj)
return string


def report_all_tables(storage):
store_section_mapping = {
'CVs': storage.cvs, 'Volumes': storage.volumes,
'Engines': storage.engines, 'Networks': storage.networks,
Expand All @@ -26,12 +71,16 @@ def contents(input_file):
print(get_section_string_nameable('Tags', storage.tags, _get_named_tags))

print("\nData Objects:")
unnamed_sections = {
'Steps': storage.steps, 'Move Changes': storage.movechanges,
'SampleSets': storage.samplesets,
'Trajectories': storage.trajectories, 'Snapshots': storage.snapshots
data_object_mapping = {
'Steps': lambda storage: storage.steps,
'Move Changes': lambda storage: storage.movechanges,
'SampleSets': lambda storage: storage.samplesets,
'Trajectories': lambda storage: storage.trajectories,
'Snapshots': lambda storage: storage.snapshots
}
for section, store in unnamed_sections.items():

for section, store_func in data_object_mapping.items():
store = store_func(storage)
print(get_unnamed_section_string(section, store))

def _item_or_items(count):
Expand Down
1 change: 1 addition & 0 deletions paths_cli/commands/equilibrate.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ def equilibrate_main(output_storage, scheme, init_conds, multiplier,
extra_steps):
import openpathsampling as paths
init_conds = scheme.initial_conditions_from_trajectories(init_conds)
scheme.assert_initial_conditions(init_conds)
simulation = paths.PathSampling(
storage=output_storage,
move_scheme=scheme,
Expand Down
203 changes: 203 additions & 0 deletions paths_cli/commands/md.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
import click

import paths_cli.utils
from paths_cli.parameters import (INPUT_FILE, OUTPUT_FILE, ENGINE,
MULTI_ENSEMBLE, INIT_SNAP)

import logging
logger = logging.getLogger(__name__)

@click.command(
"md",
short_help=("Run MD for fixed time or until a given ensemble is "
"satisfied"),
)
@INPUT_FILE.clicked(required=True)
@OUTPUT_FILE.clicked(required=True)
@ENGINE.clicked(required=False)
@MULTI_ENSEMBLE.clicked(required=False)
@click.option('-n', '--nsteps', type=int,
help="number of MD steps to run")
@INIT_SNAP.clicked(required=False)
def md(input_file, output_file, engine, ensemble, nsteps, init_frame):
"""Run MD for for time of steps or until ensembles are satisfied.

This can either take a --nsteps or --ensemble, but not both. If the
--ensemble option is specfied more than once, then this will attempt to
run until all ensembles are satisfied by a subtrajectory.

This still respects the maximum number of frames as set in the engine,
and will terminate if the trajectory gets longer than that.
"""
storage = INPUT_FILE.get(input_file)
md_main(
output_storage=OUTPUT_FILE.get(output_file),
engine=ENGINE.get(storage, engine),
ensembles=MULTI_ENSEMBLE.get(storage, ensemble),
nsteps=nsteps,
initial_frame=INIT_SNAP.get(storage, init_frame)
)

class ProgressReporter(object):
"""Generic class for a callable that reports progress.

Base class for ends-with-ensemble and fixed-length tricks.

Parameters
----------
timestep : Any
timestep, optionally with units
update_freq : int
how often to report updates
"""
def __init__(self, timestep, update_freq):
self.timestep = timestep
self.update_freq = update_freq

def steps_progress_string(self, n_steps):
"""Return string for number of frames run and time elapsed

Not newline-terminated.
"""
report_str = "Ran {n_steps} frames"
if self.timestep is not None:
report_str += " [{}]".format(str(n_steps * self.timestep))
report_str += '.'
return report_str.format(n_steps=n_steps)

def progress_string(self, n_steps):
"""Return the progress string. Subclasses may override.
"""
report_str = self.steps_progress_string(n_steps) + "\n"
return report_str.format(n_steps=n_steps)

def report_progress(self, n_steps, force=False):
"""Report the progress to the terminal.
"""
import openpathsampling as paths
if (n_steps % self.update_freq == 0) or force:
string = self.progress_string(n_steps)
paths.tools.refresh_output(string)

def __call__(self, trajectory, trusted=False):
raise NotImplementedError()


class EnsembleSatisfiedContinueConditions(ProgressReporter):
"""Continuation condition for including subtrajs for each ensemble.

This object creates a continuation condition (a callable) analogous with
the ensemble ``can_append`` method. This will tell the trajectory to
keep running until, for each of the given ensembles, a subtrajectory has
been found that will satisfy the ensemble.

Parameters
----------
ensembles: List[:class:`openpathsampling.Ensemble`]
the ensembles to satisfy
timestep : Any
timestep, optionally with units
update_freq : int
how often to report updates
"""
def __init__(self, ensembles, timestep=None, update_freq=10):
super().__init__(timestep, update_freq)
self.satisfied = {ens: False for ens in ensembles}

def progress_string(self, n_steps):
report_str = self.steps_progress_string(n_steps)
report_str += (" Found ensembles [{found}]. "
"Looking for [{missing}].\n")
found = [ens.name for ens, done in self.satisfied.items() if done]
missing = [ens.name for ens, done in self.satisfied.items()
if not done]
found_str = ",".join(found)
missing_str = ",".join(missing)
return report_str.format(n_steps=n_steps,
found=found_str,
missing=missing_str)


def _check_previous_frame(self, trajectory, start, unsatisfied):
if -start > len(trajectory):
# we've done the whole traj; don't keep going
return False
subtraj = trajectory[start:]
logger.debug(str(subtraj) + "/" + str(trajectory))
for ens in unsatisfied:
if not ens.strict_can_prepend(subtraj, trusted=True):
# test if we can't prepend because we satsify
self.satisfied[ens] = ens(subtraj) or ens(subtraj[1:])
unsatisfied.remove(ens)
return bool(unsatisfied)

def _call_untrusted(self, trajectory):
self.satisfied = {ens: False for ens in self.satisfied}
for i in range(1, len(trajectory)):
keep_going = self(trajectory[:i], trusted=True)
if not keep_going:
return False
return self(trajectory, trusted=True)

def __call__(self, trajectory, trusted=False):
if not trusted:
return self._call_untrusted(trajectory)

# below here, trusted is True
self.report_progress(len(trajectory) - 1)

unsatisfied = [ens for ens, done in self.satisfied.items()
if not done]
# TODO: update on how many ensembles left, what frame number we are

start = -1
while self._check_previous_frame(trajectory, start, unsatisfied):
start -= 1

return not all(self.satisfied.values())


class FixedLengthContinueCondition(ProgressReporter):
"""Continuation condition for fixed-length runs.

Parameters
----------
length : int
final length of the trajectory in frames
timestep : Any
timestep, optionally with units
update_freq : int
how often to report updates
"""
def __init__(self, length, timestep=None, update_freq=10):
super().__init__(timestep, update_freq)
self.length = length

def __call__(self, trajectory, trusted=False):
len_traj = len(trajectory)
self.report_progress(len_traj - 1)
return len_traj < self.length



def md_main(output_storage, engine, ensembles, nsteps, initial_frame):
import openpathsampling as paths
if nsteps is not None and ensembles:
raise RuntimeError("Options --ensemble and --nsteps cannot both be"
" used at once.")

if ensembles:
continue_cond = EnsembleSatisfiedContinueConditions(ensembles)
else:
continue_cond = FixedLengthContinueCondition(nsteps)

trajectory = engine.generate(initial_frame, running=continue_cond)
continue_cond.report_progress(len(trajectory) - 1, force=True)
paths_cli.utils.tag_final_result(trajectory, output_storage,
'final_conditions')
return trajectory, None

CLI = md
SECTION = "Simulation"
REQUIRES_OPS = (1, 0)

6 changes: 3 additions & 3 deletions paths_cli/commands/visit_all.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import click

import paths_cli.utils
from paths_cli.parameters import (INPUT_FILE, OUTPUT_FILE, ENGINE, STATES,
INIT_SNAP)

Expand Down Expand Up @@ -34,9 +35,8 @@ def visit_all_main(output_storage, states, engine, initial_frame):
timestep = getattr(engine, 'timestep', None)
visit_all_ens = paths.VisitAllStatesEnsemble(states, timestep=timestep)
trajectory = engine.generate(initial_frame, [visit_all_ens.can_append])
if output_storage is not None:
output_storage.save(trajectory)
output_storage.tags['final_conditions'] = trajectory
paths_cli.utils.tag_final_result(trajectory, output_storage,
'final_conditions')

return trajectory, None # no simulation object to return here

Expand Down
Loading