Skip to content

Update NCDF Reader to read .ncrst Restart Files #5031

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 40 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
6a75d39
Adds basic framework for reading NCRST files
Sep 2, 2018
5a88100
Removing unused imports
Sep 2, 2018
8b7dbd5
Major updates to NCRST reader
Sep 4, 2018
0c457ce
Try/except instead of it statements + docstring changes
Sep 5, 2018
d245e33
64 bit offset check
Sep 7, 2018
33cbe3e
Merge branch 'develop' into NCrestart
May 28, 2019
b420032
Need to check things
May 28, 2019
08bb2b1
Merge branch 'develop' into NCrestart
Aug 2, 2019
7676741
Some changes
Aug 5, 2019
a382762
Merge branch 'develop' into NCrestart
IAlibay Aug 5, 2019
df24168
Exception + Minor docs update
IAlibay Aug 6, 2019
93f2edb
More exceptions + start on tests
IAlibay Aug 6, 2019
78e92d0
More inpcrd changes and tests
IAlibay Aug 7, 2019
474ab59
Error type change
IAlibay Aug 7, 2019
e5d7cb6
Changes to match initial PR 2326
IAlibay Aug 16, 2019
cee802c
Merge branch 'develop' into NCrestart
IAlibay Aug 19, 2019
2554ff0
Swapping to ValueErrors
IAlibay Aug 19, 2019
8cc8161
Update _verify_units and typos
IAlibay Aug 19, 2019
5277cd0
Merge branch 'develop' into NCrestart
IAlibay Dec 29, 2019
f9d2ab5
Merge branch 'MDAnalysis:develop' into NCrestart
IAlibay Nov 30, 2022
bcc58c8
Update INPCRD.py
IAlibay Nov 30, 2022
30a2b12
working commit
jeremyleung521 Apr 16, 2025
e2e0455
test for NCRST
jeremyleung521 Apr 17, 2025
d6627c7
changelog + authors
jeremyleung521 Apr 17, 2025
c5988e6
Merge branch 'develop' of github.com:mdanalysis/mdanalysis into ncrst…
jeremyleung521 Apr 17, 2025
fb0d2e9
lint test_netcdf.py
jeremyleung521 Apr 17, 2025
0f27fc4
small code cleanup of TRJ.py
jeremyleung521 Apr 17, 2025
eb83b42
small formatting in CHANGELOG
jeremyleung521 Apr 17, 2025
eb7ba03
Merge branch 'develop' into ncrst-reader
jeremyleung521 Apr 19, 2025
50eb893
Merge branch 'develop' of github.com:mdanalysis/mdanalysis into ncrst…
jeremyleung521 May 22, 2025
abfb5a7
Merge branch 'pr/3942' into ncrst-reader
jeremyleung521 May 22, 2025
d98ebab
working, failing ExceptionWarning Tests
jeremyleung521 May 22, 2025
37b7e1e
cleanup
jeremyleung521 May 22, 2025
736db88
make NCRST work
jeremyleung521 May 27, 2025
2ab9fe1
typo in SingleFrameReaderBase
jeremyleung521 May 27, 2025
14daa07
Merge branch 'ncrst-reader' of github.com:jeremyleung521/MDAnalysis i…
jeremyleung521 May 27, 2025
5bf1ce0
Revert "typo in SingleFrameReaderBase"
jeremyleung521 May 27, 2025
614f9f6
black lint
jeremyleung521 May 27, 2025
bf68954
Reapply "typo in SingleFrameReaderBase"
jeremyleung521 May 27, 2025
264215f
black lint
jeremyleung521 May 27, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions package/AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,7 @@ Chronological list of authors
- James Rowe
- Debasish Mohanty
- Abdulrahman Elbanna
- Jeremy M.G. Leung


External code
Expand Down
4 changes: 3 additions & 1 deletion package/CHANGELOG
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ The rules for this file:

-------------------------------------------------------------------------------
??/??/?? IAlibay, orbeckst, BHM-Bob, TRY-ER, Abdulrahman-PROG, pbuslaev,
yuxuanzhuang, yuyuan871111
yuxuanzhuang, yuyuan871111, jpkrowe, jeremyleung521

* 2.10.0

Expand All @@ -31,6 +31,8 @@ Fixes
(Issue #4948, PR #5001)

Enhancements
* Allow NCDF reader to read Amber Restart Files with extension `.ncrst`
(Issue #5030, PR#5031)
* Improve parsing of topology information from LAMMPS dump files to allow
reading of mass, charge and element attributes. (Issue #3449, PR #4995)
* Added timestep support for XDR writers and readers (Issue #4905, PR #4908)
Expand Down
47 changes: 33 additions & 14 deletions package/MDAnalysis/coordinates/TRJ.py
Original file line number Diff line number Diff line change
Expand Up @@ -359,7 +359,7 @@ class NCDFReader(base.ReaderBase):
"""Reader for `AMBER NETCDF format`_ (version 1.0).

AMBER binary trajectories are automatically recognised by the
file extension ".ncdf".
file extension ".ncdf", ".nc", and '.ncrst'.

The number of atoms (`n_atoms`) does not have to be provided as it can
be read from the trajectory. The trajectory reader can randomly access
Expand Down Expand Up @@ -422,10 +422,12 @@ class NCDFReader(base.ReaderBase):
the first two frames of the trajectory.
:meth:`Writer` now also sets `convert_units`, `velocities`, `forces` and
`scale_factor` information for the :class:`NCDFWriter`.
.. versionchanged:: 2.10.0
Now reads `AMBERRESTART` convention `.ncrst` files.

"""

format = ['NCDF', 'NC']
format = ['NCDF', 'NC', 'NCRST']
multiframe = True
version = "1.0"
units = {'time': 'ps',
Expand All @@ -450,8 +452,14 @@ def __init__(self, filename, n_atoms=None, mmap=None, **kwargs):
# AMBER NetCDF files should always have a convention
try:
conventions = self.trjfile.Conventions
if not ('AMBER' in conventions.decode('utf-8').split(',') or
'AMBER' in conventions.decode('utf-8').split()):
check = [conventions.decode('utf-8').split(','),
conventions.decode('utf-8').split()]

if 'AMBER' in check[0] or 'AMBER' in check[1]:
self.is_restart = False
elif 'AMBERRESTART' in check[0] or 'AMBERRESTART' in check[1]:
self.is_restart = True
else:
errmsg = ("NCDF trajectory {0} does not conform to AMBER "
"specifications, "
"http://ambermd.org/netcdf/nctraj.xhtml "
Expand Down Expand Up @@ -530,9 +538,12 @@ def __init__(self, filename, n_atoms=None, mmap=None, **kwargs):
if self.n_frames is None:
self.n_frames = self.trjfile.variables['coordinates'].shape[0]
except KeyError:
errmsg = (f"NCDF trajectory {self.filename} does not contain "
f"frame information")
raise ValueError(errmsg) from None
if self.is_restart:
self.n_frames = 1
else:
errmsg = (f"NCDF trajectory {self.filename} does not contain "
f"frame information")
raise ValueError(errmsg) from None

try:
self.remarks = self.trjfile.title
Expand Down Expand Up @@ -645,21 +656,27 @@ def _read_frame(self, frame):
if np.dtype(type(frame)) != np.dtype(int):
# convention... for netcdf could also be a slice
raise TypeError("frame must be a positive integer or zero")
if frame >= self.n_frames or frame < 0:
if (frame >= self.n_frames or frame < 0):
raise IndexError("frame index must be 0 <= frame < {0}".format(
self.n_frames))
# note: self.trjfile.variables['coordinates'].shape == (frames, n_atoms, 3)
ts._pos[:] = self._get_var_and_scale('coordinates', frame)

if self.is_restart:
check_frame = () # AMBERRESTART convention files have dimensionless datasets
else:
check_frame = int(frame)

ts._pos[:] = self._get_var_and_scale('coordinates', check_frame)
if self.has_time:
ts.time = self._get_var_and_scale('time', frame)
ts.time = self._get_var_and_scale('time', check_frame)
if self.has_velocities:
ts._velocities[:] = self._get_var_and_scale('velocities', frame)
ts._velocities[:] = self._get_var_and_scale('velocities', check_frame)
if self.has_forces:
ts._forces[:] = self._get_var_and_scale('forces', frame)
ts._forces[:] = self._get_var_and_scale('forces', check_frame)
if self.periodic:
unitcell = np.zeros(6)
unitcell[:3] = self._get_var_and_scale('cell_lengths', frame)
unitcell[3:] = self._get_var_and_scale('cell_angles', frame)
unitcell[:3] = self._get_var_and_scale('cell_lengths', check_frame)
unitcell[3:] = self._get_var_and_scale('cell_angles', check_frame)
ts.dimensions = unitcell
if self.convert_units:
self.convert_pos_from_native(ts._pos) # in-place !
Expand All @@ -673,6 +690,8 @@ def _read_frame(self, frame):
if self.periodic:
# in-place ! (only lengths)
self.convert_pos_from_native(ts.dimensions[:3])

del check_frame
ts.frame = frame # frame labels are 0-based
self._current_frame = frame
return ts
Expand Down
68 changes: 68 additions & 0 deletions testsuite/MDAnalysisTests/coordinates/test_netcdf.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@
DLP_CONFIG,
CPPTRAJ_TRAJ_TOP,
CPPTRAJ_TRAJ,
PRMNCRST,
TRJNCRST,
)
from MDAnalysisTests.coordinates.test_trj import _TRJReaderTest
from MDAnalysisTests.coordinates.reference import RefVGV, RefTZ2
Expand Down Expand Up @@ -389,6 +391,72 @@ def test_warn_user_no_time_information(self, u):
u2 = mda.Universe(CPPTRAJ_TRAJ_TOP, CPPTRAJ_TRAJ)


class TestNCDFReader5(object):
"""NCRST Restart File with positions and forces, exported by CPPTRAJ.

Contributed by Jeremy M. G. Leung
"""

prec = 6

@pytest.fixture(scope="class")
def u(self):
return mda.Universe(PRMNCRST, TRJNCRST)

def test_positions(self, u):
"""Check positions on first frame"""
u.trajectory[0]
ref_1 = np.array(
[
[-1.1455358, -2.0177484, -0.55771565],
[-0.19042611, -2.2511053, -1.0282656],
[ 0.53238064, -1.5778863, -0.56737846],
],
dtype=np.float64,
)
assert_almost_equal(ref_1, u.atoms.positions[:3], self.prec)

def test_velocities(self, u):
"""Check forces on first frame"""
u.trajectory[0]
ref_1 = np.array(
[
[11.86471367, 31.22108269, -4.03538418],
[7.36676359, -4.68035316, 1.78124952],
[12.86675262, 1.39324546, -14.97190762],
],
dtype=np.float64,
)
assert_almost_equal(ref_1, u.atoms.velocities[:3], self.prec)

def test_forces(self, u):
"""Check forces on first frame"""
u.trajectory[0]
ref_1 = np.array(
[
[-9.7262249, -0.37627634, -24.79876137],
[-32.43384552, 37.47783279, -12.45422745],
[24.10939026, -19.80618095, -17.09523582],
],
dtype=np.float64,
)
assert_almost_equal(ref_1, u.atoms.forces[:3], self.prec)

def test_time(self, u):
"""Check time on first frame"""
ref = 5.0
assert_almost_equal(ref, u.trajectory[0].time, self.prec)

def test_dt(self, u):
"""Default 1.0 fs"""
ref = 1.0
assert_almost_equal(ref, u.trajectory.dt, self.prec)
assert_almost_equal(ref, u.trajectory.ts.dt, self.prec)

def test_box(self, u):
assert u.trajectory[0].dimensions is None


class _NCDFGenerator(object):
"""A class for generating abitrary ncdf files and exhaustively test
edge cases which might not be found in the wild"""
Expand Down
Binary file not shown.
4 changes: 3 additions & 1 deletion testsuite/MDAnalysisTests/datafiles.py
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,8 @@
"CPPTRAJ_TRAJ_TOP",
"CPPTRAJ_TRAJ", # Amber ncdf extracted from CPPTRAJ without time variable
"PRMcs", # Amber (format, Issue 1331)
"PRMNCRST", # Amber ncrst with positions/forces/velocities
"PRMNCRST", # Amber topology for ncrst with positions/forces/velocities
"TRJNCRST", # Amber ncrst with positions/forces/velocties
"PRM_NCBOX",
"TRJ_NCBOX", # Amber parm7 + nc w/ pos/forces/vels/box
"PRMNEGATIVE", # Amber negative ATOMIC_NUMBER (Issue 2306)
Expand Down Expand Up @@ -662,6 +663,7 @@
PRMcs = (_data_ref / "Amber/chitosan.prmtop").as_posix()

PRMNCRST = (_data_ref / "Amber/ace_mbondi3.parm7").as_posix()
TRJNCRST = (_data_ref / "Amber/ace_mbondi3.ncrst").as_posix()

PRM_NCBOX = (_data_ref / "Amber/ace_tip3p.parm7").as_posix()
TRJ_NCBOX = (_data_ref / "Amber/ace_tip3p.nc").as_posix()
Expand Down