hillclimber 0.1.0a1__py3-none-any.whl → 0.1.0a2__py3-none-any.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 hillclimber might be problematic. Click here for more details.

@@ -0,0 +1,335 @@
1
+ """Virtual atom definitions for creating points in space from atom groups.
2
+
3
+ Virtual atoms reduce groups of atoms to single points (or multiple points, one per group)
4
+ using strategies like center of mass (COM), center of geometry (COG), or first atom.
5
+
6
+ Resources
7
+ ---------
8
+ - https://www.plumed.org/doc-master/user-doc/html/COM
9
+ - https://www.plumed.org/doc-master/user-doc/html/CENTER
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ import dataclasses
15
+ import typing as tp
16
+
17
+ import ase
18
+
19
+ from hillclimber.interfaces import AtomSelector
20
+
21
+
22
+ @dataclasses.dataclass
23
+ class VirtualAtom:
24
+ """Creates virtual atom(s) from atom groups.
25
+
26
+ Creates ONE virtual atom for EACH group returned by the selector.
27
+ To select specific groups, use selector indexing before creating the VirtualAtom.
28
+
29
+ Parameters
30
+ ----------
31
+ atoms : AtomSelector | VirtualAtom
32
+ Source atoms or nested virtual atoms.
33
+ Use selector indexing to choose specific groups: VirtualAtom(selector[0], "com")
34
+ reduction : {"com", "cog", "first", "flatten"}, default="com"
35
+ How to reduce atoms to points:
36
+
37
+ - "com": Center of mass for each group (creates virtual site)
38
+ - "cog": Center of geometry for each group (creates virtual site)
39
+ - "first": First atom of each group (no virtual site needed)
40
+ - "flatten": Combine all groups into one, then apply reduction
41
+ label : str | None, default=None
42
+ Optional label for PLUMED virtual site commands.
43
+
44
+ Examples
45
+ --------
46
+ Create COM for each molecule:
47
+
48
+ >>> import hillclimber as hc
49
+ >>> # Two ethanols → two virtual atoms (COMs)
50
+ >>> selector = hc.SMARTSSelector("CCO")
51
+ >>> ethanols = hc.VirtualAtom(selector, reduction="com")
52
+
53
+ Select specific virtual atom using selector indexing:
54
+
55
+ >>> # Select first ethanol's COM
56
+ >>> ethanol_0 = hc.VirtualAtom(selector[0], "com")
57
+
58
+ Select multiple virtual atoms using selector slicing:
59
+
60
+ >>> # Select first two ethanols
61
+ >>> first_two = hc.VirtualAtom(selector[0:2], "com")
62
+ >>> # Select every other water
63
+ >>> water_sel = hc.SMARTSSelector("O")
64
+ >>> every_other = hc.VirtualAtom(water_sel[::2], "com")
65
+
66
+ Select specific virtual atoms using selector list indexing:
67
+
68
+ >>> # Select specific ethanols by indices
69
+ >>> selected = hc.VirtualAtom(selector[[0, 2, 4]], "com")
70
+
71
+ Flatten all groups into one virtual atom:
72
+
73
+ >>> # All water oxygens → one COM
74
+ >>> all_waters = hc.VirtualAtom(hc.SMARTSSelector("O"), reduction="flatten")
75
+
76
+ Nested virtual atoms (COM of COMs):
77
+
78
+ >>> # First: create COM for each water
79
+ >>> water_coms = hc.VirtualAtom(hc.SMARTSSelector("O"), reduction="com")
80
+ >>> # Then: create COM of all those COMs
81
+ >>> center = hc.VirtualAtom(water_coms, reduction="flatten")
82
+
83
+ Use in distance CVs:
84
+
85
+ >>> # Single distance (using selector indexing)
86
+ >>> ethanol_sel = hc.SMARTSSelector("CCO")
87
+ >>> water_sel = hc.SMARTSSelector("O")
88
+ >>> dist = hc.DistanceCV(
89
+ ... x1=hc.VirtualAtom(ethanol_sel[0], "com"),
90
+ ... x2=hc.VirtualAtom(water_sel[0], "com"),
91
+ ... prefix="d"
92
+ ... )
93
+ >>>
94
+ >>> # Multiple distances (first ethanol to all waters)
95
+ >>> dist = hc.DistanceCV(
96
+ ... x1=hc.VirtualAtom(ethanol_sel[0], "com"),
97
+ ... x2=hc.VirtualAtom(water_sel, "com"),
98
+ ... prefix="d"
99
+ ... )
100
+ >>>
101
+ >>> # Multiple distances (first two ethanols to first three waters)
102
+ >>> dist = hc.DistanceCV(
103
+ ... x1=hc.VirtualAtom(ethanol_sel[0:2], "com"),
104
+ ... x2=hc.VirtualAtom(water_sel[0:3], "com"),
105
+ ... prefix="d"
106
+ ... )
107
+ >>>
108
+ >>> # All pairwise distances
109
+ >>> dist = hc.DistanceCV(
110
+ ... x1=hc.VirtualAtom(water_sel, "com"),
111
+ ... x2=hc.VirtualAtom(water_sel, "com"),
112
+ ... prefix="d"
113
+ ... )
114
+
115
+ Resources
116
+ ---------
117
+ - https://www.plumed.org/doc-master/user-doc/html/COM
118
+ - https://www.plumed.org/doc-master/user-doc/html/CENTER
119
+ """
120
+
121
+ atoms: AtomSelector | "VirtualAtom"
122
+ reduction: tp.Literal["com", "cog", "first", "flatten"] = "com"
123
+ label: str | None = None
124
+
125
+ def __add__(self, other: "VirtualAtom") -> "VirtualAtom":
126
+ """Combine two VirtualAtoms.
127
+
128
+ Returns a new VirtualAtom that represents both sets of virtual sites.
129
+ The underlying selectors are combined using the selector's __add__ method.
130
+
131
+ Parameters
132
+ ----------
133
+ other : VirtualAtom
134
+ Another VirtualAtom to combine with this one.
135
+
136
+ Returns
137
+ -------
138
+ VirtualAtom
139
+ New VirtualAtom with combined underlying selectors.
140
+
141
+ Raises
142
+ ------
143
+ ValueError
144
+ If the two VirtualAtoms have different reduction strategies.
145
+
146
+ Examples
147
+ --------
148
+ >>> water_coms = hc.VirtualAtom(water_sel, "com")
149
+ >>> ethanol_coms = hc.VirtualAtom(ethanol_sel, "com")
150
+ >>> all_coms = water_coms + ethanol_coms
151
+ >>>
152
+ >>> # Combine with indexed selectors
153
+ >>> first_water = hc.VirtualAtom(water_sel[0], "com")
154
+ >>> first_ethanol = hc.VirtualAtom(ethanol_sel[0], "com")
155
+ >>> combined = first_water + first_ethanol # 2 COMs
156
+ """
157
+ # Reduction must be the same
158
+ if self.reduction != other.reduction:
159
+ raise ValueError(
160
+ f"Cannot combine VirtualAtoms with different reductions: "
161
+ f"{self.reduction} vs {other.reduction}"
162
+ )
163
+
164
+ # Combine the underlying selectors
165
+ combined_atoms = self.atoms + other.atoms
166
+
167
+ return VirtualAtom(
168
+ atoms=combined_atoms,
169
+ reduction=self.reduction,
170
+ label=None # Reset label for combined VirtualAtom
171
+ )
172
+
173
+ def select(self, atoms: ase.Atoms) -> list[list[int]]:
174
+ """Select atom groups for virtual atom creation.
175
+
176
+ Returns list of atom groups. Each group represents the atoms for
177
+ one virtual site.
178
+
179
+ Parameters
180
+ ----------
181
+ atoms : ase.Atoms
182
+ The atomic structure to select from.
183
+
184
+ Returns
185
+ -------
186
+ list[list[int]]
187
+ Groups of atom indices. Each inner list is one group.
188
+ """
189
+ # Get groups from source
190
+ if isinstance(self.atoms, VirtualAtom):
191
+ # Nested: inner VirtualAtom returns groups
192
+ groups = self.atoms.select(atoms)
193
+ else:
194
+ groups = self.atoms.select(atoms)
195
+
196
+ # Apply reduction strategy
197
+ if self.reduction == "flatten":
198
+ # Combine all groups into one
199
+ flat = [idx for group in groups for idx in group]
200
+ return [flat]
201
+ else:
202
+ # com, cog, first: keep groups separate
203
+ # Actual point creation happens in to_plumed()
204
+ return groups
205
+
206
+ def to_plumed(self, atoms: ase.Atoms) -> tuple[list[str], list[str]]:
207
+ """Generate PLUMED virtual site commands.
208
+
209
+ Handles nested VirtualAtoms by first generating commands for the inner
210
+ VirtualAtom, then using those labels to create the outer virtual site.
211
+
212
+ Parameters
213
+ ----------
214
+ atoms : ase.Atoms
215
+ The atomic structure to use for generating commands.
216
+
217
+ Returns
218
+ -------
219
+ labels : list[str]
220
+ Labels for the virtual sites/atoms created. These can be used
221
+ in subsequent PLUMED commands.
222
+ commands : list[str]
223
+ PLUMED command strings for creating virtual sites.
224
+ Empty if reduction="first" (no virtual sites needed).
225
+
226
+ Examples
227
+ --------
228
+ >>> va = hc.VirtualAtom(hc.SMARTSSelector("O"), reduction="com")
229
+ >>> labels, commands = va.to_plumed(atoms)
230
+ >>> print(labels)
231
+ ['vsite_123_0', 'vsite_123_1']
232
+ >>> print(commands)
233
+ ['vsite_123_0: COM ATOMS=1,2,3', 'vsite_123_1: COM ATOMS=4,5,6']
234
+
235
+ >>> # Nested VirtualAtom (COM of COMs)
236
+ >>> water_coms = hc.VirtualAtom(water_sel, "com")
237
+ >>> center = hc.VirtualAtom(water_coms, "com")
238
+ >>> labels, commands = center.to_plumed(atoms)
239
+ >>> # Commands will include both the individual COMs and the center COM
240
+ """
241
+ # Check if this is a nested VirtualAtom
242
+ if isinstance(self.atoms, VirtualAtom):
243
+ # First, generate commands for the inner VirtualAtom
244
+ inner_labels, inner_commands = self.atoms.to_plumed(atoms)
245
+
246
+ # Now create virtual site using the inner labels
247
+ commands = list(inner_commands) # Copy inner commands
248
+ labels = []
249
+
250
+ if self.reduction == "first":
251
+ # Just use the first inner label
252
+ labels = [inner_labels[0]]
253
+ elif self.reduction == "flatten":
254
+ # Create COM/COG of all inner virtual sites
255
+ base_label = self.label or f"vsite_{id(self)}"
256
+ cmd_type = "COM" if self.reduction == "com" else "CENTER"
257
+ atom_list = ",".join(inner_labels)
258
+ commands.append(f"{base_label}: {cmd_type} ATOMS={atom_list}")
259
+ labels = [base_label]
260
+ elif self.reduction in ["com", "cog"]:
261
+ # For COM/COG with nested VirtualAtoms, we create a single
262
+ # virtual site from all inner labels (similar to flatten)
263
+ base_label = self.label or f"vsite_{id(self)}"
264
+ cmd_type = "COM" if self.reduction == "com" else "CENTER"
265
+ atom_list = ",".join(inner_labels)
266
+ commands.append(f"{base_label}: {cmd_type} ATOMS={atom_list}")
267
+ labels = [base_label]
268
+ else:
269
+ # This shouldn't typically happen, but handle it
270
+ # Just pass through the inner labels
271
+ labels = inner_labels
272
+
273
+ return labels, commands
274
+
275
+ # Non-nested case: regular selector
276
+ groups = self.select(atoms)
277
+ labels = []
278
+ commands = []
279
+
280
+ for i, group in enumerate(groups):
281
+ if self.reduction == "first" or (self.reduction == "flatten" and len(group) == 1):
282
+ # No virtual site needed, use atom index directly
283
+ labels.append(str(group[0] + 1))
284
+ else:
285
+ # Create virtual site (COM or CENTER)
286
+ base_label = self.label or f"vsite_{id(self)}"
287
+ label = base_label if len(groups) == 1 else f"{base_label}_{i}"
288
+
289
+ cmd_type = "COM" if self.reduction == "com" else "CENTER"
290
+ atom_list = ",".join(str(idx + 1) for idx in group)
291
+ commands.append(f"{label}: {cmd_type} ATOMS={atom_list}")
292
+ labels.append(label)
293
+
294
+ return labels, commands
295
+
296
+ def count(self, atoms: ase.Atoms) -> int:
297
+ """Return number of virtual atoms this represents.
298
+
299
+ Parameters
300
+ ----------
301
+ atoms : ase.Atoms
302
+ The atomic structure to count groups in.
303
+
304
+ Returns
305
+ -------
306
+ int
307
+ Number of virtual atoms (groups) this will create.
308
+
309
+ Examples
310
+ --------
311
+ >>> water_sel = hc.SMARTSSelector("O")
312
+ >>> va = hc.VirtualAtom(water_sel, reduction="com")
313
+ >>> va.count(atoms) # Number of water molecules
314
+ 3
315
+ >>> # Use selector indexing to get single group
316
+ >>> va_single = hc.VirtualAtom(water_sel[0], reduction="com")
317
+ >>> va_single.count(atoms) # Single water - always 1
318
+ 1
319
+ >>> va_flat = hc.VirtualAtom(water_sel, reduction="flatten")
320
+ >>> va_flat.count(atoms) # Flattened - always 1
321
+ 1
322
+ >>> # Nested VirtualAtom
323
+ >>> water_coms = hc.VirtualAtom(water_sel, "com") # 3 COMs
324
+ >>> center = hc.VirtualAtom(water_coms, "com") # 1 COM of 3 COMs
325
+ >>> center.count(atoms)
326
+ 1
327
+ """
328
+ # For nested VirtualAtoms with com/cog/flatten reduction, we create a single virtual site
329
+ if isinstance(self.atoms, VirtualAtom) and self.reduction in ["com", "cog", "flatten"]:
330
+ return 1
331
+ # For nested VirtualAtoms with "first" reduction, pass through
332
+ elif isinstance(self.atoms, VirtualAtom) and self.reduction == "first":
333
+ return 1
334
+ # For non-nested or other cases, count the groups
335
+ return len(self.select(atoms))
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hillclimber
3
- Version: 0.1.0a1
3
+ Version: 0.1.0a2
4
4
  Summary: Python interfaces for the plumed library with enhanced sampling.
5
5
  License-File: LICENSE
6
6
  Requires-Python: >=3.11
@@ -0,0 +1,16 @@
1
+ hillclimber/__init__.py,sha256=MpTyTiou1ACRu6snlCe3Aja3_znqakX-YPV1notCdvA,901
2
+ hillclimber/actions.py,sha256=7s78RWP-YnBbbxZyA65OwnyL60v8LnKHyNalui1ypAo,1514
3
+ hillclimber/biases.py,sha256=OfqKdGNiN2Yk4tP03nzJ9vxBWLjLRvcjgJAS8gZeFHw,9317
4
+ hillclimber/calc.py,sha256=dqanaBF6BJwP6lHQqFqEIng-3bTN_DcddRV-gceboKs,665
5
+ hillclimber/cvs.py,sha256=ag1gr51D1-NJb7tkdeUQdjYfWy0WBfcMQKE1f-thFAQ,39548
6
+ hillclimber/interfaces.py,sha256=H4HKN1HldhNJeooqtS-HpJLrCFqpMPr80aPER4SKiao,3807
7
+ hillclimber/metadynamics.py,sha256=NwNG5THSjmJInBDLFuIKp3cB8P-kE8BAaftZdnSk9J0,8719
8
+ hillclimber/nodes.py,sha256=XL9uEXd2HdW2mlbwriG_fMCkZAaz4uZBOI5edO42YDA,145
9
+ hillclimber/opes.py,sha256=NYY2zcBtBxXrFcWQKplrIkthgnW03WTh4hi9MtDsXqo,12483
10
+ hillclimber/selectors.py,sha256=VoWMvTnKU9vr0dphqxGk1OdhrabbDkzq4GQkeprd6RQ,7931
11
+ hillclimber/virtual_atoms.py,sha256=GVXCJZlbx1cY_ST2G5NHQsGpdMkBLUz04aFm-cD--OA,12270
12
+ hillclimber-0.1.0a2.dist-info/METADATA,sha256=JZgeoAZPviWVz59IgjQsoWYnNuFoXbzIg-vJJDOxcTU,11254
13
+ hillclimber-0.1.0a2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
14
+ hillclimber-0.1.0a2.dist-info/entry_points.txt,sha256=RsCL3TDKfieatIWP9JHjmTzMtgWERqwpuuuDPdQ4t5g,124
15
+ hillclimber-0.1.0a2.dist-info/licenses/LICENSE,sha256=FKf4VPZYbuyRVMVSrl6HO48bnw6ih8Uur5y-h_MJAcA,13576
16
+ hillclimber-0.1.0a2.dist-info/RECORD,,
@@ -1,13 +0,0 @@
1
- hillclimber/__init__.py,sha256=M0w80B5FDzRY8-JIH_oD-RWJf5TKGRRaTfEvaWBYvtU,566
2
- hillclimber/actions.py,sha256=A4pFpeFnluQT8VpmcP_3mpA6Xc58fjhjdcIJzv5fV90,764
3
- hillclimber/calc.py,sha256=dqanaBF6BJwP6lHQqFqEIng-3bTN_DcddRV-gceboKs,665
4
- hillclimber/cvs.py,sha256=7rWKtlbYZX2jQNee5n84rn_eIh4JuIQbUjyK52k_NR8,24844
5
- hillclimber/interfaces.py,sha256=X4LItHGx7h74WzG8ESpZ7htMr_-BEkBzbJtPIRg0Upw,2471
6
- hillclimber/metadynamics.py,sha256=qn_41I-c4TilWH_aAMzboTo_SI3hsRNjm0S6OAoVKyI,8795
7
- hillclimber/nodes.py,sha256=XL9uEXd2HdW2mlbwriG_fMCkZAaz4uZBOI5edO42YDA,145
8
- hillclimber/selectors.py,sha256=x9Dyqpl9KtZkLWuMsN20AGcaUv6_bG_vSLPNKaB2iyk,3172
9
- hillclimber-0.1.0a1.dist-info/METADATA,sha256=P28Sx55LnPOi2c5LEnvzXNdlPTtkFpEGrSZDqhvyVjI,11254
10
- hillclimber-0.1.0a1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
11
- hillclimber-0.1.0a1.dist-info/entry_points.txt,sha256=RsCL3TDKfieatIWP9JHjmTzMtgWERqwpuuuDPdQ4t5g,124
12
- hillclimber-0.1.0a1.dist-info/licenses/LICENSE,sha256=FKf4VPZYbuyRVMVSrl6HO48bnw6ih8Uur5y-h_MJAcA,13576
13
- hillclimber-0.1.0a1.dist-info/RECORD,,