westpa 2022.10__cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of westpa might be problematic. Click here for more details.
- westpa/__init__.py +14 -0
- westpa/_version.py +21 -0
- westpa/analysis/__init__.py +5 -0
- westpa/analysis/core.py +746 -0
- westpa/analysis/statistics.py +27 -0
- westpa/analysis/trajectories.py +360 -0
- westpa/cli/__init__.py +0 -0
- westpa/cli/core/__init__.py +0 -0
- westpa/cli/core/w_fork.py +152 -0
- westpa/cli/core/w_init.py +230 -0
- westpa/cli/core/w_run.py +77 -0
- westpa/cli/core/w_states.py +212 -0
- westpa/cli/core/w_succ.py +99 -0
- westpa/cli/core/w_truncate.py +59 -0
- westpa/cli/tools/__init__.py +0 -0
- westpa/cli/tools/ploterr.py +506 -0
- westpa/cli/tools/plothist.py +706 -0
- westpa/cli/tools/w_assign.py +596 -0
- westpa/cli/tools/w_bins.py +166 -0
- westpa/cli/tools/w_crawl.py +119 -0
- westpa/cli/tools/w_direct.py +547 -0
- westpa/cli/tools/w_dumpsegs.py +94 -0
- westpa/cli/tools/w_eddist.py +506 -0
- westpa/cli/tools/w_fluxanl.py +378 -0
- westpa/cli/tools/w_ipa.py +833 -0
- westpa/cli/tools/w_kinavg.py +127 -0
- westpa/cli/tools/w_kinetics.py +96 -0
- westpa/cli/tools/w_multi_west.py +414 -0
- westpa/cli/tools/w_ntop.py +213 -0
- westpa/cli/tools/w_pdist.py +515 -0
- westpa/cli/tools/w_postanalysis_matrix.py +82 -0
- westpa/cli/tools/w_postanalysis_reweight.py +53 -0
- westpa/cli/tools/w_red.py +486 -0
- westpa/cli/tools/w_reweight.py +780 -0
- westpa/cli/tools/w_select.py +226 -0
- westpa/cli/tools/w_stateprobs.py +111 -0
- westpa/cli/tools/w_trace.py +599 -0
- westpa/core/__init__.py +0 -0
- westpa/core/_rc.py +673 -0
- westpa/core/binning/__init__.py +55 -0
- westpa/core/binning/_assign.cpython-312-x86_64-linux-gnu.so +0 -0
- westpa/core/binning/assign.py +449 -0
- westpa/core/binning/binless.py +96 -0
- westpa/core/binning/binless_driver.py +54 -0
- westpa/core/binning/binless_manager.py +190 -0
- westpa/core/binning/bins.py +47 -0
- westpa/core/binning/mab.py +427 -0
- westpa/core/binning/mab_driver.py +54 -0
- westpa/core/binning/mab_manager.py +198 -0
- westpa/core/data_manager.py +1694 -0
- westpa/core/extloader.py +74 -0
- westpa/core/h5io.py +995 -0
- westpa/core/kinetics/__init__.py +24 -0
- westpa/core/kinetics/_kinetics.cpython-312-x86_64-linux-gnu.so +0 -0
- westpa/core/kinetics/events.py +147 -0
- westpa/core/kinetics/matrates.py +156 -0
- westpa/core/kinetics/rate_averaging.py +266 -0
- westpa/core/progress.py +218 -0
- westpa/core/propagators/__init__.py +54 -0
- westpa/core/propagators/executable.py +715 -0
- westpa/core/reweight/__init__.py +14 -0
- westpa/core/reweight/_reweight.cpython-312-x86_64-linux-gnu.so +0 -0
- westpa/core/reweight/matrix.py +126 -0
- westpa/core/segment.py +119 -0
- westpa/core/sim_manager.py +830 -0
- westpa/core/states.py +359 -0
- westpa/core/systems.py +93 -0
- westpa/core/textio.py +74 -0
- westpa/core/trajectory.py +330 -0
- westpa/core/we_driver.py +908 -0
- westpa/core/wm_ops.py +43 -0
- westpa/core/yamlcfg.py +391 -0
- westpa/fasthist/__init__.py +34 -0
- westpa/fasthist/__main__.py +110 -0
- westpa/fasthist/_fasthist.cpython-312-x86_64-linux-gnu.so +0 -0
- westpa/mclib/__init__.py +264 -0
- westpa/mclib/__main__.py +28 -0
- westpa/mclib/_mclib.cpython-312-x86_64-linux-gnu.so +0 -0
- westpa/oldtools/__init__.py +4 -0
- westpa/oldtools/aframe/__init__.py +35 -0
- westpa/oldtools/aframe/atool.py +75 -0
- westpa/oldtools/aframe/base_mixin.py +26 -0
- westpa/oldtools/aframe/binning.py +178 -0
- westpa/oldtools/aframe/data_reader.py +560 -0
- westpa/oldtools/aframe/iter_range.py +200 -0
- westpa/oldtools/aframe/kinetics.py +117 -0
- westpa/oldtools/aframe/mcbs.py +146 -0
- westpa/oldtools/aframe/output.py +39 -0
- westpa/oldtools/aframe/plotting.py +90 -0
- westpa/oldtools/aframe/trajwalker.py +126 -0
- westpa/oldtools/aframe/transitions.py +469 -0
- westpa/oldtools/cmds/__init__.py +0 -0
- westpa/oldtools/cmds/w_ttimes.py +358 -0
- westpa/oldtools/files.py +34 -0
- westpa/oldtools/miscfn.py +23 -0
- westpa/oldtools/stats/__init__.py +4 -0
- westpa/oldtools/stats/accumulator.py +35 -0
- westpa/oldtools/stats/edfs.py +129 -0
- westpa/oldtools/stats/mcbs.py +89 -0
- westpa/tools/__init__.py +33 -0
- westpa/tools/binning.py +472 -0
- westpa/tools/core.py +340 -0
- westpa/tools/data_reader.py +159 -0
- westpa/tools/dtypes.py +31 -0
- westpa/tools/iter_range.py +198 -0
- westpa/tools/kinetics_tool.py +340 -0
- westpa/tools/plot.py +283 -0
- westpa/tools/progress.py +17 -0
- westpa/tools/selected_segs.py +154 -0
- westpa/tools/wipi.py +751 -0
- westpa/trajtree/__init__.py +4 -0
- westpa/trajtree/_trajtree.cpython-312-x86_64-linux-gnu.so +0 -0
- westpa/trajtree/trajtree.py +117 -0
- westpa/westext/__init__.py +0 -0
- westpa/westext/adaptvoronoi/__init__.py +3 -0
- westpa/westext/adaptvoronoi/adaptVor_driver.py +214 -0
- westpa/westext/hamsm_restarting/__init__.py +3 -0
- westpa/westext/hamsm_restarting/example_overrides.py +35 -0
- westpa/westext/hamsm_restarting/restart_driver.py +1165 -0
- westpa/westext/stringmethod/__init__.py +11 -0
- westpa/westext/stringmethod/fourier_fitting.py +69 -0
- westpa/westext/stringmethod/string_driver.py +253 -0
- westpa/westext/stringmethod/string_method.py +306 -0
- westpa/westext/weed/BinCluster.py +180 -0
- westpa/westext/weed/ProbAdjustEquil.py +100 -0
- westpa/westext/weed/UncertMath.py +247 -0
- westpa/westext/weed/__init__.py +10 -0
- westpa/westext/weed/weed_driver.py +182 -0
- westpa/westext/wess/ProbAdjust.py +101 -0
- westpa/westext/wess/__init__.py +6 -0
- westpa/westext/wess/wess_driver.py +207 -0
- westpa/work_managers/__init__.py +57 -0
- westpa/work_managers/core.py +396 -0
- westpa/work_managers/environment.py +134 -0
- westpa/work_managers/mpi.py +318 -0
- westpa/work_managers/processes.py +187 -0
- westpa/work_managers/serial.py +28 -0
- westpa/work_managers/threads.py +79 -0
- westpa/work_managers/zeromq/__init__.py +20 -0
- westpa/work_managers/zeromq/core.py +641 -0
- westpa/work_managers/zeromq/node.py +131 -0
- westpa/work_managers/zeromq/work_manager.py +526 -0
- westpa/work_managers/zeromq/worker.py +320 -0
- westpa-2022.10.dist-info/AUTHORS +22 -0
- westpa-2022.10.dist-info/LICENSE +21 -0
- westpa-2022.10.dist-info/METADATA +183 -0
- westpa-2022.10.dist-info/RECORD +150 -0
- westpa-2022.10.dist-info/WHEEL +6 -0
- westpa-2022.10.dist-info/entry_points.txt +29 -0
- westpa-2022.10.dist-info/top_level.txt +1 -0
westpa/analysis/core.py
ADDED
|
@@ -0,0 +1,746 @@
|
|
|
1
|
+
import itertools
|
|
2
|
+
import numpy as np
|
|
3
|
+
import pandas as pd
|
|
4
|
+
import sys
|
|
5
|
+
|
|
6
|
+
from westpa.core.binning.assign import BinMapper
|
|
7
|
+
from westpa.core.h5io import WESTPAH5File, tostr
|
|
8
|
+
from westpa.core.segment import Segment
|
|
9
|
+
from westpa.core.states import BasisState, InitialState, TargetState
|
|
10
|
+
from westpa.tools.binning import mapper_from_hdf5
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class Run:
|
|
14
|
+
"""A read-only view of a WESTPA simulation run.
|
|
15
|
+
|
|
16
|
+
Parameters
|
|
17
|
+
----------
|
|
18
|
+
h5filename : str or file-like object, default 'west.h5'
|
|
19
|
+
Pathname or stream of a main WESTPA HDF5 data file.
|
|
20
|
+
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
DESCRIPTION = 'WESTPA Run'
|
|
24
|
+
|
|
25
|
+
def __init__(self, h5filename='west.h5'):
|
|
26
|
+
self.h5filename = h5filename
|
|
27
|
+
|
|
28
|
+
def __enter__(self):
|
|
29
|
+
return self
|
|
30
|
+
|
|
31
|
+
def __exit__(self, exc_type, exc_val, traceback):
|
|
32
|
+
self.close()
|
|
33
|
+
|
|
34
|
+
@classmethod
|
|
35
|
+
def open(cls, h5filename='west.h5'):
|
|
36
|
+
"""Alternate constructor.
|
|
37
|
+
|
|
38
|
+
Parameters
|
|
39
|
+
----------
|
|
40
|
+
h5filename : str or file-like object, default 'west.h5'
|
|
41
|
+
Pathname or stream of a main WESTPA HDF5 data file.
|
|
42
|
+
|
|
43
|
+
"""
|
|
44
|
+
return cls(h5filename)
|
|
45
|
+
|
|
46
|
+
def close(self):
|
|
47
|
+
"""Close the Run instance by closing the underlying WESTPA HDF5 file."""
|
|
48
|
+
self.h5file.close()
|
|
49
|
+
|
|
50
|
+
@property
|
|
51
|
+
def closed(self):
|
|
52
|
+
"""bool: Whether the Run instance is closed."""
|
|
53
|
+
return not bool(self.h5file)
|
|
54
|
+
|
|
55
|
+
@property
|
|
56
|
+
def h5filename(self):
|
|
57
|
+
return self.h5file.filename
|
|
58
|
+
|
|
59
|
+
@h5filename.setter
|
|
60
|
+
def h5filename(self, value):
|
|
61
|
+
try:
|
|
62
|
+
h5file = WESTPAH5File(value, 'r')
|
|
63
|
+
except FileNotFoundError as e:
|
|
64
|
+
e.strerror = f'Failed to open {self.DESCRIPTION}: file {value!r} not found'
|
|
65
|
+
raise e.with_traceback(None)
|
|
66
|
+
self.h5file = h5file
|
|
67
|
+
|
|
68
|
+
@property
|
|
69
|
+
def summary(self):
|
|
70
|
+
"""pd.DataFrame: Summary data by iteration."""
|
|
71
|
+
df = pd.DataFrame(
|
|
72
|
+
self.h5file['summary'][: self.num_iterations],
|
|
73
|
+
index=range(1, self.num_iterations + 1),
|
|
74
|
+
dtype=object,
|
|
75
|
+
)
|
|
76
|
+
df.pop('norm') # should always be 1.0
|
|
77
|
+
df.pop('binhash') # not human readable
|
|
78
|
+
return df
|
|
79
|
+
|
|
80
|
+
@property
|
|
81
|
+
def num_iterations(self):
|
|
82
|
+
"""int: Number of completed iterations."""
|
|
83
|
+
if not hasattr(self, '_num_iterations'):
|
|
84
|
+
current = self.h5file.attrs['west_current_iteration']
|
|
85
|
+
grp = self.h5file.get_iter_group(current)
|
|
86
|
+
if (grp['seg_index']['status'] == Segment.SEG_STATUS_COMPLETE).all():
|
|
87
|
+
self._num_iterations = current
|
|
88
|
+
else:
|
|
89
|
+
self._num_iterations = current - 1
|
|
90
|
+
return self._num_iterations
|
|
91
|
+
|
|
92
|
+
@property
|
|
93
|
+
def iterations(self):
|
|
94
|
+
"""Sequence[Iteration]: Sequence of iterations."""
|
|
95
|
+
return [Iteration(number, self) for number in range(1, self.num_iterations + 1)]
|
|
96
|
+
|
|
97
|
+
@property
|
|
98
|
+
def num_walkers(self):
|
|
99
|
+
"""int: Total number of walkers."""
|
|
100
|
+
return sum(iteration.num_segments for iteration in self)
|
|
101
|
+
|
|
102
|
+
@property
|
|
103
|
+
def num_segments(self):
|
|
104
|
+
"""int: Total number of trajectory segments (alias self.num_walkers)."""
|
|
105
|
+
return self.num_walkers
|
|
106
|
+
|
|
107
|
+
@property
|
|
108
|
+
def walkers(self):
|
|
109
|
+
"""Iterable[Walker]: All walkers in the run."""
|
|
110
|
+
return itertools.chain.from_iterable(self)
|
|
111
|
+
|
|
112
|
+
@property
|
|
113
|
+
def recycled_walkers(self):
|
|
114
|
+
"""Iterable[Walker]: Walkers that stopped in the sink."""
|
|
115
|
+
return itertools.chain.from_iterable(iteration.recycled_walkers for iteration in self)
|
|
116
|
+
|
|
117
|
+
@property
|
|
118
|
+
def initial_walkers(self):
|
|
119
|
+
"""Iterable[Walker]: Walkers whose parents are initial states."""
|
|
120
|
+
return itertools.chain.from_iterable(iteration.initial_walkers for iteration in self)
|
|
121
|
+
|
|
122
|
+
def iteration(self, number):
|
|
123
|
+
"""Return a specific iteration.
|
|
124
|
+
|
|
125
|
+
Parameters
|
|
126
|
+
----------
|
|
127
|
+
number : int
|
|
128
|
+
Iteration number (1-based).
|
|
129
|
+
|
|
130
|
+
Returns
|
|
131
|
+
-------
|
|
132
|
+
Iteration
|
|
133
|
+
The iteration indexed by `number`.
|
|
134
|
+
|
|
135
|
+
"""
|
|
136
|
+
valid_range = range(1, self.num_iterations + 1)
|
|
137
|
+
if number not in valid_range:
|
|
138
|
+
raise ValueError(f'iteration number must be in {valid_range}')
|
|
139
|
+
return Iteration(number, self)
|
|
140
|
+
|
|
141
|
+
def __len__(self):
|
|
142
|
+
return self.num_iterations
|
|
143
|
+
|
|
144
|
+
def __iter__(self):
|
|
145
|
+
return iter(self.iterations)
|
|
146
|
+
|
|
147
|
+
def __contains__(self, iteration):
|
|
148
|
+
return iteration.run == self
|
|
149
|
+
|
|
150
|
+
def __eq__(self, other):
|
|
151
|
+
return self.h5file == other.h5file
|
|
152
|
+
|
|
153
|
+
def __hash__(self):
|
|
154
|
+
return hash(self.h5file)
|
|
155
|
+
|
|
156
|
+
def __bool__(self):
|
|
157
|
+
return not self.closed
|
|
158
|
+
|
|
159
|
+
def __repr__(self):
|
|
160
|
+
if self.closed:
|
|
161
|
+
return f'<Closed {self.DESCRIPTION} at {hex(id(self))}>'
|
|
162
|
+
return f'<{self.DESCRIPTION} with {self.num_iterations} iterations at {hex(id(self))}>'
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
class Iteration:
|
|
166
|
+
"""An iteration of a WESTPA simulation.
|
|
167
|
+
|
|
168
|
+
Parameters
|
|
169
|
+
----------
|
|
170
|
+
number : int
|
|
171
|
+
Iteration number (1-based).
|
|
172
|
+
run : Run
|
|
173
|
+
Simulation run to which the iteration belongs.
|
|
174
|
+
|
|
175
|
+
"""
|
|
176
|
+
|
|
177
|
+
def __init__(self, number, run):
|
|
178
|
+
self.number = number
|
|
179
|
+
self.run = run
|
|
180
|
+
|
|
181
|
+
@property
|
|
182
|
+
def h5group(self):
|
|
183
|
+
"""h5py.Group: HDF5 group containing the iteration data."""
|
|
184
|
+
return self.run.h5file.get_iter_group(self.number)
|
|
185
|
+
|
|
186
|
+
@property
|
|
187
|
+
def prev(self):
|
|
188
|
+
"""Iteration: Previous iteration."""
|
|
189
|
+
if self.number == 1:
|
|
190
|
+
return None
|
|
191
|
+
return self.run.iteration(self.number - 1)
|
|
192
|
+
|
|
193
|
+
@property
|
|
194
|
+
def next(self):
|
|
195
|
+
"""Iteration: Next iteration."""
|
|
196
|
+
if self.number == self.run.num_iterations:
|
|
197
|
+
return None
|
|
198
|
+
return self.run.iteration(self.number + 1)
|
|
199
|
+
|
|
200
|
+
@property
|
|
201
|
+
def summary(self):
|
|
202
|
+
"""pd.DataFrame: Iteration summary."""
|
|
203
|
+
df = pd.DataFrame(
|
|
204
|
+
self.run.h5file['summary'][[self.number - 1]],
|
|
205
|
+
index=[self.number],
|
|
206
|
+
dtype=object,
|
|
207
|
+
)
|
|
208
|
+
df.pop('norm') # should always be 1.0
|
|
209
|
+
df.pop('binhash') # not human readable
|
|
210
|
+
return df.iloc[0]
|
|
211
|
+
|
|
212
|
+
@property
|
|
213
|
+
def segment_summaries(self):
|
|
214
|
+
"""pd.DataFrame: Segment summary data for the iteration."""
|
|
215
|
+
df = pd.DataFrame(self.h5group['seg_index'][:], dtype=object)
|
|
216
|
+
|
|
217
|
+
# Make 'endpoint_type' and 'status' human-readable.
|
|
218
|
+
names = map(Segment.endpoint_type_names.get, df['endpoint_type'])
|
|
219
|
+
df['endpoint_type'] = [name.split('_')[-1] for name in names]
|
|
220
|
+
names = map(Segment.status_names.get, df['status'])
|
|
221
|
+
df['status'] = [name.split('_')[-1] for name in names]
|
|
222
|
+
|
|
223
|
+
return df
|
|
224
|
+
|
|
225
|
+
@property
|
|
226
|
+
def pcoords(self):
|
|
227
|
+
"""3D ndarray: Progress coordinate snaphots of each walker."""
|
|
228
|
+
return self.h5group['pcoord'][:]
|
|
229
|
+
|
|
230
|
+
@property
|
|
231
|
+
def weights(self):
|
|
232
|
+
"""1D ndarray: Statistical weight of each walker."""
|
|
233
|
+
return self.h5group['seg_index']['weight']
|
|
234
|
+
|
|
235
|
+
@property
|
|
236
|
+
def bin_target_counts(self):
|
|
237
|
+
"""1D ndarray, dtype=uint64: Target count for each bin."""
|
|
238
|
+
val = self.h5group.get('bin_target_counts')
|
|
239
|
+
if val is None:
|
|
240
|
+
return None
|
|
241
|
+
return val[:]
|
|
242
|
+
|
|
243
|
+
@property
|
|
244
|
+
def bin_mapper(self):
|
|
245
|
+
"""BinMapper: Bin mapper used in the iteration."""
|
|
246
|
+
if self.bin_target_counts is None:
|
|
247
|
+
return None
|
|
248
|
+
mapper, _, _ = mapper_from_hdf5(self.run.h5file['bin_topologies'], self.h5group.attrs['binhash'])
|
|
249
|
+
return mapper
|
|
250
|
+
|
|
251
|
+
@property
|
|
252
|
+
def num_bins(self):
|
|
253
|
+
"""int: Number of bins."""
|
|
254
|
+
if self.number == 1:
|
|
255
|
+
return 1
|
|
256
|
+
return self.bin_target_counts.shape[0]
|
|
257
|
+
|
|
258
|
+
@property
|
|
259
|
+
def bins(self):
|
|
260
|
+
"""Iterable[Bin]: Bins."""
|
|
261
|
+
mapper = self.bin_mapper
|
|
262
|
+
return (Bin(index, mapper) for index in range(self.num_bins))
|
|
263
|
+
|
|
264
|
+
@property
|
|
265
|
+
def num_walkers(self):
|
|
266
|
+
"""int: Number of walkers in the iteration."""
|
|
267
|
+
return self.h5group['seg_index'].shape[0]
|
|
268
|
+
|
|
269
|
+
@property
|
|
270
|
+
def num_segments(self):
|
|
271
|
+
"""int: Number of trajectory segments (alias self.num_walkers)."""
|
|
272
|
+
return self.num_walkers
|
|
273
|
+
|
|
274
|
+
@property
|
|
275
|
+
def walkers(self):
|
|
276
|
+
"""Iterable[Walker]: Walkers in the iteration."""
|
|
277
|
+
return (Walker(index, self) for index in range(self.num_walkers))
|
|
278
|
+
|
|
279
|
+
@property
|
|
280
|
+
def recycled_walkers(self):
|
|
281
|
+
"""Iterable[Walker]: Walkers that stopped in the sink."""
|
|
282
|
+
endpoint_type = self.h5group['seg_index']['endpoint_type']
|
|
283
|
+
indices = np.flatnonzero(endpoint_type == Segment.SEG_ENDPOINT_RECYCLED)
|
|
284
|
+
return (Walker(index, self) for index in indices)
|
|
285
|
+
|
|
286
|
+
@property
|
|
287
|
+
def initial_walkers(self):
|
|
288
|
+
"""Iterable[Walker]: Walkers whose parents are initial states."""
|
|
289
|
+
parent_ids = self.h5group['seg_index']['parent_id']
|
|
290
|
+
return (walker for walker, parent_id in zip(self, parent_ids) if parent_id < 0)
|
|
291
|
+
|
|
292
|
+
@property
|
|
293
|
+
def auxiliary_data(self):
|
|
294
|
+
"""h5py.Group or None: Auxiliary data stored for the iteration."""
|
|
295
|
+
return self.h5group.get('auxdata')
|
|
296
|
+
|
|
297
|
+
@property
|
|
298
|
+
def basis_state_summaries(self):
|
|
299
|
+
"""pd.DataFrame: Basis state summary data."""
|
|
300
|
+
df = pd.DataFrame(self.h5group['ibstates']['bstate_index'][:])
|
|
301
|
+
df['label'] = df['label'].str.decode('UTF-8')
|
|
302
|
+
df['auxref'] = df['auxref'].str.decode('UTF-8')
|
|
303
|
+
return df
|
|
304
|
+
|
|
305
|
+
@property
|
|
306
|
+
def basis_state_pcoords(self):
|
|
307
|
+
"""2D ndarray: Progress coordinates of each basis state."""
|
|
308
|
+
return self.h5group['ibstates']['bstate_pcoord'][:]
|
|
309
|
+
|
|
310
|
+
@property
|
|
311
|
+
def basis_states(self):
|
|
312
|
+
"""list[BasisState]: Basis states in use for the iteration."""
|
|
313
|
+
return [
|
|
314
|
+
BasisState(row.label, row.probability, pcoord=pcoord, auxref=row.auxref, state_id=row.Index)
|
|
315
|
+
for row, pcoord in zip(self.basis_state_summaries.itertuples(), self.basis_state_pcoords)
|
|
316
|
+
]
|
|
317
|
+
|
|
318
|
+
@property
|
|
319
|
+
def has_target_states(self):
|
|
320
|
+
"""bool: Whether target (sink) states are defined for this iteration."""
|
|
321
|
+
return 'tstates' in self.h5group
|
|
322
|
+
|
|
323
|
+
@property
|
|
324
|
+
def target_state_summaries(self):
|
|
325
|
+
"""pd.DataFrame or None: Target state summary data."""
|
|
326
|
+
if self.has_target_states:
|
|
327
|
+
df = pd.DataFrame(self.h5group['tstates']['index'][:])
|
|
328
|
+
df['label'] = df['label'].str.decode('UTF-8')
|
|
329
|
+
|
|
330
|
+
return df
|
|
331
|
+
else:
|
|
332
|
+
return None
|
|
333
|
+
|
|
334
|
+
@property
|
|
335
|
+
def target_state_pcoords(self):
|
|
336
|
+
"""2D ndarray or None: Progress coordinates of each target state."""
|
|
337
|
+
return self.h5group['tstates']['pcoord'][:] if self.has_target_states else None
|
|
338
|
+
|
|
339
|
+
@property
|
|
340
|
+
def target_states(self):
|
|
341
|
+
"""list[TargetState]: Target states in use for the iteration."""
|
|
342
|
+
if not self.has_target_states:
|
|
343
|
+
return []
|
|
344
|
+
return [
|
|
345
|
+
TargetState(row.label, pcoord, state_id=row.Index)
|
|
346
|
+
for row, pcoord in zip(self.target_state_summaries.itertuples(), self.target_state_pcoords)
|
|
347
|
+
]
|
|
348
|
+
|
|
349
|
+
@property
|
|
350
|
+
def sink(self):
|
|
351
|
+
"""BinUnion or None: Union of bins serving as the recycling sink."""
|
|
352
|
+
if not self.has_target_states:
|
|
353
|
+
return None
|
|
354
|
+
mapper = Iteration(self.number + 1, self.run).bin_mapper
|
|
355
|
+
return BinUnion(mapper.assign(self.target_state_pcoords), mapper)
|
|
356
|
+
|
|
357
|
+
def bin(self, index):
|
|
358
|
+
"""Return the bin with the given index.
|
|
359
|
+
|
|
360
|
+
Parameters
|
|
361
|
+
----------
|
|
362
|
+
index : int
|
|
363
|
+
Bin index (0-based).
|
|
364
|
+
|
|
365
|
+
Returns
|
|
366
|
+
-------
|
|
367
|
+
Bin
|
|
368
|
+
The bin indexed by `index`.
|
|
369
|
+
|
|
370
|
+
"""
|
|
371
|
+
return Bin(index, self.bin_mapper)
|
|
372
|
+
|
|
373
|
+
def walker(self, index):
|
|
374
|
+
"""Return the walker with the given index.
|
|
375
|
+
|
|
376
|
+
Parameters
|
|
377
|
+
----------
|
|
378
|
+
index : int
|
|
379
|
+
Walker index (0-based).
|
|
380
|
+
|
|
381
|
+
Returns
|
|
382
|
+
-------
|
|
383
|
+
Walker
|
|
384
|
+
The walker indexed by `index`.
|
|
385
|
+
|
|
386
|
+
"""
|
|
387
|
+
valid_range = range(self.num_walkers)
|
|
388
|
+
if index not in valid_range:
|
|
389
|
+
raise ValueError(f'walker index must be in {valid_range}')
|
|
390
|
+
return Walker(index, self)
|
|
391
|
+
|
|
392
|
+
def basis_state(self, index):
|
|
393
|
+
"""Return the basis state with the given index.
|
|
394
|
+
|
|
395
|
+
Parameters
|
|
396
|
+
----------
|
|
397
|
+
index : int
|
|
398
|
+
Basis state index (0-based).
|
|
399
|
+
|
|
400
|
+
Returns
|
|
401
|
+
-------
|
|
402
|
+
BasisState
|
|
403
|
+
The basis state indexed by `index`.
|
|
404
|
+
|
|
405
|
+
"""
|
|
406
|
+
row = self.h5group['ibstates']['bstate_index'][index]
|
|
407
|
+
pcoord = self.h5group['ibstates']['bstate_pcoord'][index]
|
|
408
|
+
return BasisState(
|
|
409
|
+
tostr(row['label']),
|
|
410
|
+
row['probability'],
|
|
411
|
+
pcoord=pcoord,
|
|
412
|
+
auxref=tostr(row['auxref']),
|
|
413
|
+
state_id=index,
|
|
414
|
+
)
|
|
415
|
+
|
|
416
|
+
def target_state(self, index):
|
|
417
|
+
"""Return the target state with the given index.
|
|
418
|
+
|
|
419
|
+
Parameters
|
|
420
|
+
----------
|
|
421
|
+
index : int
|
|
422
|
+
Target state index (0-based).
|
|
423
|
+
|
|
424
|
+
Returns
|
|
425
|
+
-------
|
|
426
|
+
TargetState
|
|
427
|
+
The target state indexed by `index`.
|
|
428
|
+
|
|
429
|
+
"""
|
|
430
|
+
row = self.h5group['tstates']['index'][index]
|
|
431
|
+
pcoord = self.h5group['tstates']['pcoord'][index]
|
|
432
|
+
return TargetState(tostr(row['label']), pcoord, state_id=index)
|
|
433
|
+
|
|
434
|
+
def __iter__(self):
|
|
435
|
+
return iter(self.walkers)
|
|
436
|
+
|
|
437
|
+
def __contains__(self, walker):
|
|
438
|
+
return walker.iteration == self
|
|
439
|
+
|
|
440
|
+
def __eq__(self, other):
|
|
441
|
+
return self.number == other.number and self.run == other.run
|
|
442
|
+
|
|
443
|
+
def __hash__(self):
|
|
444
|
+
return hash((self.number, self.run))
|
|
445
|
+
|
|
446
|
+
def __repr__(self):
|
|
447
|
+
return f'{self.__class__.__name__}({self.number}, {self.run})'
|
|
448
|
+
|
|
449
|
+
|
|
450
|
+
class Walker:
|
|
451
|
+
"""A walker in an iteration of a WESTPA simulation.
|
|
452
|
+
|
|
453
|
+
Parameters
|
|
454
|
+
----------
|
|
455
|
+
index : int
|
|
456
|
+
Walker index (0-based).
|
|
457
|
+
iteration : Iteration
|
|
458
|
+
Iteration to which the walker belongs.
|
|
459
|
+
|
|
460
|
+
"""
|
|
461
|
+
|
|
462
|
+
def __init__(self, index, iteration):
|
|
463
|
+
self.index = index
|
|
464
|
+
self.iteration = iteration
|
|
465
|
+
|
|
466
|
+
@property
|
|
467
|
+
def run(self):
|
|
468
|
+
"""Run: Run to which the walker belongs."""
|
|
469
|
+
return self.iteration.run
|
|
470
|
+
|
|
471
|
+
@property
|
|
472
|
+
def weight(self):
|
|
473
|
+
"""float64: Statistical weight of the walker."""
|
|
474
|
+
return self.iteration.weights[self.index]
|
|
475
|
+
|
|
476
|
+
@property
|
|
477
|
+
def pcoords(self):
|
|
478
|
+
"""2D ndarray: Progress coordinate snapshots."""
|
|
479
|
+
return self.iteration.h5group['pcoord'][self.index]
|
|
480
|
+
|
|
481
|
+
@property
|
|
482
|
+
def num_snapshots(self):
|
|
483
|
+
"""int: Number of snapshots."""
|
|
484
|
+
return self.pcoords.shape[0]
|
|
485
|
+
|
|
486
|
+
@property
|
|
487
|
+
def segment_summary(self):
|
|
488
|
+
"""pd.Series: Segment summary data."""
|
|
489
|
+
df = pd.DataFrame(
|
|
490
|
+
self.iteration.h5group['seg_index'][[self.index]],
|
|
491
|
+
index=[self.index],
|
|
492
|
+
dtype=object,
|
|
493
|
+
)
|
|
494
|
+
|
|
495
|
+
# Make 'endpoint_type' and 'status' human-readable.
|
|
496
|
+
names = map(Segment.endpoint_type_names.get, df['endpoint_type'])
|
|
497
|
+
df['endpoint_type'] = [name.split('_')[-1] for name in names]
|
|
498
|
+
names = map(Segment.status_names.get, df['status'])
|
|
499
|
+
df['status'] = [name.split('_')[-1] for name in names]
|
|
500
|
+
|
|
501
|
+
return df.iloc[0]
|
|
502
|
+
|
|
503
|
+
@property
|
|
504
|
+
def parent(self):
|
|
505
|
+
"""Walker or InitialState: The parent of the walker."""
|
|
506
|
+
parent_id = self.iteration.h5group['seg_index']['parent_id'][self.index]
|
|
507
|
+
|
|
508
|
+
if parent_id >= 0:
|
|
509
|
+
return Walker(parent_id, self.iteration.prev)
|
|
510
|
+
|
|
511
|
+
istate_id = -(parent_id + 1)
|
|
512
|
+
row = self.iteration.h5group['ibstates']['istate_index'][istate_id]
|
|
513
|
+
|
|
514
|
+
# Initial states may or may not be generated from a basis state.
|
|
515
|
+
bstate_id = row['basis_state_id']
|
|
516
|
+
try:
|
|
517
|
+
bstate = self.iteration.basis_state(bstate_id)
|
|
518
|
+
except IndexError:
|
|
519
|
+
bstate = None
|
|
520
|
+
bstate_id = None
|
|
521
|
+
|
|
522
|
+
return InitialState(
|
|
523
|
+
istate_id,
|
|
524
|
+
bstate_id,
|
|
525
|
+
row['iter_created'],
|
|
526
|
+
iter_used=row['iter_used'],
|
|
527
|
+
istate_type=row['istate_type'],
|
|
528
|
+
istate_status=row['istate_status'],
|
|
529
|
+
pcoord=self.iteration.h5group['ibstates']['istate_pcoord'][istate_id],
|
|
530
|
+
basis_state=bstate,
|
|
531
|
+
)
|
|
532
|
+
|
|
533
|
+
@property
|
|
534
|
+
def children(self):
|
|
535
|
+
"""Iterable[Walker]: The children of the walker."""
|
|
536
|
+
next = self.iteration.next
|
|
537
|
+
if next is None:
|
|
538
|
+
return ()
|
|
539
|
+
indices = np.flatnonzero(next.h5group['seg_index']['parent_id'] == self.index)
|
|
540
|
+
return (Walker(index, next) for index in indices)
|
|
541
|
+
|
|
542
|
+
@property
|
|
543
|
+
def recycled(self):
|
|
544
|
+
"""bool: True if the walker stopped in the sink, False otherwise."""
|
|
545
|
+
endpoint_type = self.iteration.h5group['seg_index']['endpoint_type'][self.index]
|
|
546
|
+
return endpoint_type == Segment.SEG_ENDPOINT_RECYCLED
|
|
547
|
+
|
|
548
|
+
@property
|
|
549
|
+
def initial(self):
|
|
550
|
+
"""bool: True if the parent of the walker is an initial state, False otherwise."""
|
|
551
|
+
return self.iteration.h5group['seg_index']['parent_id'][self.index] < 0
|
|
552
|
+
|
|
553
|
+
@property
|
|
554
|
+
def auxiliary_data(self):
|
|
555
|
+
"""dict: Auxiliary data for the walker."""
|
|
556
|
+
data = self.iteration.auxiliary_data or {}
|
|
557
|
+
return {name: data[name][self.index] for name in data}
|
|
558
|
+
|
|
559
|
+
def trace(self, **kwargs):
|
|
560
|
+
"""Return the trace (ancestral line) of the walker.
|
|
561
|
+
|
|
562
|
+
For full documentation see :class:`Trace`.
|
|
563
|
+
|
|
564
|
+
Returns
|
|
565
|
+
-------
|
|
566
|
+
Trace
|
|
567
|
+
The trace of the walker.
|
|
568
|
+
|
|
569
|
+
"""
|
|
570
|
+
return Trace(self, **kwargs)
|
|
571
|
+
|
|
572
|
+
def __eq__(self, other):
|
|
573
|
+
return self.index == other.index and self.iteration == other.iteration
|
|
574
|
+
|
|
575
|
+
def __hash__(self):
|
|
576
|
+
return hash((self.index, self.iteration))
|
|
577
|
+
|
|
578
|
+
def __repr__(self):
|
|
579
|
+
return f'{self.__class__.__name__}({self.index}, {self.iteration})'
|
|
580
|
+
|
|
581
|
+
|
|
582
|
+
class BinUnion:
|
|
583
|
+
"""A (disjoint) union of bins defined by a common bin mapper.
|
|
584
|
+
|
|
585
|
+
Parameters
|
|
586
|
+
----------
|
|
587
|
+
indices : iterable of int
|
|
588
|
+
The indices of the bins comprising the union.
|
|
589
|
+
mapper : BinMapper
|
|
590
|
+
The bin mapper defining the bins.
|
|
591
|
+
|
|
592
|
+
"""
|
|
593
|
+
|
|
594
|
+
def __init__(self, indices, mapper):
|
|
595
|
+
if not isinstance(mapper, BinMapper):
|
|
596
|
+
raise TypeError(f'mapper must be an instance of {BinMapper}')
|
|
597
|
+
|
|
598
|
+
indices = set(indices)
|
|
599
|
+
valid_range = range(mapper.nbins)
|
|
600
|
+
if any(index not in valid_range for index in indices):
|
|
601
|
+
raise ValueError(f'bin indices must be in {valid_range}')
|
|
602
|
+
|
|
603
|
+
self.indices = indices
|
|
604
|
+
self.mapper = mapper
|
|
605
|
+
|
|
606
|
+
def union(self, *others):
|
|
607
|
+
"""Return the union of the bin union and all others.
|
|
608
|
+
|
|
609
|
+
Parameters
|
|
610
|
+
----------
|
|
611
|
+
*others : BinUnion
|
|
612
|
+
Other :class:`BinUnion` instances, consisting of bins defined by
|
|
613
|
+
the same underlying bin mapper.
|
|
614
|
+
|
|
615
|
+
Returns
|
|
616
|
+
-------
|
|
617
|
+
BinUnion
|
|
618
|
+
The union of `self` and `others`.
|
|
619
|
+
|
|
620
|
+
"""
|
|
621
|
+
if any(other.mapper != self.mapper for other in others):
|
|
622
|
+
raise ValueError('bins must be defined by the same bin mapper')
|
|
623
|
+
indices = self.indices.union(*(other.indices for other in others))
|
|
624
|
+
return BinUnion(indices, self.mapper)
|
|
625
|
+
|
|
626
|
+
def intersection(self, *others):
|
|
627
|
+
"""Return the intersection of the bin union and all others.
|
|
628
|
+
|
|
629
|
+
Parameters
|
|
630
|
+
----------
|
|
631
|
+
*others : BinUnion
|
|
632
|
+
Other :class:`BinUnion` instances, consisting of bins defined by
|
|
633
|
+
the same underlying bin mapper.
|
|
634
|
+
|
|
635
|
+
Returns
|
|
636
|
+
-------
|
|
637
|
+
BinUnion
|
|
638
|
+
The itersection of `self` and `others`.
|
|
639
|
+
|
|
640
|
+
"""
|
|
641
|
+
if any(other.mapper != self.mapper for other in others):
|
|
642
|
+
raise ValueError('bins must be defined by the same bin mapper')
|
|
643
|
+
indices = self.indices.intersection(*(other.indices for other in others))
|
|
644
|
+
return BinUnion(indices, self.mapper)
|
|
645
|
+
|
|
646
|
+
def __contains__(self, coord):
|
|
647
|
+
result = self.mapper.assign([coord])
|
|
648
|
+
if result.size != 1:
|
|
649
|
+
raise ValueError('left operand must be a single coordinate tuple')
|
|
650
|
+
return result[0] in self.indices
|
|
651
|
+
|
|
652
|
+
def __or__(self, other):
|
|
653
|
+
return self.union(other)
|
|
654
|
+
|
|
655
|
+
def __and__(self, other):
|
|
656
|
+
return self.intersection(other)
|
|
657
|
+
|
|
658
|
+
def __bool__(self):
|
|
659
|
+
return bool(self.indices)
|
|
660
|
+
|
|
661
|
+
def __repr__(self):
|
|
662
|
+
return f'{self.__class__.__name__}({self.indices}, {self.mapper})'
|
|
663
|
+
|
|
664
|
+
|
|
665
|
+
class Bin(BinUnion):
|
|
666
|
+
"""A bin defined by a bin mapper.
|
|
667
|
+
|
|
668
|
+
Parameters
|
|
669
|
+
----------
|
|
670
|
+
index : int
|
|
671
|
+
The index of the bin.
|
|
672
|
+
mapper : BinMapper
|
|
673
|
+
The bin mapper defining the bin.
|
|
674
|
+
|
|
675
|
+
"""
|
|
676
|
+
|
|
677
|
+
def __init__(self, index, mapper):
|
|
678
|
+
super().__init__({index}, mapper)
|
|
679
|
+
self.index = index
|
|
680
|
+
|
|
681
|
+
def __repr__(self):
|
|
682
|
+
return f'{self.__class__.__name__}({self.index}, {self.mapper})'
|
|
683
|
+
|
|
684
|
+
|
|
685
|
+
class Trace:
|
|
686
|
+
"""A trace of a walker's ancestry.
|
|
687
|
+
|
|
688
|
+
Parameters
|
|
689
|
+
----------
|
|
690
|
+
walker : Walker
|
|
691
|
+
The terminal walker.
|
|
692
|
+
source : Bin, BinUnion, or collections.abc.Container, optional
|
|
693
|
+
A source (macro)state, specified as a container object whose
|
|
694
|
+
:meth:`__contains__` method is the indicator function for the
|
|
695
|
+
corresponding subset of progress coordinate space. The trace is
|
|
696
|
+
stopped upon encountering a walker that stopped in `source`.
|
|
697
|
+
max_length : int, optional
|
|
698
|
+
The maximum number of walkers in the trace.
|
|
699
|
+
|
|
700
|
+
"""
|
|
701
|
+
|
|
702
|
+
def __init__(self, walker, source=None, max_length=None):
|
|
703
|
+
if max_length is None:
|
|
704
|
+
max_length = sys.maxsize
|
|
705
|
+
else:
|
|
706
|
+
max_length = int(max_length)
|
|
707
|
+
if max_length < 1:
|
|
708
|
+
raise ValueError('max_length must be at least 1')
|
|
709
|
+
|
|
710
|
+
walkers = []
|
|
711
|
+
initial_state = None
|
|
712
|
+
while len(walkers) < max_length:
|
|
713
|
+
if source and walker.pcoords[-1] in source:
|
|
714
|
+
break
|
|
715
|
+
walkers.append(walker)
|
|
716
|
+
parent = walker.parent
|
|
717
|
+
if isinstance(parent, InitialState):
|
|
718
|
+
initial_state = parent
|
|
719
|
+
break
|
|
720
|
+
walker = parent
|
|
721
|
+
walkers.reverse()
|
|
722
|
+
|
|
723
|
+
self.walkers = walkers
|
|
724
|
+
self.initial_state = initial_state
|
|
725
|
+
self.source = source
|
|
726
|
+
self.max_length = max_length
|
|
727
|
+
|
|
728
|
+
def __len__(self):
|
|
729
|
+
return len(self.walkers)
|
|
730
|
+
|
|
731
|
+
def __iter__(self):
|
|
732
|
+
return iter(self.walkers)
|
|
733
|
+
|
|
734
|
+
def __contains__(self, walker):
|
|
735
|
+
return walker in self.walkers
|
|
736
|
+
|
|
737
|
+
def __getitem__(self, key):
|
|
738
|
+
return self.walkers[key]
|
|
739
|
+
|
|
740
|
+
def __repr__(self):
|
|
741
|
+
s = f'Trace({self.walkers[-1]}'
|
|
742
|
+
if self.source:
|
|
743
|
+
s += f', source={self.source}'
|
|
744
|
+
if self.max_length < sys.maxsize:
|
|
745
|
+
s += f', max_length={self.max_length}'
|
|
746
|
+
return s + ')'
|