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,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.asmatrix([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()