Skip to content

Commit

Permalink
Merge branch 'nanobind' into nanobind-interface
Browse files Browse the repository at this point in the history
  • Loading branch information
joaander committed Sep 17, 2024
2 parents f45284b + 4a789ce commit 1d13bd6
Show file tree
Hide file tree
Showing 6 changed files with 47 additions and 47 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/build_wheels.yml
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ jobs:
gsd==${{ matrix.python.oldest_gsd }}
matplotlib==${{ matrix.python.oldest_matplotlib }}
# Test only the currently ported modules
CIBW_TEST_COMMAND: "cd {package}/tests && pytest test_box_box.py test_parallel.py test_locality_*.py test_data.py test_pmft.py test_interface.py test_util.py -v --log-level=DEBUG"
CIBW_TEST_COMMAND: "cd {package}/tests && pytest test_box_box.py test_parallel.py test_locality_*.py test_data.py test_pmft.py test_util.py test_msd_msd.py test_interface.py -v --log-level=DEBUG"
# CIBW_TEST_COMMAND: "cd {package}/tests && pytest . -v --log-level=DEBUG"

- uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/run_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ jobs:
pytest tests/test_pmft.py -v
pytest tests/test_util.py -v
pytest tests/test_interface.py -v
pytest tests/test_msd_msd.py -v
# pytest tests/ -v
tests_complete:
Expand Down
1 change: 1 addition & 0 deletions doc/source/reference/credits.rst
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,7 @@ Domagoj Fijan
lengths and angles (``freud.box.Box.from_box_lengths_and_angles``), as well as a
method for returning box vector lengths and angles
(``freud.box.Box.to_box_lengths_and_angles``).
* Ported ``MSD`` to pure python.

Andrew Kerr

Expand Down
4 changes: 2 additions & 2 deletions freud/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -196,13 +196,13 @@ set(python_files
data.py
errors.py
locality.py
msd.py
parallel.py
pmft.py
interface.py
plot.py
util.py)
# cluster.py density.py diffraction.py environment.py order.py interface.py
# msd.py)
# cluster.py density.py diffraction.py environment.py order.py interface.py)

copy_files_to_build("${python_files}" "freud" "*.py")

Expand Down
4 changes: 2 additions & 2 deletions freud/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@


# cluster,; density,; diffraction,; environment,; interface,; msd,; order,
from . import box, data, interface, locality, parallel, pmft
from . import box, data, interface, locality, msd, parallel, pmft
from .box import Box
from .locality import AABBQuery, LinkCell, NeighborList
from .parallel import NumThreads, get_num_threads, set_num_threads
Expand All @@ -24,7 +24,7 @@
# "environment",
"interface",
"locality",
# "msd",
"msd",
# "order",
"parallel",
"pmft",
Expand Down
82 changes: 40 additions & 42 deletions freud/msd.pyx → freud/msd.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,63 +6,62 @@
mean-squared-displacement (MSD) of particles in periodic systems.
"""

from freud.util cimport _Compute
import logging

import numpy as np

import freud.box
import freud.parallel

cimport numpy as np

cimport freud.box
from freud.util import _Compute

logger = logging.getLogger(__name__)

# Use fastest available fft library
try:
import pyfftw

logger.info("Using PyFFTW for FFTs")

pyfftw.config.NUM_THREADS = min(1, freud.parallel.get_num_threads())
logger.info("Setting number of threads to {}".format(
freud.parallel.get_num_threads()))
logger.info(f"Setting number of threads to {freud.parallel.get_num_threads()}")

# Note that currently these functions are defined to match only the parts
# of the numpy/scipy API that are actually used below. There is no promise
# that other aspects of the API will be preserved.
def fft(x, n, axis):
a = pyfftw.empty_aligned(x.shape, 'complex64')
a = pyfftw.empty_aligned(x.shape, "complex64")
a[:] = x
fft_object = pyfftw.builders.fft(a, n=n, axis=axis)
return fft_object()

def ifft(x, axis):
a = pyfftw.empty_aligned(x.shape, 'complex64')
a = pyfftw.empty_aligned(x.shape, "complex64")
a[:] = x
fft_object = pyfftw.builders.ifft(a, axis=axis)
return fft_object()
except ImportError:
try:
from scipy.fftpack import fft, ifft

logger.info("Using SciPy's fftpack for FFTs")
except ImportError:
from numpy.fft import fft, ifft

logger.info("Using NumPy for FFTs")


def _autocorrelation(x):
r"""Compute the autocorrelation of a sequence"""
N = x.shape[0]
F = fft(x, n=2*N, axis=0)
F = fft(x, n=2 * N, axis=0)
PSD = F * F.conjugate()
res = ifft(PSD, axis=0)
res = (res[:N]).real
n = np.arange(1, N+1)[::-1] # N to 1
return res/n[:, np.newaxis]
n = np.arange(1, N + 1)[::-1] # N to 1
return res / n[:, np.newaxis]


cdef class MSD(_Compute):
class MSD(_Compute):
r"""Compute the mean squared displacement.
The mean squared displacement (MSD) measures how much particles move over
Expand Down Expand Up @@ -140,21 +139,19 @@ def _autocorrelation(x):
mode (str, optional):
Mode of calculation. Options are :code:`'window'` and
:code:`'direct'`. (Default value = :code:`'window'`).
""" # noqa: E501
cdef freud.box.Box _box
cdef _particle_msd
cdef str mode
""" # noqa: E501

def __cinit__(self, box=None, mode='window'):
def __init__(self, box=None, mode="window"):
if box is not None:
self._box = freud.util._convert_box(box)
else:
self._box = None

self._particle_msd = []

if mode not in ['window', 'direct']:
raise ValueError("Invalid mode")
if mode not in ["window", "direct"]:
msg = "Invalid mode"
raise ValueError(msg)
self.mode = mode

def compute(self, positions, images=None, reset=True):
Expand Down Expand Up @@ -192,42 +189,39 @@ def compute(self, positions, images=None, reset=True):

self._called_compute = True

positions = freud.util._convert_array(
positions, shape=(None, None, 3))
positions = freud.util._convert_array(positions, shape=(None, None, 3))
if images is not None:
images = freud.util._convert_array(
images, shape=positions.shape, dtype=np.int32)
images, shape=positions.shape, dtype=np.int32
)

# Make sure we aren't modifying the provided array
if self._box is not None and images is not None:
unwrapped_positions = positions.copy()
for i in range(positions.shape[0]):
unwrapped_positions[i, :, :] = self._box.unwrap(
unwrapped_positions[i, :, :], images[i, :, :])
positions = unwrapped_positions
positions = self._box.wrap(unwrapped_positions, images)

if self.mode == 'window':
if self.mode == "window":
# First compute the first term r^2(k+m) - r^2(k)
N = positions.shape[0]
D = np.square(positions).sum(axis=2)
D = np.append(D, np.zeros(positions.shape[:2]), axis=0)
Q = 2*D.sum(axis=0)
Q = 2 * D.sum(axis=0)
S1 = np.zeros(positions.shape[:2])
for m in range(N):
Q -= (D[m-1, :] + D[N-m, :])
S1[m, :] = Q/(N-m)
Q -= D[m - 1, :] + D[N - m, :]
S1[m, :] = Q / (N - m)

# The second term can be computed via autocorrelation
corrs = []
for i in range(positions.shape[2]):
corrs.append(_autocorrelation(positions[:, :, i]))
S2 = np.sum(corrs, axis=0)

self._particle_msd.append(S1 - 2*S2)
elif self.mode == 'direct':
self._particle_msd.append(S1 - 2 * S2)
elif self.mode == "direct":
self._particle_msd.append(
np.linalg.norm(
positions - positions[[0], :, :], axis=-1)**2)
np.linalg.norm(positions - positions[[0], :, :], axis=-1) ** 2
)

return self

Expand All @@ -249,8 +243,7 @@ def particle_msd(self):
return np.concatenate(self._particle_msd, axis=1)

def __repr__(self):
return "freud.msd.{cls}(box={box}, mode={mode})".format(
cls=type(self).__name__, box=self._box, mode=repr(self.mode))
return f"freud.msd.{type(self).__name__}(box={self._box}, mode={self.mode!r})"

def plot(self, ax=None):
"""Plot MSD.
Expand All @@ -264,19 +257,24 @@ def plot(self, ax=None):
(:class:`matplotlib.axes.Axes`): Axis with the plot.
"""
import freud.plot

if self.mode == "window":
xlabel = "Window size"
else:
xlabel = "Frame number"
return freud.plot.line_plot(list(range(len(self.msd))), self.msd,
title="MSD",
xlabel=xlabel,
ylabel="MSD",
ax=ax)
return freud.plot.line_plot(
list(range(len(self.msd))),
self.msd,
title="MSD",
xlabel=xlabel,
ylabel="MSD",
ax=ax,
)

def _repr_png_(self):
try:
import freud.plot

return freud.plot._ax_to_bytes(self.plot())
except (AttributeError, ImportError):
return None

0 comments on commit 1d13bd6

Please sign in to comment.