Skip to content

Commit 34cf3a0

Browse files
authored
Merge pull request #58 from dwhswenson/wizard-extra-help
Wizard improvements: more help; improvements loading from OPS files
2 parents f24564a + 2f2b37c commit 34cf3a0

4 files changed

Lines changed: 43 additions & 20 deletions

File tree

‎paths_cli/tests/wizard/test_load_from_ops.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ def test_get_ops_object(ops_file_fixture, with_failure):
7979
assert obj.name == 'bar'
8080
log = wizard.console.log_text
8181
assert 'name of the FOOMAGIC' in log
82-
fail_msg = 'Something went wrong'
82+
fail_msg = 'not a valid option'
8383
if with_failure:
8484
assert fail_msg in log
8585
else:

‎paths_cli/wizard/load_from_ops.py‎

Lines changed: 11 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
11
from paths_cli.parameters import INPUT_FILE
22
from paths_cli.wizard.core import get_object
3-
from paths_cli.wizard.standard_categories import CATEGORIES
4-
from paths_cli.wizard.helper import Helper
5-
from functools import partial
63

74
from paths_cli.wizard.errors import FILE_LOADING_ERROR_MSG
85
LABEL = "Load existing from OPS file"
96

7+
108
def named_objs_helper(storage, store_name):
119
def list_items(user_input, context=None):
1210
store = getattr(storage, store_name)
@@ -25,24 +23,21 @@ def _get_ops_storage(wizard):
2523
storage = INPUT_FILE.get(filename)
2624
except Exception as e:
2725
wizard.exception(FILE_LOADING_ERROR_MSG, e)
28-
return
26+
return None
2927

3028
return storage
3129

30+
3231
@get_object
3332
def _get_ops_object(wizard, storage, store_name, obj_name):
34-
name = wizard.ask(f"What's the name of the {obj_name} you want to "
35-
"load? (Type '?' to get a list of them)",
36-
helper=named_objs_helper(storage, store_name))
37-
if name:
38-
try:
39-
obj = getattr(storage, store_name)[name]
40-
except Exception as e:
41-
wizard.exception("Something went wrong when loading "
42-
f"{name}. Maybe check the spelling?", e)
43-
return
44-
else:
45-
return obj
33+
store = getattr(storage, store_name)
34+
options = {obj.name: obj for obj in store if obj.is_named}
35+
result = wizard.ask_enumerate_dict(
36+
f"What's the name of the {obj_name} you want to load?",
37+
options
38+
)
39+
return result
40+
4641

4742
def load_from_ops(wizard, store_name, obj_name):
4843
wizard.say("Okay, we'll load it from an OPS file.")

‎paths_cli/wizard/openmm.py‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,12 @@
6161
ProxyParameter(
6262
name='n_frames_max',
6363
ask="How many frames before aborting a trajectory?",
64-
helper=None,
64+
helper=("Sometimes trajectories can get stuck in "
65+
"unexpected basins. To prevent your trajectory "
66+
"from running forever, you need to add a cutoff "
67+
"trajectory length. This should be significantly "
68+
"longer than you would expect a transition to "
69+
"take."),
6570
),
6671
],
6772
compiler_plugin=OPENMM_COMPILING,

‎paths_cli/wizard/volumes.py‎

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import operator
2+
from functools import partial
23
from paths_cli.wizard.parameters import ProxyParameter
34
from paths_cli.wizard.plugin_classes import (
45
LoadFromOPS, WizardParameterObjectPlugin, WizardObjectPlugin,
@@ -7,7 +8,7 @@
78
from paths_cli.wizard.helper import EvalHelperFunc, Helper
89
from paths_cli.wizard.core import interpret_req
910
import paths_cli.compiling.volumes
10-
from functools import partial
11+
1112

1213
def _binary_func_volume(wizard, context, op):
1314
wizard.say("Let's make the first constituent volume:")
@@ -28,6 +29,7 @@ def _binary_func_volume(wizard, context, op):
2829
_LAMBDA_STR = ("What is the {minmax} allowed value for "
2930
"'{{obj_dict[cv].name}}' in this volume?")
3031

32+
3133
CV_DEFINED_VOLUME_PLUGIN = WizardParameterObjectPlugin.from_proxies(
3234
name="CV-defined volume (allowed values of CV)",
3335
category="volume",
@@ -57,24 +59,33 @@ def _binary_func_volume(wizard, context, op):
5759
compiler_plugin=paths_cli.compiling.volumes.CV_VOLUME_PLUGIN,
5860
)
5961

62+
6063
INTERSECTION_VOLUME_PLUGIN = WizardObjectPlugin(
6164
name='Intersection of two volumes (must be in both)',
6265
category="volume",
6366
intro=("This volume will be the intersection of two other volumes. "
6467
"This means that it only allows phase space points that are "
6568
"in both of the constituent volumes."),
6669
builder=partial(_binary_func_volume, op=operator.__and__),
70+
description=("Create a volume that is the intersection of two existing "
71+
"volumes -- that is, all points in this volume are in "
72+
"both of the volumes that define it."),
6773
)
6874

75+
6976
UNION_VOLUME_PLUGIN = WizardObjectPlugin(
7077
name='Union of two volumes (must be in at least one)',
7178
category="volume",
7279
intro=("This volume will be the union of two other volumes. "
7380
"This means that it allows phase space points that are in "
7481
"either of the constituent volumes."),
7582
builder=partial(_binary_func_volume, op=operator.__or__),
83+
description=("Create a volume that is the union of two existing "
84+
"volumes -- that is, any point in either of the volumes "
85+
"that define this are also in this volume"),
7686
)
7787

88+
7889
NEGATED_VOLUME_PLUGIN = WizardObjectPlugin(
7990
name='Complement of a volume (not in given volume)',
8091
category='volume',
@@ -83,15 +94,19 @@ def _binary_func_volume(wizard, context, op):
8394
builder=lambda wizard, context: ~VOLUMES_PLUGIN(
8495
wizard, volume_set_context(wizard, context, None)
8596
),
97+
description=("Create a volume that includes every that is NOT "
98+
"in the existing volume."),
8699
)
87100

101+
88102
_FIRST_STATE = ("Now let's define state states for your system. "
89103
"You'll need to define {n_states_string} of them.")
90104
_ADDITIONAL_STATES = "Okay, let's define another stable state."
91105
_VOL_DESC = ("You can describe this as either a range of values for some "
92106
"CV, or as some combination of other such volumes "
93107
"(i.e., intersection or union).")
94108

109+
95110
def volume_intro(wizard, context):
96111
as_state = context.get('depth', 0) == 0
97112
n_states = len(wizard.states)
@@ -106,24 +121,32 @@ def volume_intro(wizard, context):
106121
intro += [_VOL_DESC]
107122
return intro
108123

124+
109125
def volume_set_context(wizard, context, selected):
110126
depth = context.get('depth', 0) + 1
111127
new_context = {
112128
'depth': depth,
113129
}
114130
return new_context
115131

132+
116133
def volume_ask(wizard, context):
117134
as_state = context.get('depth', 0) == 0
118135
obj = {True: 'state', False: 'volume'}[as_state]
119136
return f"What describes this {obj}?"
120137

138+
139+
VOLUME_FROM_FILE = LoadFromOPS('volume')
140+
121141
VOLUMES_PLUGIN = WrapCategory(
122142
name='volume',
123143
intro=volume_intro,
124144
ask=volume_ask,
125145
set_context=volume_set_context,
126-
helper="No volume help yet"
146+
helper=("This will define a (hyper)volume in phase space. States and "
147+
"interfaces in OPS are described by volumes. You can combine "
148+
"ranges along different CVs in any way you want to defined the "
149+
"volume.")
127150
)
128151

129152
if __name__ == "__main__": # no-cov

0 commit comments

Comments
 (0)