Compare commits

...

26 Commits

Author SHA1 Message Date
c09549902a Added Robins adjustments for FEL 2024-05-27 14:27:09 +02:00
b7bb8cb379 Merge branch 'refs/heads/mdeval_dev' 2024-05-02 16:18:18 +02:00
33c4756e34 Fixed frame-index selection in occupancy calculation. 2024-05-02 16:17:54 +02:00
7b9f8b6773 Merge branch 'mdeval_dev' 2024-03-05 13:58:15 +01:00
c89cead81c Moved KDTree build up in its own function and made find_maxima query for each point separately. 2024-03-05 13:57:55 +01:00
31eb145a13 Merge branch 'mdeval_dev'
# Conflicts:
#	src/mdevaluate/coordinates.py
2024-02-26 14:20:12 +01:00
af3758cbef Fixed iter in Coordinates 2024-02-26 14:18:23 +01:00
93d020a4de Updated fel test to run faster 2024-02-26 14:14:29 +01:00
b5395098ce Fixed iter of Coordinates after last change 2024-02-26 13:57:44 +01:00
5e80701562 Coordinates returns now correct length after slicing 2024-02-26 13:50:46 +01:00
363e420cd8 Fixed chill ice_cluster_traj() function 2024-02-16 11:11:17 +01:00
6b77ef78e1 Fixed chill selector_ice() function 2024-02-15 11:49:20 +01:00
0c940115af Fixed LSI 2024-02-08 17:24:44 +01:00
b0f29907df Updated fel to triclinic box case 2024-02-08 17:24:22 +01:00
37bf496b21 Fixed reading nojump_matrices from non-writable directories 2024-02-06 13:29:38 +01:00
befaef2dfa Fixed van Hove calculation in plane 2024-02-06 09:38:38 +01:00
8ea7da5d2f Removed prints from number_of_neighbors 2024-01-31 15:36:51 +01:00
b405842452 Changed version number 2024-01-31 11:32:52 +01:00
f5cf453d61 Fixed isf for 2d case 2024-01-31 11:32:29 +01:00
4394f70530 Fixed pyproject.toml 2024-01-31 09:02:57 +01:00
298da3818d Changed parameter normed to density in np.histogram 2024-01-31 09:02:36 +01:00
d9278eed83 Removed prints from rdf 2024-01-31 08:59:39 +01:00
787882810c Added pyedr in requirements.txt 2024-01-30 17:03:57 +01:00
f527d25864 Changed dynamic calculations to include 2D cases 2024-01-30 16:46:11 +01:00
77771738ab Fixed parse jumps for triclinic box case 2024-01-30 16:27:59 +01:00
3e8fd04726 Fixed construction of nojump trajectory for triclinic box case 2024-01-29 18:21:36 +01:00
11 changed files with 202 additions and 94 deletions

View File

@ -4,11 +4,12 @@ build-backend = "setuptools.build_meta"
[project]
name = "mdevaluate"
version = "24.01"
version = "24.02"
dependencies = [
"mdanalysis",
"pandas",
"dask",
"pathos",
"tables"
"tables",
"pyedr"
]

View File

@ -3,4 +3,5 @@ pandas
dask
pathos
tables
pytest
pytest
pyedr

View File

@ -218,7 +218,7 @@ class Coordinates:
self.get_frame.clear_cache()
def __iter__(self):
for i in range(len(self))[self._slice]:
for i in range(len(self.frames))[self._slice]:
yield self[i]
@singledispatchmethod
@ -232,7 +232,7 @@ class Coordinates:
return sliced
def __len__(self):
return len(self.frames)
return len(self.frames[self._slice])
def __checksum__(self):
return checksum(self.frames, self.atom_filter, self._slice, self.mode)
@ -692,10 +692,6 @@ def number_of_neighbors(
elif not distinct:
dnn = 1
print(atoms[:5])
print(query_atoms[:5])
print(" ")
box = atoms.box
if np.all(np.diag(np.diag(box)) == box):
atoms = atoms % np.diag(box)

View File

@ -2,7 +2,7 @@ from typing import Callable, Optional
import numpy as np
from numpy.typing import ArrayLike
from scipy.special import legendre
from scipy.special import legendre, jn
import dask.array as darray
from functools import partial
from scipy.spatial import KDTree
@ -183,6 +183,12 @@ def msd(
displacements = displacements_without_drift(start_frame, end_frame, trajectory)
if axis == "all":
return (displacements**2).sum(axis=1).mean()
elif axis == "xy" or axis == "yx":
return (displacements[:, [0, 1]]**2).sum(axis=1).mean()
elif axis == "xz" or axis == "zx":
return (displacements[:, [0, 2]]**2).sum(axis=1).mean()
elif axis == "yz" or axis == "zy":
return (displacements[:, [1, 2]]**2).sum(axis=1).mean()
elif axis == "x":
return (displacements[:, 0] ** 2).mean()
elif axis == "y":
@ -211,6 +217,15 @@ def isf(
if axis == "all":
distance = (displacements**2).sum(axis=1) ** 0.5
return np.sinc(distance * q / np.pi).mean()
elif axis == "xy" or axis == "yx":
distance = (displacements[:, [0, 1]]**2).sum(axis=1) ** 0.5
return np.real(jn(0, distance * q)).mean()
elif axis == "xz" or axis == "zx":
distance = (displacements[:, [0, 2]]**2).sum(axis=1) ** 0.5
return np.real(jn(0, distance * q)).mean()
elif axis == "yz" or axis == "zy":
distance = (displacements[:, [1, 2]]**2).sum(axis=1) ** 0.5
return np.real(jn(0, distance * q)).mean()
elif axis == "x":
distance = np.abs(displacements[:, 0])
return np.mean(np.cos(np.abs(q * distance)))
@ -262,6 +277,12 @@ def van_hove_self(
vectors = displacements_without_drift(start_frame, end_frame, trajectory)
if axis == "all":
delta_r = (vectors**2).sum(axis=1) ** 0.5
elif axis == "xy" or axis == "yx":
delta_r = (vectors[:, [0, 1]]**2).sum(axis=1) ** 0.5
elif axis == "xz" or axis == "zx":
delta_r = (vectors[:, [0, 2]]**2).sum(axis=1) ** 0.5
elif axis == "yz" or axis == "zy":
delta_r = (vectors[:, [1, 2]]**2).sum(axis=1) ** 0.5
elif axis == "x":
delta_r = np.abs(vectors[:, 0])
elif axis == "y":
@ -423,6 +444,15 @@ def non_gaussian_parameter(
if axis == "all":
r = (vectors**2).sum(axis=1)
dimensions = 3
elif axis == "xy" or axis == "yx":
r = (vectors[:, [0, 1]]**2).sum(axis=1)
dimensions = 2
elif axis == "xz" or axis == "zx":
r = (vectors[:, [0, 2]]**2).sum(axis=1)
dimensions = 2
elif axis == "yz" or axis == "zy":
r = (vectors[:, [1, 2]]**2).sum(axis=1)
dimensions = 2
elif axis == "x":
r = vectors[:, 0] ** 2
dimensions = 1

View File

@ -126,9 +126,6 @@ def rdf(
particles_in_volume = int(
np.max(number_of_neighbors(atoms_a, query_atoms=atoms_b, r_max=bins[-1])) * 1.1
)
print(atoms_a[:5])
print(atoms_b[:5])
print(" ")
distances, indices = next_neighbors(
atoms_a,
atoms_b,
@ -309,7 +306,7 @@ def next_neighbor_distribution(
)[1]
resname_nn = reference.residue_names[nn]
count_nn = (resname_nn == atoms.residue_names.reshape(-1, 1)).sum(axis=1)
return np.histogram(count_nn, bins=bins, normed=normed)[0]
return np.histogram(count_nn, bins=bins, density=normed)[0]
def hbonds(

View File

@ -11,7 +11,7 @@ from mdevaluate.coordinates import CoordinateFrame, Coordinates
from mdevaluate.pbc import pbc_points
def a_ij(atoms: ArrayLike, N: int = 4, l: int = 3) -> tuple[NDArray, NDArray]:
def calc_aij(atoms: ArrayLike, N: int = 4, l: int = 3) -> tuple[NDArray, NDArray]:
tree = KDTree(atoms)
dist, indices = tree.query(atoms, N + 1)
@ -84,18 +84,18 @@ def count_ice_types(iceTypes: NDArray) -> NDArray:
def selector_ice(
start_frame: CoordinateFrame,
traj: Coordinates,
oxygen_atoms_water: CoordinateFrame,
chosen_ice_types: ArrayLike,
combined: bool = True,
next_neighbor_distance: float = 0.35,
) -> NDArray:
oxygen = traj.subset(atom_name="OW")
atoms = oxygen[start_frame.step]
atoms = atoms % np.diag(atoms.box)
atoms_PBC = pbc_points(atoms, thickness=1)
aij, indices = a_ij(atoms_PBC)
atoms = oxygen_atoms_water
atoms_PBC = pbc_points(atoms, thickness=next_neighbor_distance * 2.2)
aij, indices = calc_aij(atoms_PBC)
tree = KDTree(atoms_PBC)
neighbors = tree.query_ball_point(atoms_PBC, 0.35, return_length=True)
neighbors = tree.query_ball_point(
atoms_PBC, next_neighbor_distance, return_length=True
) - 1
index_SOL = atoms_PBC.tolist().index(atoms[0].tolist())
index_SOL = np.arange(index_SOL, index_SOL + len(atoms))
ice_Types = classify_ice(aij, indices, neighbors, index_SOL)
@ -117,9 +117,9 @@ def selector_ice(
def ice_types(trajectory: Coordinates, segments: int = 10000) -> pd.DataFrame:
def ice_types_distribution(frame: CoordinateFrame, selector: Callable) -> NDArray:
atoms_PBC = pbc_points(frame, thickness=1)
aij, indices = a_ij(atoms_PBC)
aij, indices = calc_aij(atoms_PBC)
tree = KDTree(atoms_PBC)
neighbors = tree.query_ball_point(atoms_PBC, 0.35, return_length=True)
neighbors = tree.query_ball_point(atoms_PBC, 0.35, return_length=True) - 1
index = selector(frame, atoms_PBC)
ice_types_data = classify_ice(aij, indices, neighbors, index)
ice_parts_data = count_ice_types(ice_types_data)
@ -161,8 +161,8 @@ def ice_types(trajectory: Coordinates, segments: int = 10000) -> pd.DataFrame:
def ice_clusters_traj(
traj: Coordinates, segments: int = 10000, skip: float = 0.1
) -> list:
def ice_clusters(frame: CoordinateFrame, traj: Coordinates) -> Tuple[float, list]:
selection = selector_ice(frame, traj, [0, 1, 2])
def ice_clusters(frame: CoordinateFrame) -> Tuple[float, list]:
selection = selector_ice(frame, [0, 1, 2])
if len(selection) == 0:
return frame.time, []
else:
@ -194,6 +194,6 @@ def ice_clusters_traj(
np.int_(np.linspace(len(traj) * skip, len(traj) - 1, num=segments))
)
all_clusters = [
ice_clusters(traj[frame_index], traj) for frame_index in frame_indices
ice_clusters(traj[frame_index]) for frame_index in frame_indices
]
return all_clusters

View File

@ -1,9 +1,9 @@
from functools import partial
from typing import Optional
import numpy as np
from numpy.typing import ArrayLike, NDArray
from numpy.polynomial.polynomial import Polynomial as Poly
import math
from scipy.spatial import KDTree
import pandas as pd
import multiprocessing as mp
@ -11,6 +11,56 @@ import multiprocessing as mp
from ..coordinates import Coordinates
def _pbc_points_reduced(
coordinates: ArrayLike,
pore_geometry: str,
box: Optional[NDArray] = None,
thickness: Optional[float] = None,
) -> tuple[NDArray, NDArray]:
if box is None:
box = coordinates.box
if pore_geometry == "cylindrical":
grid = np.array([[i, j, k] for k in [-1, 0, 1] for j in [0] for i in [0]])
indices = np.tile(np.arange(len(coordinates)), 3)
elif pore_geometry == "slit":
grid = np.array(
[[i, j, k] for k in [0] for j in [1, 0, -1] for i in [-1, 0, 1]]
)
indices = np.tile(np.arange(len(coordinates)), 9)
else:
raise ValueError(
f"pore_geometry is {pore_geometry}, should either be "
f"'cylindrical' or 'slit'"
)
coordinates_pbc = np.concatenate([coordinates + v @ box for v in grid], axis=0)
size = np.diag(box)
if thickness is not None:
mask = np.all(coordinates_pbc > -thickness, axis=1)
coordinates_pbc = coordinates_pbc[mask]
indices = indices[mask]
mask = np.all(coordinates_pbc < size + thickness, axis=1)
coordinates_pbc = coordinates_pbc[mask]
indices = indices[mask]
return coordinates_pbc, indices
def _build_tree(points, box, r_max, pore_geometry):
if np.all(np.diag(np.diag(box)) == box):
tree = KDTree(points % box, boxsize=box)
points_pbc_index = None
else:
points_pbc, points_pbc_index = _pbc_points_reduced(
points,
pore_geometry,
box,
thickness=r_max + 0.01,
)
tree = KDTree(points_pbc)
return tree, points_pbc_index
def occupation_matrix(
trajectory: Coordinates,
edge_length: float = 0.05,
@ -28,11 +78,7 @@ def occupation_matrix(
z_bins = np.arange(0, box[2][2] + edge_length, edge_length)
bins = [x_bins, y_bins, z_bins]
# Trajectory is split for parallel computing
size = math.ceil(len(frame_indices) / nodes)
indices = [
np.arange(len(frame_indices))[i : i + size]
for i in range(0, len(frame_indices), size)
]
indices = np.array_split(frame_indices, nodes)
pool = mp.Pool(nodes)
results = pool.map(
partial(_calc_histogram, trajectory=trajectory, bins=bins), indices
@ -72,19 +118,19 @@ def _calc_histogram(
def find_maxima(
occupation_df: pd.DataFrame, box: ArrayLike, edge_length: float = 0.05
occupation_df: pd.DataFrame, box: ArrayLike, radius: float, pore_geometry: str
) -> pd.DataFrame:
maxima_df = occupation_df.copy()
maxima_df["maxima"] = None
points = np.array(maxima_df[["x", "y", "z"]])
tree = KDTree(points, boxsize=box)
all_neighbors = tree.query_ball_point(
points, edge_length * 3 ** (1 / 2) + edge_length / 100
)
tree, points_pbc_index = _build_tree(points, box, radius, pore_geometry)
for i in range(len(maxima_df)):
if maxima_df.loc[i, "maxima"] is not None:
continue
neighbors = np.array(all_neighbors[i])
maxima_pos = maxima_df.loc[i, ["x", "y", "z"]]
neighbors = np.array(tree.query_ball_point(maxima_pos, radius))
if points_pbc_index is not None:
neighbors = points_pbc_index[neighbors]
neighbors = neighbors[neighbors != i]
if len(neighbors) == 0:
maxima_df.loc[i, "maxima"] = True
@ -104,23 +150,39 @@ def _calc_energies(
maxima_df: pd.DataFrame,
bins: ArrayLike,
box: NDArray,
pore_geometry: str,
T: float,
nodes: int = 8,
) -> NDArray:
points = np.array(maxima_df[["x", "y", "z"]])
tree = KDTree(points, boxsize=box)
tree, points_pbc_index = _build_tree(points, box, bins[-1], pore_geometry)
maxima = maxima_df.loc[maxima_indices, ["x", "y", "z"]]
maxima_occupations = np.array(maxima_df.loc[maxima_indices, "occupation"])
num_of_neighbors = np.max(
tree.query_ball_point(maxima, bins[-1], return_length=True)
)
distances, indices = tree.query(
maxima, k=num_of_neighbors, distance_upper_bound=bins[-1]
)
split_maxima = []
for i in range(0, len(maxima), 1000):
split_maxima.append(maxima[i : i + 1000])
distances = []
indices = []
for maxima in split_maxima:
distances_step, indices_step = tree.query(
maxima, k=num_of_neighbors, distance_upper_bound=bins[-1], workers=nodes
)
distances.append(distances_step)
indices.append(indices_step)
distances = np.concatenate(distances)
indices = np.concatenate(indices)
all_energy_hist = []
all_occupied_bins_hist = []
if distances.ndim == 1:
current_distances = distances[1:][distances[1:] <= bins[-1]]
current_indices = indices[1:][distances[1:] <= bins[-1]]
if points_pbc_index is None:
current_indices = indices[1:][distances[1:] <= bins[-1]]
else:
current_indices = points_pbc_index[indices[1:][distances[1:] <= bins[-1]]]
energy = (
-np.log(maxima_df.loc[current_indices, "occupation"] / maxima_occupations)
* T
@ -131,8 +193,12 @@ def _calc_energies(
return result
for i, maxima_occupation in enumerate(maxima_occupations):
current_distances = distances[i, 1:][distances[i, 1:] <= bins[-1]]
current_indices = indices[i, 1:][distances[i, 1:] <= bins[-1]]
if points_pbc_index is None:
current_indices = indices[i, 1:][distances[i, 1:] <= bins[-1]]
else:
current_indices = points_pbc_index[
indices[i, 1:][distances[i, 1:] <= bins[-1]]
]
energy = (
-np.log(maxima_df.loc[current_indices, "occupation"] / maxima_occupation)
* T
@ -168,9 +234,12 @@ def distance_resolved_energies(
distance_bins: ArrayLike,
r_bins: ArrayLike,
box: NDArray,
pore_geometry: str,
temperature: float,
nodes: int = 8,
) -> pd.DataFrame:
results = []
distances = []
for i in range(len(distance_bins) - 1):
maxima_indices = np.array(
maxima_df.index[
@ -179,11 +248,22 @@ def distance_resolved_energies(
* (maxima_df["maxima"] == True)
]
)
results.append(
_calc_energies(maxima_indices, maxima_df, r_bins, box, temperature)
)
try:
results.append(
_calc_energies(
maxima_indices,
maxima_df,
r_bins,
box,
pore_geometry,
temperature,
nodes,
)
)
distances.append((distance_bins[i] + distance_bins[i + 1]) / 2)
except ValueError:
pass
distances = (distance_bins[:-1] + distance_bins[1:]) / 2
radii = (r_bins[:-1] + r_bins[1:]) / 2
d = np.array([d for d in distances for r in radii])
r = np.array([r for d in distances for r in radii])
@ -192,7 +272,11 @@ def distance_resolved_energies(
def find_energy_maxima(
energy_df: pd.DataFrame, r_min: float, r_max: float
energy_df: pd.DataFrame,
r_min: float,
r_max: float,
r_eval: float = None,
degree: int = 2,
) -> pd.DataFrame:
distances = []
energies = []
@ -201,6 +285,9 @@ def find_energy_maxima(
x = np.array(data_d["r"])
y = np.array(data_d["energy"])
mask = (x >= r_min) * (x <= r_max)
p3 = Poly.fit(x[mask], y[mask], deg=2)
energies.append(np.max(p3(np.linspace(r_min, r_max, 1000))))
p3 = Poly.fit(x[mask], y[mask], deg=degree)
if r_eval is None:
energies.append(np.max(p3(np.linspace(r_min, r_max, 1000))))
else:
energies.append(p3(r_eval))
return pd.DataFrame({"d": distances, "energy": energies})

View File

@ -265,7 +265,7 @@ def LSI(
)
distributions = np.array(
[
LSI_distribution(trajectory[frame_index], trajectory, bins, selector=None)
LSI_distribution(trajectory[frame_index], bins, selector=None)
for frame_index in frame_indices
]
)

View File

@ -156,7 +156,7 @@ def nojump(frame: CoordinateFrame, usecache: bool = True) -> CoordinateFrame:
[m[i0 : abstep + 1].sum(axis=0) for m in reader.nojump_matrices]
).T
)
* frame.box.diagonal()
@ frame.box
)
reader._nojump_cache[abstep] = delta
@ -173,7 +173,7 @@ def nojump(frame: CoordinateFrame, usecache: bool = True) -> CoordinateFrame:
]
).T
)
* frame.box.diagonal()
@ frame.box
)
return frame - delta

View File

@ -152,7 +152,7 @@ def nojump_load_filename(reader: BaseReader):
)
if os.path.exists(full_path_fallback):
return full_path_fallback
if os.path.exists(fname) or is_writeable(directory):
if os.path.exists(full_path) or is_writeable(directory):
return full_path
else:
user_data_dir = os.path.join("/data/", os.environ["HOME"].split("/")[-1])
@ -187,19 +187,33 @@ def nojump_save_filename(reader: BaseReader):
def parse_jumps(trajectory: Coordinates):
prev = trajectory[0].whole
box = prev.box.diagonal()
box = prev.box
SparseData = namedtuple("SparseData", ["data", "row", "col"])
jump_data = (
SparseData(data=array("b"), row=array("l"), col=array("l")),
SparseData(data=array("b"), row=array("l"), col=array("l")),
SparseData(data=array("b"), row=array("l"), col=array("l")),
)
for i, curr in enumerate(trajectory):
if i % 500 == 0:
logger.debug("Parse jumps Step: %d", i)
delta = ((curr - prev) / box).round().astype(np.int8)
r3 = np.subtract(curr, prev)
delta_z = np.array(np.rint(np.divide(r3[:, 2], box[2][2])), dtype=np.int8)
r2 = np.subtract(
r3,
(np.rint(np.divide(r3[:, 2], box[2][2])))[:, np.newaxis]
* box[2][np.newaxis, :],
)
delta_y = np.array(np.rint(np.divide(r2[:, 1], box[1][1])), dtype=np.int8)
r1 = np.subtract(
r2,
(np.rint(np.divide(r2[:, 1], box[1][1])))[:, np.newaxis]
* box[1][np.newaxis, :],
)
delta_x = np.array(np.rint(np.divide(r1[:, 0], box[0][0])), dtype=np.int8)
delta = np.array([delta_x, delta_y, delta_z]).T
prev = curr
box = prev.box
for d in range(3):
(col,) = np.where(delta[:, d] != 0)
jump_data[d].col.extend(col)

View File

@ -13,42 +13,24 @@ def trajectory(request):
def test_get_fel(trajectory):
test_array = np.array(
[
174.46253634,
174.60905476,
178.57658092,
182.43001192,
180.57916378,
176.49886217,
178.96018547,
181.13561782,
178.31026314,
176.08903996,
180.71215345,
181.59703135,
180.34329368,
187.02474488,
197.99167477,
214.05788031,
245.58571282,
287.52457507,
331.53492965,
]
)
test_array = np.array([210., 214., 209., 192., 200., 193., 230., 218., 266.])
OW = trajectory.subset(atom_name="OW")
box = np.diag(trajectory[0].box)
box_voxels = (box // [0.05, 0.05, 0.05] + [1, 1, 1]) * [0.05, 0.05, 0.05]
occupation_matrix = fel.occupation_matrix(OW, skip=0, segments=1000)
maxima_matrix = fel.find_maxima(occupation_matrix, box=box_voxels, edge_length=0.05)
maxima_matrix = fel.add_distances(maxima_matrix, "cylindrical", box / 2)
r_bins = np.arange(0, 2, 0.02)
distance_bins = np.arange(0.05, 2.05, 0.1)
box = trajectory[0].box
box_voxels = (np.diag(box) // [0.05, 0.05, 0.05] + [1, 1, 1]) * [0.05, 0.05, 0.05]
occupation_matrix = fel.occupation_matrix(OW, skip=0, segments=10)
radius_maxima = 0.05 * 3 ** (1 / 2) + 0.05 / 100
maxima_matrix = fel.find_maxima(
occupation_matrix,
box=box_voxels,
radius=radius_maxima,
pore_geometry="cylindrical",
)
maxima_matrix = fel.add_distances(maxima_matrix, "cylindrical", np.diag(box) / 2)
r_bins = np.arange(0, 0.5, 0.02)
distance_bins = np.arange(1.8, 1.9, 0.01)
energy_df = fel.distance_resolved_energies(
maxima_matrix, distance_bins, r_bins, box, 225
maxima_matrix, distance_bins, r_bins, box, "cylindrical", 225
)
result = fel.find_energy_maxima(energy_df, r_min=0.05, r_max=0.15)
assert (np.round(np.array(result["energy"])) == np.round(test_array)).all()