From 2f21136347e94610d5d54db32850ec5bb307f9fc Mon Sep 17 00:00:00 2001 From: MrJulEnergy Date: Fri, 3 Apr 2026 11:59:19 +0200 Subject: [PATCH 1/9] add gromacs to frames node --- ipsuite/data_loading/add_data_gromacs.py | 240 +++++++++++++++++++++++ 1 file changed, 240 insertions(+) create mode 100644 ipsuite/data_loading/add_data_gromacs.py diff --git a/ipsuite/data_loading/add_data_gromacs.py b/ipsuite/data_loading/add_data_gromacs.py new file mode 100644 index 00000000..80fefa7c --- /dev/null +++ b/ipsuite/data_loading/add_data_gromacs.py @@ -0,0 +1,240 @@ +import typing +import warnings +from pathlib import Path + +import h5py +import MDAnalysis as mda +import numpy as np +import znh5md +import tqdm +import zntrack +from ase import Atoms +from ase.calculators.singlepoint import SinglePointCalculator +from MDAnalysis.auxiliary.EDR import EDRReader + +_TYPE_TO_ELEMENT = { + "CL": "Cl", + "NA": "Na", + "MG": "Mg", + "ZN": "Zn", + "FE": "Fe", + "CA": "Ca", + "MN": "Mn", + "CU": "Cu", + "LI": "Li", + "AL": "Al", + "SI": "Si", + "BR": "Br", + "SE": "Se", +} + + +def _get_symbols(u: mda.Universe) -> list[str]: + """Extract element symbols from a Universe, trying multiple strategies.""" + # 1. Use elements attribute if available + try: + return list(u.atoms.elements) + except (mda.exceptions.NoDataError, AttributeError): + pass + + # 2. Use atom types (usually cleaner than names for CHARMM-GUI) + types = u.atoms.types + symbols = [] + for t in types: + t_upper = t.upper() + if t_upper in _TYPE_TO_ELEMENT: + symbols.append(_TYPE_TO_ELEMENT[t_upper]) + elif len(t) <= 2 and t[0].isalpha(): + # Capitalize properly: first letter upper, rest lower + symbols.append(t[0].upper() + t[1:].lower() if len(t) > 1 else t.upper()) + else: + # Last resort: take leading alphabetic characters from atom name + symbols.append(t[0].upper()) + return symbols + + +def gmx_to_ase( + topology: str, + trajectory: str | None = None, + edr: str | None = None, + start: int | None = None, + stop: int | None = None, + step: int | None = None, +) -> list[Atoms]: + """Convert a GROMACS trajectory to a list of ASE Atoms objects. + + Extracts all available information: positions, velocities, forces, + and (via the .edr file) energies and stress. + + Parameters + ---------- + topology : str + Path to a GROMACS topology/structure file (.gro, .tpr). + trajectory : str | None + Path to a trajectory file (.xtc, .trr). If None, only the single + structure from the topology file is returned. + edr : str | None + Path to a GROMACS energy file (.edr). If given, per-frame energies + and stress tensors are attached via SinglePointCalculator. + start, stop, step : int | None + Slice parameters for selecting a subset of frames. + + Returns + ------- + list[Atoms] + One ASE Atoms object per frame. Each Atoms has: + - positions (always) + - cell and pbc (always) + - velocities (if present in trajectory) + - forces (if present in trajectory, e.g. .trr) + - calculator with energy/stress/forces (if .edr provided or forces + present), plus all EDR terms stored in calc.results + """ + if trajectory is not None: + u = mda.Universe(topology, trajectory) + else: + u = mda.Universe(topology) + + symbols = _get_symbols(u) + + # Load EDR data if provided + edr_data = None + if edr is not None: + with warnings.catch_warnings(): + warnings.simplefilter("ignore") + reader = EDRReader(edr) + edr_all = reader.get_data(list(reader.terms)) + edr_times = edr_all.pop("Time") + edr_data = {term: values for term, values in edr_all.items()} + edr_terms = list(edr_data.keys()) + + frames = [] + for ts in tqdm.tqdm(u.trajectory[start:stop:step]): + positions = ts.positions.copy() + box = ts.dimensions + + atoms = Atoms(symbols=symbols, positions=positions, pbc=True) + + if box is not None and all(box[:3] > 0): + atoms.set_cell(box, scale_atoms=False) + + # Velocities (e.g. from .gro or .trr) + if ts.has_velocities: + # MDAnalysis: Å/ps, ASE: Å/fs -> divide by 1000 + atoms.set_velocities(ts.velocities / 1000.0) + + # Forces and energies via SinglePointCalculator + forces = ts.forces.copy() if ts.has_forces else None + energy = None + stress = None + extra_results = {} + + if edr_data is not None: + # Match EDR frame to trajectory time + idx = np.argmin(np.abs(edr_times - ts.time)) + energy = float(edr_data["Potential"][idx]) # kJ/mol + + # Build Voigt stress from pressure tensor if available + try: + pxx = edr_data["Pres-XX"][idx] + pyy = edr_data["Pres-YY"][idx] + pzz = edr_data["Pres-ZZ"][idx] + pyz = edr_data["Pres-YZ"][idx] + pxz = edr_data["Pres-XZ"][idx] + pxy = edr_data["Pres-XY"][idx] + # GROMACS pressure in bar -> store as-is (not ASE native eV/ų) + stress = np.array([pxx, pyy, pzz, pyz, pxz, pxy]) + except KeyError: + pass + + # Store all EDR terms for this frame + for term in edr_terms: + extra_results[term] = float(edr_data[term][idx]) + + if energy is not None or forces is not None: + calc = SinglePointCalculator( + atoms, + energy=energy, + forces=forces, + stress=stress, + ) + calc.results.update(extra_results) + atoms.calc = calc + + frames.append(atoms) + + return frames + + +class Gmx2Frames(zntrack.Node): + """Convert GROMACS output files to ASE Atoms frames. + + Reads topology, trajectory, and optionally energy (.edr) files + to produce a list of ASE Atoms with positions, velocities, forces, + energies, and stress where available. + + Parameters + ---------- + topology : Path + Path to a GROMACS topology/structure file (.gro, .tpr). + trajectory : Path, optional + Path to a trajectory file (.xtc, .trr). + edr : Path, optional + Path to a GROMACS energy file (.edr). + start : int, optional + First frame index to read. + stop : int, optional + Last frame index (exclusive) to read. + step : int, optional + Step size for frame selection. + + Examples + -------- + >>> with project: + ... md = ips.Gmx2Frames( + ... topology="gromacs/system.gro", + ... trajectory="gromacs/production.xtc", + ... edr="gromacs/production.edr", + ... start=1, + ... ) + """ + topology: Path = zntrack.deps_path() + trajectory: Path = zntrack.deps_path(None) + edr: Path = zntrack.deps_path(None) + start: int = zntrack.params(None) + stop: int = zntrack.params(None) + step: int = zntrack.params(None) + + frames_path: Path = zntrack.outs_path(zntrack.nwd / "frames.h5") + + def run(self): + + data = gmx_to_ase( + topology=str(self.topology), + trajectory=str(self.trajectory) if self.trajectory else None, + edr=str(self.edr) if self.edr else None, + start=self.start, + stop=self.stop, + step=self.step, + ) + frame_io = znh5md.IO(self.frames_path) + frame_io.extend(data) + + @property + def frames(self) -> typing.List[Atoms]: + with self.state.fs.open(self.frames_path, "rb") as f: + with h5py.File(f) as file: + return znh5md.IO(file_handle=file)[:] + + +if __name__ == "__main__": + # Example: load the production trajectory with energies + frames = gmx_to_ase( + "gromacs/system.gro", + "gromacs/production.xtc", + edr="gromacs/production.edr", + ) + print(f"Loaded {len(frames)} frames, {len(frames[0])} atoms per frame") + print(f"Cell: {frames[0].cell.cellpar()}") + print(f"Potential energy (frame 0): {frames[0].get_potential_energy()} kJ/mol") + print(f"All EDR terms on frame 0: {list(frames[1].calc.results.keys())}") From 4584b121c7144f5e8dbf4e44741c9e5d0695d929 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 3 Apr 2026 10:00:25 +0000 Subject: [PATCH 2/9] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- ipsuite/data_loading/add_data_gromacs.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ipsuite/data_loading/add_data_gromacs.py b/ipsuite/data_loading/add_data_gromacs.py index 80fefa7c..f37fa6f8 100644 --- a/ipsuite/data_loading/add_data_gromacs.py +++ b/ipsuite/data_loading/add_data_gromacs.py @@ -5,8 +5,8 @@ import h5py import MDAnalysis as mda import numpy as np -import znh5md import tqdm +import znh5md import zntrack from ase import Atoms from ase.calculators.singlepoint import SinglePointCalculator @@ -198,6 +198,7 @@ class Gmx2Frames(zntrack.Node): ... start=1, ... ) """ + topology: Path = zntrack.deps_path() trajectory: Path = zntrack.deps_path(None) edr: Path = zntrack.deps_path(None) @@ -208,7 +209,6 @@ class Gmx2Frames(zntrack.Node): frames_path: Path = zntrack.outs_path(zntrack.nwd / "frames.h5") def run(self): - data = gmx_to_ase( topology=str(self.topology), trajectory=str(self.trajectory) if self.trajectory else None, From c4a2475f5b88f62f156f313e9223dd83b1bfa026 Mon Sep 17 00:00:00 2001 From: MrJulEnergy Date: Fri, 3 Apr 2026 12:29:54 +0200 Subject: [PATCH 3/9] fix code rabbit remarks --- ipsuite/data_loading/add_data_gromacs.py | 44 +++++++++++++++++------- 1 file changed, 32 insertions(+), 12 deletions(-) diff --git a/ipsuite/data_loading/add_data_gromacs.py b/ipsuite/data_loading/add_data_gromacs.py index f37fa6f8..d3a47444 100644 --- a/ipsuite/data_loading/add_data_gromacs.py +++ b/ipsuite/data_loading/add_data_gromacs.py @@ -1,3 +1,4 @@ +import logging import typing import warnings from pathlib import Path @@ -10,8 +11,11 @@ import zntrack from ase import Atoms from ase.calculators.singlepoint import SinglePointCalculator +from ase.units import kJ, mol from MDAnalysis.auxiliary.EDR import EDRReader +logger = logging.getLogger(__name__) + _TYPE_TO_ELEMENT = { "CL": "Cl", "NA": "Na", @@ -53,6 +57,22 @@ def _get_symbols(u: mda.Universe) -> list[str]: return symbols +def _match_edr_frame( + edr_times: np.ndarray, traj_time: float, tolerance: float = 0.1 +) -> int: + """Find the EDR index closest to a trajectory time, warning on large gaps.""" + idx = int(np.argmin(np.abs(edr_times - traj_time))) + time_diff = abs(edr_times[idx] - traj_time) + if time_diff > tolerance: + logger.warning( + "EDR time %.3f ps does not match trajectory time %.3f ps (diff=%.3f ps)", + edr_times[idx], + traj_time, + time_diff, + ) + return idx + + def gmx_to_ase( topology: str, trajectory: str | None = None, @@ -105,7 +125,7 @@ def gmx_to_ase( reader = EDRReader(edr) edr_all = reader.get_data(list(reader.terms)) edr_times = edr_all.pop("Time") - edr_data = {term: values for term, values in edr_all.items()} + edr_data = dict(edr_all) edr_terms = list(edr_data.keys()) frames = [] @@ -130,9 +150,8 @@ def gmx_to_ase( extra_results = {} if edr_data is not None: - # Match EDR frame to trajectory time - idx = np.argmin(np.abs(edr_times - ts.time)) - energy = float(edr_data["Potential"][idx]) # kJ/mol + idx = _match_edr_frame(edr_times, ts.time) + energy = float(edr_data["Potential"][idx]) * (kJ / mol) # convert to eV # Build Voigt stress from pressure tensor if available try: @@ -200,15 +219,15 @@ class Gmx2Frames(zntrack.Node): """ topology: Path = zntrack.deps_path() - trajectory: Path = zntrack.deps_path(None) - edr: Path = zntrack.deps_path(None) - start: int = zntrack.params(None) - stop: int = zntrack.params(None) - step: int = zntrack.params(None) + trajectory: Path | None = zntrack.deps_path(None) + edr: Path | None = zntrack.deps_path(None) + start: int | None = zntrack.params(None) + stop: int | None = zntrack.params(None) + step: int | None = zntrack.params(None) frames_path: Path = zntrack.outs_path(zntrack.nwd / "frames.h5") - def run(self): + def run(self) -> None: data = gmx_to_ase( topology=str(self.topology), trajectory=str(self.trajectory) if self.trajectory else None, @@ -236,5 +255,6 @@ def frames(self) -> typing.List[Atoms]: ) print(f"Loaded {len(frames)} frames, {len(frames[0])} atoms per frame") print(f"Cell: {frames[0].cell.cellpar()}") - print(f"Potential energy (frame 0): {frames[0].get_potential_energy()} kJ/mol") - print(f"All EDR terms on frame 0: {list(frames[1].calc.results.keys())}") + print(f"Potential energy (frame 0): {frames[0].get_potential_energy()} eV") + if len(frames) >= 2: + print(f"All EDR terms on frame 1: {list(frames[1].calc.results.keys())}") From 2928b0842435c324e87b519769218032f77810c7 Mon Sep 17 00:00:00 2001 From: "coderabbitai[bot]" <136622811+coderabbitai[bot]@users.noreply.github.com> Date: Wed, 6 May 2026 20:03:04 +0200 Subject: [PATCH 4/9] =?UTF-8?q?=F0=9F=93=9D=20Add=20docstrings=20to=20`add?= =?UTF-8?q?Gmx2FramesNode`=20(#464)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 📝 Add docstrings to `addGmx2FramesNode` Docstrings generation was requested by @MrJulEnergy. * https://github.com/zincware/IPSuite/pull/463#issuecomment-4182827901 The following files were modified: * `ipsuite/data_loading/add_data_gromacs.py` * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Julian Peters <108301472+MrJulEnergy@users.noreply.github.com> --- ipsuite/data_loading/add_data_gromacs.py | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/ipsuite/data_loading/add_data_gromacs.py b/ipsuite/data_loading/add_data_gromacs.py index d3a47444..6355e0c1 100644 --- a/ipsuite/data_loading/add_data_gromacs.py +++ b/ipsuite/data_loading/add_data_gromacs.py @@ -34,7 +34,15 @@ def _get_symbols(u: mda.Universe) -> list[str]: - """Extract element symbols from a Universe, trying multiple strategies.""" + """ + Produce a list of element symbols for the atoms in an MDAnalysis Universe by using available per-atom metadata and sensible fallbacks. + + Parameters: + u (mda.Universe): MDAnalysis Universe containing atoms to derive symbols for. + + Returns: + list[str]: Element symbols (e.g., "C", "Cl", "Na") for each atom in the Universe in atom order. + """ # 1. Use elements attribute if available try: return list(u.atoms.elements) @@ -228,6 +236,14 @@ class Gmx2Frames(zntrack.Node): frames_path: Path = zntrack.outs_path(zntrack.nwd / "frames.h5") def run(self) -> None: + """ + Convert the configured GROMACS inputs into ASE Atoms frames and persist them + to the node's HDF5 output at self.frames_path. + + The node's topology, optional trajectory, optional EDR file, and slicing + parameters (start, stop, step) are used to produce the frames which are + written to the frames_path via znh5md. + """ data = gmx_to_ase( topology=str(self.topology), trajectory=str(self.trajectory) if self.trajectory else None, @@ -241,6 +257,12 @@ def run(self) -> None: @property def frames(self) -> typing.List[Atoms]: + """ + Return all ASE `Atoms` frames stored in the node's HDF5 frames file. + + Returns: + typing.List[Atoms]: A list of ASE `Atoms` objects read from the HDF5 file at `self.frames_path`. + """ with self.state.fs.open(self.frames_path, "rb") as f: with h5py.File(f) as file: return znh5md.IO(file_handle=file)[:] From d8207ed960c58a9d31e5962d13c9b43ccfff7041 Mon Sep 17 00:00:00 2001 From: "coderabbitai[bot]" <136622811+coderabbitai[bot]@users.noreply.github.com> Date: Wed, 6 May 2026 20:19:58 +0200 Subject: [PATCH 5/9] fix: apply CodeRabbit auto-fixes (#468) Fixed 1 file(s) based on 2 unresolved review comments. Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Co-authored-by: CodeRabbit --- ipsuite/data_loading/add_data_gromacs.py | 26 +++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/ipsuite/data_loading/add_data_gromacs.py b/ipsuite/data_loading/add_data_gromacs.py index 6355e0c1..5d3397d9 100644 --- a/ipsuite/data_loading/add_data_gromacs.py +++ b/ipsuite/data_loading/add_data_gromacs.py @@ -69,14 +69,25 @@ def _match_edr_frame( edr_times: np.ndarray, traj_time: float, tolerance: float = 0.1 ) -> int: """Find the EDR index closest to a trajectory time, warning on large gaps.""" - idx = int(np.argmin(np.abs(edr_times - traj_time))) + # Use binary search to find insertion point + insert_idx = np.searchsorted(edr_times, traj_time) + + # Determine nearest neighbor (left or right) + if insert_idx == 0: + idx = 0 + elif insert_idx == len(edr_times): + idx = len(edr_times) - 1 + else: + # Compare distances to left and right neighbors + left_diff = abs(traj_time - edr_times[insert_idx - 1]) + right_diff = abs(edr_times[insert_idx] - traj_time) + idx = insert_idx - 1 if left_diff <= right_diff else insert_idx + time_diff = abs(edr_times[idx] - traj_time) if time_diff > tolerance: - logger.warning( - "EDR time %.3f ps does not match trajectory time %.3f ps (diff=%.3f ps)", - edr_times[idx], - traj_time, - time_diff, + raise ValueError( + f"EDR time {edr_times[idx]:.3f} ps does not match trajectory time " + f"{traj_time:.3f} ps (diff={time_diff:.3f} ps, tolerance={tolerance:.3f} ps)" ) return idx @@ -152,7 +163,8 @@ def gmx_to_ase( atoms.set_velocities(ts.velocities / 1000.0) # Forces and energies via SinglePointCalculator - forces = ts.forces.copy() if ts.has_forces else None + # MDAnalysis forces are in kJ/(mol·Å), convert to ASE units (eV/Å) + forces = ts.forces.copy() * (kJ / mol) if ts.has_forces else None energy = None stress = None extra_results = {} From c259ba863d973affb76975c63edd22afce6646bc Mon Sep 17 00:00:00 2001 From: MrJulEnergy Date: Wed, 6 May 2026 20:30:08 +0200 Subject: [PATCH 6/9] rename to AddDataGMX --- ipsuite/data_loading/add_data_gromacs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ipsuite/data_loading/add_data_gromacs.py b/ipsuite/data_loading/add_data_gromacs.py index 5d3397d9..b7421155 100644 --- a/ipsuite/data_loading/add_data_gromacs.py +++ b/ipsuite/data_loading/add_data_gromacs.py @@ -205,7 +205,7 @@ def gmx_to_ase( return frames -class Gmx2Frames(zntrack.Node): +class AddDataGMX(zntrack.Node): """Convert GROMACS output files to ASE Atoms frames. Reads topology, trajectory, and optionally energy (.edr) files From c695783c41001eb008969f4f335a5f121b53af2b Mon Sep 17 00:00:00 2001 From: MrJulEnergy Date: Wed, 6 May 2026 20:32:18 +0200 Subject: [PATCH 7/9] pre commit cleanup --- ipsuite/data_loading/add_data_gromacs.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/ipsuite/data_loading/add_data_gromacs.py b/ipsuite/data_loading/add_data_gromacs.py index b7421155..0a2d7c96 100644 --- a/ipsuite/data_loading/add_data_gromacs.py +++ b/ipsuite/data_loading/add_data_gromacs.py @@ -35,13 +35,15 @@ def _get_symbols(u: mda.Universe) -> list[str]: """ - Produce a list of element symbols for the atoms in an MDAnalysis Universe by using available per-atom metadata and sensible fallbacks. + Produce a list of element symbols for the atoms in an MDAnalysis Universe by using + available per-atom metadata and sensible fallbacks. Parameters: u (mda.Universe): MDAnalysis Universe containing atoms to derive symbols for. Returns: - list[str]: Element symbols (e.g., "C", "Cl", "Na") for each atom in the Universe in atom order. + list[str]: Element symbols (e.g., "C", "Cl", "Na") for each atom + in the Universe in atom order. """ # 1. Use elements attribute if available try: @@ -273,7 +275,8 @@ def frames(self) -> typing.List[Atoms]: Return all ASE `Atoms` frames stored in the node's HDF5 frames file. Returns: - typing.List[Atoms]: A list of ASE `Atoms` objects read from the HDF5 file at `self.frames_path`. + typing.List[Atoms]: A list of ASE `Atoms` objects read + from the HDF5 file at `self.frames_path`. """ with self.state.fs.open(self.frames_path, "rb") as f: with h5py.File(f) as file: From 88fd54a53b9225c3d897019599f15196490a2efd Mon Sep 17 00:00:00 2001 From: MrJulEnergy Date: Wed, 6 May 2026 20:44:22 +0200 Subject: [PATCH 8/9] add new node to init --- ipsuite/__init__.pyi | 3 ++- ipsuite/data_loading/__init__.py | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/ipsuite/__init__.pyi b/ipsuite/__init__.pyi index a63c4cdf..7899e651 100644 --- a/ipsuite/__init__.pyi +++ b/ipsuite/__init__.pyi @@ -75,7 +75,7 @@ from .configuration_selection import ( ) # Data -from .data_loading import AddData, AddDataH5MD +from .data_loading import AddData, AddDataH5MD, AddDataGMX # Datasets from .datasets import MD22Dataset @@ -156,6 +156,7 @@ __all__ = [ # Data "AddData", "AddDataH5MD", + "AddDataGMX", # Datasets "MD22Dataset", # Bootstrap diff --git a/ipsuite/data_loading/__init__.py b/ipsuite/data_loading/__init__.py index 891a55a2..6d23eee5 100644 --- a/ipsuite/data_loading/__init__.py +++ b/ipsuite/data_loading/__init__.py @@ -2,5 +2,6 @@ from ipsuite.data_loading.add_data_ase import AddData from ipsuite.data_loading.add_data_h5md import AddDataH5MD +from ipsuite.data_loading.add_data_gromacs import AddDataGMX -__all__ = ["AddData", "AddDataH5MD"] +__all__ = ["AddData", "AddDataH5MD", "AddDataGMX"] From 67b2ee2a81e895475512817bc5f2c4401b646f28 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 6 May 2026 18:48:21 +0000 Subject: [PATCH 9/9] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- ipsuite/__init__.pyi | 2 +- ipsuite/data_loading/__init__.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ipsuite/__init__.pyi b/ipsuite/__init__.pyi index 7899e651..908291e0 100644 --- a/ipsuite/__init__.pyi +++ b/ipsuite/__init__.pyi @@ -75,7 +75,7 @@ from .configuration_selection import ( ) # Data -from .data_loading import AddData, AddDataH5MD, AddDataGMX +from .data_loading import AddData, AddDataGMX, AddDataH5MD # Datasets from .datasets import MD22Dataset diff --git a/ipsuite/data_loading/__init__.py b/ipsuite/data_loading/__init__.py index 6d23eee5..48353d26 100644 --- a/ipsuite/data_loading/__init__.py +++ b/ipsuite/data_loading/__init__.py @@ -1,7 +1,7 @@ """ipsuite data loading module.""" from ipsuite.data_loading.add_data_ase import AddData -from ipsuite.data_loading.add_data_h5md import AddDataH5MD from ipsuite.data_loading.add_data_gromacs import AddDataGMX +from ipsuite.data_loading.add_data_h5md import AddDataH5MD __all__ = ["AddData", "AddDataH5MD", "AddDataGMX"]