westpa 2022.10__cp312-cp312-macosx_10_9_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-darwin.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-darwin.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-darwin.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-darwin.so +0 -0
- westpa/mclib/__init__.py +264 -0
- westpa/mclib/__main__.py +28 -0
- westpa/mclib/_mclib.cpython-312-darwin.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-darwin.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 +5 -0
- westpa-2022.10.dist-info/entry_points.txt +29 -0
- westpa-2022.10.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"""westext.stringmethod - Plugin to drive the adaptive evolution of one or more
|
|
2
|
+
strings of Voronoi bins
|
|
3
|
+
|
|
4
|
+
Joshua L. Adelman 2011
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from .string_method import DefaultStringMethod, WESTStringMethod
|
|
8
|
+
from .string_driver import StringDriver
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
__all__ = ['DefaultStringMethod', 'WESTStringMethod', 'StringDriver']
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import scipy
|
|
3
|
+
import scipy.optimize
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class FourierFit:
|
|
7
|
+
def __init__(self, P=2, ndims=2, maxiters=100, tol=1.0e-6):
|
|
8
|
+
super().__init__()
|
|
9
|
+
|
|
10
|
+
self.P = P
|
|
11
|
+
self.maxiters = maxiters
|
|
12
|
+
self.ndims = ndims
|
|
13
|
+
self.tol = tol
|
|
14
|
+
self.pp = []
|
|
15
|
+
self.t0 = None
|
|
16
|
+
self.w0 = None
|
|
17
|
+
|
|
18
|
+
def calc_string(self, w, t, x_meas):
|
|
19
|
+
tlen = len(t)
|
|
20
|
+
t = np.linspace(0.0, 1.0, tlen)
|
|
21
|
+
x_est = x_meas[0, :] + (x_meas[-1, :] - x_meas[0, :]) * t[:, np.newaxis]
|
|
22
|
+
for i in range(self.ndims):
|
|
23
|
+
for j in range(self.P):
|
|
24
|
+
x_est[:, i] += w[i, j] * np.sin((j + 1) * np.pi * t)
|
|
25
|
+
return x_est
|
|
26
|
+
|
|
27
|
+
def _optimize_dist(self, tk, x_meas, w, k):
|
|
28
|
+
x_target = x_meas[k, :]
|
|
29
|
+
x_est = x_meas[0, :] + (x_meas[-1, :] - x_meas[0, :]) * tk
|
|
30
|
+
for i in range(self.ndims):
|
|
31
|
+
for j in range(self.P):
|
|
32
|
+
x_est[i] += w[i, j] * np.sin((j + 1) * np.pi * tk)
|
|
33
|
+
|
|
34
|
+
err = x_target - x_est
|
|
35
|
+
return err
|
|
36
|
+
|
|
37
|
+
def _optimize_w(self, w, x_meas, t, k, weight):
|
|
38
|
+
x_target = x_meas[:, k]
|
|
39
|
+
x_est = x_meas[0, k] + (x_meas[-1, k] - x_meas[0, k]) * t
|
|
40
|
+
|
|
41
|
+
for j in range(self.P):
|
|
42
|
+
x_est += w[j] * np.sin((j + 1) * np.pi * t)
|
|
43
|
+
|
|
44
|
+
err = weight * (x_target - x_est)
|
|
45
|
+
return err
|
|
46
|
+
|
|
47
|
+
def optimize(self, data, weight, w0, t0):
|
|
48
|
+
ncenters = data.shape[0]
|
|
49
|
+
self.w0 = w0
|
|
50
|
+
self.t0 = t0
|
|
51
|
+
if weight is None:
|
|
52
|
+
weight = np.ones_like(t0)
|
|
53
|
+
|
|
54
|
+
for iiter in range(self.maxiters):
|
|
55
|
+
self.pp.append(self.calc_string(self.w0, self.t0, data))
|
|
56
|
+
if iiter > 0:
|
|
57
|
+
err = np.sum((self.pp[-1] - self.pp[-2]) ** 2) / ncenters
|
|
58
|
+
print('{} -- {}'.format(iiter, err))
|
|
59
|
+
if err < self.tol:
|
|
60
|
+
break
|
|
61
|
+
else:
|
|
62
|
+
print(iiter)
|
|
63
|
+
# Optimize tk
|
|
64
|
+
for ci in range(ncenters):
|
|
65
|
+
self.t0[ci] = scipy.optimize.leastsq(self._optimize_dist, self.t0[ci], args=(data, self.w0, ci))[0]
|
|
66
|
+
|
|
67
|
+
# Optimize wij
|
|
68
|
+
for k in range(self.ndims):
|
|
69
|
+
self.w0[k, :] = scipy.optimize.leastsq(self._optimize_w, self.w0[k, :], args=(data, self.t0, k, weight))[0]
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import types
|
|
3
|
+
|
|
4
|
+
import numpy as np
|
|
5
|
+
|
|
6
|
+
import westpa
|
|
7
|
+
from westpa.core import extloader
|
|
8
|
+
from westpa.core.binning import VoronoiBinMapper
|
|
9
|
+
from westpa.core.yamlcfg import check_bool, ConfigItemMissing
|
|
10
|
+
|
|
11
|
+
from westpa.westext.stringmethod import WESTStringMethod, DefaultStringMethod
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
log = logging.getLogger(__name__)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class StringDriver:
|
|
18
|
+
def __init__(self, sim_manager, plugin_config):
|
|
19
|
+
super().__init__()
|
|
20
|
+
|
|
21
|
+
if not sim_manager.work_manager.is_master:
|
|
22
|
+
return
|
|
23
|
+
|
|
24
|
+
self.sim_manager = sim_manager
|
|
25
|
+
self.data_manager = sim_manager.data_manager
|
|
26
|
+
self.system = sim_manager.system
|
|
27
|
+
|
|
28
|
+
# Parameters from config file
|
|
29
|
+
self.windowsize = plugin_config.get('windowsize', 10)
|
|
30
|
+
self.update_interval = plugin_config.get('update_interval', 10)
|
|
31
|
+
self.initial_update = plugin_config.get('initial_update', 20)
|
|
32
|
+
self.priority = plugin_config.get('priority', 0)
|
|
33
|
+
|
|
34
|
+
self.write_avg_pos = check_bool(plugin_config.get('write_avgpos', True))
|
|
35
|
+
self.do_update = check_bool(plugin_config.get('do_update', True))
|
|
36
|
+
self.init_from_data = check_bool(plugin_config.get('init_from_data', True))
|
|
37
|
+
|
|
38
|
+
self.dfunc = self.get_dfunc_method(plugin_config)
|
|
39
|
+
|
|
40
|
+
# Load method to calculate average position in a bin
|
|
41
|
+
# If the method is defined in an external module, correctly bind it
|
|
42
|
+
ap = self.get_avgpos_method(plugin_config)
|
|
43
|
+
if hasattr(ap, 'im_class'):
|
|
44
|
+
self.get_avgpos = ap
|
|
45
|
+
else:
|
|
46
|
+
self.get_avgpos = types.MethodType(ap, self)
|
|
47
|
+
|
|
48
|
+
# Get initial set of string centers
|
|
49
|
+
centers = self.get_initial_centers()
|
|
50
|
+
|
|
51
|
+
try:
|
|
52
|
+
sm_params = self.system.sm_params
|
|
53
|
+
except AttributeError as e:
|
|
54
|
+
log.error(
|
|
55
|
+
'String Driver Error: system does not define sm_params. \
|
|
56
|
+
This is required and should be added to the system definition; {}'.format(
|
|
57
|
+
e
|
|
58
|
+
)
|
|
59
|
+
)
|
|
60
|
+
raise
|
|
61
|
+
|
|
62
|
+
# Initialize the string
|
|
63
|
+
str_method = self.get_string_method(plugin_config)
|
|
64
|
+
|
|
65
|
+
try:
|
|
66
|
+
self.strings = str_method(centers, **sm_params)
|
|
67
|
+
except (TypeError, AssertionError) as e:
|
|
68
|
+
log.error('String Driver Error: Failed during initialization of string method: {}'.format(e))
|
|
69
|
+
raise
|
|
70
|
+
|
|
71
|
+
# Update the BinMapper
|
|
72
|
+
self.update_bin_mapper()
|
|
73
|
+
|
|
74
|
+
# Register callback
|
|
75
|
+
sim_manager.register_callback(sim_manager.prepare_new_iteration, self.prepare_new_iteration, self.priority)
|
|
76
|
+
|
|
77
|
+
westpa.rc.pstatus('-westext.stringmethod -----------------\n')
|
|
78
|
+
westpa.rc.pstatus('windowsize: {}\n'.format(self.windowsize))
|
|
79
|
+
westpa.rc.pstatus('update interval: {}\n'.format(self.update_interval))
|
|
80
|
+
westpa.rc.pstatus('initial update: {}\n'.format(self.initial_update))
|
|
81
|
+
westpa.rc.pstatus('priority: {}\n'.format(self.priority))
|
|
82
|
+
westpa.rc.pstatus('write average positions: {}\n'.format(self.write_avg_pos))
|
|
83
|
+
westpa.rc.pstatus('do update: {}\n'.format(self.do_update))
|
|
84
|
+
westpa.rc.pstatus('initialize from WE data: {}\n'.format(self.init_from_data))
|
|
85
|
+
westpa.rc.pstatus('----------------------------------------\n')
|
|
86
|
+
westpa.rc.pflush()
|
|
87
|
+
|
|
88
|
+
def dfunc(self):
|
|
89
|
+
raise NotImplementedError
|
|
90
|
+
|
|
91
|
+
def get_avgpos(self, n_iter):
|
|
92
|
+
raise NotImplementedError
|
|
93
|
+
|
|
94
|
+
def get_dfunc_method(self, plugin_config):
|
|
95
|
+
try:
|
|
96
|
+
methodname = plugin_config['dfunc_method']
|
|
97
|
+
except KeyError:
|
|
98
|
+
raise ConfigItemMissing('dfunc_method')
|
|
99
|
+
|
|
100
|
+
dfunc_method = extloader.get_object(methodname)
|
|
101
|
+
|
|
102
|
+
log.info('loaded stringmethod dfunc method {!r}'.format(dfunc_method))
|
|
103
|
+
|
|
104
|
+
return dfunc_method
|
|
105
|
+
|
|
106
|
+
def get_avgpos_method(self, plugin_config):
|
|
107
|
+
try:
|
|
108
|
+
methodname = plugin_config['avgpos_method']
|
|
109
|
+
except KeyError:
|
|
110
|
+
raise ConfigItemMissing('avgpos_method')
|
|
111
|
+
|
|
112
|
+
if methodname.lower() == 'cartesian':
|
|
113
|
+
avgpos_method = self.avgpos_cartesian
|
|
114
|
+
else:
|
|
115
|
+
avgpos_method = extloader.get_object(methodname)
|
|
116
|
+
|
|
117
|
+
log.info('loaded stringmethod avgpos method {!r}'.format(avgpos_method))
|
|
118
|
+
|
|
119
|
+
return avgpos_method
|
|
120
|
+
|
|
121
|
+
def get_string_method(self, plugin_config):
|
|
122
|
+
try:
|
|
123
|
+
methodname = plugin_config['string_method']
|
|
124
|
+
except KeyError:
|
|
125
|
+
raise ConfigItemMissing('string_method')
|
|
126
|
+
|
|
127
|
+
if methodname.lower() == 'default':
|
|
128
|
+
str_method = DefaultStringMethod
|
|
129
|
+
else:
|
|
130
|
+
str_method = extloader.get_object(methodname)
|
|
131
|
+
|
|
132
|
+
assert issubclass(str_method, WESTStringMethod)
|
|
133
|
+
log.debug('loaded stringmethod string method {!r}'.format(str_method))
|
|
134
|
+
|
|
135
|
+
return str_method
|
|
136
|
+
|
|
137
|
+
def get_initial_centers(self):
|
|
138
|
+
self.data_manager.open_backing()
|
|
139
|
+
|
|
140
|
+
with self.data_manager.lock:
|
|
141
|
+
n_iter = max(self.data_manager.current_iteration - 1, 1)
|
|
142
|
+
iter_group = self.data_manager.get_iter_group(n_iter)
|
|
143
|
+
|
|
144
|
+
# First attempt to initialize string from data rather than system
|
|
145
|
+
centers = None
|
|
146
|
+
if self.init_from_data:
|
|
147
|
+
log.info('Attempting to initialize stringmethod from data')
|
|
148
|
+
|
|
149
|
+
try:
|
|
150
|
+
binhash = iter_group.attrs['binhash'].encode()
|
|
151
|
+
bin_mapper = self.data_manager.get_bin_mapper(binhash)
|
|
152
|
+
|
|
153
|
+
centers = bin_mapper.centers
|
|
154
|
+
|
|
155
|
+
except Exception:
|
|
156
|
+
log.warning('Initializing string centers from data failed; Using definition in system instead.')
|
|
157
|
+
centers = self.system.bin_mapper.centers
|
|
158
|
+
else:
|
|
159
|
+
log.info('Initializing string centers from system definition')
|
|
160
|
+
centers = self.system.bin_mapper.centers
|
|
161
|
+
|
|
162
|
+
self.data_manager.close_backing()
|
|
163
|
+
|
|
164
|
+
return centers
|
|
165
|
+
|
|
166
|
+
def update_bin_mapper(self):
|
|
167
|
+
'''Update the bin_mapper using the current string'''
|
|
168
|
+
|
|
169
|
+
westpa.rc.pstatus('westext.stringmethod: Updating bin mapper\n')
|
|
170
|
+
westpa.rc.pflush()
|
|
171
|
+
|
|
172
|
+
try:
|
|
173
|
+
dfargs = getattr(self.system, 'dfargs', None)
|
|
174
|
+
dfkwargs = getattr(self.system, 'dfkwargs', None)
|
|
175
|
+
self.system.bin_mapper = VoronoiBinMapper(self.dfunc, self.strings.centers, dfargs=dfargs, dfkwargs=dfkwargs)
|
|
176
|
+
except (ValueError, TypeError) as e:
|
|
177
|
+
log.error('StringDriver Error: Failed updating the bin mapper: {}'.format(e))
|
|
178
|
+
raise
|
|
179
|
+
|
|
180
|
+
def avgpos_cartesian(self, n_iter):
|
|
181
|
+
'''Get average position of replicas in each bin as of n_iter for the
|
|
182
|
+
the user selected update interval'''
|
|
183
|
+
|
|
184
|
+
nbins = self.system.bin_mapper.nbins
|
|
185
|
+
ndim = self.system.pcoord_ndim
|
|
186
|
+
|
|
187
|
+
avg_pos = np.zeros((nbins, ndim), dtype=self.system.pcoord_dtype)
|
|
188
|
+
sum_bin_weight = np.zeros((nbins,), dtype=self.system.pcoord_dtype)
|
|
189
|
+
|
|
190
|
+
start_iter = max(n_iter - min(self.windowsize, n_iter), 2)
|
|
191
|
+
stop_iter = n_iter + 1
|
|
192
|
+
|
|
193
|
+
for n in range(start_iter, stop_iter):
|
|
194
|
+
with self.data_manager.lock:
|
|
195
|
+
iter_group = self.data_manager.get_iter_group(n)
|
|
196
|
+
seg_index = iter_group['seg_index'][...]
|
|
197
|
+
|
|
198
|
+
pcoords = iter_group['pcoord'][:, -1, :] # Only read final point
|
|
199
|
+
bin_indices = self.system.bin_mapper.assign(pcoords)
|
|
200
|
+
weights = seg_index['weight']
|
|
201
|
+
|
|
202
|
+
pcoord_w = pcoords * weights[:, np.newaxis]
|
|
203
|
+
uniq_indices = np.unique(bin_indices)
|
|
204
|
+
|
|
205
|
+
for indx in uniq_indices:
|
|
206
|
+
avg_pos[indx, :] += pcoord_w[bin_indices == indx].sum(axis=0)
|
|
207
|
+
|
|
208
|
+
sum_bin_weight += np.bincount(bin_indices.astype(int), weights=weights, minlength=nbins)
|
|
209
|
+
|
|
210
|
+
# Some bins might have zero samples so exclude to avoid divide by zero
|
|
211
|
+
occ_ind = np.nonzero(sum_bin_weight)
|
|
212
|
+
avg_pos[occ_ind] /= sum_bin_weight[occ_ind][:, np.newaxis]
|
|
213
|
+
|
|
214
|
+
return avg_pos, sum_bin_weight
|
|
215
|
+
|
|
216
|
+
def prepare_new_iteration(self):
|
|
217
|
+
n_iter = self.sim_manager.n_iter
|
|
218
|
+
|
|
219
|
+
with self.data_manager.lock:
|
|
220
|
+
iter_group = self.data_manager.get_iter_group(n_iter)
|
|
221
|
+
|
|
222
|
+
try:
|
|
223
|
+
del iter_group['stringmethod']
|
|
224
|
+
except KeyError:
|
|
225
|
+
pass
|
|
226
|
+
|
|
227
|
+
sm_global_group = self.data_manager.we_h5file.require_group('stringmethod')
|
|
228
|
+
last_update = int(sm_global_group.attrs.get('last_update', 0))
|
|
229
|
+
|
|
230
|
+
if n_iter - last_update < self.update_interval or n_iter < self.initial_update or not self.do_update:
|
|
231
|
+
log.debug('Not updating string this iteration')
|
|
232
|
+
return
|
|
233
|
+
else:
|
|
234
|
+
log.debug('Updating string - n_iter: {}'.format(n_iter))
|
|
235
|
+
|
|
236
|
+
westpa.rc.pstatus('-westext.stringmethod -----------------\n')
|
|
237
|
+
westpa.rc.pstatus('westext.stringmethod: Calculating average position in string images\n')
|
|
238
|
+
westpa.rc.pflush()
|
|
239
|
+
|
|
240
|
+
avg_pos, sum_bin_weight = self.get_avgpos(n_iter)
|
|
241
|
+
|
|
242
|
+
westpa.rc.pstatus('westext.stringmethod: Updating string\n')
|
|
243
|
+
westpa.rc.pflush()
|
|
244
|
+
|
|
245
|
+
self.strings.update_string_centers(avg_pos, sum_bin_weight)
|
|
246
|
+
|
|
247
|
+
westpa.rc.pstatus('westext.stringmethod: String lengths: {}\n'.format(self.strings.length))
|
|
248
|
+
westpa.rc.pflush()
|
|
249
|
+
|
|
250
|
+
# Update the bin definitions
|
|
251
|
+
self.update_bin_mapper()
|
|
252
|
+
|
|
253
|
+
sm_global_group.attrs['last_update'] = n_iter
|
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
from abc import ABCMeta, abstractmethod, abstractproperty
|
|
2
|
+
import logging
|
|
3
|
+
|
|
4
|
+
try:
|
|
5
|
+
from collections.abc import Iterable
|
|
6
|
+
except ImportError:
|
|
7
|
+
from collections import Iterable
|
|
8
|
+
|
|
9
|
+
import numpy as np
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
try:
|
|
13
|
+
import scipy
|
|
14
|
+
import scipy.interpolate
|
|
15
|
+
import scipy.linalg
|
|
16
|
+
|
|
17
|
+
SCIPY_FLAG = True
|
|
18
|
+
except Exception:
|
|
19
|
+
SCIPY_FLAG = False
|
|
20
|
+
|
|
21
|
+
from .fourier_fitting import FourierFit
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
log = logging.getLogger(__name__)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class WESTStringMethod:
|
|
28
|
+
___metaclass__ = ABCMeta
|
|
29
|
+
|
|
30
|
+
def __init__(self, centers, **kwargs):
|
|
31
|
+
pass
|
|
32
|
+
|
|
33
|
+
@abstractproperty
|
|
34
|
+
def centers(self):
|
|
35
|
+
"""Return the centers of all of the strings"""
|
|
36
|
+
pass
|
|
37
|
+
|
|
38
|
+
@abstractproperty
|
|
39
|
+
def length(self):
|
|
40
|
+
"""Return a list of the lengths of each string"""
|
|
41
|
+
pass
|
|
42
|
+
|
|
43
|
+
@abstractmethod
|
|
44
|
+
def update_string_centers(self, avgcoords, binprob):
|
|
45
|
+
"""Given a set of average coordinates (avgcoords) in each bin
|
|
46
|
+
and the individual probabilities for each bin (binprob), update
|
|
47
|
+
the string centers
|
|
48
|
+
"""
|
|
49
|
+
pass
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class DefaultStringMethod(WESTStringMethod):
|
|
53
|
+
"""Implementation of a method to evolve one or more pseudo-1D strings in a high dimensional
|
|
54
|
+
progress coordinate space.
|
|
55
|
+
|
|
56
|
+
**Parameters**
|
|
57
|
+
centers: A numpy array of size (number of total centers,pcoord dim) that stores
|
|
58
|
+
the positions of all of the string images
|
|
59
|
+
slen: An iterable containing the number of centers in each string
|
|
60
|
+
slabels: An list containing the relative positions in each string of any state label
|
|
61
|
+
progress coordinates if present. These progress coordinates will be ignored in the
|
|
62
|
+
calculation. None if no labels
|
|
63
|
+
mpairs: A list of lists containing the indices of pairs of centers that should move together.
|
|
64
|
+
None if strings move independently
|
|
65
|
+
dtau: Parameter controlling the rate at which centers move toward the average value in the bin
|
|
66
|
+
kappa: Parameter controlling the smoothing of the string
|
|
67
|
+
fixed_ends: Boolean flag specifying whether to fix ends of the strings
|
|
68
|
+
sciflag: Boolean flag specifying whether to attempt to use scipy methods which are
|
|
69
|
+
generally more efficient
|
|
70
|
+
fourierflag: Boolean flag specifying whether to user fourier fitting method
|
|
71
|
+
fourier_P: Integer value specifying how many fourier modes to use in fitting
|
|
72
|
+
fourier_maxiters: Maximum number of iterations of fourier fitting procedure
|
|
73
|
+
fourier_tol: Tolerance for ending fourier fitting
|
|
74
|
+
"""
|
|
75
|
+
|
|
76
|
+
def __init__(
|
|
77
|
+
self,
|
|
78
|
+
centers,
|
|
79
|
+
slen=None,
|
|
80
|
+
slabels=None,
|
|
81
|
+
mpairs=None,
|
|
82
|
+
dtau=0.1,
|
|
83
|
+
kappa=0.1,
|
|
84
|
+
sciflag=None,
|
|
85
|
+
fixed_ends=True,
|
|
86
|
+
fourierflag=False,
|
|
87
|
+
fourier_P=2,
|
|
88
|
+
fourier_maxiters=100,
|
|
89
|
+
fourier_tol=1.0e-6,
|
|
90
|
+
**kwargs
|
|
91
|
+
):
|
|
92
|
+
super().__init__(centers, **kwargs)
|
|
93
|
+
|
|
94
|
+
self._SCIPY_FLAG = None
|
|
95
|
+
self._fixed_ends = fixed_ends
|
|
96
|
+
|
|
97
|
+
if sciflag is None:
|
|
98
|
+
self._SCIPY_FLAG = SCIPY_FLAG
|
|
99
|
+
else:
|
|
100
|
+
self._SCIPY_FLAG = SCIPY_FLAG & sciflag
|
|
101
|
+
|
|
102
|
+
self._dtau = dtau
|
|
103
|
+
self._kappa = kappa
|
|
104
|
+
|
|
105
|
+
self._centers = centers
|
|
106
|
+
|
|
107
|
+
self._nstrings = len(slen) # Number of strings
|
|
108
|
+
self._N = centers.shape[0]
|
|
109
|
+
self._ndim = centers.shape[1] # Number of progress coordinates
|
|
110
|
+
|
|
111
|
+
if slen and isinstance(slen, Iterable):
|
|
112
|
+
self._slen = np.array(slen)
|
|
113
|
+
elif slen:
|
|
114
|
+
self._slen = np.array([slen])
|
|
115
|
+
else:
|
|
116
|
+
self._slen = np.array([self._N])
|
|
117
|
+
log.warning('Input parameters do not define slen; assuming system is composed of a single string')
|
|
118
|
+
|
|
119
|
+
self._mpairs = mpairs
|
|
120
|
+
|
|
121
|
+
assert np.sum(self._slen) == self._N
|
|
122
|
+
|
|
123
|
+
self._skip_dim = np.array(slabels) if slabels is not None else np.array([])
|
|
124
|
+
|
|
125
|
+
# Get iterable of slicing objects to get per string center coordinates to perform calculation on
|
|
126
|
+
self._strindx = []
|
|
127
|
+
|
|
128
|
+
indx_all = np.arange(self._ndim)
|
|
129
|
+
self._indx_take = np.setdiff1d(indx_all, self._skip_dim)
|
|
130
|
+
self._ndim_take = len(self._indx_take)
|
|
131
|
+
|
|
132
|
+
start_count = 0
|
|
133
|
+
for sl in slen:
|
|
134
|
+
self._strindx.append(np.index_exp[start_count : (start_count + sl), self._indx_take])
|
|
135
|
+
start_count += sl
|
|
136
|
+
|
|
137
|
+
# Fourier fitting parameters
|
|
138
|
+
self._FFIT_FLAG = fourierflag
|
|
139
|
+
self._ffp = fourier_P
|
|
140
|
+
self._ffmaxiters = fourier_maxiters
|
|
141
|
+
self._fftol = fourier_tol
|
|
142
|
+
|
|
143
|
+
# Create dict to hold kappan and A objects for all unique lengths of strings
|
|
144
|
+
self._kappan = {}
|
|
145
|
+
self._A = {}
|
|
146
|
+
|
|
147
|
+
self.finalize_init()
|
|
148
|
+
|
|
149
|
+
@property
|
|
150
|
+
def centers(self):
|
|
151
|
+
return self._centers
|
|
152
|
+
|
|
153
|
+
@property
|
|
154
|
+
def length(self):
|
|
155
|
+
L = []
|
|
156
|
+
for sid, si in enumerate(self._strindx):
|
|
157
|
+
L.append(self.calculate_length(self._centers[si])[-1])
|
|
158
|
+
|
|
159
|
+
return L
|
|
160
|
+
|
|
161
|
+
def calculate_length(self, x):
|
|
162
|
+
dd = x - np.roll(x, 1, axis=0)
|
|
163
|
+
dd[0, :] = 0.0
|
|
164
|
+
return np.cumsum(np.sqrt((dd * dd).sum(axis=1)))
|
|
165
|
+
|
|
166
|
+
def finalize_init(self):
|
|
167
|
+
# Set up A and kappan for each string
|
|
168
|
+
uslen = np.unique(self._slen)
|
|
169
|
+
|
|
170
|
+
for ulen in uslen:
|
|
171
|
+
self._kappan[ulen] = self._kappa * self._dtau * ulen
|
|
172
|
+
self._A[ulen] = None
|
|
173
|
+
|
|
174
|
+
if self._SCIPY_FLAG:
|
|
175
|
+
ud = np.zeros((ulen,))
|
|
176
|
+
ld = np.zeros((ulen,))
|
|
177
|
+
d = np.ones((ulen,))
|
|
178
|
+
|
|
179
|
+
d[1:-1] = 2.0 * self._kappan[ulen] + 1.0
|
|
180
|
+
ud[2:] = -self._kappan[ulen]
|
|
181
|
+
ld[:-2] = -self._kappan[ulen]
|
|
182
|
+
|
|
183
|
+
self._A[ulen] = np.mat([ud, d, ld])
|
|
184
|
+
|
|
185
|
+
else:
|
|
186
|
+
self._A[ulen] = np.eye(ulen)
|
|
187
|
+
di = np.diag_indices(ulen, ndim=2)
|
|
188
|
+
ii = (di[0][1:-1], di[1][1:-1])
|
|
189
|
+
|
|
190
|
+
self._A[ulen][ii] = 2.0 * self._kappan[ulen] + 1.0
|
|
191
|
+
|
|
192
|
+
dd = np.zeros((ulen - 1,))
|
|
193
|
+
dd[1:] = -self._kappan[ulen]
|
|
194
|
+
self._A[ulen] += np.diag(dd, k=1)
|
|
195
|
+
|
|
196
|
+
dd = np.zeros((ulen - 1,))
|
|
197
|
+
dd[:-1] = -self._kappan[ulen]
|
|
198
|
+
self._A[ulen] += np.diag(dd, k=-1)
|
|
199
|
+
|
|
200
|
+
def update_string_centers(self, avgcoords, binprob):
|
|
201
|
+
"""Update the position of all string centers
|
|
202
|
+
**Parameters**
|
|
203
|
+
avgcoords: Average position of replicas in each voronoi cell
|
|
204
|
+
binprob: The total weight in each voronoi cell
|
|
205
|
+
|
|
206
|
+
"""
|
|
207
|
+
assert self.centers.shape == avgcoords.shape
|
|
208
|
+
|
|
209
|
+
# If centers are paired, calculate their average position
|
|
210
|
+
if self._mpairs is not None:
|
|
211
|
+
for pi in self._mpairs:
|
|
212
|
+
pprob = binprob[pi]
|
|
213
|
+
if np.sum(pprob) == 0.0:
|
|
214
|
+
continue
|
|
215
|
+
|
|
216
|
+
idx = np.ix_(pi, self._indx_take)
|
|
217
|
+
pavg = np.ma.array(avgcoords[idx])
|
|
218
|
+
zind = np.where(pprob == 0)[0]
|
|
219
|
+
pavg[zind, :] = np.ma.masked
|
|
220
|
+
|
|
221
|
+
avgcoords[idx] = pavg.mean(axis=0)
|
|
222
|
+
|
|
223
|
+
for sid, si in enumerate(self._strindx):
|
|
224
|
+
x = avgcoords.copy()[si]
|
|
225
|
+
centers = self.centers[si]
|
|
226
|
+
occupied = np.nonzero(binprob[si[0]])
|
|
227
|
+
|
|
228
|
+
N = self._slen[sid]
|
|
229
|
+
|
|
230
|
+
# if avgcoords has missing values fill them by linearly interpolating
|
|
231
|
+
# present data
|
|
232
|
+
if occupied[0].shape != N:
|
|
233
|
+
notocc = np.ones((N,), dtype=bool) # unoccupied
|
|
234
|
+
notocc[occupied] = False
|
|
235
|
+
|
|
236
|
+
# marked paired centers as occupied to avoid reseting averaged value if
|
|
237
|
+
# at least one is occupied
|
|
238
|
+
if self._mpairs is not None:
|
|
239
|
+
for pi in self._mpairs:
|
|
240
|
+
totprob = np.sum(binprob[pi])
|
|
241
|
+
if totprob == 0.0:
|
|
242
|
+
continue
|
|
243
|
+
else:
|
|
244
|
+
for m in pi:
|
|
245
|
+
if (m >= si[0].start) and (m < si[0].stop):
|
|
246
|
+
notocc[m - si[0].start] = False
|
|
247
|
+
|
|
248
|
+
cfunc = lambda z: z.nonzero()[0]
|
|
249
|
+
|
|
250
|
+
# Handle ends first
|
|
251
|
+
if notocc[0]:
|
|
252
|
+
x[0, :] = centers[0, :]
|
|
253
|
+
notocc[0] = False
|
|
254
|
+
if notocc[-1]:
|
|
255
|
+
x[-1, :] = centers[-1, :]
|
|
256
|
+
notocc[-1] = False
|
|
257
|
+
|
|
258
|
+
# interpolate values for unoccupied bins
|
|
259
|
+
if self._SCIPY_FLAG:
|
|
260
|
+
for k in range(self._ndim_take):
|
|
261
|
+
f = scipy.interpolate.interp1d(cfunc(~notocc), x[~notocc, k], kind='linear')
|
|
262
|
+
x[notocc, k] = f(cfunc(notocc))
|
|
263
|
+
else:
|
|
264
|
+
for k in range(self._ndim_take):
|
|
265
|
+
x[notocc, k] = np.interp(cfunc(notocc), cfunc(~notocc), x[~notocc, k])
|
|
266
|
+
|
|
267
|
+
if self._fixed_ends:
|
|
268
|
+
x[0, :] = centers[0, :]
|
|
269
|
+
x[-1, :] = centers[-1, :]
|
|
270
|
+
|
|
271
|
+
psi = centers
|
|
272
|
+
psi_new = np.zeros_like(psi)
|
|
273
|
+
|
|
274
|
+
b = psi - self._dtau * (psi - x)
|
|
275
|
+
|
|
276
|
+
# Update and smooth the string
|
|
277
|
+
if self._SCIPY_FLAG:
|
|
278
|
+
for k in range(self._ndim_take):
|
|
279
|
+
psi_new[:, k] = scipy.linalg.solve_banded((1, 1), self._A[N], b[:, k])
|
|
280
|
+
else:
|
|
281
|
+
for k in range(self._ndim_take):
|
|
282
|
+
psi_new[:, k] = np.linalg.solve(self._A[N], b[:, k])
|
|
283
|
+
|
|
284
|
+
# Optionally smooth using fourier method
|
|
285
|
+
if self._FFIT_FLAG:
|
|
286
|
+
w0 = np.zeros((self._ndim_take, self._ffp), np.float)
|
|
287
|
+
t0 = np.linspace(0, 1, psi_new.shape[0])
|
|
288
|
+
|
|
289
|
+
ff = FourierFit(P=self._ffp, maxiters=self._ffmaxiters)
|
|
290
|
+
ff.optimize(psi_new, None, w0, t0)
|
|
291
|
+
psi_new = ff.pp[-1][:]
|
|
292
|
+
|
|
293
|
+
# Enforce equal spacing between centers along the string
|
|
294
|
+
L = self.calculate_length(psi_new)
|
|
295
|
+
L /= L[-1]
|
|
296
|
+
g2 = np.linspace(0, 1, N)
|
|
297
|
+
|
|
298
|
+
if self._SCIPY_FLAG:
|
|
299
|
+
for k in range(self._ndim_take):
|
|
300
|
+
f = scipy.interpolate.interp1d(L, psi_new[:, k], kind='linear')
|
|
301
|
+
psi_new[:, k] = f(g2)
|
|
302
|
+
else:
|
|
303
|
+
for k in range(self._ndim_take):
|
|
304
|
+
psi_new[:, k] = np.interp(g2, L, psi_new[:, k])
|
|
305
|
+
|
|
306
|
+
self.centers[si] = psi_new.copy()
|