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.
- westpa/__init__.py +14 -0
- westpa/_version.py +21 -0
- westpa/analysis/__init__.py +5 -0
- westpa/analysis/core.py +749 -0
- westpa/analysis/statistics.py +27 -0
- westpa/analysis/trajectories.py +369 -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 +68 -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 +597 -0
- westpa/cli/tools/w_bins.py +166 -0
- westpa/cli/tools/w_crawl.py +119 -0
- westpa/cli/tools/w_direct.py +557 -0
- westpa/cli/tools/w_dumpsegs.py +94 -0
- westpa/cli/tools/w_eddist.py +506 -0
- westpa/cli/tools/w_fluxanl.py +376 -0
- westpa/cli/tools/w_ipa.py +832 -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 +491 -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_timings.py +113 -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.c +36018 -0
- westpa/core/binning/_assign.cpython-312-aarch64-linux-gnu.so +0 -0
- westpa/core/binning/_assign.pyx +370 -0
- westpa/core/binning/assign.py +454 -0
- westpa/core/binning/binless.py +96 -0
- westpa/core/binning/binless_driver.py +54 -0
- westpa/core/binning/binless_manager.py +189 -0
- westpa/core/binning/bins.py +47 -0
- westpa/core/binning/mab.py +506 -0
- westpa/core/binning/mab_driver.py +54 -0
- westpa/core/binning/mab_manager.py +197 -0
- westpa/core/data_manager.py +1761 -0
- westpa/core/extloader.py +74 -0
- westpa/core/h5io.py +1079 -0
- westpa/core/kinetics/__init__.py +24 -0
- westpa/core/kinetics/_kinetics.c +45174 -0
- westpa/core/kinetics/_kinetics.cpython-312-aarch64-linux-gnu.so +0 -0
- westpa/core/kinetics/_kinetics.pyx +815 -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 +592 -0
- westpa/core/propagators/loaders.py +196 -0
- westpa/core/reweight/__init__.py +14 -0
- westpa/core/reweight/_reweight.c +36899 -0
- westpa/core/reweight/_reweight.cpython-312-aarch64-linux-gnu.so +0 -0
- westpa/core/reweight/_reweight.pyx +439 -0
- westpa/core/reweight/matrix.py +126 -0
- westpa/core/segment.py +119 -0
- westpa/core/sim_manager.py +839 -0
- westpa/core/states.py +359 -0
- westpa/core/systems.py +93 -0
- westpa/core/textio.py +74 -0
- westpa/core/trajectory.py +603 -0
- westpa/core/we_driver.py +910 -0
- westpa/core/wm_ops.py +43 -0
- westpa/core/yamlcfg.py +298 -0
- westpa/fasthist/__init__.py +34 -0
- westpa/fasthist/_fasthist.c +38755 -0
- westpa/fasthist/_fasthist.cpython-312-aarch64-linux-gnu.so +0 -0
- westpa/fasthist/_fasthist.pyx +222 -0
- westpa/mclib/__init__.py +271 -0
- westpa/mclib/__main__.py +28 -0
- westpa/mclib/_mclib.c +34610 -0
- westpa/mclib/_mclib.cpython-312-aarch64-linux-gnu.so +0 -0
- westpa/mclib/_mclib.pyx +226 -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 +153 -0
- westpa/oldtools/aframe/output.py +39 -0
- westpa/oldtools/aframe/plotting.py +88 -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 +361 -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 +96 -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 +343 -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.c +17829 -0
- westpa/trajtree/_trajtree.cpython-312-aarch64-linux-gnu.so +0 -0
- westpa/trajtree/_trajtree.pyx +130 -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 +192 -0
- westpa/westext/wess/ProbAdjust.py +101 -0
- westpa/westext/wess/__init__.py +6 -0
- westpa/westext/wess/wess_driver.py +217 -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 +201 -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 +635 -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.13.dist-info/METADATA +179 -0
- westpa-2022.13.dist-info/RECORD +162 -0
- westpa-2022.13.dist-info/WHEEL +7 -0
- westpa-2022.13.dist-info/entry_points.txt +30 -0
- westpa-2022.13.dist-info/licenses/LICENSE +21 -0
- westpa-2022.13.dist-info/top_level.txt +1 -0
|
Binary file
|
westpa/mclib/_mclib.pyx
ADDED
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
|
|
2
|
+
from __future__ import division
|
|
3
|
+
import numpy, sys, math, os
|
|
4
|
+
cimport numpy, cython
|
|
5
|
+
from cpython.buffer cimport *
|
|
6
|
+
from numpy cimport *
|
|
7
|
+
|
|
8
|
+
from libc.math cimport floor, ceil, log10
|
|
9
|
+
#from libc.stdlib cimport RAND_MAX, rand, srand
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
cpdef msort(input_array):
|
|
13
|
+
return numpy.sort(input_array, axis=0)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
ctypedef fused _fptype:
|
|
17
|
+
numpy.float32_t
|
|
18
|
+
numpy.float64_t
|
|
19
|
+
|
|
20
|
+
# Number of bytes in a dataset above which correlation time estimates are
|
|
21
|
+
# not performed by generating synthetic data sets once and then evaluating
|
|
22
|
+
# the autocorrelation elements on them, but rather by generating a new
|
|
23
|
+
# synthetic dataset each time an autocorrelation element is required.
|
|
24
|
+
# Essentially, this limits the RAM used during the correlation time
|
|
25
|
+
# calculation to approximately CORRELTIME_CROSSOVER. Keep in mind that
|
|
26
|
+
# the space savings is at the cost of needing *many* more random numbers,
|
|
27
|
+
# which will almost certainly become the rate-limiting step of the
|
|
28
|
+
# calculation.
|
|
29
|
+
cdef Py_ssize_t CORRELTIME_CROSSOVER = 512*1024*1024
|
|
30
|
+
|
|
31
|
+
numpy.random.seed()
|
|
32
|
+
|
|
33
|
+
@cython.cdivision(True)
|
|
34
|
+
@cython.boundscheck(False)
|
|
35
|
+
@cython.wraparound(False)
|
|
36
|
+
cdef _fptype _autocorrel_elem(_fptype[:] xs, long k):
|
|
37
|
+
cdef:
|
|
38
|
+
long N = xs.shape[0]
|
|
39
|
+
long i
|
|
40
|
+
double cdif
|
|
41
|
+
double xbar = 0.0
|
|
42
|
+
double norm = 0.0
|
|
43
|
+
double rho = 0.0
|
|
44
|
+
|
|
45
|
+
with nogil:
|
|
46
|
+
# calculate the mean manually, because it's faster than calling out to
|
|
47
|
+
# numpy.mean
|
|
48
|
+
for i in range(N):
|
|
49
|
+
xbar += xs[i]
|
|
50
|
+
xbar /= N
|
|
51
|
+
|
|
52
|
+
for i in range(N):
|
|
53
|
+
cdif = xs[i] - xbar
|
|
54
|
+
norm += cdif*cdif
|
|
55
|
+
|
|
56
|
+
if i < N-k:
|
|
57
|
+
rho += cdif * (xs[i+k]-xbar)
|
|
58
|
+
|
|
59
|
+
rho *= N / ((N-k)*norm)
|
|
60
|
+
|
|
61
|
+
return <_fptype> rho
|
|
62
|
+
|
|
63
|
+
cpdef autocorrel_elem(xs, k):
|
|
64
|
+
'''Calculate the ``k``-lag sample autocorrelation of ``xs``. ``xs`` must be float32 or float64.'''
|
|
65
|
+
|
|
66
|
+
cdef:
|
|
67
|
+
int typecode = PyArray_TYPE(xs)
|
|
68
|
+
|
|
69
|
+
if typecode == NPY_FLOAT64:
|
|
70
|
+
return _autocorrel_elem[numpy.float64_t](xs,k)
|
|
71
|
+
elif typecode == NPY_FLOAT32:
|
|
72
|
+
return _autocorrel_elem[numpy.float32_t](xs,k)
|
|
73
|
+
else:
|
|
74
|
+
raise TypeError('unsupported type')
|
|
75
|
+
|
|
76
|
+
cpdef Py_ssize_t get_bssize(double alpha) nogil:
|
|
77
|
+
'''Return a bootstrap data set size appropriate for the given confidence level.'''
|
|
78
|
+
|
|
79
|
+
cdef:
|
|
80
|
+
Py_ssize_t bssize=1, i
|
|
81
|
+
|
|
82
|
+
#return int(10**(math.ceil(-math.log10(alpha)) + 1))
|
|
83
|
+
for i in range(<long> ceil(-log10(alpha)) + 1):
|
|
84
|
+
bssize *= 10
|
|
85
|
+
return bssize
|
|
86
|
+
|
|
87
|
+
cpdef mcbs_ci(dataset, estimator, alpha, dlen, n_sets=None, args=None, kwargs=None, sort=msort):
|
|
88
|
+
'''Perform a Monte Carlo bootstrap estimate for the (1-``alpha``) confidence interval
|
|
89
|
+
on the given ``dataset`` with the given ``estimator``. This routine is not appropriate
|
|
90
|
+
for time-correlated data.
|
|
91
|
+
|
|
92
|
+
Returns ``(estimate, ci_lb, ci_ub)`` where ``estimate`` is the application of the
|
|
93
|
+
given ``estimator`` to the input ``dataset``, and ``ci_lb`` and ``ci_ub`` are the
|
|
94
|
+
lower and upper limits, respectively, of the (1-``alpha``) confidence interval on
|
|
95
|
+
``estimate``.
|
|
96
|
+
|
|
97
|
+
``estimator`` is called as ``estimator(dataset, *args, **kwargs)``. Common estimators include:
|
|
98
|
+
* numpy.mean -- calculate the confidence interval on the mean of ``dataset``
|
|
99
|
+
* numpy.median -- calculate a confidence interval on the median of ``dataset``
|
|
100
|
+
* numpy.std -- calculate a confidence interval on the standard deviation of ``datset``.
|
|
101
|
+
|
|
102
|
+
``n_sets`` is the number of synthetic data sets to generate using the given ``estimator``,
|
|
103
|
+
which will be chosen using `get_bssize()`_ if ``n_sets`` is not given.
|
|
104
|
+
|
|
105
|
+
``sort`` can be used
|
|
106
|
+
to override the sorting routine used to calculate the confidence interval, which should
|
|
107
|
+
only be necessary for estimators returning vectors rather than scalars.
|
|
108
|
+
'''
|
|
109
|
+
|
|
110
|
+
if alpha > 0.5:
|
|
111
|
+
raise ValueError('alpha ({}) > 0.5'.format(alpha))
|
|
112
|
+
|
|
113
|
+
args = args or ()
|
|
114
|
+
kwargs = kwargs or {}
|
|
115
|
+
|
|
116
|
+
# dataset SHOULD be a dictionary.
|
|
117
|
+
d_input = dataset.copy()
|
|
118
|
+
# Here, we're dumping in any extra kwarg arguments to pass in to the estimator.
|
|
119
|
+
try:
|
|
120
|
+
d_input.update(kwargs)
|
|
121
|
+
except Exception:
|
|
122
|
+
pass
|
|
123
|
+
|
|
124
|
+
fhat = estimator(**d_input)
|
|
125
|
+
|
|
126
|
+
try:
|
|
127
|
+
estimator_shape = fhat.shape
|
|
128
|
+
except AttributeError:
|
|
129
|
+
estimator_shape = ()
|
|
130
|
+
|
|
131
|
+
try:
|
|
132
|
+
estimator_dtype = fhat.dtype
|
|
133
|
+
except AttributeError:
|
|
134
|
+
estimator_dtype = type(fhat)
|
|
135
|
+
|
|
136
|
+
n_sets = n_sets or get_bssize(alpha)
|
|
137
|
+
|
|
138
|
+
f_synth = numpy.empty((n_sets,) + estimator_shape, dtype=estimator_dtype)
|
|
139
|
+
|
|
140
|
+
for i in xrange(n_sets):
|
|
141
|
+
indices = numpy.random.randint(dlen, size=(dlen,))
|
|
142
|
+
d_synth = {}
|
|
143
|
+
for key, dset in dataset.iteritems():
|
|
144
|
+
d_synth[key] = numpy.take(dset, indices, axis=0)
|
|
145
|
+
d_input = d_synth.copy()
|
|
146
|
+
try:
|
|
147
|
+
d_input.update(kwargs)
|
|
148
|
+
except Exception:
|
|
149
|
+
pass
|
|
150
|
+
f_synth[i] = estimator(**d_input)
|
|
151
|
+
del indices
|
|
152
|
+
|
|
153
|
+
f_synth_sorted = sort(f_synth)
|
|
154
|
+
lbi = math.floor(n_sets*alpha/2.0)
|
|
155
|
+
ubi = math.ceil(n_sets*(1-alpha/2.0))
|
|
156
|
+
lb = f_synth_sorted[lbi]
|
|
157
|
+
ub = f_synth_sorted[ubi]
|
|
158
|
+
sterr = numpy.std(f_synth_sorted)
|
|
159
|
+
|
|
160
|
+
del f_synth_sorted, f_synth
|
|
161
|
+
return (fhat, lb, ub, sterr)
|
|
162
|
+
|
|
163
|
+
cpdef mcbs_correltime(dataset, alpha, n_sets = None):
|
|
164
|
+
'''Calculate the correlation time of the given ``dataset``, significant to the
|
|
165
|
+
(1-``alpha``) level, using the method described in Huber & Kim, "Weighted-ensemble
|
|
166
|
+
Brownian dynamics simulations for protein association reactions" (1996),
|
|
167
|
+
doi:10.1016/S0006-3495(96)79552-8. An appropriate balance between space and speed
|
|
168
|
+
is chosen based on the size of the input data.
|
|
169
|
+
|
|
170
|
+
Returns 0 for data statistically uncorrelated with (1-alpha) confidence, otherwise
|
|
171
|
+
the correlation length. (Thus, the appropriate stride for blocking is the
|
|
172
|
+
result of this function plus one.)'''
|
|
173
|
+
|
|
174
|
+
n_sets = n_sets or get_bssize(alpha)
|
|
175
|
+
if dataset.nbytes * n_sets > CORRELTIME_CROSSOVER:
|
|
176
|
+
return mcbs_correltime_fullauto(dataset, alpha, n_sets)
|
|
177
|
+
else:
|
|
178
|
+
return mcbs_correltime_small(dataset, alpha, n_sets)
|
|
179
|
+
|
|
180
|
+
cpdef mcbs_correltime_fullauto(dataset, alpha, n_sets):
|
|
181
|
+
'''Specialization of `mcbs_correltime`_ for large datasets, where rather than generating
|
|
182
|
+
``n_sets`` synthetic datasets and then evaluating autocorrelation elements on them, a
|
|
183
|
+
synthetic data set is generated for each autocorrelation element required. This trades time
|
|
184
|
+
for space.'''
|
|
185
|
+
|
|
186
|
+
for k in xrange(1,len(dataset)//2):
|
|
187
|
+
data_rho, synrho_lb, synrho_ub = mcbs_ci(dataset, autocorrel_elem, alpha, n_sets, args=(k,))
|
|
188
|
+
if data_rho > synrho_lb and data_rho < synrho_ub:
|
|
189
|
+
return k-1
|
|
190
|
+
else:
|
|
191
|
+
return len(dataset)
|
|
192
|
+
#raise ValueError('correlation length exceeds half the data set length')
|
|
193
|
+
|
|
194
|
+
cpdef mcbs_correltime_small(dataset, alpha, n_sets):
|
|
195
|
+
'''Specialization of `mcbs_correltime`_ for small-to-mediam datasets, where ``n_sets``
|
|
196
|
+
synthetic datasets are generated and stored, and then autocorrelation elements
|
|
197
|
+
calculated on them. This implementation trades space for time.'''
|
|
198
|
+
|
|
199
|
+
if alpha > 0.5:
|
|
200
|
+
raise ValueError('alpha ({}) > 0.5'.format(alpha))
|
|
201
|
+
|
|
202
|
+
dlen = len(dataset)
|
|
203
|
+
lbi = math.floor(n_sets*alpha/2.0)
|
|
204
|
+
ubi = math.ceil(n_sets*(1-alpha/2.0))
|
|
205
|
+
|
|
206
|
+
synth_sets = numpy.empty((n_sets,)+dataset.shape, dtype=dataset.dtype)
|
|
207
|
+
synth_acf_elems = numpy.empty((n_sets,), numpy.float64)
|
|
208
|
+
|
|
209
|
+
for i in xrange(n_sets):
|
|
210
|
+
synth_sets[i] = numpy.take(dataset,numpy.random.randint(dlen,size=(dlen,)))
|
|
211
|
+
|
|
212
|
+
for k in xrange(1,dlen//2):
|
|
213
|
+
data_rho = autocorrel_elem(dataset, k)
|
|
214
|
+
|
|
215
|
+
for i in xrange(n_sets):
|
|
216
|
+
synth_acf_elems[i] = autocorrel_elem(synth_sets[i],k)
|
|
217
|
+
|
|
218
|
+
synth_acf_elems.sort()
|
|
219
|
+
|
|
220
|
+
if data_rho > synth_acf_elems[lbi] and data_rho < synth_acf_elems[ubi]:
|
|
221
|
+
return k-1
|
|
222
|
+
else:
|
|
223
|
+
#raise ValueError('correlation length exceeds half the data set length')
|
|
224
|
+
return len(dataset)
|
|
225
|
+
|
|
226
|
+
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"""WEST Analyis framework -- an unholy mess of classes exploiting each other"""
|
|
2
|
+
|
|
3
|
+
from . import atool
|
|
4
|
+
from .atool import WESTAnalysisTool
|
|
5
|
+
from .base_mixin import ArgumentError, AnalysisMixin
|
|
6
|
+
from .binning import BinningMixin
|
|
7
|
+
from .data_reader import WESTDataReaderMixin, ExtDataReaderMixin, BFDataManager
|
|
8
|
+
from .iter_range import IterRangeMixin
|
|
9
|
+
from .kinetics import KineticsAnalysisMixin
|
|
10
|
+
from .mcbs import MCBSMixin
|
|
11
|
+
from .output import CommonOutputMixin
|
|
12
|
+
from .plotting import PlottingMixin
|
|
13
|
+
from .trajwalker import TrajWalker
|
|
14
|
+
from .transitions import TransitionAnalysisMixin, TransitionEventAccumulator, BFTransitionAnalysisMixin
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
__all__ = [
|
|
18
|
+
'atool',
|
|
19
|
+
'AnalysisMixin',
|
|
20
|
+
'ArgumentError',
|
|
21
|
+
'WESTAnalysisTool',
|
|
22
|
+
'IterRangeMixin',
|
|
23
|
+
'WESTDataReaderMixin',
|
|
24
|
+
'ExtDataReaderMixin',
|
|
25
|
+
'BFDataManager',
|
|
26
|
+
'BinningMixin',
|
|
27
|
+
'MCBSMixin',
|
|
28
|
+
'TrajWalker',
|
|
29
|
+
'TransitionAnalysisMixin',
|
|
30
|
+
'TransitionEventAccumulator',
|
|
31
|
+
'BFTransitionAnalysisMixin',
|
|
32
|
+
'KineticsAnalysisMixin',
|
|
33
|
+
'CommonOutputMixin',
|
|
34
|
+
'PlottingMixin',
|
|
35
|
+
]
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
import h5py
|
|
5
|
+
|
|
6
|
+
log = logging.getLogger(__name__)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class WESTAnalysisTool:
|
|
10
|
+
def __init__(self):
|
|
11
|
+
super().__init__()
|
|
12
|
+
# Whether a west.cfg is required to run a program based on this tool
|
|
13
|
+
self.config_required = False
|
|
14
|
+
|
|
15
|
+
# Analysis HDF5 filename and object
|
|
16
|
+
self.anal_h5name = None
|
|
17
|
+
self.anal_h5file = None
|
|
18
|
+
|
|
19
|
+
# Whether this is being used in a brute analysis
|
|
20
|
+
self.bf_mode = False
|
|
21
|
+
|
|
22
|
+
# A way to override some arguments on a per-mixin basis without having to subclass
|
|
23
|
+
# (messy, but it doesn't seem crucial enough so far to make it cleaner)
|
|
24
|
+
self.include_args = {}
|
|
25
|
+
|
|
26
|
+
def add_args(self, parser, upcall=True):
|
|
27
|
+
'''Add arguments to a parser common to all analyses of this type.'''
|
|
28
|
+
if upcall:
|
|
29
|
+
try:
|
|
30
|
+
upfunc = super().add_args
|
|
31
|
+
except AttributeError:
|
|
32
|
+
pass
|
|
33
|
+
else:
|
|
34
|
+
upfunc(parser)
|
|
35
|
+
|
|
36
|
+
group = parser.add_argument_group('general analysis options')
|
|
37
|
+
group.add_argument(
|
|
38
|
+
'-A',
|
|
39
|
+
'--analysis-file',
|
|
40
|
+
dest='anal_h5name',
|
|
41
|
+
metavar='H5FILE',
|
|
42
|
+
default='analysis.h5',
|
|
43
|
+
help='Store intermediate and final results in H5FILE (default: %(default)s).',
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
def process_args(self, args, upcall=True):
|
|
47
|
+
self.anal_h5name = args.anal_h5name
|
|
48
|
+
|
|
49
|
+
if upcall:
|
|
50
|
+
try:
|
|
51
|
+
upfunc = super().process_args
|
|
52
|
+
except AttributeError:
|
|
53
|
+
pass
|
|
54
|
+
else:
|
|
55
|
+
upfunc(args)
|
|
56
|
+
|
|
57
|
+
def open_analysis_backing(self):
|
|
58
|
+
if self.anal_h5file is None:
|
|
59
|
+
self.anal_h5file = h5py.File(self.anal_h5name)
|
|
60
|
+
|
|
61
|
+
def close_analysis_backing(self):
|
|
62
|
+
try:
|
|
63
|
+
self.anal_h5file.close()
|
|
64
|
+
self.anal_h5file = None
|
|
65
|
+
except AttributeError:
|
|
66
|
+
pass
|
|
67
|
+
|
|
68
|
+
def require_analysis_group(self, groupname, replace=False):
|
|
69
|
+
self.open_analysis_backing()
|
|
70
|
+
if replace:
|
|
71
|
+
try:
|
|
72
|
+
del self.anal_h5file[groupname]
|
|
73
|
+
except KeyError:
|
|
74
|
+
pass
|
|
75
|
+
return self.anal_h5file.require_group(groupname)
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
class ArgumentError(RuntimeError):
|
|
2
|
+
def __init__(self, *args, **kwargs):
|
|
3
|
+
super().__init__(*args, **kwargs)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class AnalysisMixin:
|
|
7
|
+
def __init__(self):
|
|
8
|
+
super().__init__()
|
|
9
|
+
|
|
10
|
+
def add_args(self, parser, upcall=True):
|
|
11
|
+
if upcall:
|
|
12
|
+
try:
|
|
13
|
+
upfunc = super().add_args
|
|
14
|
+
except AttributeError:
|
|
15
|
+
pass
|
|
16
|
+
else:
|
|
17
|
+
upfunc(parser)
|
|
18
|
+
|
|
19
|
+
def process_args(self, args, upcall=True):
|
|
20
|
+
if upcall:
|
|
21
|
+
try:
|
|
22
|
+
upfunc = super().process_args
|
|
23
|
+
except AttributeError:
|
|
24
|
+
pass
|
|
25
|
+
else:
|
|
26
|
+
upfunc(args)
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
|
|
5
|
+
import westpa
|
|
6
|
+
from westpa.oldtools.aframe import AnalysisMixin
|
|
7
|
+
|
|
8
|
+
log = logging.getLogger(__name__)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class BinningMixin(AnalysisMixin):
|
|
12
|
+
'''A mixin for performing binning on WEST data.'''
|
|
13
|
+
|
|
14
|
+
def __init__(self):
|
|
15
|
+
super().__init__()
|
|
16
|
+
|
|
17
|
+
self.mapper = None
|
|
18
|
+
self.n_bins = None
|
|
19
|
+
|
|
20
|
+
self.discard_bin_assignments = False
|
|
21
|
+
self.binning_h5gname = 'binning'
|
|
22
|
+
self.binning_h5group = None
|
|
23
|
+
self.mapper_hash = None
|
|
24
|
+
|
|
25
|
+
def add_args(self, parser, upcall=True):
|
|
26
|
+
if upcall:
|
|
27
|
+
try:
|
|
28
|
+
upfunc = super().add_args
|
|
29
|
+
except AttributeError:
|
|
30
|
+
pass
|
|
31
|
+
else:
|
|
32
|
+
upfunc(parser)
|
|
33
|
+
|
|
34
|
+
group = parser.add_argument_group('binning options')
|
|
35
|
+
egroup = group.add_mutually_exclusive_group()
|
|
36
|
+
egroup.add_argument(
|
|
37
|
+
'--binexpr',
|
|
38
|
+
'--binbounds',
|
|
39
|
+
dest='binexpr',
|
|
40
|
+
help='''Construct rectilinear bins from BINEXPR. This must be a list of lists of bin boundaries
|
|
41
|
+
(one list of bin boundaries for each dimension of the progress coordinate), formatted as a Python
|
|
42
|
+
expression. E.g. "[[0,1,2,4,inf], [-inf,0,inf]]".''',
|
|
43
|
+
)
|
|
44
|
+
group.add_argument(
|
|
45
|
+
'--discard-bin-assignments',
|
|
46
|
+
dest='discard_bin_assignments',
|
|
47
|
+
action='store_true',
|
|
48
|
+
help='''Discard any existing bin assignments stored in the analysis HDF5 file.''',
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
def process_args(self, args, upcall=True):
|
|
52
|
+
if args.binexpr:
|
|
53
|
+
westpa.rc.pstatus("Constructing rectilinear bin boundaries from the following expression: '{}'".format(args.binexpr))
|
|
54
|
+
self.mapper = self.mapper_from_expr(args.binexpr)
|
|
55
|
+
else:
|
|
56
|
+
westpa.rc.pstatus('Loading bin boundaries from WEST system')
|
|
57
|
+
system = westpa.rc.get_system_driver()
|
|
58
|
+
self.mapper = system.bin_mapper
|
|
59
|
+
|
|
60
|
+
self.n_bins = self.mapper.nbins
|
|
61
|
+
_pdat, self.mapper_hash = self.mapper.pickle_and_hash()
|
|
62
|
+
westpa.rc.pstatus(' {:d} bins'.format(self.n_bins))
|
|
63
|
+
westpa.rc.pstatus(' identity hash {}'.format(self.mapper_hash))
|
|
64
|
+
|
|
65
|
+
self.discard_bin_assignments = bool(args.discard_bin_assignments)
|
|
66
|
+
|
|
67
|
+
if upcall:
|
|
68
|
+
try:
|
|
69
|
+
upfunc = super().process_args
|
|
70
|
+
except AttributeError:
|
|
71
|
+
pass
|
|
72
|
+
else:
|
|
73
|
+
upfunc(args)
|
|
74
|
+
|
|
75
|
+
def mapper_from_expr(self, expr):
|
|
76
|
+
from westpa.core.binning import RectilinearBinMapper
|
|
77
|
+
|
|
78
|
+
namespace = {'numpy': np, 'np': np, 'inf': float('inf')}
|
|
79
|
+
|
|
80
|
+
try:
|
|
81
|
+
return RectilinearBinMapper(eval(expr, namespace))
|
|
82
|
+
except TypeError as e:
|
|
83
|
+
if 'has no len' in str(e):
|
|
84
|
+
raise ValueError('invalid bin boundary specification; you probably forgot to make a list of lists')
|
|
85
|
+
|
|
86
|
+
def write_bin_labels(self, dest, header='# bin labels:\n', format='# bin {bin_index:{max_iwidth}d} -- {label!s}\n'):
|
|
87
|
+
'''Print labels for all bins in ``self.mapper`` to ``dest``. If provided, ``header``
|
|
88
|
+
is printed before any labels. The ``format`` string specifies how bin labels are to be printed. Valid entries are:
|
|
89
|
+
* ``bin_index`` -- the zero-based index of the bin
|
|
90
|
+
* ``label`` -- the label, as obtained by ``bin.label``
|
|
91
|
+
* ``max_iwidth`` -- the maximum width (in characters) of the bin index, for pretty alignment
|
|
92
|
+
'''
|
|
93
|
+
dest.write(header or '')
|
|
94
|
+
max_iwidth = len(str(self.mapper.nbins - 1))
|
|
95
|
+
for ibin, label in enumerate(self.mapper.labels):
|
|
96
|
+
dest.write(format.format(bin_index=ibin, label=label, max_iwidth=max_iwidth))
|
|
97
|
+
|
|
98
|
+
def require_binning_group(self):
|
|
99
|
+
if self.binning_h5group is None:
|
|
100
|
+
self.binning_h5group = self.anal_h5file.require_group(self.binning_h5gname)
|
|
101
|
+
return self.binning_h5group
|
|
102
|
+
|
|
103
|
+
def delete_binning_group(self):
|
|
104
|
+
self.binning_h5group = None
|
|
105
|
+
del self.anal_h5file[self.binning_h5gname]
|
|
106
|
+
|
|
107
|
+
def record_data_binhash(self, h5object):
|
|
108
|
+
'''Record the identity hash for self.mapper as an attribute on the given HDF5 object (group or dataset)'''
|
|
109
|
+
h5object.attrs['binhash'] = self.mapper_hash
|
|
110
|
+
|
|
111
|
+
def check_data_binhash(self, h5object):
|
|
112
|
+
'''Check whether the recorded bin identity hash on the given HDF5 object matches the identity hash for self.mapper'''
|
|
113
|
+
return h5object.attrs.get('binhash') == self.mapper_hash
|
|
114
|
+
|
|
115
|
+
def assign_to_bins(self):
|
|
116
|
+
'''Assign WEST segment data to bins. Requires the DataReader mixin to be in the inheritance tree'''
|
|
117
|
+
self.require_binning_group()
|
|
118
|
+
|
|
119
|
+
n_iters = self.last_iter - self.first_iter + 1
|
|
120
|
+
max_n_segs = self.max_iter_segs_in_range(self.first_iter, self.last_iter)
|
|
121
|
+
pcoord_len = self.get_pcoord_len(self.first_iter)
|
|
122
|
+
|
|
123
|
+
assignments = np.zeros((n_iters, max_n_segs, pcoord_len), np.min_scalar_type(self.n_bins))
|
|
124
|
+
populations = np.zeros((n_iters, pcoord_len, self.n_bins), np.float64)
|
|
125
|
+
|
|
126
|
+
westpa.rc.pstatus('Assigning to bins...')
|
|
127
|
+
|
|
128
|
+
for iiter, n_iter in enumerate(range(self.first_iter, self.last_iter + 1)):
|
|
129
|
+
westpa.rc.pstatus('\r Iteration {:d}'.format(n_iter), end='')
|
|
130
|
+
seg_index = self.get_seg_index(n_iter)
|
|
131
|
+
pcoords = self.get_iter_group(n_iter)['pcoord'][...]
|
|
132
|
+
weights = seg_index['weight']
|
|
133
|
+
|
|
134
|
+
for seg_id in range(len(seg_index)):
|
|
135
|
+
assignments[iiter, seg_id, :] = self.mapper.assign(pcoords[seg_id, :, :])
|
|
136
|
+
|
|
137
|
+
for it in range(pcoord_len):
|
|
138
|
+
populations[iiter, it, :] = np.bincount(assignments[iiter, : len(seg_index), it], weights, minlength=self.n_bins)
|
|
139
|
+
|
|
140
|
+
westpa.rc.pflush()
|
|
141
|
+
del pcoords, weights, seg_index
|
|
142
|
+
|
|
143
|
+
assignments_ds = self.binning_h5group.create_dataset('bin_assignments', data=assignments, compression='gzip')
|
|
144
|
+
populations_ds = self.binning_h5group.create_dataset('bin_populations', data=populations, compression='gzip')
|
|
145
|
+
|
|
146
|
+
for h5object in (self.binning_h5group, assignments_ds, populations_ds):
|
|
147
|
+
self.record_data_iter_range(h5object)
|
|
148
|
+
self.record_data_iter_step(h5object, 1)
|
|
149
|
+
self.record_data_binhash(h5object)
|
|
150
|
+
|
|
151
|
+
westpa.rc.pstatus()
|
|
152
|
+
|
|
153
|
+
def require_bin_assignments(self):
|
|
154
|
+
self.require_binning_group()
|
|
155
|
+
do_assign = False
|
|
156
|
+
if self.discard_bin_assignments:
|
|
157
|
+
westpa.rc.pstatus('Discarding existing bin assignments.')
|
|
158
|
+
do_assign = True
|
|
159
|
+
elif 'bin_assignments' not in self.binning_h5group:
|
|
160
|
+
do_assign = True
|
|
161
|
+
elif not self.check_data_iter_range_least(self.binning_h5group):
|
|
162
|
+
westpa.rc.pstatus('Existing bin assignments are for incompatible first/last iterations; deleting assignments.')
|
|
163
|
+
do_assign = True
|
|
164
|
+
elif not self.check_data_binhash(self.binning_h5group):
|
|
165
|
+
westpa.rc.pstatus('Bin definitions have changed; deleting existing bin assignments.')
|
|
166
|
+
do_assign = True
|
|
167
|
+
|
|
168
|
+
if do_assign:
|
|
169
|
+
self.delete_binning_group()
|
|
170
|
+
self.assign_to_bins()
|
|
171
|
+
else:
|
|
172
|
+
westpa.rc.pstatus('Using existing bin assignments.')
|
|
173
|
+
|
|
174
|
+
def get_bin_assignments(self, first_iter=None, last_iter=None):
|
|
175
|
+
return self.slice_per_iter_data(self.binning_h5group['bin_assignments'], first_iter, last_iter)
|
|
176
|
+
|
|
177
|
+
def get_bin_populations(self, first_iter=None, last_iter=None):
|
|
178
|
+
return self.slice_per_iter_data(self.binning_h5group['bin_populations'], first_iter, last_iter)
|