CodeEntropy 2.4.2__py3-none-any.whl → 2.5.0__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.
- CodeEntropy/__init__.py +1 -1
- CodeEntropy/entropy/vibrational.py +9 -3
- CodeEntropy/levels/axes.py +261 -157
- CodeEntropy/levels/nodes/covariance.py +6 -6
- CodeEntropy/trajectory/mda.py +28 -1
- {codeentropy-2.4.2.dist-info → codeentropy-2.5.0.dist-info}/METADATA +21 -26
- {codeentropy-2.4.2.dist-info → codeentropy-2.5.0.dist-info}/RECORD +10 -10
- {codeentropy-2.4.2.dist-info → codeentropy-2.5.0.dist-info}/WHEEL +1 -1
- {codeentropy-2.4.2.dist-info → codeentropy-2.5.0.dist-info}/entry_points.txt +0 -0
- {codeentropy-2.4.2.dist-info → codeentropy-2.5.0.dist-info}/licenses/LICENSE +0 -0
CodeEntropy/__init__.py
CHANGED
|
@@ -148,7 +148,7 @@ class VibrationalEntropy:
|
|
|
148
148
|
Eigenvalues as a NumPy array.
|
|
149
149
|
"""
|
|
150
150
|
matrix = np.asarray(matrix, dtype=float)
|
|
151
|
-
return la.
|
|
151
|
+
return la.eigvalsh(matrix)
|
|
152
152
|
|
|
153
153
|
def _convert_lambda_units(self, lambdas: np.ndarray) -> np.ndarray:
|
|
154
154
|
"""Convert eigenvalues into SI units using run_manager.
|
|
@@ -185,7 +185,10 @@ class VibrationalEntropy:
|
|
|
185
185
|
"""Convert eigenvalues to frequencies with robust filtering.
|
|
186
186
|
|
|
187
187
|
Filters out eigenvalues that are complex, non-positive, or near-zero to
|
|
188
|
-
avoid invalid frequencies and unstable entropies.
|
|
188
|
+
avoid invalid frequencies and unstable entropies. The near-zero
|
|
189
|
+
tolerance scales with the spectrum's own magnitude (as in
|
|
190
|
+
``numpy.linalg.matrix_rank``) rather than using a fixed cutoff, since
|
|
191
|
+
the eigensolver noise floor around a true null mode is not constant.
|
|
189
192
|
|
|
190
193
|
Args:
|
|
191
194
|
lambdas: Eigenvalues (post unit conversion).
|
|
@@ -197,8 +200,11 @@ class VibrationalEntropy:
|
|
|
197
200
|
lambdas = np.asarray(lambdas)
|
|
198
201
|
lambdas = np.real_if_close(lambdas, tol=1000)
|
|
199
202
|
|
|
203
|
+
scale = float(np.max(np.abs(lambdas))) if lambdas.size else 0.0
|
|
204
|
+
atol = lambdas.size * np.finfo(float).eps * scale
|
|
205
|
+
|
|
200
206
|
valid_mask = (
|
|
201
|
-
np.isreal(lambdas) & (lambdas > 0) & (~np.isclose(lambdas, 0, atol=
|
|
207
|
+
np.isreal(lambdas) & (lambdas > 0) & (~np.isclose(lambdas, 0, atol=atol))
|
|
202
208
|
)
|
|
203
209
|
|
|
204
210
|
removed = int(len(lambdas) - np.count_nonzero(valid_mask))
|
CodeEntropy/levels/axes.py
CHANGED
|
@@ -73,17 +73,24 @@ class AxesCalculator:
|
|
|
73
73
|
(previous/next in sequence) using MDAnalysis bonded selections.
|
|
74
74
|
- If there are *no* bonds to other residues:
|
|
75
75
|
* Use a custom principal axes, from a moment-of-inertia (MOI) tensor
|
|
76
|
-
that uses positions of heavy atoms only, but
|
|
76
|
+
that uses positions of heavy atoms only, but includes masses of
|
|
77
77
|
heavy atom + bonded hydrogens.
|
|
78
78
|
* Set translational axes equal to rotational axes (as per the original
|
|
79
79
|
code convention).
|
|
80
|
-
|
|
80
|
+
|
|
81
|
+
- If bonded to only one other residue:
|
|
82
|
+
* Translational axes are principal axes of data_container.
|
|
83
|
+
* Find edge heavy atom (i.e. heavy atoms bonded to neighbour residue).
|
|
84
|
+
Compute rotation centre and axes as in get_terminal_axes.
|
|
85
|
+
Compute custom MOI, using heavy atom positions and
|
|
86
|
+
heavy atom + hydrogen masses.
|
|
87
|
+
|
|
88
|
+
- If bonded to at least two other residues:
|
|
81
89
|
* Translational axes are principal axes of data_container.
|
|
82
|
-
* Find edge heavy atoms (i.e. heavy atoms bonded to neighbour residues)
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
positions and heavy atom + hydrogen masses.
|
|
90
|
+
* Find edge heavy atoms (i.e. heavy atoms bonded to neighbour residues).
|
|
91
|
+
Compute rotation centre and axes as in get_non_terminal_axes.
|
|
92
|
+
Compute a custom MOI, using heavy atom positions and
|
|
93
|
+
heavy atom + hydrogen masses.
|
|
87
94
|
|
|
88
95
|
Args:
|
|
89
96
|
data_container (MDAnalysis.Universe or AtomGroup):
|
|
@@ -137,39 +144,27 @@ class AxesCalculator:
|
|
|
137
144
|
masses=ua_masses,
|
|
138
145
|
dimensions=data_container.dimensions[:3],
|
|
139
146
|
)
|
|
140
|
-
rot_axes, moment_of_inertia = self.
|
|
147
|
+
rot_axes, moment_of_inertia = self.get_principal_axes_from_tensor(
|
|
148
|
+
moi_tensor
|
|
149
|
+
)
|
|
141
150
|
trans_axes = rot_axes # per original convention
|
|
142
151
|
rot_center = np.array(residue.center_of_mass())
|
|
143
152
|
else:
|
|
144
153
|
make_whole(data_container.atoms)
|
|
145
|
-
trans_axes = data_container.atoms
|
|
146
|
-
|
|
154
|
+
trans_axes = self.get_principal_axes_from_group(data_container.atoms)
|
|
147
155
|
if len(edge_atom_set) == 1:
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
else:
|
|
155
|
-
# last residue: last heavy atom
|
|
156
|
-
last_index = len(uas) - 1
|
|
157
|
-
last = None
|
|
158
|
-
if last_index > 0 and last is None:
|
|
159
|
-
heavy_atom = uas[last_index]
|
|
160
|
-
last = heavy_atom
|
|
161
|
-
edges = [edge_atom_set[0], last]
|
|
162
|
-
|
|
163
|
-
backbone = self.get_chain(residue, edge_atom_set[0], last)
|
|
156
|
+
edge_atom = edge_atom_set[0]
|
|
157
|
+
rot_center, rot_axes = self.get_terminal_axes(
|
|
158
|
+
residue=residue,
|
|
159
|
+
edge=edge_atom,
|
|
160
|
+
dimensions=data_container.dimensions[:3],
|
|
161
|
+
)
|
|
164
162
|
else:
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
backbone_center = backbone_center / len(backbone)
|
|
171
|
-
rot_center, rot_axes = self.get_residue_custom_axes(edges, backbone_center)
|
|
172
|
-
|
|
163
|
+
rot_center, rot_axes = self.get_non_terminal_axes(
|
|
164
|
+
residue=residue,
|
|
165
|
+
edges=edge_atom_set,
|
|
166
|
+
dimensions=data_container.dimensions[:3],
|
|
167
|
+
)
|
|
173
168
|
moment_of_inertia = self.get_custom_residue_moment_of_inertia(
|
|
174
169
|
center_of_mass=rot_center,
|
|
175
170
|
positions=uas.positions,
|
|
@@ -224,14 +219,14 @@ class AxesCalculator:
|
|
|
224
219
|
masses=topology.residue_ua_masses,
|
|
225
220
|
dimensions=dimensions,
|
|
226
221
|
)
|
|
227
|
-
rot_axes, moment_of_inertia = self.
|
|
222
|
+
rot_axes, moment_of_inertia = self.get_principal_axes_from_tensor(
|
|
228
223
|
moment_of_inertia_tensor
|
|
229
224
|
)
|
|
230
225
|
trans_axes = rot_axes
|
|
231
226
|
else:
|
|
232
227
|
make_whole(mol.atoms)
|
|
233
|
-
trans_axes = mol.atoms
|
|
234
|
-
rot_axes, moment_of_inertia = self.
|
|
228
|
+
trans_axes = self.get_principal_axes_from_group(mol.atoms)
|
|
229
|
+
rot_axes, moment_of_inertia = self.get_molecule_axes(residue_atoms)
|
|
235
230
|
center = residue_atoms.center_of_mass(unwrap=True)
|
|
236
231
|
|
|
237
232
|
return trans_axes, rot_axes, center, moment_of_inertia
|
|
@@ -247,18 +242,17 @@ class AxesCalculator:
|
|
|
247
242
|
Use the same approach as residue level rotational.
|
|
248
243
|
Identify residue of interest and neighbours, then select
|
|
249
244
|
edge heavy atoms (i.e. heavy atoms bonded to neighbour residues).
|
|
250
|
-
If there are no bonds to
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
245
|
+
- If there are *no* bonds to other residues, use a custom principal axes
|
|
246
|
+
from a moment-of-inertia (MOI) tensor that uses positions of heavy atoms
|
|
247
|
+
only, but includes masses of heavy atom + bonded hydrogens.
|
|
248
|
+
- If bonded to only one other residue, see get_terminal_axes.
|
|
249
|
+
- If bonded to at least two other residues, see get_non_terminal_axes.
|
|
254
250
|
|
|
255
251
|
- Rotational axes:
|
|
256
252
|
Identify heavy atoms in the residue/molecule of interest and choose
|
|
257
253
|
the `index`-th heavy atom (where index corresponds to the bead index).
|
|
258
254
|
Use bonded topology around that heavy atom to determine UA rotational
|
|
259
|
-
axes (see :meth:`get_bonded_axes`).
|
|
260
|
-
Compute a custom MOI tensor using heavy-atom coordinates but UA masses
|
|
261
|
-
(heavy + bonded H masses), then compute the principal axes from it.
|
|
255
|
+
axes (see :meth:`get_bonded_axes`). Compute a custom MOI tensor.
|
|
262
256
|
|
|
263
257
|
Args:
|
|
264
258
|
data_container (MDAnalysis.Universe or AtomGroup):
|
|
@@ -289,72 +283,52 @@ class AxesCalculator:
|
|
|
289
283
|
# only the one residue => use principal axes
|
|
290
284
|
residue = data_container
|
|
291
285
|
trans_center = data_container.atoms.center_of_mass(unwrap=True)
|
|
292
|
-
trans_axes = data_container.atoms
|
|
293
|
-
residue_heavy_atoms = heavy_atoms
|
|
286
|
+
trans_axes = self.get_principal_axes_from_group(data_container.atoms)
|
|
294
287
|
else:
|
|
295
288
|
# residue of interest has at least one neighbour
|
|
296
|
-
if res_position == -1:
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
289
|
+
if res_position == -1 or res_position == 1:
|
|
290
|
+
# look at a terminal residue
|
|
291
|
+
if res_position == -1:
|
|
292
|
+
# first residue
|
|
293
|
+
residue = data_container.residues[0]
|
|
294
|
+
resindex = residue.resindex
|
|
295
|
+
resindex_next = resindex + 1
|
|
296
|
+
edge_atom_set = data_container.select_atoms(
|
|
297
|
+
f"resindex {resindex} and bonded resindex {resindex_next}"
|
|
298
|
+
)
|
|
299
|
+
else:
|
|
300
|
+
# last residue
|
|
301
|
+
residue = data_container.residues[1]
|
|
302
|
+
resindex = residue.resindex
|
|
303
|
+
resindex_prev = resindex - 1
|
|
304
|
+
edge_atom_set = data_container.select_atoms(
|
|
305
|
+
f"resindex {resindex} and bonded resindex {resindex_prev}"
|
|
306
|
+
)
|
|
307
|
+
edge_atom = edge_atom_set[0]
|
|
308
|
+
trans_center, trans_axes = self.get_terminal_axes(
|
|
309
|
+
residue=residue,
|
|
310
|
+
edge=edge_atom,
|
|
311
|
+
dimensions=data_container.dimensions[:3],
|
|
303
312
|
)
|
|
304
|
-
|
|
305
|
-
edges = [residue.atoms[0], second_edge[0]]
|
|
306
|
-
backbone = self.get_chain(
|
|
307
|
-
residue, residue.atoms[0], second_edge.atoms[0]
|
|
308
|
-
)
|
|
309
|
-
|
|
310
|
-
elif res_position == 0:
|
|
313
|
+
else:
|
|
311
314
|
# between 2 residues
|
|
312
315
|
residue = data_container.residues[1]
|
|
313
316
|
resindex = residue.resindex
|
|
314
317
|
resindex_next = resindex + 1
|
|
315
318
|
resindex_prev = resindex - 1
|
|
316
|
-
|
|
317
|
-
edge_set = data_container.select_atoms(
|
|
319
|
+
edge_atom_set = data_container.select_atoms(
|
|
318
320
|
f"resindex {resindex} and "
|
|
319
321
|
f"(bonded resindex {resindex_prev} or "
|
|
320
322
|
f"resindex {resindex_next})"
|
|
321
323
|
)
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
else:
|
|
327
|
-
# last resid
|
|
328
|
-
# always resindex 1 in data_container
|
|
329
|
-
residue = data_container.residues[1]
|
|
330
|
-
resindex = residue.resindex
|
|
331
|
-
resindex_prev = resindex - 1
|
|
332
|
-
first_edge = data_container.select_atoms(
|
|
333
|
-
f"resindex {resindex} and bonded resindex {resindex_prev}"
|
|
324
|
+
trans_center, trans_axes = self.get_non_terminal_axes(
|
|
325
|
+
residue=residue,
|
|
326
|
+
edges=edge_atom_set,
|
|
327
|
+
dimensions=data_container.dimensions[:3],
|
|
334
328
|
)
|
|
335
|
-
|
|
336
|
-
last_index = len(heavy_atoms) - 1
|
|
337
|
-
last = None
|
|
338
|
-
# look for last heavy atom
|
|
339
|
-
# with only one bond to another
|
|
340
|
-
if last_index > 0 and last is None:
|
|
341
|
-
heavy_atom = heavy_atoms[last_index]
|
|
342
|
-
last = heavy_atom
|
|
343
|
-
|
|
344
|
-
edges = [first_edge.atoms[0], last]
|
|
345
|
-
backbone = self.get_chain(residue, first_edge.atoms[0], last)
|
|
346
|
-
|
|
347
|
-
backbone_center = np.zeros(3)
|
|
348
|
-
for heavy_atom in backbone:
|
|
349
|
-
backbone_center += heavy_atom.position
|
|
350
|
-
backbone_center = backbone_center / len(backbone)
|
|
351
|
-
|
|
352
|
-
trans_center, trans_axes = self.get_residue_custom_axes(
|
|
353
|
-
edges, backbone_center
|
|
354
|
-
)
|
|
355
|
-
residue_heavy_atoms = residue.atoms.select_atoms("mass 2 to 999")
|
|
356
|
-
|
|
357
329
|
# look for heavy atoms in residue of interest
|
|
330
|
+
residue_heavy_atoms = residue.atoms.select_atoms("mass 2 to 999")
|
|
331
|
+
|
|
358
332
|
heavy_atom_indices = []
|
|
359
333
|
for atom in residue_heavy_atoms:
|
|
360
334
|
heavy_atom_indices.append(atom.index)
|
|
@@ -444,12 +418,12 @@ class AxesCalculator:
|
|
|
444
418
|
masses=topology.residue_ua_masses,
|
|
445
419
|
dimensions=dimensions,
|
|
446
420
|
)
|
|
447
|
-
trans_axes, _moment_of_inertia = self.
|
|
421
|
+
trans_axes, _moment_of_inertia = self.get_principal_axes_from_tensor(
|
|
448
422
|
moment_of_inertia_tensor
|
|
449
423
|
)
|
|
450
424
|
else:
|
|
451
425
|
make_whole(residue_atoms)
|
|
452
|
-
trans_axes =
|
|
426
|
+
trans_axes = self.get_principal_axes_from_group(residue_atoms)
|
|
453
427
|
|
|
454
428
|
center = heavy_atom.position
|
|
455
429
|
rot_axes, moment_of_inertia = self.get_bonded_axes_from_topology(
|
|
@@ -506,10 +480,10 @@ class AxesCalculator:
|
|
|
506
480
|
ua_all = u.atoms[topology.ua_all_atom_indices]
|
|
507
481
|
|
|
508
482
|
if len(heavy_bonded) == 0:
|
|
509
|
-
custom_axes, custom_moment_of_inertia = self.
|
|
483
|
+
custom_axes, custom_moment_of_inertia = self.get_molecule_axes(ua_all)
|
|
510
484
|
|
|
511
485
|
if len(heavy_bonded) == 1 and len(light_bonded) == 0:
|
|
512
|
-
custom_axes = self.
|
|
486
|
+
custom_axes = self.get_bonded_vector_axes(
|
|
513
487
|
a=heavy_atom.position,
|
|
514
488
|
b_list=[heavy_bonded[0].position],
|
|
515
489
|
c=np.zeros(3),
|
|
@@ -517,7 +491,7 @@ class AxesCalculator:
|
|
|
517
491
|
)
|
|
518
492
|
|
|
519
493
|
if len(heavy_bonded) == 1 and len(light_bonded) >= 1:
|
|
520
|
-
custom_axes = self.
|
|
494
|
+
custom_axes = self.get_bonded_vector_axes(
|
|
521
495
|
a=heavy_atom.position,
|
|
522
496
|
b_list=[heavy_bonded[0].position],
|
|
523
497
|
c=light_bonded[0].position,
|
|
@@ -525,7 +499,7 @@ class AxesCalculator:
|
|
|
525
499
|
)
|
|
526
500
|
|
|
527
501
|
if len(heavy_bonded) >= 2:
|
|
528
|
-
custom_axes = self.
|
|
502
|
+
custom_axes = self.get_bonded_vector_axes(
|
|
529
503
|
a=heavy_atom.position,
|
|
530
504
|
b_list=heavy_bonded.positions,
|
|
531
505
|
c=heavy_bonded[1].position,
|
|
@@ -579,10 +553,14 @@ class AxesCalculator:
|
|
|
579
553
|
rot_center: (3,) rotation centre,
|
|
580
554
|
lies on the E1-E2 vector
|
|
581
555
|
rot_axes: (3,3) rotation axes of residue
|
|
556
|
+
|
|
557
|
+
Raises:
|
|
558
|
+
ValueError: If axes cannot be normalized due to degeneracy.
|
|
559
|
+
|
|
582
560
|
"""
|
|
583
|
-
first_edge_centre_of_geometry_vector = center - edges[0]
|
|
561
|
+
first_edge_centre_of_geometry_vector = center - edges[0]
|
|
584
562
|
# look for projection of E1-O onto E1-E2 (E1-C)
|
|
585
|
-
first_edge_second_edge_vector = edges[1]
|
|
563
|
+
first_edge_second_edge_vector = edges[1] - edges[0]
|
|
586
564
|
first_edge_origin_vector = (
|
|
587
565
|
np.dot(first_edge_second_edge_vector, first_edge_centre_of_geometry_vector)
|
|
588
566
|
/ (np.linalg.norm(first_edge_second_edge_vector) ** 2)
|
|
@@ -594,15 +572,119 @@ class AxesCalculator:
|
|
|
594
572
|
)
|
|
595
573
|
y_axis = origin_centre_of_geometry_vector
|
|
596
574
|
z_axis = np.cross(x_axis, y_axis)
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
575
|
+
unscaled_rot_axes = np.array((x_axis, y_axis, z_axis), dtype=float)
|
|
576
|
+
mod = np.sqrt(np.sum(unscaled_rot_axes**2, axis=1))
|
|
577
|
+
if np.any(np.isclose(mod, 0.0)):
|
|
578
|
+
raise ValueError("Degenerate custom axes: cannot normalize (zero norm).")
|
|
579
|
+
rot_axes = unscaled_rot_axes / mod[:, np.newaxis]
|
|
580
|
+
rot_center = first_edge_origin_vector + edges[0]
|
|
581
|
+
return rot_center, rot_axes
|
|
582
|
+
|
|
583
|
+
def get_terminal_axes(self, residue, edge, dimensions):
|
|
584
|
+
"""
|
|
585
|
+
Compute rotation axes at the residue level/translation axes at the UA level
|
|
586
|
+
for the terminal residues in a polymer, given the edge atom
|
|
587
|
+
(i.e. atom bonded to neighbour residue) and residue of interest.
|
|
588
|
+
Find all heavy atoms bonded to edge heavy atom and compute
|
|
589
|
+
their average position. Find all other heavy atoms in residue
|
|
590
|
+
and compute their average position. The three points are now used to
|
|
591
|
+
obtain determine residue rotational axes. (see get_residue_custom_axes)
|
|
592
|
+
If there are only two heavy atoms in the residue/all heavy atoms are bonded
|
|
593
|
+
to edge atom, x-axis is set along the vector between the
|
|
594
|
+
edge atom and average position of bonded atoms, y-axis is arbitrary
|
|
595
|
+
and z-axis is paralel to the two. This is the same as case 2 in get_bonded_axes.
|
|
596
|
+
If there no heavy atoms bonded to the edge atom (i.e. the edge atom is the only
|
|
597
|
+
heavy atom in the residue), centre is set on edge atom and axes are principal
|
|
598
|
+
axes.
|
|
599
|
+
|
|
600
|
+
Args:
|
|
601
|
+
residue: MDAnalysis AtomGroup
|
|
602
|
+
edge: MDAnalysis atom
|
|
603
|
+
dimensions: (3,) dimensions of the simulation box
|
|
604
|
+
|
|
605
|
+
Returns:
|
|
606
|
+
rot_center: (3,) rotation centre,
|
|
607
|
+
rot_axes: (3,3) rotation axes of residue
|
|
608
|
+
"""
|
|
609
|
+
heavy_atoms = residue.atoms.select_atoms("mass 2 to 999")
|
|
610
|
+
bonded_atoms = residue.atoms.select_atoms(
|
|
611
|
+
f"(mass 2 to 999) and bonded index {edge.index}"
|
|
612
|
+
)
|
|
613
|
+
if len(bonded_atoms) == 0:
|
|
614
|
+
# there is only one heavy atom in the residue
|
|
615
|
+
rot_center = edge.position
|
|
616
|
+
rot_axes = self.get_principal_axes_from_group(residue.atoms)
|
|
617
|
+
else:
|
|
618
|
+
average_bonded = np.zeros(3)
|
|
619
|
+
for bonded_atom in bonded_atoms:
|
|
620
|
+
average_bonded += bonded_atom.position
|
|
621
|
+
average_bonded /= len(bonded_atoms)
|
|
622
|
+
# find the average position of all other heavy atoms in residue
|
|
623
|
+
other_atoms = []
|
|
624
|
+
for atom in heavy_atoms:
|
|
625
|
+
if atom != edge and atom not in bonded_atoms:
|
|
626
|
+
other_atoms.append(atom)
|
|
627
|
+
if len(other_atoms) > 0:
|
|
628
|
+
average_other_atoms = np.zeros(3)
|
|
629
|
+
for atom in other_atoms:
|
|
630
|
+
average_other_atoms += atom.position
|
|
631
|
+
average_other_atoms /= len(other_atoms)
|
|
632
|
+
rot_center, rot_axes = self.get_residue_custom_axes(
|
|
633
|
+
[edge.position, average_other_atoms], average_bonded
|
|
634
|
+
)
|
|
635
|
+
else:
|
|
636
|
+
rot_center = edge.position
|
|
637
|
+
rot_axes = self.get_bonded_vector_axes(
|
|
638
|
+
a=edge.position,
|
|
639
|
+
b_list=[average_bonded],
|
|
640
|
+
c=np.zeros(3),
|
|
641
|
+
dimensions=dimensions,
|
|
642
|
+
)
|
|
643
|
+
return rot_center, rot_axes
|
|
644
|
+
|
|
645
|
+
def get_non_terminal_axes(self, residue, edges, dimensions):
|
|
646
|
+
"""
|
|
647
|
+
Compute rotation axes at the residue level/ translation axes at
|
|
648
|
+
the UA level for the non-terminal residues in a linear polymer, given the
|
|
649
|
+
edge atoms (i.e. heavy atoms bonded to neighbour residues) and
|
|
650
|
+
residue of interest. Find the shortest chain between edge atoms: the backbone.
|
|
651
|
+
Edges + backbone average position determine the residue rotational axes.
|
|
652
|
+
(see get_residue_custom_axes). If the two edge heavy atoms
|
|
653
|
+
are bonded to each other (i.e. there is no backbone), x-axis is set
|
|
654
|
+
along the vector between the edge atom and average position of bonded
|
|
655
|
+
atoms, y-axis is arbitrary and z-axis is paralel to the two. This is the
|
|
656
|
+
same as case 2 in get_bonded_axes.
|
|
657
|
+
Args:
|
|
658
|
+
residue: MDAnalysis AtomGroup
|
|
659
|
+
edges: MDAnalysis AtomGroup
|
|
660
|
+
dimensions: (3,) dimensions of the simulation box
|
|
661
|
+
|
|
662
|
+
Returns:
|
|
663
|
+
rot_center: (3,) rotation centre,
|
|
664
|
+
rot_axes: (3,3) rotation axes of residue
|
|
665
|
+
"""
|
|
666
|
+
backbone = self.get_chain(residue, edges[0], edges[1])
|
|
667
|
+
backbone_center = np.zeros(3)
|
|
668
|
+
if len(backbone) > 0:
|
|
669
|
+
for heavy_atom in backbone:
|
|
670
|
+
backbone_center += heavy_atom.position
|
|
671
|
+
backbone_center /= len(backbone)
|
|
672
|
+
rot_center, rot_axes = self.get_residue_custom_axes(
|
|
673
|
+
edges.positions, backbone_center
|
|
674
|
+
)
|
|
675
|
+
else:
|
|
676
|
+
rot_center = (edges[0].position + edges[1].position) / 2
|
|
677
|
+
rot_axes = self.get_bonded_vector_axes(
|
|
678
|
+
a=rot_center,
|
|
679
|
+
b_list=[edges[0].position],
|
|
680
|
+
c=np.zeros(3),
|
|
681
|
+
dimensions=dimensions,
|
|
682
|
+
)
|
|
602
683
|
return rot_center, rot_axes
|
|
603
684
|
|
|
604
685
|
def get_bonded_axes(self, system, atom, dimensions: np.ndarray):
|
|
605
|
-
|
|
686
|
+
"""
|
|
687
|
+
Compute UA rotational axes from bonded topology around a heavy atom.
|
|
606
688
|
|
|
607
689
|
For a given heavy atom, use its bonded atoms to get the axes for rotating
|
|
608
690
|
forces around. Few cases for choosing united atom axes, which are dependent
|
|
@@ -669,11 +751,11 @@ class AxesCalculator:
|
|
|
669
751
|
|
|
670
752
|
# case1
|
|
671
753
|
if len(heavy_bonded) == 0:
|
|
672
|
-
custom_axes, custom_moment_of_inertia = self.
|
|
754
|
+
custom_axes, custom_moment_of_inertia = self.get_molecule_axes(ua_all)
|
|
673
755
|
|
|
674
756
|
# case2
|
|
675
757
|
if len(heavy_bonded) == 1 and len(light_bonded) == 0:
|
|
676
|
-
custom_axes = self.
|
|
758
|
+
custom_axes = self.get_bonded_vector_axes(
|
|
677
759
|
a=atom.position,
|
|
678
760
|
b_list=[heavy_bonded[0].position],
|
|
679
761
|
c=np.zeros(3),
|
|
@@ -682,7 +764,7 @@ class AxesCalculator:
|
|
|
682
764
|
|
|
683
765
|
# case3
|
|
684
766
|
if len(heavy_bonded) == 1 and len(light_bonded) >= 1:
|
|
685
|
-
custom_axes = self.
|
|
767
|
+
custom_axes = self.get_bonded_vector_axes(
|
|
686
768
|
a=atom.position,
|
|
687
769
|
b_list=[heavy_bonded[0].position],
|
|
688
770
|
c=light_bonded[0].position,
|
|
@@ -692,7 +774,7 @@ class AxesCalculator:
|
|
|
692
774
|
# case4 (not used in original 2019 code; case5 used instead)
|
|
693
775
|
# case5
|
|
694
776
|
if len(heavy_bonded) >= 2:
|
|
695
|
-
custom_axes = self.
|
|
777
|
+
custom_axes = self.get_bonded_vector_axes(
|
|
696
778
|
a=atom.position,
|
|
697
779
|
b_list=heavy_bonded.positions,
|
|
698
780
|
c=heavy_bonded[1].position,
|
|
@@ -732,14 +814,15 @@ class AxesCalculator:
|
|
|
732
814
|
bonded_H_atoms = bonded_atoms.select_atoms("mass 1 to 1.1")
|
|
733
815
|
return bonded_heavy_atoms, bonded_H_atoms
|
|
734
816
|
|
|
735
|
-
def
|
|
736
|
-
"""Get principal axes and sorted principal moments
|
|
817
|
+
def get_molecule_axes(self, molecule):
|
|
818
|
+
"""Get principal axes and sorted principal moments.
|
|
737
819
|
|
|
738
|
-
Compute the principal axes and moments of inertia for a molecule
|
|
739
|
-
|
|
820
|
+
Compute the principal axes and moments of inertia for a molecule from its
|
|
821
|
+
moment of inertia tensor.
|
|
740
822
|
|
|
741
|
-
The
|
|
742
|
-
|
|
823
|
+
- The molecule is made whole to ensure correct handling of PBC. The tensor
|
|
824
|
+
is taken after that, so it does not depend on where the molecule sits
|
|
825
|
+
relative to the periodic boundary.
|
|
743
826
|
- The moments are obtained by diagonalising the moment of inertia tensor.
|
|
744
827
|
- Eigenvalues are returned sorted from largest to smallest magnitude.
|
|
745
828
|
|
|
@@ -752,17 +835,10 @@ class AxesCalculator:
|
|
|
752
835
|
- principal_axes: (3, 3) axes.
|
|
753
836
|
- moment_of_inertia: (3,) moments sorted descending by absolute value.
|
|
754
837
|
"""
|
|
755
|
-
moment_of_inertia_tensor = molecule.moment_of_inertia(unwrap=True)
|
|
756
838
|
make_whole(molecule.atoms)
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
eigenvalues, _ = np.linalg.eig(moment_of_inertia_tensor)
|
|
760
|
-
order = np.argsort(np.abs(eigenvalues))[::-1]
|
|
761
|
-
moment_of_inertia = eigenvalues[order]
|
|
839
|
+
return self.get_principal_axes_from_tensor(molecule.moment_of_inertia())
|
|
762
840
|
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
def get_custom_axes(
|
|
841
|
+
def get_bonded_vector_axes(
|
|
766
842
|
self,
|
|
767
843
|
a: np.ndarray,
|
|
768
844
|
b_list: Sequence[np.ndarray],
|
|
@@ -994,44 +1070,72 @@ class AxesCalculator:
|
|
|
994
1070
|
|
|
995
1071
|
return moment_of_inertia_tensor
|
|
996
1072
|
|
|
997
|
-
def
|
|
998
|
-
self,
|
|
1073
|
+
def get_principal_axes_from_tensor(
|
|
1074
|
+
self,
|
|
1075
|
+
moment_of_inertia_tensor: np.ndarray,
|
|
1076
|
+
degeneracy_rtol: float = 100 * np.finfo(np.float32).eps,
|
|
1077
|
+
reference_vector: Sequence[float] = (1.0, 2.0, 3.0),
|
|
999
1078
|
) -> tuple[np.ndarray, np.ndarray]:
|
|
1000
|
-
"""Compute principal axes and moments
|
|
1079
|
+
"""Compute the principal axes and moments of a moment of inertia tensor.
|
|
1001
1080
|
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1081
|
+
The eigenvectors of a symmetric tensor are only defined up to a sign, and,
|
|
1082
|
+
for equal moments, up to a rotation within the degenerate eigenspace. The
|
|
1083
|
+
axes are returned in a canonical form, so that a given tensor always
|
|
1084
|
+
produces the same axes. The convention is:
|
|
1006
1085
|
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1086
|
+
- Moments are sorted by descending absolute value.
|
|
1087
|
+
- Within a degenerate eigenspace (moments equal to within
|
|
1088
|
+
``degeneracy_rtol`` of the largest), the basis is the eigenvectors of a
|
|
1089
|
+
reference tensor built from ``reference_vector``, projected onto that
|
|
1090
|
+
eigenspace.
|
|
1091
|
+
- The first two axes are signed to have a positive projection onto
|
|
1092
|
+
``reference_vector``, and the third is their cross product.
|
|
1013
1093
|
|
|
1014
1094
|
Args:
|
|
1015
|
-
moment_of_inertia_tensor: (3, 3)
|
|
1095
|
+
moment_of_inertia_tensor: (3, 3) symmetric inertia tensor.
|
|
1096
|
+
degeneracy_rtol: Relative tolerance, with respect to the largest
|
|
1097
|
+
moment, for treating moments as degenerate. The default is a
|
|
1098
|
+
multiple of the float32 precision of the coordinates.
|
|
1099
|
+
reference_vector: Fixed direction used to choose axis signs and the
|
|
1100
|
+
basis within a degenerate eigenspace. Its components must be
|
|
1101
|
+
distinct, so that no symmetric or axis-aligned direction is
|
|
1102
|
+
perpendicular to it or ties on its components.
|
|
1016
1103
|
|
|
1017
1104
|
Returns:
|
|
1018
1105
|
Tuple[np.ndarray, np.ndarray]:
|
|
1019
1106
|
- principal_axes: (3, 3) principal axes (rows).
|
|
1020
|
-
-
|
|
1107
|
+
- principal_moments: (3,) moments sorted by descending magnitude.
|
|
1021
1108
|
"""
|
|
1022
|
-
|
|
1023
|
-
order = np.
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1109
|
+
principal_moments, eigenvectors = np.linalg.eigh(moment_of_inertia_tensor)
|
|
1110
|
+
order = np.argsort(np.abs(principal_moments))[::-1]
|
|
1111
|
+
principal_moments = principal_moments[order]
|
|
1112
|
+
eigenvectors = eigenvectors[:, order]
|
|
1113
|
+
|
|
1114
|
+
reference = np.asarray(reference_vector, dtype=float)
|
|
1115
|
+
gaps = np.abs(np.diff(np.abs(principal_moments)))
|
|
1116
|
+
boundaries = (
|
|
1117
|
+
np.flatnonzero(gaps > degeneracy_rtol * np.abs(principal_moments[0])) + 1
|
|
1118
|
+
)
|
|
1119
|
+
for subspace in np.split(np.arange(3), boundaries):
|
|
1120
|
+
if len(subspace) > 1:
|
|
1121
|
+
basis = eigenvectors[:, subspace]
|
|
1122
|
+
_, rotation = np.linalg.eigh(basis.T @ np.diag(reference) @ basis)
|
|
1123
|
+
eigenvectors[:, subspace] = basis @ rotation
|
|
1124
|
+
|
|
1125
|
+
for axis in (0, 1):
|
|
1126
|
+
if eigenvectors[:, axis] @ reference < 0:
|
|
1127
|
+
eigenvectors[:, axis] *= -1
|
|
1128
|
+
|
|
1129
|
+
z_axis = np.cross(eigenvectors[:, 0], eigenvectors[:, 1])
|
|
1130
|
+
principal_axes = np.array([eigenvectors[:, 0], eigenvectors[:, 1], z_axis])
|
|
1131
|
+
return principal_axes, principal_moments
|
|
1132
|
+
|
|
1133
|
+
def get_principal_axes_from_group(self, group) -> np.ndarray:
|
|
1134
|
+
"""Return the canonical principal axes (rows) of an atom group."""
|
|
1135
|
+
principal_axes, _ = self.get_principal_axes_from_tensor(
|
|
1136
|
+
group.atoms.moment_of_inertia()
|
|
1137
|
+
)
|
|
1138
|
+
return principal_axes
|
|
1035
1139
|
|
|
1036
1140
|
def get_UA_masses(self, molecule) -> list[float]:
|
|
1037
1141
|
"""Return united-atom (UA) masses for a molecule.
|
|
@@ -506,8 +506,8 @@ class FrameCovarianceNode:
|
|
|
506
506
|
# principal axes
|
|
507
507
|
make_whole(residue.atoms)
|
|
508
508
|
make_whole(bead)
|
|
509
|
-
trans_axes = residue.atoms
|
|
510
|
-
rot_axes, moi = axes_manager.
|
|
509
|
+
trans_axes = axes_manager.get_principal_axes_from_group(residue.atoms)
|
|
510
|
+
rot_axes, moi = axes_manager.get_molecule_axes(bead)
|
|
511
511
|
center = bead.center_of_mass(unwrap=True)
|
|
512
512
|
|
|
513
513
|
force_vecs.append(
|
|
@@ -658,8 +658,8 @@ class FrameCovarianceNode:
|
|
|
658
658
|
make_whole(mol.atoms)
|
|
659
659
|
make_whole(bead)
|
|
660
660
|
|
|
661
|
-
trans_axes = mol.atoms
|
|
662
|
-
rot_axes, moi = axes_manager.
|
|
661
|
+
trans_axes = axes_manager.get_principal_axes_from_group(mol.atoms)
|
|
662
|
+
rot_axes, moi = axes_manager.get_molecule_axes(bead)
|
|
663
663
|
center = bead.center_of_mass(unwrap=True)
|
|
664
664
|
return (
|
|
665
665
|
np.asarray(trans_axes),
|
|
@@ -688,8 +688,8 @@ class FrameCovarianceNode:
|
|
|
688
688
|
make_whole(mol.atoms)
|
|
689
689
|
make_whole(bead)
|
|
690
690
|
|
|
691
|
-
trans_axes = mol.atoms
|
|
692
|
-
rot_axes, moi = axes_manager.
|
|
691
|
+
trans_axes = axes_manager.get_principal_axes_from_group(mol.atoms)
|
|
692
|
+
rot_axes, moi = axes_manager.get_molecule_axes(bead)
|
|
693
693
|
center = bead.center_of_mass(unwrap=True)
|
|
694
694
|
|
|
695
695
|
return (
|
CodeEntropy/trajectory/mda.py
CHANGED
|
@@ -354,7 +354,10 @@ class UniverseOperations:
|
|
|
354
354
|
select_atom_force = u_force.select_atoms("all")
|
|
355
355
|
|
|
356
356
|
coordinates = self._extract_timeseries(select_atom, kind="positions")
|
|
357
|
-
dimensions
|
|
357
|
+
if u.dimensions is None:
|
|
358
|
+
dimensions = self._build_non_periodic_dimensions(coordinates)
|
|
359
|
+
else:
|
|
360
|
+
dimensions = self._extract_timeseries(select_atom, kind="dimensions")
|
|
358
361
|
|
|
359
362
|
forces = self._extract_force_timeseries_with_fallback(
|
|
360
363
|
select_atom_force,
|
|
@@ -374,6 +377,30 @@ class UniverseOperations:
|
|
|
374
377
|
|
|
375
378
|
return new_universe
|
|
376
379
|
|
|
380
|
+
@staticmethod
|
|
381
|
+
def _build_non_periodic_dimensions(coordinates: np.ndarray) -> np.ndarray:
|
|
382
|
+
"""Build a box for a trajectory without box dimensions.
|
|
383
|
+
|
|
384
|
+
The axes and covariance code needs a box for the minimum image
|
|
385
|
+
convention and unwrapping. The box is cubic and more than twice the
|
|
386
|
+
largest interatomic distance, so neither step changes the coordinates.
|
|
387
|
+
|
|
388
|
+
Args:
|
|
389
|
+
coordinates: Positions with shape ``(n_frames, n_atoms, 3)``.
|
|
390
|
+
|
|
391
|
+
Returns:
|
|
392
|
+
Array of shape ``(n_frames, 6)`` holding the box lengths and 90
|
|
393
|
+
degree angles for every frame.
|
|
394
|
+
"""
|
|
395
|
+
span = float(np.max(np.ptp(coordinates, axis=1)))
|
|
396
|
+
length = max(10.0 * span, 100.0)
|
|
397
|
+
box = np.array([length, length, length, 90.0, 90.0, 90.0], dtype=np.float32)
|
|
398
|
+
logger.warning(
|
|
399
|
+
"Trajectory has no box dimensions; treating it as non-periodic "
|
|
400
|
+
f"using a {length:.1f} A cubic box."
|
|
401
|
+
)
|
|
402
|
+
return np.tile(box, (coordinates.shape[0], 1))
|
|
403
|
+
|
|
377
404
|
def _extract_timeseries(self, atomgroup, *, kind: str) -> np.ndarray:
|
|
378
405
|
"""Extract a time series array using explicit frame indexing.
|
|
379
406
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: CodeEntropy
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.5.0
|
|
4
4
|
Summary: CodeEntropy is a Python package for computing the configurational entropy of macromolecular systems using forces sampled from molecular dynamics (MD) simulations. It implements the multiscale cell correlation method to provide accurate and efficient entropy estimates, supporting a wide range of applications in molecular simulation and statistical mechanics.
|
|
5
5
|
Keywords: entropy,macromolecular systems,MD simulation
|
|
6
6
|
Author-email: Arghya 'Argo' Chakravorty <arghyac@umich.edu>, Donald Chung-HK <donald.chung@stfc.ac.uk>, Sarah Fegan <sarah.fegan@stfc.ac.uk>, James Gebbie-Rayet <james.gebbie@stfc.ac.uk>, Sarah Harris <sarah.harris@sheffield.ac.uk>, Richard Henchman <rhen7213@uni.sydney.edu.au>, Jonathan Higham <j.higham4@lancaster.ac.uk>, Jas Kalayan <jas.kalayan@stfc.ac.uk>, Ioana Papa <iapapa1@sheffield.ac.uk>
|
|
@@ -16,34 +16,29 @@ Classifier: License :: OSI Approved :: MIT License
|
|
|
16
16
|
Classifier: Natural Language :: English
|
|
17
17
|
Classifier: Development Status :: 5 - Production/Stable
|
|
18
18
|
License-File: LICENSE
|
|
19
|
-
Requires-Dist: numpy>=2.3,<
|
|
19
|
+
Requires-Dist: numpy>=2.3.2,<2.5.0
|
|
20
20
|
Requires-Dist: mdanalysis>=2.10,<3.0
|
|
21
|
-
Requires-Dist: pandas>=3,<
|
|
22
|
-
Requires-Dist: psutil>=
|
|
23
|
-
Requires-Dist: PyYAML>=6.0,<7.0
|
|
24
|
-
Requires-Dist:
|
|
25
|
-
Requires-Dist:
|
|
26
|
-
Requires-Dist:
|
|
27
|
-
Requires-Dist:
|
|
28
|
-
Requires-Dist:
|
|
29
|
-
Requires-Dist:
|
|
30
|
-
Requires-Dist:
|
|
31
|
-
Requires-Dist:
|
|
32
|
-
Requires-Dist:
|
|
33
|
-
Requires-Dist:
|
|
34
|
-
Requires-Dist:
|
|
35
|
-
Requires-Dist:
|
|
36
|
-
Requires-Dist: sphinx>=9.1,<9.2 ; extra == "docs"
|
|
37
|
-
Requires-Dist: nbsphinx>=0.9,<1.0 ; extra == "docs"
|
|
38
|
-
Requires-Dist: sphinx_rtd_theme>=3,<3.2 ; extra == "docs"
|
|
39
|
-
Requires-Dist: sphinxcontrib-contentui>=0.2,<1.0 ; extra == "docs"
|
|
40
|
-
Requires-Dist: sphinxcontrib-details-directive>=0.1,<1.0 ; extra == "docs"
|
|
21
|
+
Requires-Dist: pandas>=2.3.3,<4.0
|
|
22
|
+
Requires-Dist: psutil>=5.9.4,<8.0
|
|
23
|
+
Requires-Dist: PyYAML>=6.0.3,<7.0
|
|
24
|
+
Requires-Dist: rich>=13.0.0,<16.0
|
|
25
|
+
Requires-Dist: art>=5.0,<7.0
|
|
26
|
+
Requires-Dist: networkx>=2.8,<4.0
|
|
27
|
+
Requires-Dist: waterEntropy>=2,<3.0
|
|
28
|
+
Requires-Dist: requests>=2.32.2,<3.0
|
|
29
|
+
Requires-Dist: rdkit>=2025.3.4,<2026
|
|
30
|
+
Requires-Dist: numba>=0.63.0,<0.70
|
|
31
|
+
Requires-Dist: dask>=2025.4.0,<2027.1.0
|
|
32
|
+
Requires-Dist: dask-jobqueue>=0.9.0,<1.0
|
|
33
|
+
Requires-Dist: distributed>=2025.4.0,<2027.1.0
|
|
34
|
+
Requires-Dist: sphinx>=8.0.0,<10.0 ; extra == "docs"
|
|
35
|
+
Requires-Dist: nbsphinx>=0.9.3,<1.0 ; extra == "docs"
|
|
41
36
|
Requires-Dist: sphinx_copybutton>=0.5,<1.0 ; extra == "docs"
|
|
42
|
-
Requires-Dist: furo>=2025.
|
|
37
|
+
Requires-Dist: furo>=2025.12.19,<2026.0 ; extra == "docs"
|
|
43
38
|
Requires-Dist: markupsafe>=3.0,<4.0 ; extra == "docs"
|
|
44
|
-
Requires-Dist: pre-commit>=
|
|
45
|
-
Requires-Dist: ruff>=0.
|
|
46
|
-
Requires-Dist: pylint>=
|
|
39
|
+
Requires-Dist: pre-commit>=3.5.0,<5.0 ; extra == "pre-commit"
|
|
40
|
+
Requires-Dist: ruff>=0.15.2,<1.0 ; extra == "pre-commit"
|
|
41
|
+
Requires-Dist: pylint>=3.0.0,<5.0 ; extra == "pre-commit"
|
|
47
42
|
Requires-Dist: rstcheck>=6.2,<7.0 ; extra == "pre-commit"
|
|
48
43
|
Requires-Dist: pytest>=9.0,<10.0 ; extra == "testing"
|
|
49
44
|
Requires-Dist: pytest-cov>=7.0,<8.0 ; extra == "testing"
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
CodeEntropy/__init__.py,sha256=
|
|
1
|
+
CodeEntropy/__init__.py,sha256=QM8PCDQ0OM8HKjPNiIHHpPFtqLhnyTcGo8Tc6-_Nh-s,404
|
|
2
2
|
CodeEntropy/__main__.py,sha256=5i_ZrxQaq0q2aPllbB4SZ6BApV6oUFCgx0cGyjfMPrU,108
|
|
3
3
|
CodeEntropy/cli.py,sha256=xgSgagCV6HQ3CoXUpG7p7Ef5hCExgxp7orz6dZZWA6c,960
|
|
4
4
|
CodeEntropy/config/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -11,7 +11,7 @@ CodeEntropy/entropy/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSu
|
|
|
11
11
|
CodeEntropy/entropy/configurational.py,sha256=3erd1Ul4sGL5BMBDYq33f27aRTZqiEb6hv1bVfWo9QA,2392
|
|
12
12
|
CodeEntropy/entropy/graph.py,sha256=LeDF2KxxNVgpetrIE9j0dnW64UhGywDLqoBJEf54vtc,4612
|
|
13
13
|
CodeEntropy/entropy/orientational.py,sha256=gfpYqCKQjAAx22rrUdbiLvzfEIXy1nwJoub1rgYKCvY,3415
|
|
14
|
-
CodeEntropy/entropy/vibrational.py,sha256=
|
|
14
|
+
CodeEntropy/entropy/vibrational.py,sha256=WeQJ1bKQPSxPrREJ8CDhk9Ph2xeunrWZ0zu76y-H6Ow,9733
|
|
15
15
|
CodeEntropy/entropy/water.py,sha256=Ob3V08DyQNSWDMqpQ27rqjgHeU5AlKZfjqf9EqUdgqc,8225
|
|
16
16
|
CodeEntropy/entropy/workflow.py,sha256=bxg8AVXGk7uwlv4u2AZ4kTQ26PMge_Z5ubWghdLtn9U,16100
|
|
17
17
|
CodeEntropy/entropy/nodes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -20,7 +20,7 @@ CodeEntropy/entropy/nodes/configurational.py,sha256=-3j2ji_p-twPjZyjQ1T3tgN0pYLS
|
|
|
20
20
|
CodeEntropy/entropy/nodes/orientational.py,sha256=yPN_XRJ1LOxJ7hOE2cnsjIbgQlI2avM1g3mFbGhW6fM,2481
|
|
21
21
|
CodeEntropy/entropy/nodes/vibrational.py,sha256=3uhRqaMpK8u7QK2SDQ6RLRcr2rS3sPh6-GlTaNdTFWw,16978
|
|
22
22
|
CodeEntropy/levels/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
23
|
-
CodeEntropy/levels/axes.py,sha256=
|
|
23
|
+
CodeEntropy/levels/axes.py,sha256=wmotkCwWcc7RzicWJQzXVwYFqkyw9WqWpyeRF1NB3zU,50338
|
|
24
24
|
CodeEntropy/levels/forces.py,sha256=c7XnNIBTFCHHXllr1WxTIrxEV_CkUB2b1vdFO8hUofY,11595
|
|
25
25
|
CodeEntropy/levels/hierarchy.py,sha256=3cd4t6Tywg7VMgw4TboFF5VAHdhG4FaGBXsMptchisY,5158
|
|
26
26
|
CodeEntropy/levels/linalg.py,sha256=PJlrKtIOYcYDycW3Tbetm6k94FKFZTq-hm28qiu_tS0,3565
|
|
@@ -47,7 +47,7 @@ CodeEntropy/levels/nodes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZ
|
|
|
47
47
|
CodeEntropy/levels/nodes/accumulators.py,sha256=XM7phCAIo0RK9YAtx2wrdo2oinmpwXYrH9mp1_LQD7U,6259
|
|
48
48
|
CodeEntropy/levels/nodes/axes_topology.py,sha256=QzrWbCL9YA8fzF4_WWpQGaIAAsOnjY6cVH7INDE40dk,11285
|
|
49
49
|
CodeEntropy/levels/nodes/beads.py,sha256=9Ow065FY4jrIqPKCZJCvDNfIekJ17OBSX-bmEFsxtbc,8010
|
|
50
|
-
CodeEntropy/levels/nodes/covariance.py,sha256=
|
|
50
|
+
CodeEntropy/levels/nodes/covariance.py,sha256=EY_-Ps-e4qtI9RIhV47j7cuvP3znJovGt6kK6yC77sU,30294
|
|
51
51
|
CodeEntropy/levels/nodes/detect_levels.py,sha256=YrM5SSceLiMqbOhr8Fy6SV-E_zkKRpvgHfDT8Grbin8,1864
|
|
52
52
|
CodeEntropy/levels/nodes/detect_molecules.py,sha256=jILCddEicVngA7FjvQzafbG8HslaRICpT6hj2Uyi6YY,3059
|
|
53
53
|
CodeEntropy/molecules/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -56,10 +56,10 @@ CodeEntropy/results/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSu
|
|
|
56
56
|
CodeEntropy/results/reporter.py,sha256=ZmCEKhh_h7-0JGumlyrpH0Y-FaG8aGv-KkzdRAamEAQ,18368
|
|
57
57
|
CodeEntropy/trajectory/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
58
58
|
CodeEntropy/trajectory/frames.py,sha256=MSx_swzFjo4TgkZTR1y6W-1zuKNPg_C36I_uFqvOKpk,4354
|
|
59
|
-
CodeEntropy/trajectory/mda.py,sha256=
|
|
59
|
+
CodeEntropy/trajectory/mda.py,sha256=epKpAzxkNQi3rE7HJc_tsK_ZY4c575LdXmWewJoP1qE,16954
|
|
60
60
|
CodeEntropy/trajectory/source.py,sha256=NfYsdm1amr29h2tmJP-RKyCp3phb1SOAtAELJmGm740,1475
|
|
61
|
-
codeentropy-2.
|
|
62
|
-
codeentropy-2.
|
|
63
|
-
codeentropy-2.
|
|
64
|
-
codeentropy-2.
|
|
65
|
-
codeentropy-2.
|
|
61
|
+
codeentropy-2.5.0.dist-info/entry_points.txt,sha256=8Oa3H9ErsrGtx5Hc6hIHOw73wK3Ylkvfgtl_4cBb_GU,52
|
|
62
|
+
codeentropy-2.5.0.dist-info/licenses/LICENSE,sha256=hgMaAAVW1Q6dL586OHoz8vFO-6BYn-d2aAFBpVQylFc,1072
|
|
63
|
+
codeentropy-2.5.0.dist-info/WHEEL,sha256=lqN8jXt_QloFOGcx9kStvWbryhfEXoI2F2z-YURm350,81
|
|
64
|
+
codeentropy-2.5.0.dist-info/METADATA,sha256=OAUfGJ1rsObNQ9ftW76e4CZr3m6PFxG1ycdM6B5HdsI,6348
|
|
65
|
+
codeentropy-2.5.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|