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