created wrappers around the isf to allow for access of more raw data and the variance for accurate kai4 calculation

This commit is contained in:
robrobo
2025-07-17 12:50:49 +02:00
parent ec4094cd92
commit dae2d6ed95

View File

@@ -199,7 +199,7 @@ def msd(
raise ValueError('Parameter axis has to be ether "all", "x", "y", or "z"!') raise ValueError('Parameter axis has to be ether "all", "x", "y", or "z"!')
def isf( def isf_raw(
start_frame: CoordinateFrame, start_frame: CoordinateFrame,
end_frame: CoordinateFrame, end_frame: CoordinateFrame,
q: float = 22.7, q: float = 22.7,
@@ -216,29 +216,59 @@ def isf(
displacements = displacements_without_drift(start_frame, end_frame, trajectory) displacements = displacements_without_drift(start_frame, end_frame, trajectory)
if axis == "all": if axis == "all":
distance = (displacements**2).sum(axis=1) ** 0.5 distance = (displacements**2).sum(axis=1) ** 0.5
return np.sinc(distance * q / np.pi).mean() return np.sinc(distance * q / np.pi)
elif axis == "xy" or axis == "yx": elif axis == "xy" or axis == "yx":
distance = (displacements[:, [0, 1]]**2).sum(axis=1) ** 0.5 distance = (displacements[:, [0, 1]]**2).sum(axis=1) ** 0.5
return np.real(jn(0, distance * q)).mean() return np.real(jn(0, distance * q))
elif axis == "xz" or axis == "zx": elif axis == "xz" or axis == "zx":
distance = (displacements[:, [0, 2]]**2).sum(axis=1) ** 0.5 distance = (displacements[:, [0, 2]]**2).sum(axis=1) ** 0.5
return np.real(jn(0, distance * q)).mean() return np.real(jn(0, distance * q))
elif axis == "yz" or axis == "zy": elif axis == "yz" or axis == "zy":
distance = (displacements[:, [1, 2]]**2).sum(axis=1) ** 0.5 distance = (displacements[:, [1, 2]]**2).sum(axis=1) ** 0.5
return np.real(jn(0, distance * q)).mean() return np.real(jn(0, distance * q))
elif axis == "x": elif axis == "x":
distance = np.abs(displacements[:, 0]) distance = np.abs(displacements[:, 0])
return np.mean(np.cos(np.abs(q * distance))) return np.cos(np.abs(q * distance))
elif axis == "y": elif axis == "y":
distance = np.abs(displacements[:, 1]) distance = np.abs(displacements[:, 1])
return np.mean(np.cos(np.abs(q * distance))) return np.cos(np.abs(q * distance))
elif axis == "z": elif axis == "z":
distance = np.abs(displacements[:, 2]) distance = np.abs(displacements[:, 2])
return np.mean(np.cos(np.abs(q * distance))) return np.cos(np.abs(q * distance))
else: else:
raise ValueError('Parameter axis has to be ether "all", "x", "y", or "z"!') raise ValueError('Parameter axis has to be ether "all", "x", "y", or "z"!')
def isf(
start_frame: CoordinateFrame,
end_frame: CoordinateFrame,
q: float = 22.7,
trajectory: Coordinates = None,
axis: str = "all",
) -> float:
"""
Incoherent intermediate scattering function averaged over all particles.
See isf_raw for details.
"""
return isf_raw(start_frame, end_frame, q=q, trajectory=trajectory, axis=axis).mean()
def isf_mean_var(
start_frame: CoordinateFrame,
end_frame: CoordinateFrame,
q: float = 22.7,
trajectory: Coordinates = None,
axis: str = "all",
) -> float:
"""
Incoherent intermediate scattering function averaged over all particles and the
variance.
See isf_raw for details.
"""
values = isf_raw(start_frame, end_frame, q=q, trajectory=trajectory, axis=axis)
return values.mean(), values.var()
def rotational_autocorrelation( def rotational_autocorrelation(
start_frame: CoordinateFrame, end_frame: CoordinateFrame, order: int = 2 start_frame: CoordinateFrame, end_frame: CoordinateFrame, order: int = 2
) -> float: ) -> float:
@@ -465,4 +495,7 @@ def non_gaussian_parameter(
else: else:
raise ValueError('Parameter axis has to be ether "all", "x", "y", or "z"!') raise ValueError('Parameter axis has to be ether "all", "x", "y", or "z"!')
return (np.mean(r**2) / ((1 + 2 / dimensions) * (np.mean(r) ** 2))) - 1 mean_r = np.mean(r)
if mean_r == 0.0:
return 0.0
return (np.mean(r**2) / ((1 + 2 / dimensions) * (mean_r ** 2))) - 1