diff --git a/ipsuite/configuration_generation/__init__.pyi b/ipsuite/configuration_generation/__init__.pyi index fc2d236e..26f257d8 100644 --- a/ipsuite/configuration_generation/__init__.pyi +++ b/ipsuite/configuration_generation/__init__.pyi @@ -1,6 +1,13 @@ """Module for generating new configurations based on smiles.""" +from .gmx import Smiles2Gromacs from .packmol import MultiPackmol, Packmol from .smiles_to_atoms import SmilesToAtoms, SmilesToConformers -__all__ = ["SmilesToAtoms", "Packmol", "SmilesToConformers", "MultiPackmol"] +__all__ = [ + "SmilesToAtoms", + "Packmol", + "SmilesToConformers", + "MultiPackmol", + "Smiles2Gromacs", +] diff --git a/ipsuite/configuration_generation/gmx.py b/ipsuite/configuration_generation/gmx.py new file mode 100644 index 00000000..3ba47be4 --- /dev/null +++ b/ipsuite/configuration_generation/gmx.py @@ -0,0 +1,463 @@ +import contextlib +import pathlib +import re +import shutil +import subprocess + +import h5py +import MDAnalysis as mda +import znh5md +import zntrack +from ase import Atoms +from ase.calculators.singlepoint import SinglePointCalculator +from ase.data import atomic_numbers +from MDAnalysis.coordinates.timestep import Timestep +from pint import UnitRegistry +from rdkit import Chem +from rdkit.Chem import AllChem, rdMolDescriptors + +from ipsuite import base + +# Initialize pint unit registry +ureg = UnitRegistry() + + +def timestep_to_atoms(u: mda.Universe, ts: Timestep) -> Atoms: + """Convert an MDAnalysis timestep to an ASE atoms object. + + Parameters + ---------- + u : MDAnalysis.Universe + The MDAnalysis Universe object containing topology information. + ts : MDAnalysis.coordinates.Timestep + The MDAnalysis Timestep object to convert. + + Returns + ------- + ase.Atoms + The converted ASE Atoms object. + """ + # Set the universe's trajectory to the provided timestep + u.trajectory.ts = ts + + # Extract positions from timestep + positions = ts.positions + + # Extract masses and elements from the universe + names = u.atoms.names + cell = u.dimensions + + # Adapted from ASE gro reader + symbols = [] + for name in names: + if name in atomic_numbers: + symbols.append(name) + elif name[0] in atomic_numbers: + symbols.append(name[0]) + elif name[-1] in atomic_numbers: + symbols.append(name[-1]) + else: + # not an atomic symbol + # if we can not determine the symbol, we use + # the dummy symbol X + symbols.append("X") + + forces = ts.forces * ureg.kilocalories / ureg.mol / ureg.angstrom + # convert to eV/Å + forces.ito(ureg.eV / ureg.angstrom / ureg.particle) + forces = forces.magnitude + energy = 0 + with contextlib.suppress(KeyError): + energy = ts.aux["Total Energy"] * ureg.kilocalories / ureg.mol + energy.ito(ureg.eV / ureg.particle) + energy = energy.magnitude + + atoms = Atoms(symbols, positions=positions, cell=cell, pbc=True) + atoms.calc = SinglePointCalculator(atoms, energy=energy, forces=forces) + atoms.info["h5md_time"] = (ts.time * ureg.picosecond).to(ureg.femtosecond).magnitude + + return atoms + + +def smiles_to_pdb( + smiles: str, + file: str, + identifier: str | None = None, + cwd: pathlib.Path = pathlib.Path(), +) -> Chem.Mol: + """Convert a SMILES string to a PDB file and return the RDKit molecule object.""" + m = Chem.MolFromSmiles(smiles) + m = Chem.AddHs(m) + AllChem.EmbedMolecule(m) + AllChem.UFFOptimizeMolecule(m) + pdb_block = Chem.MolToPDBBlock(m) + + if identifier is not None: + if identifier in pdb_block: + raise ValueError( + f"Can not use '{identifier}' because it is present in the file." + ) + if len(identifier) != 3: + raise ValueError("Identifier must be 3 characters long") + pdb_block = pdb_block.replace("UNL", identifier) + + working_directory = pathlib.Path(cwd) + + with (working_directory / file).open("w") as f: + f.write(pdb_block) + return m + + +def get_box(density: float, molecules: list[Chem.Mol], counts: list[int]) -> float: + """ + Compute the box size for a cubic box with the desired density and given molecules. + + Args: + density (float): Desired density in g/cm^3. + molecules (Chem.Mol): Variable number of RDKit molecule objects. + + Returns: + float: The side length of the cubic box in angstroms. + """ + total_mass = ( + sum( + rdMolDescriptors.CalcExactMolWt(mol) * count + for mol, count in zip(molecules, counts) + ) + * ureg.dalton + ) # in atomic mass units (amu) + total_mass_g = total_mass.to(ureg.gram) # convert amu to grams + density_g_per_cm3 = density * (ureg.gram / ureg.centimeter**3) + + volume_cm3 = total_mass_g / density_g_per_cm3 # volume in cm^3 + volume_angstrom3 = volume_cm3.to(ureg.angstrom**3) # convert cm^3 to angstrom^3 + side_length = volume_angstrom3 ** (1 / 3) # side length of the cubic box in angstroms + + return side_length.magnitude + + +def create_pack_script( + files: list[str], + counts: list[int], + box_size: float, + tolerance: float, + cwd: pathlib.Path = pathlib.Path(), +): + """ + Create a PACKMOL input script to pack molecules into a box. + + Attributes + ---------- + files: list[str] + List of file names for the molecules to pack. + counts: list[int] + Number of each molecule to pack. + box_size: float + Side length of the cubic box in angstroms. + """ + if len(files) != len(counts): + raise ValueError("The number of files must match the number of counts") + + box_size -= tolerance + + script = f"""tolerance {tolerance} +output box.pdb +filetype pdb +""" + + for file, count in zip(files, counts): + structure_block = f"""structure {file} + number {count} + inside box 0. 0. 0. {box_size} {box_size} {box_size} +end structure +""" + script += structure_block + + with (cwd / "packmol.inp").open("w") as f: + f.write(script) + + +def extract_atomtypes(input_file: pathlib.Path, output_file: pathlib.Path): + content = input_file.read_text() + + # Regular expression to match the [ atomtypes ] section + atomtypes_regex = re.compile(r"(\[ atomtypes \].*?)(?=\n\[|\Z)", re.DOTALL) + + # Find the atomtypes section + atomtypes_section = atomtypes_regex.search(content) + + if atomtypes_section: + atomtypes_section_text = atomtypes_section.group(0) + + # Write the atomtypes section to the output file + output_file.write_text(atomtypes_section_text) + + # Remove the atomtypes section from the original content + modified_content = atomtypes_regex.sub("", content) + + # Write the modified content back to the input file + input_file.write_text(modified_content) + + +def combine_atomtype_files(files: list[pathlib.Path], output_file: pathlib.Path): + """Read all the files and write a single output file. Removes duplicates.""" + header = [] + atomtypes = [] + for file in files: + with file.open("r") as f: + lines = f.readlines() + for idx, line in enumerate(lines): + if idx in [0, 1]: + if len(header) < 2: + header.append(line) + else: + atomtypes.append(line) + + atomtypes = list(set(atomtypes)) + with output_file.open("w") as f: + f.writelines(header) + f.writelines(atomtypes) + + +def validate_mdp(path): + necessary_keys = ["nstxout", "nstfout"] + path = pathlib.Path(path) + with path.open("r") as f: + content = f.read() + for key in necessary_keys: + if key not in content: + raise ValueError( + f"Key '{key}' is required in {path.name} for writing a trajectory" + ) + + +class Smiles2Gromacs(base.IPSNode): + """Gromacs Node. + + Attributes + ---------- + smiles: list[str] + List of SMILES strings for the molecules to be packed. + count: list[int] + Number of each molecule to pack. + labels: list[str] + List of 3-letter labels for each molecule. + density: float + Density of the packed box in g/cm^3. + mdp_files: list[str | pathlib.Path] + List of paths to the Gromacs MDP files. + itp_files: list[str | None]|None + if given, for each label either the path to the + ITP file or None. The order must match the order + of the labels. + pdb_files: list[str | pathlib.Path]|None + if given, for each label either the path to the + PDB file or None. The order must match the order + of the labels. + + Installation + ------------ + To install the required software, run the following commands: + + .. code-block:: bash + + conda install conda-forge::gromacs + conda install conda-forge::acpype + pip install MDAnalysis pyedr + + """ + + smiles: list[str] = zntrack.params() + count: list[int] = zntrack.params() + labels: list[str] = zntrack.params() + density: float = zntrack.params() + fudgeLJ: float = zntrack.params(1.0) + fudgeQQ: float = zntrack.params(1.0) + tolerance: float = zntrack.params(2.0) + cleanup: bool = zntrack.params(True) + + mdp_files: list[str | pathlib.Path] = zntrack.deps_path() + itp_files: list[str | None] = zntrack.deps_path(None) + pdb_files: list[str | pathlib.Path] = zntrack.deps_path(None) + + traj_file: list[Atoms] = zntrack.outs_path(zntrack.nwd / "structures.h5") + + output_dir: pathlib.Path = zntrack.outs_path(zntrack.nwd / "gromacs") + + def _post_init_(self): + if len(self.smiles) != len(self.count): + raise ValueError("The number of smiles must match the number of counts") + if len(self.smiles) != len(self.labels): + raise ValueError("The number of smiles must match the number of labels") + + if isinstance(self.output_dir, str): + self.output_dir = pathlib.Path(self.output_dir) + if self.output_dir.exists(): + shutil.rmtree(self.output_dir) + self.mdp_files = [pathlib.Path(mdp_file) for mdp_file in self.mdp_files] + + @property + def atoms(self): + with self.state.fs.open(self.traj_file, "rb") as f: + with h5py.File(f) as file: + return znh5md.IO(file_handle=file)[:] + + def _run_acpype(self): + for idx, (label, charge) in enumerate(zip(self.labels, self.charges)): + if self.itp_files is not None and self.itp_files[idx] is not None: + path = self.output_dir / f"{label}.acpype" + path.mkdir(exist_ok=True) + shutil.copy(self.itp_files[idx], path / f"{label}_GMX.itp") + else: + cmd = ["acpype", "-i", f"{label}.pdb", "-n", str(charge), "-b", label] + subprocess.run(cmd, check=True, cwd=self.output_dir) + + def _create_box_gro(self): + cmd = [ + "echo", + "0", + "|", + "gmx", + "editconf", + "-f", + self.box, + "-o", + "box.gro", + "-box", + str((self.box_size * ureg.angstrom).to(ureg.nanometer).magnitude), + ] + subprocess.run(" ".join(cmd), shell=True, check=True, cwd=self.output_dir) + + def _create_species_top_atomtypes(self): + for idx, label in enumerate(self.labels): + if self.itp_files is not None and self.itp_files[idx] is not None: + file = self.itp_files[idx] + else: + file = self.output_dir / f"{label}.acpype/{label}_GMX.itp" + shutil.copy(file, self.output_dir / f"{label}.itp") + # shutil.copy(file.with_suffix(".top"), self.output_dir / f"{label}.top") + extract_atomtypes( + self.output_dir / f"{label}.itp", + self.output_dir / f"{label}_atomtypes.itp", + ) + + combine_atomtype_files( + [self.output_dir / f"{label}_atomtypes.itp" for label in self.labels], + self.output_dir / "atomtypes.itp", + ) + + def _create_box_top(self): + with (self.output_dir / "box.top").open("w") as f: + f.write("[ defaults ]") + f.write("\n") + f.write("; nbfunc comb-rule gen-pairs fudgeLJ fudgeQQ\n") + f.write( + f"1 2 yes {self.fudgeLJ} " + f" {self.fudgeQQ}\n" + ) + f.write("\n") + f.write("; Include atomtypes\n") + f.write('#include "atomtypes.itp"\n') + f.write("\n") + f.write("; Include topology\n") + for label in self.labels: + f.write(f'#include "{label}.itp"\n') + + f.write("\n") + f.write("[ system ]\n") + f.write(" GMX\n") + + f.write("\n") + f.write("[ molecules ]\n") + for label, count in zip(self.labels, self.count): + f.write(f"{label} {count}\n") + + def _run_gmx(self): + for mdp_file in self.mdp_files: + cmd = [ + "gmx", + "grompp", + "-f", + mdp_file.resolve().as_posix(), + "-c", + "box.gro", + "-p", + "box.top", + "-o", + "box.tpr", + "-v", + ] + print(f"Running {' '.join(cmd)}") + subprocess.run(cmd, check=True, cwd=self.output_dir) + cmd = ["gmx", "mdrun", "-ntmpi", "1", "-v", "-deffnm", "box"] + subprocess.run(cmd, check=True, cwd=self.output_dir) + + def _pack_box(self): + mols = [] + charges = [] + for idx, (smiles, label) in enumerate(zip(self.smiles, self.labels)): + if self.pdb_files is not None and self.pdb_files[idx] is not None: + shutil.copy(self.pdb_files[idx], self.output_dir / f"{label}.pdb") + m = Chem.MolFromSmiles(smiles) + m = Chem.AddHs(m) + AllChem.EmbedMolecule(m) + AllChem.UFFOptimizeMolecule(m) + mols.append(m) + else: + mols.append( + smiles_to_pdb(smiles, f"{label}.pdb", label, cwd=self.output_dir) + ) + # get the charge of the molecule + charges.append(Chem.GetFormalCharge(mols[-1])) + self.charges = charges + self.box_size = get_box(self.density, mols, self.count) + create_pack_script( + [f"{label}.pdb" for label in self.labels], + self.count, + self.box_size, + self.tolerance, + cwd=self.output_dir, + ) + cmd = ["packmol < packmol.inp"] + subprocess.run(cmd, check=True, shell=True, cwd=self.output_dir) + self.box = "box.pdb" + + def _convert_trajectory(self): + gro = self.output_dir / "box.gro" + trr = self.output_dir / "box.trr" + edr = self.output_dir / "box.edr" + u = mda.Universe(gro, trr) + aux = mda.auxiliary.EDR.EDRReader(edr) + u.trajectory.add_auxiliary(auxdata=aux) + + images = [] + for ts in u.trajectory: + images.append(timestep_to_atoms(u, ts)) + + io = znh5md.IO(self.traj_file, store="time") + io.extend(images) + + def run(self): + self.output_dir.mkdir(exist_ok=True, parents=True) + validate_mdp(self.mdp_files[-1]) + + self._pack_box() + self._create_box_gro() + + self._run_acpype() + + self._create_species_top_atomtypes() + self._create_box_top() + self._run_gmx() + self._convert_trajectory() + + if self.cleanup: + paths = list(self.output_dir.iterdir()) + with (self.output_dir / "info.txt").open("w") as f: + f.write("The following data has been removed:\n") + f.write("\n".join([file.name for file in paths])) + for path in paths: + if path.is_file(): + path.unlink() + else: + shutil.rmtree(path) diff --git a/ipsuite/nodes.py b/ipsuite/nodes.py index 24d22d16..301487e5 100644 --- a/ipsuite/nodes.py +++ b/ipsuite/nodes.py @@ -38,6 +38,7 @@ class _Nodes: MultiPackmol = "ipsuite.configuration_generation.MultiPackmol" SmilesToAtoms = "ipsuite.configuration_generation.SmilesToAtoms" SmilesToConformers = "ipsuite.configuration_generation.SmilesToConformers" + Smiles2Gromacs = "ipsuite.configuration_generation.Smiles2Gromacs" # Data AddData = "ipsuite.data_loading.AddData" diff --git a/poetry.lock b/poetry.lock index 52af69e1..09d4cced 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2028,6 +2028,17 @@ files = [ numpy = ">=1.13" packaging = "*" +[[package]] +name = "fasteners" +version = "0.19" +description = "A python package that provides useful locks" +optional = false +python-versions = ">=3.6" +files = [ + {file = "fasteners-0.19-py3-none-any.whl", hash = "sha256:758819cb5d94cdedf4e836988b74de396ceacb8e2794d21f82d131fd9ee77237"}, + {file = "fasteners-0.19.tar.gz", hash = "sha256:b4f37c3ac52d8a445af3a66bce57b33b5e90b97c696b7b984f530cf8f0ded09c"}, +] + [[package]] name = "fastjsonschema" version = "2.20.0" @@ -2452,6 +2463,22 @@ pyparsing = "*" [package.extras] full = ["numpy", "ply"] +[[package]] +name = "griddataformats" +version = "1.0.2" +description = "Reading and writing of data on regular grids in Python" +optional = false +python-versions = ">=3.9" +files = [ + {file = "GridDataFormats-1.0.2-py3-none-any.whl", hash = "sha256:4562bb2da9a064a8e6640e5fb853e204c8f32f6403651703f6b0ef58a009054c"}, + {file = "GridDataFormats-1.0.2.tar.gz", hash = "sha256:b93cf7f36fce33dbc428026f26dba560d5c7ba2387caca495bad920f90094502"}, +] + +[package.dependencies] +mrcfile = "*" +numpy = ">=1.21" +scipy = "*" + [[package]] name = "grpcio" version = "1.65.1" @@ -3779,6 +3806,68 @@ scipy = ">=1.2.3" docs = ["atomman", "myst_nb", "nglview", "numpydoc", "ovito", "pydata-sphinx-theme", "sphinx", "sphinx_copybutton", "sphinx_rtd_theme"] test = ["atomman", "ovito", "pytest", "sympy"] +[[package]] +name = "mda-xdrlib" +version = "0.2.0" +description = "Stand-alone XDRLIB module (from cpython 3.10.8)" +optional = false +python-versions = ">=3.7" +files = [ + {file = "mda_xdrlib-0.2.0-py3-none-any.whl", hash = "sha256:0d1757b339f5db2d017a89ddaae06a82cd7a2cce26b8063df9f52b64e933fb47"}, + {file = "mda_xdrlib-0.2.0.tar.gz", hash = "sha256:f26f7158a83c32b96d15b530fce2cbc1190c4b7024e41faa4ab3e3db74e272af"}, +] + +[package.extras] +testing = ["pytest"] + +[[package]] +name = "mdanalysis" +version = "2.7.0" +description = "An object-oriented toolkit to analyze molecular dynamics trajectories." +optional = false +python-versions = ">=3.9" +files = [ + {file = "MDAnalysis-2.7.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:752a7e79f2195e284bd5c6c74510e7a587050aa15a5fd5d1f0b1c173238c7f9e"}, + {file = "MDAnalysis-2.7.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d9f5506b70614aa21974488c61b150dcf443c2ef50bacba0ff9c5369fdd1dec8"}, + {file = "MDAnalysis-2.7.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cd8d2d5318f5adaa2b4143c60f5006bb9f5ef294eea87bea5b35127cc8f30399"}, + {file = "MDAnalysis-2.7.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3f6924273c498c7134cfe985f2779ebf2f1d4204161c354ae8175779a20794e9"}, + {file = "MDAnalysis-2.7.0-cp310-cp310-win_amd64.whl", hash = "sha256:348372eacd74a6be0cd7e2af9a5d6f0aae6b86c16868937bd57f4636e4e6141e"}, + {file = "MDAnalysis-2.7.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:45a6ad90705ff50ddae7f0d7a2b3ddfd0185e3e8314930874b9fc3ac56853ec9"}, + {file = "MDAnalysis-2.7.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3be89d52e3442d58b65eef89626147d1e319a7912a913c7fccd78ba878418800"}, + {file = "MDAnalysis-2.7.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9358a88e4558febc4eca58dc69aeaf836b8eacc92556e0205c6c40497907e3ab"}, + {file = "MDAnalysis-2.7.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1fd4b24cfb5fc63e9ae758251af947702f1a033f9bb38e54e206453c6afd3db0"}, + {file = "MDAnalysis-2.7.0-cp311-cp311-win_amd64.whl", hash = "sha256:eea51334fb3cc45475003e992c63205f5c5ea34920154ab2cea9d44120b5904b"}, + {file = "MDAnalysis-2.7.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:486794d7e957e959f612e1f664878e0244594864c057bff0748eca6fef7d1523"}, + {file = "MDAnalysis-2.7.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:aaf7c158a65e032d15ef63cc08a6790d87ec3d7ee6037eb778d1959aec6c305a"}, + {file = "MDAnalysis-2.7.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5613c102d2ba4ac6ff2527ba9ea1b2564fdd90f3e06e921752bbcad82c6b173e"}, + {file = "MDAnalysis-2.7.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cf975db416007242e5674ff44803ce8e36313c5602f401244858e26e848b9411"}, + {file = "MDAnalysis-2.7.0-cp312-cp312-win_amd64.whl", hash = "sha256:3bcc653057e8ea6f35a32e9993d840ae919842fa0756a966966d6eedc1072ee5"}, + {file = "MDAnalysis-2.7.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c7dc2b9601126cdd19e2b39ee38fc359977f5ab949ae416fc2470b110fc2c75a"}, + {file = "MDAnalysis-2.7.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1bd57db448afd1b73f0ea8954f3f1fdf9033442d18cc9f2ef3f1c59930a24e0b"}, + {file = "MDAnalysis-2.7.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:81dfcd1f519fd288921eda7f97db0d1d287d55645c5cbe96031baf6209bfd2f5"}, + {file = "MDAnalysis-2.7.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:501a473d65ef6e296f50a8d246a77f31ca41752be1f7edc76f88e468d672a41d"}, + {file = "MDAnalysis-2.7.0-cp39-cp39-win_amd64.whl", hash = "sha256:345043a6365237a55d20f8dc0e31aa1d4e09e348ca8e1877dee6a2a6f6500c0b"}, + {file = "MDAnalysis-2.7.0.tar.gz", hash = "sha256:572e82945e5d058e3749ec5f18e6b3831ef7f2119cb54672567ae9a977201e93"}, +] + +[package.dependencies] +fasteners = "*" +GridDataFormats = ">=0.4.0" +joblib = ">=0.12" +matplotlib = ">=1.5.1" +mda-xdrlib = "*" +mmtf-python = ">=1.0.0" +numpy = ">=1.22.3,<2.0" +packaging = "*" +scipy = ">=1.5.0" +threadpoolctl = "*" +tqdm = ">=4.43.0" + +[package.extras] +analysis = ["biopython (>=1.80)", "networkx (>=2.0)", "scikit-learn", "seaborn", "tidynamics (>=1.0.0)"] +doc = ["mdanalysis-sphinx-theme (>=1.3.0)", "pybtex", "pybtex-docutils", "sphinx", "sphinx-sitemap", "sphinxcontrib-bibtex"] +extra-formats = ["chemfiles (>=0.10)", "gsd (>3.0.0)", "h5py (>=2.10)", "netCDF4 (>=1.0)", "parmed", "pyedr (>=0.7.0)", "pytng (>=0.2.3)", "rdkit (>=2020.03.1)"] + [[package]] name = "mir-allegro" version = "0.2.0" @@ -3859,6 +3948,24 @@ numpy = [ [package.extras] dev = ["absl-py", "pyink", "pylint (>=2.6.0)", "pytest", "pytest-xdist"] +[[package]] +name = "mmtf-python" +version = "1.1.3" +description = "A decoding libary for the PDB mmtf format" +optional = false +python-versions = "*" +files = [ + {file = "mmtf-python-1.1.3.tar.gz", hash = "sha256:12a02fe1b7131f0a2b8ce45b46f1e0cdd28b9818fe4499554c26884987ea0c32"}, + {file = "mmtf_python-1.1.3-py2.py3-none-any.whl", hash = "sha256:502031c509a8a6d73e042781abbd88b84c1afffe65097eb0c1b70f329ffd1e6e"}, +] + +[package.dependencies] +msgpack = ">=1.0.0" + +[package.extras] +dev = ["check-manifest"] +test = ["coverage"] + [[package]] name = "monty" version = "2024.7.12" @@ -3887,6 +3994,20 @@ docs = ["sphinx"] gmpy = ["gmpy2 (>=2.1.0a4)"] tests = ["pytest (>=4.6)"] +[[package]] +name = "mrcfile" +version = "1.5.3" +description = "MRC file I/O library" +optional = false +python-versions = "*" +files = [ + {file = "mrcfile-1.5.3-py2.py3-none-any.whl", hash = "sha256:fbf2b5583afae38656343f2d6bac67d85e0e798b2fd608be63ecd2758cd67c61"}, + {file = "mrcfile-1.5.3.tar.gz", hash = "sha256:3f304c02cb9f0900b26683679c5d3d750da64b5c370b58d69af8a8ddf720c0ce"}, +] + +[package.dependencies] +numpy = ">=1.16.0" + [[package]] name = "msgpack" version = "1.0.8" @@ -5010,22 +5131,22 @@ wcwidth = "*" [[package]] name = "protobuf" -version = "4.25.3" +version = "4.25.4" description = "" optional = false python-versions = ">=3.8" files = [ - {file = "protobuf-4.25.3-cp310-abi3-win32.whl", hash = "sha256:d4198877797a83cbfe9bffa3803602bbe1625dc30d8a097365dbc762e5790faa"}, - {file = "protobuf-4.25.3-cp310-abi3-win_amd64.whl", hash = "sha256:209ba4cc916bab46f64e56b85b090607a676f66b473e6b762e6f1d9d591eb2e8"}, - {file = "protobuf-4.25.3-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:f1279ab38ecbfae7e456a108c5c0681e4956d5b1090027c1de0f934dfdb4b35c"}, - {file = "protobuf-4.25.3-cp37-abi3-manylinux2014_aarch64.whl", hash = "sha256:e7cb0ae90dd83727f0c0718634ed56837bfeeee29a5f82a7514c03ee1364c019"}, - {file = "protobuf-4.25.3-cp37-abi3-manylinux2014_x86_64.whl", hash = "sha256:7c8daa26095f82482307bc717364e7c13f4f1c99659be82890dcfc215194554d"}, - {file = "protobuf-4.25.3-cp38-cp38-win32.whl", hash = "sha256:f4f118245c4a087776e0a8408be33cf09f6c547442c00395fbfb116fac2f8ac2"}, - {file = "protobuf-4.25.3-cp38-cp38-win_amd64.whl", hash = "sha256:c053062984e61144385022e53678fbded7aea14ebb3e0305ae3592fb219ccfa4"}, - {file = "protobuf-4.25.3-cp39-cp39-win32.whl", hash = "sha256:19b270aeaa0099f16d3ca02628546b8baefe2955bbe23224aaf856134eccf1e4"}, - {file = "protobuf-4.25.3-cp39-cp39-win_amd64.whl", hash = "sha256:e3c97a1555fd6388f857770ff8b9703083de6bf1f9274a002a332d65fbb56c8c"}, - {file = "protobuf-4.25.3-py3-none-any.whl", hash = "sha256:f0700d54bcf45424477e46a9f0944155b46fb0639d69728739c0e47bab83f2b9"}, - {file = "protobuf-4.25.3.tar.gz", hash = "sha256:25b5d0b42fd000320bd7830b349e3b696435f3b329810427a6bcce6a5492cc5c"}, + {file = "protobuf-4.25.4-cp310-abi3-win32.whl", hash = "sha256:db9fd45183e1a67722cafa5c1da3e85c6492a5383f127c86c4c4aa4845867dc4"}, + {file = "protobuf-4.25.4-cp310-abi3-win_amd64.whl", hash = "sha256:ba3d8504116a921af46499471c63a85260c1a5fc23333154a427a310e015d26d"}, + {file = "protobuf-4.25.4-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:eecd41bfc0e4b1bd3fa7909ed93dd14dd5567b98c941d6c1ad08fdcab3d6884b"}, + {file = "protobuf-4.25.4-cp37-abi3-manylinux2014_aarch64.whl", hash = "sha256:4c8a70fdcb995dcf6c8966cfa3a29101916f7225e9afe3ced4395359955d3835"}, + {file = "protobuf-4.25.4-cp37-abi3-manylinux2014_x86_64.whl", hash = "sha256:3319e073562e2515c6ddc643eb92ce20809f5d8f10fead3332f71c63be6a7040"}, + {file = "protobuf-4.25.4-cp38-cp38-win32.whl", hash = "sha256:7e372cbbda66a63ebca18f8ffaa6948455dfecc4e9c1029312f6c2edcd86c4e1"}, + {file = "protobuf-4.25.4-cp38-cp38-win_amd64.whl", hash = "sha256:051e97ce9fa6067a4546e75cb14f90cf0232dcb3e3d508c448b8d0e4265b61c1"}, + {file = "protobuf-4.25.4-cp39-cp39-win32.whl", hash = "sha256:90bf6fd378494eb698805bbbe7afe6c5d12c8e17fca817a646cd6a1818c696ca"}, + {file = "protobuf-4.25.4-cp39-cp39-win_amd64.whl", hash = "sha256:ac79a48d6b99dfed2729ccccee547b34a1d3d63289c71cef056653a846a2240f"}, + {file = "protobuf-4.25.4-py3-none-any.whl", hash = "sha256:bfbebc1c8e4793cfd58589acfb8a1026be0003e852b9da7db5a4285bde996978"}, + {file = "protobuf-4.25.4.tar.gz", hash = "sha256:0dc4a62cc4052a036ee2204d26fe4d835c62827c855c8a03f29fe6da146b380d"}, ] [[package]] @@ -8044,13 +8165,13 @@ dask = ["bokeh (>=2.4.2,<3.0.0)", "dask (>=2022.12.1,<2023.0.0)", "dask-jobqueue [[package]] name = "znh5md" -version = "0.3.1" +version = "0.3.2" description = "ASE Interface for the H5MD format." optional = false python-versions = "<4.0,>=3.10" files = [ - {file = "znh5md-0.3.1-py3-none-any.whl", hash = "sha256:f3000f5478fc5625d86b710fa2a96221038ee367d14f2989285054f0fe3808ba"}, - {file = "znh5md-0.3.1.tar.gz", hash = "sha256:1b0ebd26c325b195eeb709d4bd31b38d24a025c650e7b33c7acc39cab2fd47b3"}, + {file = "znh5md-0.3.2-py3-none-any.whl", hash = "sha256:79e06e191a560bc45cd63a248f5095f9dff5021f75e31546b2c238f47a5e089e"}, + {file = "znh5md-0.3.2.tar.gz", hash = "sha256:ef37e2ff0ad0e4a7e0d19c8e5742c78ad1a7100a05a3f1e3a7927a17691e86cc"}, ] [package.dependencies] @@ -8120,4 +8241,4 @@ nequip = [] [metadata] lock-version = "2.0" python-versions = ">=3.10,<3.12" -content-hash = "3132165cf287f77d94c5b898bcbc7c78234e9fd8a4f8ab02e98c4d7fab2b0e03" +content-hash = "3a527395fd88f96e0da6bf5e84d32898a27d43737851c381f2040bffdb790c14" diff --git a/pyproject.toml b/pyproject.toml index 37096343..ad685da6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -15,7 +15,7 @@ repository = "https://github.com/zincware/ipsuite" # tensorflow-io-gcs-filesystem python = ">=3.10,<3.12" -znh5md = "^0.3.1" +znh5md = "^0.3.2" ase = "^3.23" seaborn = "^0.12.2" @@ -54,6 +54,7 @@ pytest = "^7.2.1" coverage = "^7.2.1" imageio = "^2.28.1" dvc-s3 = "^3.0.1" +mdanalysis = "^2.7.0" [tool.poetry.group.notebook.dependencies] jupyterlab = "^3.6.1" @@ -88,12 +89,6 @@ preview = true [tool.ruff] -# Enable pycodestyle (`E`) and Pyflakes (`F`) codes by default. -select = ["A", "B", "C", "E", "F", "I", "C90", "C4", "TID", "PTH"] # "D", "S" -# ignore = ["D203", "D213", "D104", "D102", "D100", ] -ignore = [ - "B905", # not supported in older python versions -] # Exclude a variety of commonly ignored directories. exclude = [ @@ -119,7 +114,6 @@ exclude = [ "venv", "tests", ] -per-file-ignores = { } # Same as Black. line-length = 90 @@ -127,6 +121,14 @@ line-length = 90 # Assume Python 3.10. target-version = "py310" +[tool.ruff.lint] +# Enable pycodestyle (`E`) and Pyflakes (`F`) codes by default. +select = ["A", "B", "C", "E", "F", "I", "C90", "C4", "TID", "PTH"] # "D", "S" +# ignore = ["D203", "D213", "D104", "D102", "D100", ] +ignore = [ + "B905", # not supported in older python versions +] + [tool.codespell] ignore-words-list = "dscribe" skip = "poetry.lock,ipsuite/static_data/*,docs/source/examples/06_Bootstrapping_Datasets.ipynb"