diff --git a/package/MDAnalysis/topology/CRDParser.py b/package/MDAnalysis/topology/CRDParser.py index d1896423a8..c0322824ba 100644 --- a/package/MDAnalysis/topology/CRDParser.py +++ b/package/MDAnalysis/topology/CRDParser.py @@ -89,7 +89,8 @@ class CRDParser(TopologyReaderBase): Type and mass are not longer guessed here. Until 3.0 these will still be set by default through through universe.guess_TopologyAttrs() API. """ - format = 'CRD' + + format = "CRD" def parse(self, **kwargs): """Create the Topology object @@ -102,8 +103,10 @@ def parse(self, **kwargs): ---- Could use the resnum and temp factor better """ - extformat = FORTRANReader('2I10,2X,A8,2X,A8,3F20.10,2X,A8,2X,A8,F20.10') - stdformat = FORTRANReader('2I5,1X,A4,1X,A4,3F10.5,1X,A4,1X,A4,F10.5') + extformat = FORTRANReader( + "2I10,2X,A8,2X,A8,3F20.10,2X,A8,2X,A8,F20.10" + ) + stdformat = FORTRANReader("2I5,1X,A4,1X,A4,3F10.5,1X,A4,1X,A4,F10.5") atomids = [] atomnames = [] @@ -116,21 +119,36 @@ def parse(self, **kwargs): with openany(self.filename) as crd: for linenum, line in enumerate(crd): # reading header - if line.split()[0] == '*': + if line.split()[0] == "*": continue - elif line.split()[-1] == 'EXT' and int(line.split()[0]): + elif line.split()[-1] == "EXT" and int(line.split()[0]): r = extformat continue - elif line.split()[0] == line.split()[-1] and line.split()[0] != '*': + elif ( + line.split()[0] == line.split()[-1] + and line.split()[0] != "*" + ): r = stdformat continue # anything else should be an atom try: - (serial, resnum, resName, name, - x, y, z, segid, resid, tempFactor) = r.read(line) + ( + serial, + resnum, + resName, + name, + x, + y, + z, + segid, + resid, + tempFactor, + ) = r.read(line) except Exception: - errmsg = (f"Check CRD format at line {linenum + 1}: " - f"{line.rstrip()}") + errmsg = ( + f"Check CRD format at line {linenum + 1}: " + f"{line.rstrip()}" + ) raise ValueError(errmsg) from None atomids.append(serial) @@ -150,22 +168,28 @@ def parse(self, **kwargs): resnums = np.array(resnums, dtype=np.int32) segids = np.array(segids, dtype=object) - atom_residx, (res_resids, res_resnames, res_resnums, res_segids) = change_squash( - (resids, resnames), (resids, resnames, resnums, segids)) - res_segidx, (seg_segids,) = change_squash( - (res_segids,), (res_segids,)) - - top = Topology(len(atomids), len(res_resids), len(seg_segids), - attrs=[ - Atomids(atomids), - Atomnames(atomnames), - Tempfactors(tempfactors), - Resids(res_resids), - Resnames(res_resnames), - Resnums(res_resnums), - Segids(seg_segids), - ], - atom_resindex=atom_residx, - residue_segindex=res_segidx) + atom_residx, (res_resids, res_resnames, res_resnums, res_segids) = ( + change_squash( + (resids, resnames), (resids, resnames, resnums, segids) + ) + ) + res_segidx, (seg_segids,) = change_squash((res_segids,), (res_segids,)) + + top = Topology( + len(atomids), + len(res_resids), + len(seg_segids), + attrs=[ + Atomids(atomids), + Atomnames(atomnames), + Tempfactors(tempfactors), + Resids(res_resids), + Resnames(res_resnames), + Resnums(res_resnums), + Segids(seg_segids), + ], + atom_resindex=atom_residx, + residue_segindex=res_segidx, + ) return top diff --git a/package/MDAnalysis/topology/DLPolyParser.py b/package/MDAnalysis/topology/DLPolyParser.py index 5452dbad3c..6c03ed62c2 100644 --- a/package/MDAnalysis/topology/DLPolyParser.py +++ b/package/MDAnalysis/topology/DLPolyParser.py @@ -69,7 +69,8 @@ class ConfigParser(TopologyReaderBase): Removed type and mass guessing (attributes guessing takes place now through universe.guess_TopologyAttrs() API). """ - format = 'CONFIG' + + format = "CONFIG" def parse(self, **kwargs): with openany(self.filename) as inf: @@ -117,10 +118,9 @@ def parse(self, **kwargs): Atomids(ids), Resids(np.array([1])), Resnums(np.array([1])), - Segids(np.array(['SYSTEM'], dtype=object)), + Segids(np.array(["SYSTEM"], dtype=object)), ] - top = Topology(n_atoms, 1, 1, - attrs=attrs) + top = Topology(n_atoms, 1, 1, attrs=attrs) return top @@ -130,7 +130,8 @@ class HistoryParser(TopologyReaderBase): .. versionadded:: 0.10.1 """ - format = 'HISTORY' + + format = "HISTORY" def parse(self, **kwargs): with openany(self.filename) as inf: @@ -143,10 +144,10 @@ def parse(self, **kwargs): line = inf.readline() while not (len(line.split()) == 4 or len(line.split()) == 5): line = inf.readline() - if line == '': + if line == "": raise EOFError("End of file reached when reading HISTORY.") - while line and not line.startswith('timestep'): + while line and not line.startswith("timestep"): name = line[:8].strip() names.append(name) try: @@ -179,9 +180,8 @@ def parse(self, **kwargs): Atomids(ids), Resids(np.array([1])), Resnums(np.array([1])), - Segids(np.array(['SYSTEM'], dtype=object)), + Segids(np.array(["SYSTEM"], dtype=object)), ] - top = Topology(n_atoms, 1, 1, - attrs=attrs) + top = Topology(n_atoms, 1, 1, attrs=attrs) return top diff --git a/package/MDAnalysis/topology/DMSParser.py b/package/MDAnalysis/topology/DMSParser.py index 84c04fd2ac..691b47eed6 100644 --- a/package/MDAnalysis/topology/DMSParser.py +++ b/package/MDAnalysis/topology/DMSParser.py @@ -63,8 +63,9 @@ class Atomnums(AtomAttr): """The number for each Atom""" - attrname = 'atomnums' - singular = 'atomnum' + + attrname = "atomnums" + singular = "atomnum" class DMSParser(TopologyReaderBase): @@ -100,7 +101,8 @@ class DMSParser(TopologyReaderBase): through universe.guess_TopologyAttrs() API). """ - format = 'DMS' + + format = "DMS" def parse(self, **kwargs): """Parse DMS file *filename* and return the Topology object""" @@ -121,28 +123,29 @@ def dict_factory(cursor, row): attrs = {} # Row factories for different data types - facs = {np.int32: lambda c, r: r[0], - np.float32: lambda c, r: r[0], - object: lambda c, r: str(r[0].strip())} + facs = { + np.int32: lambda c, r: r[0], + np.float32: lambda c, r: r[0], + object: lambda c, r: str(r[0].strip()), + } with sqlite3.connect(self.filename) as con: # Selecting single column, so just strip tuple for attrname, dt in [ - ('id', np.int32), - ('anum', np.int32), - ('mass', np.float32), - ('charge', np.float32), - ('name', object), - ('resname', object), - ('resid', np.int32), - ('chain', object), - ('segid', object), + ("id", np.int32), + ("anum", np.int32), + ("mass", np.float32), + ("charge", np.float32), + ("name", object), + ("resname", object), + ("resid", np.int32), + ("chain", object), + ("segid", object), ]: try: cur = con.cursor() cur.row_factory = facs[dt] - cur.execute('SELECT {} FROM particle' - ''.format(attrname)) + cur.execute("SELECT {} FROM particle" "".format(attrname)) vals = cur.fetchall() except sqlite3.DatabaseError: errmsg = "Failed reading the atoms from DMS Database" @@ -152,7 +155,7 @@ def dict_factory(cursor, row): try: cur.row_factory = dict_factory - cur.execute('SELECT * FROM bond') + cur.execute("SELECT * FROM bond") bonds = cur.fetchall() except sqlite3.DatabaseError: errmsg = "Failed reading the bonds from DMS Database" @@ -161,35 +164,36 @@ def dict_factory(cursor, row): bondlist = [] bondorder = {} for b in bonds: - desc = tuple(sorted([b['p0'], b['p1']])) + desc = tuple(sorted([b["p0"], b["p1"]])) bondlist.append(desc) - bondorder[desc] = b['order'] - attrs['bond'] = bondlist - attrs['bondorder'] = bondorder + bondorder[desc] = b["order"] + attrs["bond"] = bondlist + attrs["bondorder"] = bondorder topattrs = [] # Bundle in Atom level objects for attr, cls in [ - ('id', Atomids), - ('anum', Atomnums), - ('mass', Masses), - ('charge', Charges), - ('name', Atomnames), - ('chain', ChainIDs), + ("id", Atomids), + ("anum", Atomnums), + ("mass", Masses), + ("charge", Charges), + ("name", Atomnames), + ("chain", ChainIDs), ]: topattrs.append(cls(attrs[attr])) # Residues - atom_residx, (res_resids, - res_resnums, - res_resnames, - res_segids) = change_squash( - (attrs['resid'], attrs['resname'], attrs['segid']), - (attrs['resid'], - attrs['resid'].copy(), - attrs['resname'], - attrs['segid']), + atom_residx, (res_resids, res_resnums, res_resnames, res_segids) = ( + change_squash( + (attrs["resid"], attrs["resname"], attrs["segid"]), + ( + attrs["resid"], + attrs["resid"].copy(), + attrs["resname"], + attrs["segid"], + ), ) + ) n_residues = len(res_resids) topattrs.append(Resids(res_resids)) @@ -197,8 +201,9 @@ def dict_factory(cursor, row): topattrs.append(Resnames(res_resnames)) if any(res_segids) and not any(val is None for val in res_segids): - res_segidx, (res_segids,) = change_squash((res_segids,), - (res_segids,)) + res_segidx, (res_segids,) = change_squash( + (res_segids,), (res_segids,) + ) uniq_seg = np.unique(res_segids) idx2seg = {idx: res_segids[idx] for idx in res_segidx} @@ -211,14 +216,18 @@ def dict_factory(cursor, row): topattrs.append(Segids(res_segids)) else: n_segments = 1 - topattrs.append(Segids(np.array(['SYSTEM'], dtype=object))) + topattrs.append(Segids(np.array(["SYSTEM"], dtype=object))) res_segidx = None - topattrs.append(Bonds(attrs['bond'])) + topattrs.append(Bonds(attrs["bond"])) - top = Topology(len(attrs['id']), n_residues, n_segments, - attrs=topattrs, - atom_resindex=atom_residx, - residue_segindex=res_segidx) + top = Topology( + len(attrs["id"]), + n_residues, + n_segments, + attrs=topattrs, + atom_resindex=atom_residx, + residue_segindex=res_segidx, + ) return top diff --git a/package/MDAnalysis/topology/ExtendedPDBParser.py b/package/MDAnalysis/topology/ExtendedPDBParser.py index ef4f25fee4..e1d7679198 100644 --- a/package/MDAnalysis/topology/ExtendedPDBParser.py +++ b/package/MDAnalysis/topology/ExtendedPDBParser.py @@ -60,35 +60,36 @@ class ExtendedPDBParser(PDBParser.PDBParser): """Parser that handles non-standard "extended" PDB file. - Extended PDB files (MDAnalysis format specifier *XPDB*) may contain residue - sequence numbers up to 99,999 by utilizing the insertion character field of - the PDB standard. - - Creates a Topology with the following Attributes (if present): - - serials - - names - - altLocs - - chainids - - tempfactors - - occupancies - - resids - - resnames - - segids - - elements - - bonds - - formalcharges - - .. note:: - - By default, atomtypes and masses will be guessed on Universe creation. - This may change in release 3.0. - See :ref:`Guessers` for more information. - - - See Also - -------- - :class:`MDAnalysis.coordinates.PDB.ExtendedPDBReader` - - .. versionadded:: 0.8 + Extended PDB files (MDAnalysis format specifier *XPDB*) may contain residue + sequence numbers up to 99,999 by utilizing the insertion character field of + the PDB standard. + + Creates a Topology with the following Attributes (if present): + - serials + - names + - altLocs + - chainids + - tempfactors + - occupancies + - resids + - resnames + - segids + - elements + - bonds + - formalcharges + + .. note:: + + By default, atomtypes and masses will be guessed on Universe creation. + This may change in release 3.0. + See :ref:`Guessers` for more information. + + + See Also + -------- + :class:`MDAnalysis.coordinates.PDB.ExtendedPDBReader` + + .. versionadded:: 0.8 """ - format = 'XPDB' + + format = "XPDB" diff --git a/package/MDAnalysis/topology/FHIAIMSParser.py b/package/MDAnalysis/topology/FHIAIMSParser.py index a47d367ec9..c3aa80aced 100644 --- a/package/MDAnalysis/topology/FHIAIMSParser.py +++ b/package/MDAnalysis/topology/FHIAIMSParser.py @@ -77,7 +77,8 @@ class FHIAIMSParser(TopologyReaderBase): Removed type and mass guessing (attributes guessing takes place now through universe.guess_TopologyAttrs() API). """ - format = ['IN', 'FHIAIMS'] + + format = ["IN", "FHIAIMS"] def parse(self, **kwargs): """Read the file and return the structure. @@ -99,18 +100,22 @@ def parse(self, **kwargs): continue # we are now seeing something that's neither atom nor lattice raise ValueError( - 'Non-conforming line: ({0})in FHI-AIMS input file {0}'.format(line, self.filename)) + "Non-conforming line: ({0})in FHI-AIMS input file {0}".format( + line, self.filename + ) + ) names = np.asarray(names) natoms = len(names) - attrs = [Atomnames(names), - Atomids(np.arange(natoms) + 1), - Resids(np.array([1])), - Resnums(np.array([1])), - Segids(np.array(['SYSTEM'], dtype=object)), - Elements(names)] + attrs = [ + Atomnames(names), + Atomids(np.arange(natoms) + 1), + Resids(np.array([1])), + Resnums(np.array([1])), + Segids(np.array(["SYSTEM"], dtype=object)), + Elements(names), + ] - top = Topology(natoms, 1, 1, - attrs=attrs) + top = Topology(natoms, 1, 1, attrs=attrs) return top diff --git a/package/MDAnalysis/topology/GMSParser.py b/package/MDAnalysis/topology/GMSParser.py index 2ea7fe2300..41e85f73ec 100644 --- a/package/MDAnalysis/topology/GMSParser.py +++ b/package/MDAnalysis/topology/GMSParser.py @@ -60,10 +60,11 @@ AtomAttr, ) + class AtomicCharges(AtomAttr): - attrname = 'atomiccharges' - singular = 'atomiccharge' - per_object = 'atom' + attrname = "atomiccharges" + singular = "atomiccharge" + per_object = "atom" class GMSParser(TopologyReaderBase): @@ -86,7 +87,8 @@ class GMSParser(TopologyReaderBase): through universe.guess_TopologyAttrs() API). """ - format = 'GMS' + + format = "GMS" def parse(self, **kwargs): """Read list of atoms from a GAMESS file.""" @@ -98,16 +100,18 @@ def parse(self, **kwargs): line = inf.readline() if not line: raise EOFError - if re.match(r'^\s+ATOM\s+ATOMIC\s+COORDINATES\s*\(BOHR\).*',\ - line): + if re.match( + r"^\s+ATOM\s+ATOMIC\s+COORDINATES\s*\(BOHR\).*", line + ): break - line = inf.readline() # skip + line = inf.readline() # skip while True: line = inf.readline() - _m = re.match(\ -r'^\s*([A-Za-z_][A-Za-z_0-9]*)\s+([0-9]+\.[0-9]+)\s+(\-?[0-9]+\.[0-9]+)\s+(\-?[0-9]+\.[0-9]+)\s+(\-?[0-9]+\.[0-9]+).*', - line) + _m = re.match( + r"^\s*([A-Za-z_][A-Za-z_0-9]*)\s+([0-9]+\.[0-9]+)\s+(\-?[0-9]+\.[0-9]+)\s+(\-?[0-9]+\.[0-9]+)\s+(\-?[0-9]+\.[0-9]+).*", + line, + ) if _m is None: break name = _m.group(1) @@ -115,7 +119,7 @@ def parse(self, **kwargs): names.append(name) at_charges.append(at_charge) - #TODO: may be use coordinates info from _m.group(3-5) ?? + # TODO: may be use coordinates info from _m.group(3-5) ?? n_atoms = len(names) attrs = [ @@ -124,9 +128,8 @@ def parse(self, **kwargs): AtomicCharges(np.array(at_charges, dtype=np.int32)), Resids(np.array([1])), Resnums(np.array([1])), - Segids(np.array(['SYSTEM'], dtype=object)), + Segids(np.array(["SYSTEM"], dtype=object)), ] - top = Topology(n_atoms, 1, 1, - attrs=attrs) + top = Topology(n_atoms, 1, 1, attrs=attrs) return top diff --git a/package/MDAnalysis/topology/GROParser.py b/package/MDAnalysis/topology/GROParser.py index 368b7d5daf..dab8ef691c 100644 --- a/package/MDAnalysis/topology/GROParser.py +++ b/package/MDAnalysis/topology/GROParser.py @@ -78,7 +78,8 @@ class GROParser(TopologyReaderBase): through universe.guess_TopologyAttrs() API). """ - format = 'GRO' + + format = "GRO" def parse(self, **kwargs): """Return the *Topology* object for this file""" @@ -104,14 +105,16 @@ def parse(self, **kwargs): indices[i] = int(line[15:20]) except (ValueError, TypeError): errmsg = ( - f"Couldn't read the following line of the .gro file:\n" - f"{line}") + f"Couldn't read the following line of the .gro file:\n" + f"{line}" + ) raise IOError(errmsg) from None # Check all lines had names if not np.all(names): - missing = np.where(names == '') - raise IOError("Missing atom name on line: {0}" - "".format(missing[0][0] + 3)) # 2 header, 1 based + missing = np.where(names == "") + raise IOError( + "Missing atom name on line: {0}" "".format(missing[0][0] + 3) + ) # 2 header, 1 based # Fix wrapping of resids (if we ever saw a wrap) if np.any(resids == 0): @@ -132,9 +135,9 @@ def parse(self, **kwargs): for s in starts: resids[s:] += 100000 - residx, (new_resids, new_resnames) = change_squash( - (resids, resnames), (resids, resnames)) + (resids, resnames), (resids, resnames) + ) # new_resids is len(residues) # so resindex 0 has resid new_resids[0] @@ -144,12 +147,16 @@ def parse(self, **kwargs): Resids(new_resids), Resnums(new_resids.copy()), Resnames(new_resnames), - Segids(np.array(['SYSTEM'], dtype=object)) + Segids(np.array(["SYSTEM"], dtype=object)), ] - top = Topology(n_atoms=n_atoms, n_res=len(new_resids), n_seg=1, - attrs=attrs, - atom_resindex=residx, - residue_segindex=None) + top = Topology( + n_atoms=n_atoms, + n_res=len(new_resids), + n_seg=1, + attrs=attrs, + atom_resindex=residx, + residue_segindex=None, + ) return top diff --git a/package/MDAnalysis/topology/GSDParser.py b/package/MDAnalysis/topology/GSDParser.py index 64746dd87e..45f7e570b0 100644 --- a/package/MDAnalysis/topology/GSDParser.py +++ b/package/MDAnalysis/topology/GSDParser.py @@ -104,13 +104,16 @@ class GSDParser(TopologyReaderBase): GSD file. """ - format = 'GSD' + + format = "GSD" def __init__(self, filename): if not HAS_GSD: - errmsg = ("GSDParser: To read a Topology from a Hoomd GSD " - "file, please install gsd") + errmsg = ( + "GSDParser: To read a Topology from a Hoomd GSD " + "file, please install gsd" + ) raise ImportError(errmsg) super(GSDParser, self).__init__(filename) @@ -121,35 +124,39 @@ def parse(self, **kwargs): """ attrs = {} - with gsd.hoomd.open(self.filename, mode='r') as t : + with gsd.hoomd.open(self.filename, mode="r") as t: # Here it is assumed that the particle data does not change in the # trajectory. snap = t[0] natoms = snap.particles.N - ptypes = snap.particles.types atypes = [ptypes[idx] for idx in snap.particles.typeid] if len(atypes) != natoms: raise IOError("Number of types does not equal natoms.") - attrs['types'] = Atomtypes(np.array(atypes, dtype=object)) + attrs["types"] = Atomtypes(np.array(atypes, dtype=object)) # set radii, masses, charges p = snap.particles - attrs['diameter'] = Radii(np.array(p.diameter / 2.,dtype=np.float32)) - attrs['mass'] = Masses(np.array(p.mass,dtype=np.float64)) - attrs['charge'] = Charges(np.array(p.charge,dtype=np.float32)) + attrs["diameter"] = Radii( + np.array(p.diameter / 2.0, dtype=np.float32) + ) + attrs["mass"] = Masses(np.array(p.mass, dtype=np.float64)) + attrs["charge"] = Charges(np.array(p.charge, dtype=np.float32)) # set bonds, angles, dihedrals, impropers - for attrname, attr, in ( - ('bonds', Bonds), - ('angles', Angles), - ('dihedrals', Dihedrals), - ('impropers', Impropers), + for ( + attrname, + attr, + ) in ( + ("bonds", Bonds), + ("angles", Angles), + ("dihedrals", Dihedrals), + ("impropers", Impropers), ): try: - val = getattr(snap,attrname) + val = getattr(snap, attrname) vals = [tuple(b_instance) for b_instance in val.group] except: vals = [] @@ -160,7 +167,7 @@ def parse(self, **kwargs): bodies = np.unique(blist).astype(np.int32) # this fixes the fact that the Topology constructor gets stuck in an # infinite loop if any resid is negative. - if (blist<0).any() : + if (blist < 0).any(): m = blist.min() blist += abs(m) bodies = np.unique(blist).astype(np.int32) @@ -172,9 +179,8 @@ def parse(self, **kwargs): attrs.append(Resids(bodies)) attrs.append(Resnums(bodies)) attrs.append(Resnames(bodies)) - attrs.append(Segids(np.array(['SYSTEM'], dtype=object))) + attrs.append(Segids(np.array(["SYSTEM"], dtype=object))) - top = Topology(natoms, nbodies, 1, - attrs=attrs, atom_resindex=blist) + top = Topology(natoms, nbodies, 1, attrs=attrs, atom_resindex=blist) return top diff --git a/package/MDAnalysis/topology/HoomdXMLParser.py b/package/MDAnalysis/topology/HoomdXMLParser.py index b64e91de2f..7b57bb5900 100644 --- a/package/MDAnalysis/topology/HoomdXMLParser.py +++ b/package/MDAnalysis/topology/HoomdXMLParser.py @@ -81,7 +81,8 @@ class HoomdXMLParser(TopologyReaderBase): - Masses """ - format = 'XML' + + format = "XML" def parse(self, **kwargs): """Parse Hoomd XML file @@ -102,21 +103,21 @@ def parse(self, **kwargs): with openany(self.filename) as stream: tree = ET.parse(stream) root = tree.getroot() - configuration = root.find('configuration') - natoms = int(configuration.get('natoms')) + configuration = root.find("configuration") + natoms = int(configuration.get("natoms")) attrs = {} - atype = configuration.find('type') - atypes = atype.text.strip().split('\n') + atype = configuration.find("type") + atypes = atype.text.strip().split("\n") if len(atypes) != natoms: raise IOError("Number of types does not equal natoms.") - attrs['types'] = Atomtypes(np.array(atypes, dtype=object)) + attrs["types"] = Atomtypes(np.array(atypes, dtype=object)) for attrname, attr, mapper, dtype in ( - ('diameter', Radii, lambda x: float(x) / 2., np.float32), - ('mass', Masses, float, np.float64), - ('charge', Charges, float, np.float32), + ("diameter", Radii, lambda x: float(x) / 2.0, np.float32), + ("mass", Masses, float, np.float64), + ("charge", Charges, float, np.float32), ): try: val = configuration.find(attrname) @@ -125,34 +126,38 @@ def parse(self, **kwargs): pass else: attrs[attrname] = attr(np.array(vals, dtype=dtype)) - for attrname, attr, in ( - ('bond', Bonds), - ('angle', Angles), - ('dihedral', Dihedrals), - ('improper', Impropers), + for ( + attrname, + attr, + ) in ( + ("bond", Bonds), + ("angle", Angles), + ("dihedral", Dihedrals), + ("improper", Impropers), ): try: val = configuration.find(attrname) - vals = [tuple(int(el) for el in line.split()[1:]) - for line in val.text.strip().split('\n') - if line.strip()] + vals = [ + tuple(int(el) for el in line.split()[1:]) + for line in val.text.strip().split("\n") + if line.strip() + ] except: vals = [] attrs[attrname] = attr(vals) - if 'mass' not in attrs: - attrs['mass'] = Masses(np.zeros(natoms)) - if 'charge' not in attrs: - attrs['charge'] = Charges(np.zeros(natoms, dtype=np.float32)) + if "mass" not in attrs: + attrs["mass"] = Masses(np.zeros(natoms)) + if "charge" not in attrs: + attrs["charge"] = Charges(np.zeros(natoms, dtype=np.float32)) attrs = list(attrs.values()) attrs.append(Atomids(np.arange(natoms) + 1)) attrs.append(Resids(np.array([1]))) attrs.append(Resnums(np.array([1]))) - attrs.append(Segids(np.array(['SYSTEM'], dtype=object))) + attrs.append(Segids(np.array(["SYSTEM"], dtype=object))) - top = Topology(natoms, 1, 1, - attrs=attrs) + top = Topology(natoms, 1, 1, attrs=attrs) return top diff --git a/package/MDAnalysis/topology/ITPParser.py b/package/MDAnalysis/topology/ITPParser.py index 9968f854df..1aa87b3864 100644 --- a/package/MDAnalysis/topology/ITPParser.py +++ b/package/MDAnalysis/topology/ITPParser.py @@ -162,8 +162,9 @@ class Chargegroups(AtomAttr): """The charge group for each Atom""" - attrname = 'chargegroups' - singular = 'chargegroup' + + attrname = "chargegroups" + singular = "chargegroup" class GmxTopIterator: @@ -197,17 +198,17 @@ def iter_from_file(self, path): self.file_stack.append(infile) for line in self.clean_file_lines(infile): - if line.startswith('#include'): + if line.startswith("#include"): inc = line.split(None, 1)[1][1:-1] for line in self.iter_from_file(inc): yield line - elif line.startswith('#define'): + elif line.startswith("#define"): self.define(line) - elif line.startswith('#if'): + elif line.startswith("#if"): self.do_if(line, infile) - elif line.startswith('#else'): + elif line.startswith("#else"): self.skip_until_endif(infile) - elif line.startswith('#'): # ignore #if and others + elif line.startswith("#"): # ignore #if and others pass elif line: line = self.substitute_defined(line) @@ -230,42 +231,42 @@ def substitute_defined(self, line): for k, v in self.defines.items(): if k in split: split[split.index(k)] = str(v) - line = ' '.join(split) + line = " ".join(split) return line def clean_file_lines(self, infile): for line in infile: - line = line.split(';')[0].strip() # ; is for comments + line = line.split(";")[0].strip() # ; is for comments yield line def do_if(self, line, infile): ifdef, variable = line.split() - if ifdef == '#ifdef': + if ifdef == "#ifdef": if self.defines.get(variable) in (False, None): self.skip_until_else(infile) - elif ifdef == '#ifndef': + elif ifdef == "#ifndef": if self.defines.get(variable) not in (False, None): self.skip_until_else(infile) def skip_until_else(self, infile): """Skip lines until #if condition ends""" for line in self.clean_file_lines(infile): - if line.startswith('#if'): + if line.startswith("#if"): self.skip_until_endif(infile) - elif line.startswith('#endif') or line.startswith('#else'): + elif line.startswith("#endif") or line.startswith("#else"): break else: - raise IOError('Missing #endif in {}'.format(self.current_file)) + raise IOError("Missing #endif in {}".format(self.current_file)) def skip_until_endif(self, infile): """Skip lines until #endif""" for line in self.clean_file_lines(infile): - if line.startswith('#if'): + if line.startswith("#if"): self.skip_until_endif(infile) - elif line.startswith('#endif'): + elif line.startswith("#endif"): break else: - raise IOError('Missing #endif in {}'.format(self.current_file)) + raise IOError("Missing #endif in {}".format(self.current_file)) def find_path(self, path): try: @@ -285,7 +286,7 @@ def find_path(self, path): include_path = os.path.join(self.include_dir, path) if os.path.exists(include_path): return include_path - raise IOError('Could not find {}'.format(path)) + raise IOError("Could not find {}".format(path)) class Molecule: @@ -308,58 +309,78 @@ def __init__(self, name): self.impropers = defaultdict(list) self.parsers = { - 'atoms': self.parse_atoms, - 'bonds': self.parse_bonds, - 'angles': self.parse_angles, - 'dihedrals': self.parse_dihedrals, - 'constraints': self.parse_constraints, - 'settles': self.parse_settles + "atoms": self.parse_atoms, + "bonds": self.parse_bonds, + "angles": self.parse_angles, + "dihedrals": self.parse_dihedrals, + "constraints": self.parse_constraints, + "settles": self.parse_settles, } self.resolved_residue_attrs = False @property def atom_order(self): - return [self.ids, self.types, self.resids, self.resnames, - self.names, self.chargegroups, self.charges, - self.masses] + return [ + self.ids, + self.types, + self.resids, + self.resnames, + self.names, + self.chargegroups, + self.charges, + self.masses, + ] @property def params(self): return [self.bonds, self.angles, self.dihedrals, self.impropers] - + def parse_atoms(self, line): values = line.split() for lst in self.atom_order: try: lst.append(values.pop(0)) except IndexError: # ran out of values - lst.append('') - + lst.append("") + def parse_bonds(self, line): - self.add_param(line, self.bonds, n_funct=2, - funct_values=(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)) + self.add_param( + line, + self.bonds, + n_funct=2, + funct_values=(1, 2, 3, 4, 5, 6, 7, 8, 9, 10), + ) def parse_angles(self, line): - self.add_param(line, self.angles, n_funct=3, - funct_values=(1, 2, 3, 4, 5, 6, 8, 10)) - + self.add_param( + line, + self.angles, + n_funct=3, + funct_values=(1, 2, 3, 4, 5, 6, 8, 10), + ) + def parse_dihedrals(self, line): - dih = self.add_param(line, self.dihedrals, n_funct=4, - funct_values=(1, 3, 5, 8, 9, 10, 11)) + dih = self.add_param( + line, + self.dihedrals, + n_funct=4, + funct_values=(1, 3, 5, 8, 9, 10, 11), + ) if not dih: - self.add_param(line, self.impropers, n_funct=4, - funct_values=(2, 4)) + self.add_param( + line, self.impropers, n_funct=4, funct_values=(2, 4) + ) def parse_constraints(self, line): self.add_param(line, self.bonds, n_funct=2, funct_values=(1, 2)) def parse_settles(self, line): - # [ settles ] is a triangular constraint for + # [ settles ] is a triangular constraint for # water molecules. - # In ITP files this is defined with only the - # oxygen atom index. The next two atoms are - # assumed to be hydrogens. Unlike TPRParser, + # In ITP files this is defined with only the + # oxygen atom index. The next two atoms are + # assumed to be hydrogens. Unlike TPRParser, # the manual only lists this format (as of 2019). # These are treated as 2 bonds. # No angle component is included to avoid discrepancies @@ -370,21 +391,25 @@ def parse_settles(self, line): except ValueError: pass else: - self.bonds[(base, base+1)].append("settles") - self.bonds[(base, base+2)].append("settles") + self.bonds[(base, base + 1)].append("settles") + self.bonds[(base, base + 2)].append("settles") def resolve_residue_attrs(self): """Figure out residue borders and assign moltypes and molnums""" resids = np.array(self.resids, dtype=np.int32) resnames = np.array(self.resnames, dtype=object) - self.residx, (self.resids, resnames) = change_squash((resids,), (resids, resnames)) + self.residx, (self.resids, resnames) = change_squash( + (resids,), (resids, resnames) + ) self.resnames = list(resnames) self.moltypes = [self.name] * len(self.resids) self.molnums = np.array([1] * len(self.resids)) self.resolved_residue_attrs = True - def shift_indices(self, atomid=0, resid=0, molnum=0, cgnr=0, n_res=0, n_atoms=0): + def shift_indices( + self, atomid=0, resid=0, molnum=0, cgnr=0, n_res=0, n_atoms=0 + ): """ Get attributes ready for adding onto a larger topology. @@ -405,26 +430,33 @@ def shift_indices(self, atomid=0, resid=0, molnum=0, cgnr=0, n_res=0, n_atoms=0) if not self.resolved_residue_attrs: self.resolve_residue_attrs() - resids = list(np.array(self.resids)+resid) - residx = list(np.array(self.residx)+n_res) + resids = list(np.array(self.resids) + resid) + residx = list(np.array(self.residx) + n_res) molnums = list(np.array(self.molnums) + molnum) ids = list(np.array(self.ids, dtype=int) + atomid) try: cg = np.array(self.chargegroups, dtype=int) except ValueError: - cg = np.arange(1, len(self.chargegroups)+1) - chargegroups = list(cg+cgnr) - - atom_order = [ids, self.types, resids, self.resnames, - self.names, chargegroups, self.charges, - self.masses] + cg = np.arange(1, len(self.chargegroups) + 1) + chargegroups = list(cg + cgnr) + + atom_order = [ + ids, + self.types, + resids, + self.resnames, + self.names, + chargegroups, + self.charges, + self.masses, + ] new_params = [] for p in self.params: new = {} for indices, values in p.items(): - new[tuple(np.array(indices)+n_atoms)] = values + new[tuple(np.array(indices) + n_atoms)] = values new_params.append(new) return atom_order, new_params, molnums, self.moltypes, residx @@ -491,11 +523,15 @@ class ITPParser(TopologyReaderBase): mass guessing behavior """ - format = 'ITP' - def parse(self, include_dir='/usr/local/gromacs/share/gromacs/top/', - infer_system=True, - **kwargs): + format = "ITP" + + def parse( + self, + include_dir="/usr/local/gromacs/share/gromacs/top/", + infer_system=True, + **kwargs, + ): """Parse ITP file into Topology Parameters @@ -503,13 +539,13 @@ def parse(self, include_dir='/usr/local/gromacs/share/gromacs/top/', include_dir: str, optional A directory in which to look for other files included from the original file, if the files are not first found - in the current directory. + in the current directory. Default: "/usr/local/gromacs/share/gromacs/top/" infer_system: bool, optional (default True) If a ``[ molecules ]`` directive is not found within the the - topology file, create a Topology with one of every - ``[ moleculetype ]`` defined. If a ``[ molecules ]`` directive is + topology file, create a Topology with one of every + ``[ moleculetype ]`` defined. If a ``[ molecules ]`` directive is found, this keyword is ignored. Returns @@ -522,30 +558,32 @@ def parse(self, include_dir='/usr/local/gromacs/share/gromacs/top/', self._molecules = [] # for order self.current_mol = None self.parser = self._pass - self.system_molecules = [] + self.system_molecules = [] # Open and check itp validity with openany(self.filename) as itpfile: self.lines = GmxTopIterator(itpfile, include_dir, kwargs) for line in self.lines: - if '[' in line and ']' in line: - section = line.split('[')[1].split(']')[0].strip() + if "[" in line and "]" in line: + section = line.split("[")[1].split("]")[0].strip() - if section == 'atomtypes': + if section == "atomtypes": self.parser = self.parse_atomtypes - elif section == 'moleculetype': + elif section == "moleculetype": self.parser = self.parse_moleculetype - elif section == 'molecules': + elif section == "molecules": self.parser = self.parse_molecules - + elif self.current_mol: - self.parser = self.current_mol.parsers.get(section, self._pass) + self.parser = self.current_mol.parsers.get( + section, self._pass + ) else: self.parser = self._pass - + else: self.parser(line) @@ -559,23 +597,23 @@ def parse(self, include_dir='/usr/local/gromacs/share/gromacs/top/', self.masses = np.array(self.masses, dtype=object) if not all(self.charges): - empty = self.charges == '' + empty = self.charges == "" self.charges[empty] = [ ( self.atomtypes.get(x)["charge"] if x in self.atomtypes.keys() - else '' + else "" ) for x in self.types[empty] ] if not all(self.masses): - empty = self.masses == '' + empty = self.masses == "" self.masses[empty] = [ ( self.atomtypes.get(x)["mass"] if x in self.atomtypes.keys() - else '' + else "" ) for x in self.types[empty] ] @@ -593,16 +631,18 @@ def parse(self, include_dir='/usr/local/gromacs/share/gromacs/top/', attrs.append(Attr(np.array(vals, dtype=dtype))) if not all(self.masses): - empty = self.masses == '' + empty = self.masses == "" self.masses[empty] = Masses.missing_value_label - attrs.append(Masses(np.array(self.masses, dtype=np.float64), - guessed=False)) + attrs.append( + Masses(np.array(self.masses, dtype=np.float64), guessed=False) + ) self.elements = DefaultGuesser(None).guess_types(self.types) if all(e.capitalize() in SYMB2Z for e in self.elements): - attrs.append(Elements(np.array(self.elements, - dtype=object), guessed=True)) + attrs.append( + Elements(np.array(self.elements, dtype=object), guessed=True) + ) warnings.warn( "The elements attribute has been populated by guessing " "elements from atom types. This behaviour has been " @@ -610,13 +650,15 @@ def parse(self, include_dir='/usr/local/gromacs/share/gromacs/top/', "to the new guessing API. " "This behavior will be removed in release 3.0. " "Please see issue #4698 for more information. ", - DeprecationWarning + DeprecationWarning, ) else: - warnings.warn("Element information is missing, elements attribute " - "will not be populated. If needed these can be " - "guessed using universe.guess_TopologyAttrs(" - "to_guess=['elements']).") + warnings.warn( + "Element information is missing, elements attribute " + "will not be populated. If needed these can be " + "guessed using universe.guess_TopologyAttrs(" + "to_guess=['elements'])." + ) # residue stuff resids = np.array(self.resids, dtype=np.int32) @@ -627,24 +669,28 @@ def parse(self, include_dir='/usr/local/gromacs/share/gromacs/top/', attrs.append(Resnames(resnames)) attrs.append(Moltypes(np.array(self.moltypes, dtype=object))) attrs.append(Molnums(molnums)) - + n_atoms = len(self.ids) n_residues = len(self.resids) n_segments = len(self.system_molecules) attrs.append(Segids(np.array(self.system_molecules, dtype=object))) - segidx = molnums-1 + segidx = molnums - 1 - top = Topology(n_atoms, n_residues, n_segments, - attrs=attrs, - atom_resindex=self.residx, - residue_segindex=segidx) + top = Topology( + n_atoms, + n_residues, + n_segments, + attrs=attrs, + atom_resindex=self.residx, + residue_segindex=segidx, + ) # connectivity stuff for dct, Attr, attrname in ( - (self.bonds, Bonds, 'bonds'), - (self.angles, Angles, 'angles'), - (self.dihedrals, Dihedrals, 'dihedrals'), - (self.impropers, Impropers, 'impropers') + (self.bonds, Bonds, "bonds"), + (self.angles, Angles, "angles"), + (self.dihedrals, Dihedrals, "dihedrals"), + (self.impropers, Impropers, "impropers"), ): if dct: indices, types = zip(*list(dct.items())) @@ -652,7 +698,7 @@ def parse(self, include_dir='/usr/local/gromacs/share/gromacs/top/', indices, types = [], [] types = [reduce_singular(t) for t in types] - + tattr = Attr(indices, types=types) top.add_TopologyAttr(tattr) @@ -662,17 +708,17 @@ def _pass(self, line): pass def parse_atomtypes(self, line): - keys = ['type_bonded', 'atomic_number', 'mass', 'charge', 'p_type'] + keys = ["type_bonded", "atomic_number", "mass", "charge", "p_type"] fields = line.split() if len(fields[5]) == 1 and fields[5].isalpha(): values = fields[1:6] elif len(fields[3]) == 1 and fields[3].isalpha(): - values = '', '', fields[1], fields[2], fields[3] + values = "", "", fields[1], fields[2], fields[3] elif len(fields[4]) == 1 and fields[4].isalpha(): if fields[1][0].isalpha(): - values = fields[1], '', fields[2], fields[3], fields[4] + values = fields[1], "", fields[2], fields[3], fields[4] else: - values = '', fields[1], fields[2], fields[3], fields[4] + values = "", fields[1], fields[2], fields[3], fields[4] self.atomtypes[fields[0]] = dict(zip(keys, values)) def parse_moleculetype(self, line): @@ -682,7 +728,7 @@ def parse_moleculetype(self, line): def parse_molecules(self, line): name, n_mol = line.split() - self.system_molecules.extend([name]*int(n_mol)) + self.system_molecules.extend([name] * int(n_mol)) def build_system(self): self.ids = [] @@ -697,9 +743,16 @@ def build_system(self): self.molnums = [] self.residx = [] - self.atom_order = [self.ids, self.types, self.resids, self.resnames, - self.names, self.chargegroups, self.charges, - self.masses] + self.atom_order = [ + self.ids, + self.types, + self.resids, + self.resnames, + self.names, + self.chargegroups, + self.charges, + self.masses, + ] self.bonds = defaultdict(list) self.angles = defaultdict(list) @@ -717,9 +770,14 @@ def build_system(self): n_res = len(self.resids) n_atoms = len(self.ids) - shifted = mol.shift_indices(atomid=atomid, resid=resid, - n_res=n_res, cgnr=cgnr, molnum=i, - n_atoms=n_atoms) + shifted = mol.shift_indices( + atomid=atomid, + resid=resid, + n_res=n_res, + cgnr=cgnr, + molnum=i, + n_atoms=n_atoms, + ) atom_order, params, molnums, moltypes, residx = shifted for system_attr, mol_attr in zip(self.atom_order, atom_order): diff --git a/package/MDAnalysis/topology/LAMMPSParser.py b/package/MDAnalysis/topology/LAMMPSParser.py index 2f2ef6ac94..c7cdcea30b 100644 --- a/package/MDAnalysis/topology/LAMMPSParser.py +++ b/package/MDAnalysis/topology/LAMMPSParser.py @@ -110,34 +110,36 @@ # Sections will all start with one of these words # and run until the next section title -SECTIONS = set([ - 'Atoms', # Molecular topology sections - 'Velocities', - 'Masses', - 'Ellipsoids', - 'Lines', - 'Triangles', - 'Bodies', - 'Bonds', # Forcefield sections - 'Angles', - 'Dihedrals', - 'Impropers', - 'Pair', - 'Pair LJCoeffs', - 'PairIJ Coeffs', - 'Bond Coeffs', - 'Angle Coeffs', - 'Dihedral Coeffs', - 'Improper Coeffs', - 'BondBond Coeffs', # Class 2 FF sections - 'BondAngle Coeffs', - 'MiddleBondTorsion Coeffs', - 'EndBondTorsion Coeffs', - 'AngleTorsion Coeffs', - 'AngleAngleTorsion Coeffs', - 'BondBond13 Coeffs', - 'AngleAngle Coeffs', -]) +SECTIONS = set( + [ + "Atoms", # Molecular topology sections + "Velocities", + "Masses", + "Ellipsoids", + "Lines", + "Triangles", + "Bodies", + "Bonds", # Forcefield sections + "Angles", + "Dihedrals", + "Impropers", + "Pair", + "Pair LJCoeffs", + "PairIJ Coeffs", + "Bond Coeffs", + "Angle Coeffs", + "Dihedral Coeffs", + "Improper Coeffs", + "BondBond Coeffs", # Class 2 FF sections + "BondAngle Coeffs", + "MiddleBondTorsion Coeffs", + "EndBondTorsion Coeffs", + "AngleTorsion Coeffs", + "AngleAngleTorsion Coeffs", + "BondBond13 Coeffs", + "AngleAngle Coeffs", + ] +) # We usually check by splitting around whitespace, so check # if any SECTION keywords will trip up on this # and add them @@ -146,31 +148,33 @@ SECTIONS.add(val.split()[0]) -HEADERS = set([ - 'atoms', - 'bonds', - 'angles', - 'dihedrals', - 'impropers', - 'atom types', - 'bond types', - 'angle types', - 'dihedral types', - 'improper types', - 'extra bond per atom', - 'extra angle per atom', - 'extra dihedral per atom', - 'extra improper per atom', - 'extra special per atom', - 'ellipsoids', - 'lines', - 'triangles', - 'bodies', - 'xlo xhi', - 'ylo yhi', - 'zlo zhi', - 'xy xz yz', -]) +HEADERS = set( + [ + "atoms", + "bonds", + "angles", + "dihedrals", + "impropers", + "atom types", + "bond types", + "angle types", + "dihedral types", + "improper types", + "extra bond per atom", + "extra angle per atom", + "extra dihedral per atom", + "extra improper per atom", + "extra special per atom", + "ellipsoids", + "lines", + "triangles", + "bodies", + "xlo xhi", + "ylo yhi", + "zlo zhi", + "xy xz yz", + ] +) class DATAParser(TopologyReaderBase): @@ -192,12 +196,13 @@ class as the topology and coordinate reader share many common through universe.guess_TopologyAttrs() API). """ - format = 'DATA' + + format = "DATA" def iterdata(self): with openany(self.filename) as f: for line in f: - line = line.partition('#')[0].strip() + line = line.partition("#")[0].strip() if line: yield line @@ -211,19 +216,19 @@ def grab_datafile(self): """ f = list(self.iterdata()) - starts = [i for i, line in enumerate(f) - if line.split()[0] in SECTIONS] + starts = [i for i, line in enumerate(f) if line.split()[0] in SECTIONS] starts += [None] header = {} - for line in f[:starts[0]]: + for line in f[: starts[0]]: for token in HEADERS: if line.endswith(token): header[token] = line.split(token)[0] continue - sects = {f[l]:f[l+1:starts[i+1]] - for i, l in enumerate(starts[:-1])} + sects = { + f[l]: f[l + 1 : starts[i + 1]] for i, l in enumerate(starts[:-1]) + } return header, sects @@ -248,7 +253,7 @@ def _interpret_atom_style(atom_style): atom_style = atom_style.split() - for attr in ['id', 'type', 'resid', 'charge', 'x', 'y', 'z']: + for attr in ["id", "type", "resid", "charge", "x", "y", "z"]: try: location = atom_style.index(attr) except ValueError: @@ -256,11 +261,13 @@ def _interpret_atom_style(atom_style): else: style_dict[attr] = location - reqd_attrs = ['id', 'type', 'x', 'y', 'z'] + reqd_attrs = ["id", "type", "x", "y", "z"] missing_attrs = [attr for attr in reqd_attrs if attr not in style_dict] if missing_attrs: - raise ValueError("atom_style string missing required field(s): {}" - "".format(', '.join(missing_attrs))) + raise ValueError( + "atom_style string missing required field(s): {}" + "".format(", ".join(missing_attrs)) + ) return style_dict @@ -273,40 +280,43 @@ def parse(self, **kwargs): """ # Can pass atom_style to help parsing try: - self.style_dict = self._interpret_atom_style(kwargs['atom_style']) + self.style_dict = self._interpret_atom_style(kwargs["atom_style"]) except KeyError: self.style_dict = None head, sects = self.grab_datafile() try: - masses = self._parse_masses(sects['Masses']) + masses = self._parse_masses(sects["Masses"]) except KeyError: masses = None - if 'Atoms' not in sects: + if "Atoms" not in sects: raise ValueError("Data file was missing Atoms section") try: - top = self._parse_atoms(sects['Atoms'], masses) + top = self._parse_atoms(sects["Atoms"], masses) except Exception: errmsg = ( "Failed to parse atoms section. You can supply a description " "of the atom_style as a keyword argument, " - "eg mda.Universe(..., atom_style='id resid x y z')") + "eg mda.Universe(..., atom_style='id resid x y z')" + ) raise ValueError(errmsg) from None # create mapping of id to index (ie atom id 10 might be the 0th atom) mapping = {atom_id: i for i, atom_id in enumerate(top.ids.values)} for attr, L, nentries in [ - (Bonds, 'Bonds', 2), - (Angles, 'Angles', 3), - (Dihedrals, 'Dihedrals', 4), - (Impropers, 'Impropers', 4) + (Bonds, "Bonds", 2), + (Angles, "Angles", 3), + (Dihedrals, "Dihedrals", 4), + (Impropers, "Impropers", 4), ]: try: - type, sect = self._parse_bond_section(sects[L], nentries, mapping) + type, sect = self._parse_bond_section( + sects[L], nentries, mapping + ) except KeyError: type, sect = [], [] @@ -314,8 +324,9 @@ def parse(self, **kwargs): return top - def read_DATA_timestep(self, n_atoms, TS_class, TS_kwargs, - atom_style=None): + def read_DATA_timestep( + self, n_atoms, TS_class, TS_kwargs, atom_style=None + ): """Read a DATA file and try and extract x, v, box. - positions @@ -338,19 +349,19 @@ def read_DATA_timestep(self, n_atoms, TS_class, TS_kwargs, unitcell = self._parse_box(header) try: - positions, ordering = self._parse_pos(sects['Atoms']) + positions, ordering = self._parse_pos(sects["Atoms"]) except KeyError as err: errmsg = f"Position information not found: {err}" raise IOError(errmsg) from None - if 'Velocities' in sects: - velocities = self._parse_vel(sects['Velocities'], ordering) + if "Velocities" in sects: + velocities = self._parse_vel(sects["Velocities"], ordering) else: velocities = None - ts = TS_class.from_coordinates(positions, - velocities=velocities, - **TS_kwargs) + ts = TS_class.from_coordinates( + positions, velocities=velocities, **TS_kwargs + ) ts.dimensions = unitcell return ts @@ -365,20 +376,22 @@ def _parse_pos(self, datalines): if self.style_dict is None: if len(datalines[0].split()) in (7, 10): - style_dict = {'id': 0, 'x': 4, 'y': 5, 'z': 6} + style_dict = {"id": 0, "x": 4, "y": 5, "z": 6} else: - style_dict = {'id': 0, 'x': 3, 'y': 4, 'z': 5} + style_dict = {"id": 0, "x": 3, "y": 4, "z": 5} else: style_dict = self.style_dict for i, line in enumerate(datalines): line = line.split() - ids[i] = line[style_dict['id']] + ids[i] = line[style_dict["id"]] - pos[i, :] = [line[style_dict['x']], - line[style_dict['y']], - line[style_dict['z']]] + pos[i, :] = [ + line[style_dict["x"]], + line[style_dict["y"]], + line[style_dict["z"]], + ] order = np.argsort(ids) pos = pos[order] @@ -435,7 +448,9 @@ def _parse_bond_section(self, datalines, nentries, mapping): for line in datalines: line = line.split() # map to 0 based int - section.append(tuple([mapping[int(x)] for x in line[2:2 + nentries]])) + section.append( + tuple([mapping[int(x)] for x in line[2 : 2 + nentries]]) + ) type.append(line[1]) return tuple(type), tuple(section) @@ -471,18 +486,16 @@ def _parse_atoms(self, datalines, massdict=None): n_atoms = len(datalines) if self.style_dict is None: - sd = {'id': 0, - 'resid': 1, - 'type': 2} + sd = {"id": 0, "resid": 1, "type": 2} # Fields per line n = len(datalines[0].split()) if n in (7, 10): - sd['charge'] = 3 + sd["charge"] = 3 else: sd = self.style_dict - has_charge = 'charge' in sd - has_resid = 'resid' in sd + has_charge = "charge" in sd + has_resid = "resid" in sd # atom ids aren't necessarily sequential atom_ids = np.zeros(n_atoms, dtype=np.int32) @@ -500,12 +513,12 @@ def _parse_atoms(self, datalines, massdict=None): # these numpy array are already typed correctly, # so just pass the raw strings # and let numpy handle the conversion - atom_ids[i] = line[sd['id']] + atom_ids[i] = line[sd["id"]] if has_resid: - resids[i] = line[sd['resid']] - types[i] = line[sd['type']] + resids[i] = line[sd["resid"]] + types[i] = line[sd["type"]] if has_charge: - charges[i] = line[sd['charge']] + charges[i] = line[sd["charge"]] # at this point, we've read the atoms section, # but it's still (potentially) unordered @@ -536,11 +549,11 @@ def _parse_atoms(self, datalines, massdict=None): attrs.append(Atomids(atom_ids)) attrs.append(Resids(resids)) attrs.append(Resnums(resids.copy())) - attrs.append(Segids(np.array(['SYSTEM'], dtype=object))) + attrs.append(Segids(np.array(["SYSTEM"], dtype=object))) - top = Topology(n_atoms, n_residues, 1, - attrs=attrs, - atom_resindex=residx) + top = Topology( + n_atoms, n_residues, 1, attrs=attrs, atom_resindex=residx + ) return top @@ -559,18 +572,18 @@ def _parse_masses(self, datalines): return masses def _parse_box(self, header): - x1, x2 = np.float32(header['xlo xhi'].split()) + x1, x2 = np.float32(header["xlo xhi"].split()) x = x2 - x1 - y1, y2 = np.float32(header['ylo yhi'].split()) + y1, y2 = np.float32(header["ylo yhi"].split()) y = y2 - y1 - z1, z2 = np.float32(header['zlo zhi'].split()) + z1, z2 = np.float32(header["zlo zhi"].split()) z = z2 - z1 - if 'xy xz yz' in header: + if "xy xz yz" in header: # Triclinic unitcell = np.zeros((3, 3), dtype=np.float32) - xy, xz, yz = np.float32(header['xy xz yz'].split()) + xy, xz, yz = np.float32(header["xy xz yz"].split()) unitcell[0][0] = x unitcell[1][0] = xy @@ -584,7 +597,7 @@ def _parse_box(self, header): # Orthogonal unitcell = np.zeros(6, dtype=np.float32) unitcell[:3] = x, y, z - unitcell[3:] = 90., 90., 90. + unitcell[3:] = 90.0, 90.0, 90.0 return unitcell @@ -598,7 +611,8 @@ class LammpsDumpParser(TopologyReaderBase): .. versionchanged:: 2.0.0 .. versionadded:: 0.19.0 """ - format = 'LAMMPSDUMP' + + format = "LAMMPSDUMP" def parse(self, **kwargs): with openany(self.filename) as fin: @@ -634,17 +648,25 @@ def parse(self, **kwargs): attrs.append(Atomids(indices)) attrs.append(Atomtypes(types)) attrs.append(Masses(np.ones(natoms, dtype=np.float64), guessed=True)) - warnings.warn('Guessed all Masses to 1.0') + warnings.warn("Guessed all Masses to 1.0") attrs.append(Resids(np.array([1], dtype=int))) attrs.append(Resnums(np.array([1], dtype=int))) - attrs.append(Segids(np.array(['SYSTEM'], dtype=object))) + attrs.append(Segids(np.array(["SYSTEM"], dtype=object))) return Topology(natoms, 1, 1, attrs=attrs) @functools.total_ordering class LAMMPSAtom(object): # pragma: no cover - __slots__ = ("index", "name", "type", "chainid", "charge", "mass", "_positions") + __slots__ = ( + "index", + "name", + "type", + "chainid", + "charge", + "mass", + "_positions", + ) def __init__(self, index, name, type, chain_id, charge=0, mass=1): self.index = index @@ -655,8 +677,15 @@ def __init__(self, index, name, type, chain_id, charge=0, mass=1): self.mass = mass def __repr__(self): - return "" + return ( + "" + ) def __lt__(self, other): return self.index < other.index @@ -668,12 +697,22 @@ def __hash__(self): return hash(self.index) def __getattr__(self, attr): - if attr == 'pos': + if attr == "pos": return self._positions[self.index] else: super(LAMMPSAtom, self).__getattribute__(attr) def __iter__(self): pos = self.pos - return iter((self.index + 1, self.chainid, self.type, self.charge, - self.mass, pos[0], pos[1], pos[2])) + return iter( + ( + self.index + 1, + self.chainid, + self.type, + self.charge, + self.mass, + pos[0], + pos[1], + pos[2], + ) + ) diff --git a/package/MDAnalysis/topology/MMTFParser.py b/package/MDAnalysis/topology/MMTFParser.py index 3abc6a281c..90af2f46d1 100644 --- a/package/MDAnalysis/topology/MMTFParser.py +++ b/package/MDAnalysis/topology/MMTFParser.py @@ -106,15 +106,15 @@ def _parse_mmtf(fn): - if fn.endswith('gz'): + if fn.endswith("gz"): return mmtf.parse_gzip(fn) else: return mmtf.parse(fn) class Models(SegmentAttr): - attrname = 'models' - singular = 'model' + attrname = "models" + singular = "model" transplants = defaultdict(list) def models(self): @@ -130,16 +130,16 @@ def models(self): """ model_ids = np.unique(self.segments.models) - return [self.select_atoms('model {}'.format(i)) - for i in model_ids] + return [self.select_atoms("model {}".format(i)) for i in model_ids] - transplants['Universe'].append( - ('models', property(models, None, None, models.__doc__))) + transplants["Universe"].append( + ("models", property(models, None, None, models.__doc__)) + ) class ModelSelection(RangeSelection): - token = 'model' - field = 'models' + token = "model" + field = "models" def apply(self, group): mask = np.zeros(len(group), dtype=bool) @@ -157,7 +157,7 @@ def apply(self, group): class MMTFParser(base.TopologyReaderBase): - format = 'MMTF' + format = "MMTF" @staticmethod def _format_hint(thing): @@ -168,9 +168,9 @@ def _format_hint(thing): return isinstance(thing, mmtf.MMTFDecoder) @due.dcite( - Doi('10.1371/journal.pcbi.1005575'), + Doi("10.1371/journal.pcbi.1005575"), description="MMTF Parser", - path='MDAnalysis.topology.MMTFParser', + path="MDAnalysis.topology.MMTFParser", ) def parse(self, **kwargs): if isinstance(self.filename, mmtf.MMTFDecoder): @@ -191,45 +191,58 @@ def iter_atoms(field): attrs = [] # required - charges = Charges(list(iter_atoms('formalChargeList'))) - names = Atomnames(list(iter_atoms('atomNameList'))) - types = Atomtypes(list(iter_atoms('elementList'))) + charges = Charges(list(iter_atoms("formalChargeList"))) + names = Atomnames(list(iter_atoms("atomNameList"))) + types = Atomtypes(list(iter_atoms("elementList"))) attrs.extend([charges, names, types]) - #optional are empty list if empty, sometimes arrays + # optional are empty list if empty, sometimes arrays if len(mtop.atom_id_list): attrs.append(Atomids(mtop.atom_id_list)) else: # must have this attribute for MDA attrs.append(Atomids(np.arange(natoms), guessed=True)) if mtop.alt_loc_list: - attrs.append(AltLocs([val.replace('\x00', '').strip() - for val in mtop.alt_loc_list])) + attrs.append( + AltLocs( + [ + val.replace("\x00", "").strip() + for val in mtop.alt_loc_list + ] + ) + ) else: - attrs.append(AltLocs(['']*natoms)) + attrs.append(AltLocs([""] * natoms)) if len(mtop.b_factor_list): attrs.append(Tempfactors(mtop.b_factor_list)) else: - attrs.append(Tempfactors([0]*natoms)) + attrs.append(Tempfactors([0] * natoms)) if len(mtop.occupancy_list): attrs.append(Occupancies(mtop.occupancy_list)) else: - attrs.append(Occupancies([1]*natoms)) + attrs.append(Occupancies([1] * natoms)) # Residue things # required resids = Resids(mtop.group_id_list) resnums = Resnums(resids.values.copy()) - resnames = Resnames([mtop.group_list[i]['groupName'] - for i in mtop.group_type_list]) + resnames = Resnames( + [mtop.group_list[i]["groupName"] for i in mtop.group_type_list] + ) attrs.extend([resids, resnums, resnames]) # optional # mmtf empty icode is '\x00' rather than '' if mtop.ins_code_list: - attrs.append(ICodes([val.replace('\x00', '').strip() - for val in mtop.ins_code_list])) + attrs.append( + ICodes( + [ + val.replace("\x00", "").strip() + for val in mtop.ins_code_list + ] + ) + ) else: - attrs.append(ICodes(['']*nresidues)) + attrs.append(ICodes([""] * nresidues)) # Segment things # optional @@ -237,18 +250,21 @@ def iter_atoms(field): attrs.append(Segids(mtop.chain_name_list)) else: # required for MDAnalysis - attrs.append(Segids(['SYSTEM'] * nsegments, guessed=True)) + attrs.append(Segids(["SYSTEM"] * nsegments, guessed=True)) mods = np.repeat(np.arange(mtop.num_models), mtop.chains_per_model) attrs.append(Models(mods)) - #attrs.append(chainids) + # attrs.append(chainids) # number of atoms in a given group id - groupID_2_natoms = {i:len(g['atomNameList']) - for i, g in enumerate(mtop.group_list)} + groupID_2_natoms = { + i: len(g["atomNameList"]) for i, g in enumerate(mtop.group_list) + } # mapping of atoms to residues - resindex = np.repeat(np.arange(nresidues), - [groupID_2_natoms[i] for i in mtop.group_type_list]) + resindex = np.repeat( + np.arange(nresidues), + [groupID_2_natoms[i] for i in mtop.group_type_list], + ) # mapping of residues to segments segindex = np.repeat(np.arange(nsegments), mtop.groups_per_chain) @@ -260,7 +276,7 @@ def iter_atoms(field): bonds = [] for gtype in mtop.group_type_list: g = mtop.group_list[gtype] - bondlist = g['bondAtomList'] + bondlist = g["bondAtomList"] for x, y in zip(bondlist[1::2], bondlist[::2]): if x > y: @@ -270,16 +286,21 @@ def iter_atoms(field): offset += groupID_2_natoms[gtype] # add inter group bonds if not mtop.bond_atom_list is None: # optional field - for x, y in zip(mtop.bond_atom_list[1::2], - mtop.bond_atom_list[::2]): + for x, y in zip( + mtop.bond_atom_list[1::2], mtop.bond_atom_list[::2] + ): if x > y: x, y = y, x bonds.append((x, y)) attrs.append(Bonds(bonds)) - top = Topology(natoms, nresidues, nsegments, - atom_resindex=resindex, - residue_segindex=segindex, - attrs=attrs) + top = Topology( + natoms, + nresidues, + nsegments, + atom_resindex=resindex, + residue_segindex=segindex, + attrs=attrs, + ) return top diff --git a/package/MDAnalysis/topology/MOL2Parser.py b/package/MDAnalysis/topology/MOL2Parser.py index 5c81e7346c..114633c0c4 100644 --- a/package/MDAnalysis/topology/MOL2Parser.py +++ b/package/MDAnalysis/topology/MOL2Parser.py @@ -137,7 +137,8 @@ class MOL2Parser(TopologyReaderBase): through universe.guess_TopologyAttrs() API). """ - format = 'MOL2' + + format = "MOL2" def parse(self, **kwargs): """Parse MOL2 file *filename* and return the dict `structure`. @@ -159,8 +160,10 @@ def parse(self, **kwargs): blocks[-1]["lines"].append(line) if not len(blocks): - raise ValueError("The mol2 file '{0}' needs to have at least one" - " @MOLECULE block".format(self.filename)) + raise ValueError( + "The mol2 file '{0}' needs to have at least one" + " @MOLECULE block".format(self.filename) + ) block = blocks[0] sections = {} @@ -178,8 +181,11 @@ def parse(self, **kwargs): atom_lines, bond_lines = sections["atom"], sections.get("bond") if not len(atom_lines): - raise ValueError("The mol2 block ({0}:{1}) has no atoms".format( - os.path.basename(self.filename), block["start_line"])) + raise ValueError( + "The mol2 block ({0}:{1}) has no atoms".format( + os.path.basename(self.filename), block["start_line"] + ) + ) ids = [] names = [] @@ -187,36 +193,43 @@ def parse(self, **kwargs): resids = [] resnames = [] charges = [] - has_charges = sections['molecule'][3].strip() != 'NO_CHARGES' + has_charges = sections["molecule"][3].strip() != "NO_CHARGES" for a in atom_lines: columns = a.split() if len(columns) >= 9: - aid, name, x, y, z, atom_type, \ - resid, resname, charge = columns[:9] + aid, name, x, y, z, atom_type, resid, resname, charge = ( + columns[:9] + ) elif len(columns) < 6: - raise ValueError(f"The @ATOM block in mol2 file" - f" {os.path.basename(self.filename)}" - f" should have at least 6 fields to be" - f" unpacked: atom_id atom_name x y z" - f" atom_type [subst_id[subst_name" - f" [charge [status_bit]]]]") + raise ValueError( + f"The @ATOM block in mol2 file" + f" {os.path.basename(self.filename)}" + f" should have at least 6 fields to be" + f" unpacked: atom_id atom_name x y z" + f" atom_type [subst_id[subst_name" + f" [charge [status_bit]]]]" + ) else: aid, name, x, y, z, atom_type = columns[:6] id_name_charge = [1, None, None] for i in range(6, len(columns)): - id_name_charge[i-6] = columns[i] + id_name_charge[i - 6] = columns[i] resid, resname, charge = id_name_charge if has_charges: if charge is None: - raise ValueError(f"The mol2 file {self.filename}" - f" indicates a charge model" - f"{sections['molecule'][3]}, but" - f" no charge provided in line: {a}") + raise ValueError( + f"The mol2 file {self.filename}" + f" indicates a charge model" + f"{sections['molecule'][3]}, but" + f" no charge provided in line: {a}" + ) else: if charge is not None: - raise ValueError(f"The mol2 file {self.filename}" - f" indicates no charges, but charge" - f" {charge} provided in line: {a}.") + raise ValueError( + f"The mol2 file {self.filename}" + f" indicates no charges, but charge" + f" {charge} provided in line: {a}." + ) ids.append(aid) names.append(name) @@ -234,13 +247,15 @@ def parse(self, **kwargs): validated_elements[i] = SYBYL2SYMB[at] else: invalid_elements.add(at) - validated_elements[i] = '' + validated_elements[i] = "" # Print single warning for all unknown elements, if any if invalid_elements: - warnings.warn("Unknown elements found for some " - f"atoms: {invalid_elements}. " - "These have been given an empty element record.") + warnings.warn( + "Unknown elements found for some " + f"atoms: {invalid_elements}. " + "These have been given an empty element record." + ) attrs = [] attrs.append(Atomids(np.array(ids, dtype=np.int32))) @@ -249,29 +264,32 @@ def parse(self, **kwargs): if has_charges: attrs.append(Charges(np.array(charges, dtype=np.float32))) - if not np.all(validated_elements == ''): + if not np.all(validated_elements == ""): attrs.append(Elements(validated_elements)) resids = np.array(resids, dtype=np.int32) resnames = np.array(resnames, dtype=object) if np.all(resnames): - residx, resids, (resnames,) = squash_by( - resids, resnames) + residx, resids, (resnames,) = squash_by(resids, resnames) n_residues = len(resids) attrs.append(Resids(resids)) attrs.append(Resnums(resids.copy())) attrs.append(Resnames(resnames)) elif not np.any(resnames): - residx, resids, _ = squash_by(resids,) + residx, resids, _ = squash_by( + resids, + ) n_residues = len(resids) attrs.append(Resids(resids)) attrs.append(Resnums(resids.copy())) else: - raise ValueError(f"Some atoms in the mol2 file {self.filename}" - f" have subst_name while some do not.") + raise ValueError( + f"Some atoms in the mol2 file {self.filename}" + f" have subst_name while some do not." + ) - attrs.append(Segids(np.array(['SYSTEM'], dtype=object))) + attrs.append(Segids(np.array(["SYSTEM"], dtype=object))) # don't add Bonds if there are none (Issue #3057) if bond_lines: @@ -287,8 +305,8 @@ def parse(self, **kwargs): bonds.append(bond) attrs.append(Bonds(bonds, order=bondorder)) - top = Topology(n_atoms, n_residues, 1, - attrs=attrs, - atom_resindex=residx) + top = Topology( + n_atoms, n_residues, 1, attrs=attrs, atom_resindex=residx + ) return top diff --git a/package/MDAnalysis/topology/MinimalParser.py b/package/MDAnalysis/topology/MinimalParser.py index ce0598bf3e..39f25f66ef 100644 --- a/package/MDAnalysis/topology/MinimalParser.py +++ b/package/MDAnalysis/topology/MinimalParser.py @@ -56,12 +56,13 @@ class MinimalParser(TopologyReaderBase): This requires that the coordinate format has """ - format = 'MINIMAL' + + format = "MINIMAL" def parse(self, **kwargs): """Return the minimal *Topology* object""" try: - n_atoms = kwargs['n_atoms'] + n_atoms = kwargs["n_atoms"] except KeyError: reader = get_reader_for(self.filename) n_atoms = reader.parse_n_atoms(self.filename, **kwargs) diff --git a/package/MDAnalysis/topology/PDBQTParser.py b/package/MDAnalysis/topology/PDBQTParser.py index 88c3fe3ba4..f3b5c5fa6c 100644 --- a/package/MDAnalysis/topology/PDBQTParser.py +++ b/package/MDAnalysis/topology/PDBQTParser.py @@ -111,10 +111,11 @@ class PDBQTParser(TopologyReaderBase): Removed mass guessing (attributes guessing takes place now through universe.guess_TopologyAttrs() API). - .. _reference: + .. _reference: https://autodock.scripps.edu/wp-content/uploads/sites/56/2021/10/AutoDock4.2.6_UserGuide.pdf """ - format = 'PDBQT' + + format = "PDBQT" def parse(self, **kwargs): """Parse atom information from PDBQT file *filename*. @@ -139,7 +140,7 @@ def parse(self, **kwargs): with util.openany(self.filename) as f: for line in f: line = line.strip() - if not line.startswith(('ATOM', 'HETATM')): + if not line.startswith(("ATOM", "HETATM")): continue record_types.append(line[:6].strip()) serials.append(int(line[6:11])) @@ -158,14 +159,14 @@ def parse(self, **kwargs): attrs = [] for attrlist, Attr, dtype in ( - (record_types, RecordTypes, object), - (serials, Atomids, np.int32), - (names, Atomnames, object), - (altlocs, AltLocs, object), - (occupancies, Occupancies, np.float32), - (tempfactors, Tempfactors, np.float32), - (charges, Charges, np.float32), - (atomtypes, Atomtypes, object), + (record_types, RecordTypes, object), + (serials, Atomids, np.int32), + (names, Atomnames, object), + (altlocs, AltLocs, object), + (occupancies, Occupancies, np.float32), + (tempfactors, Tempfactors, np.float32), + (charges, Charges, np.float32), + (atomtypes, Atomtypes, object), ): attrs.append(Attr(np.array(attrlist, dtype=dtype))) @@ -177,7 +178,8 @@ def parse(self, **kwargs): attrs.append(ChainIDs(chainids)) residx, (resids, icodes, resnames, chainids) = change_squash( - (resids, icodes), (resids, icodes, resnames, chainids)) + (resids, icodes), (resids, icodes, resnames, chainids) + ) n_residues = len(resids) attrs.append(Resids(resids)) attrs.append(Resnums(resids.copy())) @@ -188,9 +190,13 @@ def parse(self, **kwargs): n_segments = len(segids) attrs.append(Segids(segids)) - top = Topology(n_atoms, n_residues, n_segments, - attrs=attrs, - atom_resindex=residx, - residue_segindex=segidx) + top = Topology( + n_atoms, + n_residues, + n_segments, + attrs=attrs, + atom_resindex=residx, + residue_segindex=segidx, + ) return top diff --git a/package/MDAnalysis/topology/PQRParser.py b/package/MDAnalysis/topology/PQRParser.py index 65c98a70d6..496ddfb141 100644 --- a/package/MDAnalysis/topology/PQRParser.py +++ b/package/MDAnalysis/topology/PQRParser.py @@ -103,7 +103,8 @@ class PQRParser(TopologyReaderBase): through universe.guess_TopologyAttrs() API). """ - format = 'PQR' + + format = "PQR" @staticmethod def guess_flavour(line): @@ -126,11 +127,11 @@ def guess_flavour(line): try: float(fields[-1]) except ValueError: - flavour = 'GROMACS' + flavour = "GROMACS" else: - flavour = 'ORIGINAL' + flavour = "ORIGINAL" else: - flavour = 'NO_CHAINID' + flavour = "NO_CHAINID" return flavour def parse(self, **kwargs): @@ -161,20 +162,50 @@ def parse(self, **kwargs): if flavour is None: flavour = self.guess_flavour(line) - if flavour == 'ORIGINAL': - (recordName, serial, name, resName, - chainID, resSeq, x, y, z, charge, - radius) = fields - elif flavour == 'GROMACS': - (recordName, serial, name, resName, - resSeq, x, y, z, charge, - radius, element) = fields + if flavour == "ORIGINAL": + ( + recordName, + serial, + name, + resName, + chainID, + resSeq, + x, + y, + z, + charge, + radius, + ) = fields + elif flavour == "GROMACS": + ( + recordName, + serial, + name, + resName, + resSeq, + x, + y, + z, + charge, + radius, + element, + ) = fields chainID = "SYSTEM" elements.append(element) - elif flavour == 'NO_CHAINID': + elif flavour == "NO_CHAINID": # files without the chainID - (recordName, serial, name, resName, - resSeq, x, y, z, charge, radius) = fields + ( + recordName, + serial, + name, + resName, + resSeq, + x, + y, + z, + charge, + radius, + ) = fields chainID = "SYSTEM" try: @@ -184,7 +215,7 @@ def parse(self, **kwargs): resid = int(resSeq[:-1]) icode = resSeq[-1] else: - icode = '' + icode = "" record_types.append(recordName) serials.append(serial) @@ -216,7 +247,8 @@ def parse(self, **kwargs): residx, (resids, resnames, icodes, chainIDs) = change_squash( (resids, resnames, icodes, chainIDs), - (resids, resnames, icodes, chainIDs)) + (resids, resnames, icodes, chainIDs), + ) n_residues = len(resids) attrs.append(Resids(resids)) @@ -229,9 +261,13 @@ def parse(self, **kwargs): n_segments = len(chainIDs) attrs.append(Segids(chainIDs)) - top = Topology(n_atoms, n_residues, n_segments, - attrs=attrs, - atom_resindex=residx, - residue_segindex=segidx) + top = Topology( + n_atoms, + n_residues, + n_segments, + attrs=attrs, + atom_resindex=residx, + residue_segindex=segidx, + ) return top diff --git a/package/MDAnalysis/topology/PSFParser.py b/package/MDAnalysis/topology/PSFParser.py index f247544262..531c2ae592 100644 --- a/package/MDAnalysis/topology/PSFParser.py +++ b/package/MDAnalysis/topology/PSFParser.py @@ -1,5 +1,5 @@ # -*- Mode: python; tab-width: 4; indent-tabs-mode:nil; coding:utf-8 -*- -# vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4 +# vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4 # # MDAnalysis --- https://www.mdanalysis.org # Copyright (c) 2006-2017 The MDAnalysis Development Team and contributors @@ -64,7 +64,7 @@ Bonds, Angles, Dihedrals, - Impropers + Impropers, ) from ..core.topology import Topology @@ -94,7 +94,8 @@ class PSFParser(TopologyReaderBase): .. versionchanged:: 2.8.0 PSFParser now reads string resids and converts them to integers. """ - format = 'PSF' + + format = "PSF" def parse(self, **kwargs): """Parse PSF file into Topology @@ -107,18 +108,19 @@ def parse(self, **kwargs): with openany(self.filename) as psffile: header = next(psffile) if not header.startswith("PSF"): - err = ("{0} is not valid PSF file (header = {1})" - "".format(self.filename, header)) + err = "{0} is not valid PSF file (header = {1})" "".format( + self.filename, header + ) logger.error(err) raise ValueError(err) header_flags = header[3:].split() if "NAMD" in header_flags: - self._format = "NAMD" # NAMD/VMD + self._format = "NAMD" # NAMD/VMD elif "EXT" in header_flags: - self._format = "EXTENDED" # CHARMM + self._format = "EXTENDED" # CHARMM else: - self._format = "STANDARD" # CHARMM + self._format = "STANDARD" # CHARMM next(psffile) title = next(psffile).split() @@ -129,28 +131,28 @@ def parse(self, **kwargs): # psfremarks = [psffile.next() for i in range(int(title[0]))] for _ in range(int(title[0])): next(psffile) - logger.debug("PSF file {0}: format {1}" - "".format(self.filename, self._format)) + logger.debug( + "PSF file {0}: format {1}" + "".format(self.filename, self._format) + ) # Atoms first and mandatory - top = self._parse_sec( - psffile, ('NATOM', 1, 1, self._parseatoms)) + top = self._parse_sec(psffile, ("NATOM", 1, 1, self._parseatoms)) # Then possibly other sections sections = ( - #("atoms", ("NATOM", 1, 1, self._parseatoms)), + # ("atoms", ("NATOM", 1, 1, self._parseatoms)), (Bonds, ("NBOND", 2, 4, self._parsesection)), (Angles, ("NTHETA", 3, 3, self._parsesection)), (Dihedrals, ("NPHI", 4, 2, self._parsesection)), (Impropers, ("NIMPHI", 4, 2, self._parsesection)), - #("donors", ("NDON", 2, 4, self._parsesection)), - #("acceptors", ("NACC", 2, 4, self._parsesection)) + # ("donors", ("NDON", 2, 4, self._parsesection)), + # ("acceptors", ("NACC", 2, 4, self._parsesection)) ) try: for attr, info in sections: next(psffile) - top.add_TopologyAttr( - attr(self._parse_sec(psffile, info))) + top.add_TopologyAttr(attr(self._parse_sec(psffile, info))) except StopIteration: # Reached the end of the file before we expected for attr in (Bonds, Angles, Dihedrals, Impropers): @@ -173,15 +175,16 @@ def _parse_sec(self, psffile, section_info): header = header.split() # Get the number num = float(header[0]) - sect_type = header[1].strip('!:') + sect_type = header[1].strip("!:") # Make sure the section type matches the desc if not sect_type == desc: - err = ("Expected section {0} but found {1}" - "".format(desc, sect_type)) + err = "Expected section {0} but found {1}" "".format( + desc, sect_type + ) logger.error(err) raise ValueError(err) # Now figure out how many lines to read - numlines = int(ceil(num/per_line)) + numlines = int(ceil(num / per_line)) psffile_next = functools.partial(next, psffile) return parsefunc(psffile_next, atoms_per, numlines) @@ -238,23 +241,42 @@ def _parseatoms(self, lines, atoms_per, numlines): """ # how to partition the line into the individual atom components atom_parsers = { - 'STANDARD': lambda l: - (l[:8], l[9:13].strip() or "SYSTEM", l[14:18], - l[19:23].strip(), l[24:28].strip(), - l[29:33].strip(), l[34:48], l[48:62]), + "STANDARD": lambda l: ( + l[:8], + l[9:13].strip() or "SYSTEM", + l[14:18], + l[19:23].strip(), + l[24:28].strip(), + l[29:33].strip(), + l[34:48], + l[48:62], + ), # l[62:70], l[70:84], l[84:98] ignore IMOVE, ECH and EHA, - 'EXTENDED': lambda l: - (l[:10], l[11:19].strip() or "SYSTEM", l[20:28], - l[29:37].strip(), l[38:46].strip(), - l[47:51].strip(), l[52:66], l[66:70]), + "EXTENDED": lambda l: ( + l[:10], + l[11:19].strip() or "SYSTEM", + l[20:28], + l[29:37].strip(), + l[38:46].strip(), + l[47:51].strip(), + l[52:66], + l[66:70], + ), # l[70:78], l[78:84], l[84:98] ignore IMOVE, ECH and EHA, - 'NAMD': lambda l: l.split()[:8], + "NAMD": lambda l: l.split()[:8], } atom_parser = atom_parsers[self._format] # once partitioned, assigned each component the correct type - set_type = lambda x: (int(x[0]) - 1, x[1] or "SYSTEM", - atoi(x[2]), x[3], - x[4], x[5], float(x[6]), float(x[7])) + set_type = lambda x: ( + int(x[0]) - 1, + x[1] or "SYSTEM", + atoi(x[2]), + x[3], + x[4], + x[5], + float(x[6]), + float(x[7]), + ) # Oli: I don't think that this is the correct OUTPUT format: # psf_atom_format = " %5d %4s %4d %4s %-4s %-4s %10.6f %7.4f%s\n" @@ -289,13 +311,17 @@ def _parseatoms(self, lines, atoms_per, numlines): except ValueError: # last ditch attempt: this *might* be a NAMD/VMD # space-separated "PSF" file from VMD version < 1.9.1 - atom_parser = atom_parsers['NAMD'] + atom_parser = atom_parsers["NAMD"] vals = set_type(atom_parser(line)) - logger.warning("Guessing that this is actually a" - " NAMD-type PSF file..." - " continuing with fingers crossed!") - logger.debug("First NAMD-type line: {0}: {1}" - "".format(i, line.rstrip())) + logger.warning( + "Guessing that this is actually a" + " NAMD-type PSF file..." + " continuing with fingers crossed!" + ) + logger.debug( + "First NAMD-type line: {0}: {1}" + "".format(i, line.rstrip()) + ) atomids[i] = vals[0] segids[i] = vals[1] @@ -316,8 +342,8 @@ def _parseatoms(self, lines, atoms_per, numlines): # Residue # resids, resnames residx, (new_resids, new_resnames, perres_segids) = change_squash( - (resids, resnames, segids), - (resids, resnames, segids)) + (resids, resnames, segids), (resids, resnames, segids) + ) # transform from atom:Rid to atom:Rix residueids = Resids(new_resids) residuenums = Resnums(new_resids.copy()) @@ -327,13 +353,24 @@ def _parseatoms(self, lines, atoms_per, numlines): segidx, perseg_segids = squash_by(perres_segids)[:2] segids = Segids(perseg_segids) - top = Topology(len(atomids), len(new_resids), len(segids), - attrs=[atomids, atomnames, atomtypes, - charges, masses, - residueids, residuenums, residuenames, - segids], - atom_resindex=residx, - residue_segindex=segidx) + top = Topology( + len(atomids), + len(new_resids), + len(segids), + attrs=[ + atomids, + atomnames, + atomtypes, + charges, + masses, + residueids, + residuenums, + residuenames, + segids, + ], + atom_resindex=residx, + residue_segindex=segidx, + ) return top @@ -344,5 +381,5 @@ def _parsesection(self, lines, atoms_per, numlines): # Subtract 1 from each number to ensure zero-indexing for the atoms fields = np.int64(lines().split()) - 1 for j in range(0, len(fields), atoms_per): - section.append(tuple(fields[j:j+atoms_per])) + section.append(tuple(fields[j : j + atoms_per])) return section diff --git a/package/MDAnalysis/topology/ParmEdParser.py b/package/MDAnalysis/topology/ParmEdParser.py index 2cfc0df0da..b7f9331e4a 100644 --- a/package/MDAnalysis/topology/ParmEdParser.py +++ b/package/MDAnalysis/topology/ParmEdParser.py @@ -27,5 +27,5 @@ "This module is deprecated as of MDAnalysis version 2.0.0." "It will be removed in MDAnalysis version 3.0.0." "Please import the ParmEd classes from MDAnalysis.converters instead.", - category=DeprecationWarning + category=DeprecationWarning, ) diff --git a/package/MDAnalysis/topology/TOPParser.py b/package/MDAnalysis/topology/TOPParser.py index 4f2ce631fc..ba61bea0b0 100644 --- a/package/MDAnalysis/topology/TOPParser.py +++ b/package/MDAnalysis/topology/TOPParser.py @@ -111,20 +111,21 @@ Bonds, Angles, Dihedrals, - Impropers + Impropers, ) import warnings import logging -logger = logging.getLogger('MDAnalysis.topology.TOPParser') +logger = logging.getLogger("MDAnalysis.topology.TOPParser") class TypeIndices(AtomAttr): """Numerical type of each Atom""" - attrname = 'type_indices' - singular = 'type_index' - level = 'atom' + + attrname = "type_indices" + singular = "type_index" + level = "atom" class TOPParser(TopologyReaderBase): @@ -173,7 +174,8 @@ class TOPParser(TopologyReaderBase): .. versionchanged:: 2.7.0 gets Segments and chainIDs from flag RESIDUE_CHAINID, when present """ - format = ['TOP', 'PRMTOP', 'PARM7'] + + format = ["TOP", "PRMTOP", "PARM7"] def parse(self, **kwargs): """Parse Amber PRMTOP topology file *filename*. @@ -188,8 +190,13 @@ def parse(self, **kwargs): "CHARGE": (1, 5, self.parse_charges, "charge", 0), "ATOMIC_NUMBER": (1, 10, self.parse_elements, "elements", 0), "MASS": (1, 5, self.parse_masses, "mass", 0), - "ATOM_TYPE_INDEX": (1, 10, self.parse_type_indices, "type_indices", - 0), + "ATOM_TYPE_INDEX": ( + 1, + 10, + self.parse_type_indices, + "type_indices", + 0, + ), "AMBER_ATOM_TYPE": (1, 20, self.parse_types, "types", 0), "RESIDUE_LABEL": (1, 20, self.parse_resnames, "resname", 11), "RESIDUE_POINTER": (1, 10, self.parse_residx, "respoint", 11), @@ -198,7 +205,13 @@ def parse(self, **kwargs): "ANGLES_INC_HYDROGEN": (4, 10, self.parse_bonded, "angh", 4), "ANGLES_WITHOUT_HYDROGEN": (4, 10, self.parse_bonded, "anga", 5), "DIHEDRALS_INC_HYDROGEN": (5, 10, self.parse_bonded, "dihh", 6), - "DIHEDRALS_WITHOUT_HYDROGEN": (5, 10, self.parse_bonded, "diha", 7), + "DIHEDRALS_WITHOUT_HYDROGEN": ( + 5, + 10, + self.parse_bonded, + "diha", + 7, + ), "RESIDUE_CHAINID": (1, 20, self.parse_chainids, "segids", 11), } @@ -211,21 +224,26 @@ def parse(self, **kwargs): if not header.startswith("%VE"): raise ValueError( "{0} is not a valid TOP file. %VE Missing in header" - "".format(self.filename)) + "".format(self.filename) + ) title = next(self.topfile).split() if not (title[1] == "TITLE"): # Raise a separate warning if Chamber-style TOP is detected if title[1] == "CTITLE": - emsg = ("{0} is detected as a Chamber-style TOP file. " - "At this time MDAnalysis does not support such " - "topologies".format(self.filename)) + emsg = ( + "{0} is detected as a Chamber-style TOP file. " + "At this time MDAnalysis does not support such " + "topologies".format(self.filename) + ) else: - emsg = ("{0} is not a valid TOP file. " - "'TITLE' missing in header".format(self.filename)) + emsg = ( + "{0} is not a valid TOP file. " + "'TITLE' missing in header".format(self.filename) + ) raise ValueError(emsg) - while not header.startswith('%FLAG POINTERS'): + while not header.startswith("%FLAG POINTERS"): header = next(self.topfile) next(self.topfile) @@ -238,14 +256,17 @@ def parse(self, **kwargs): while next_section is not None: try: - (num_per_record, per_line, - func, name, sect_num) = sections[next_section] + (num_per_record, per_line, func, name, sect_num) = ( + sections[next_section] + ) except KeyError: + def next_getter(): return self.skipper() + else: num = sys_info[sect_num] * num_per_record - numlines = (num // per_line) + numlines = num // per_line if num % per_line != 0: numlines += 1 @@ -265,56 +286,72 @@ def next_getter(): try: next_section = line.split("%FLAG")[1].strip() except IndexError: - errmsg = (f"%FLAG section not found, formatting error " - f"for PARM7 file {self.filename} ") + errmsg = ( + f"%FLAG section not found, formatting error " + f"for PARM7 file {self.filename} " + ) raise IndexError(errmsg) from None # strip out a few values to play with them - n_atoms = len(attrs['name']) + n_atoms = len(attrs["name"]) - resptrs = attrs.pop('respoint') + resptrs = attrs.pop("respoint") resptrs.append(n_atoms) residx = np.zeros(n_atoms, dtype=np.int32) for i, (x, y) in enumerate(zip(resptrs[:-1], resptrs[1:])): residx[x:y] = i - n_res = len(attrs['resname']) + n_res = len(attrs["resname"]) # Deal with recreating bonds and angle records here - attrs['bonds'] = Bonds([i for i in itertools.chain( - attrs.pop('bonda'), attrs.pop('bondh'))]) + attrs["bonds"] = Bonds( + [ + i + for i in itertools.chain( + attrs.pop("bonda"), attrs.pop("bondh") + ) + ] + ) - attrs['angles'] = Angles([i for i in itertools.chain( - attrs.pop('anga'), attrs.pop('angh'))]) + attrs["angles"] = Angles( + [i for i in itertools.chain(attrs.pop("anga"), attrs.pop("angh"))] + ) - attrs['dihedrals'], attrs['impropers'] = self.parse_dihedrals( - attrs.pop('diha'), attrs.pop('dihh')) + attrs["dihedrals"], attrs["impropers"] = self.parse_dihedrals( + attrs.pop("diha"), attrs.pop("dihh") + ) # Warn user if elements not in topology - if 'elements' not in attrs: - msg = ("ATOMIC_NUMBER record not found, elements attribute will " - "not be populated. If needed these can be guessed using " - "universe.guess_TopologyAttrs(to_guess=['elements']).") + if "elements" not in attrs: + msg = ( + "ATOMIC_NUMBER record not found, elements attribute will " + "not be populated. If needed these can be guessed using " + "universe.guess_TopologyAttrs(to_guess=['elements'])." + ) logger.warning(msg) warnings.warn(msg) - elif np.any(attrs['elements'].values == ""): + elif np.any(attrs["elements"].values == ""): # only send out one warning that some elements are unknown - msg = ("Unknown ATOMIC_NUMBER value found for some atoms, these " - "have been given an empty element record. If needed these " - "can be guessed using " - "universe.guess_TopologyAttrs(to_guess=['elements']).") + msg = ( + "Unknown ATOMIC_NUMBER value found for some atoms, these " + "have been given an empty element record. If needed these " + "can be guessed using " + "universe.guess_TopologyAttrs(to_guess=['elements'])." + ) logger.warning(msg) warnings.warn(msg) # atom ids are mandatory - attrs['atomids'] = Atomids(np.arange(n_atoms) + 1) - attrs['resids'] = Resids(np.arange(n_res) + 1) - attrs['resnums'] = Resnums(np.arange(n_res) + 1) + attrs["atomids"] = Atomids(np.arange(n_atoms) + 1) + attrs["resids"] = Resids(np.arange(n_res) + 1) + attrs["resnums"] = Resnums(np.arange(n_res) + 1) # Amber's 'RESIDUE_CHAINID' is a by-residue attribute, turn it into # a by-atom attribute when present. See PR #4007. if "segids" in attrs and len(attrs["segids"]) == n_res: - segidx, (segids,) = change_squash((attrs["segids"],), (attrs["segids"],)) + segidx, (segids,) = change_squash( + (attrs["segids"],), (attrs["segids"],) + ) chainids = [attrs["segids"][r] for r in residx] attrs["segids"] = Segids(segids) @@ -467,8 +504,8 @@ def parse_elements(self, num_per_record, numlines): """ vals = self.parsesection_mapper( - numlines, - lambda x: Z2SYMB[int(x)] if int(x) > 0 else "") + numlines, lambda x: Z2SYMB[int(x)] if int(x) > 0 else "" + ) attr = Elements(np.array(vals, dtype=object)) return attr @@ -556,8 +593,10 @@ def parse_chunks(self, data, chunksize): Therefore, to extract the required information, we split out the list into chunks of size num_per_record, and only extract the atom ids. """ - vals = [tuple(data[x:x+chunksize-1]) - for x in range(0, len(data), chunksize)] + vals = [ + tuple(data[x : x + chunksize - 1]) + for x in range(0, len(data), chunksize) + ] return vals def parse_bonded(self, num_per_record, numlines): @@ -603,7 +642,7 @@ def parsesection_mapper(self, numlines, mapper): section = [] def get_fmt(file): - """ Skips '%COMMENT' lines until it gets the FORMAT specification + """Skips '%COMMENT' lines until it gets the FORMAT specification for the section.""" line = next(file) if line[:7] == "%FORMAT": @@ -622,7 +661,7 @@ def get_fmt(file): for i in range(numlines): l = next(self.topfile) for j in range(len(x.entries)): - val = l[x.entries[j].start:x.entries[j].stop].strip() + val = l[x.entries[j].start : x.entries[j].stop].strip() if val: section.append(mapper(val)) return section @@ -665,7 +704,7 @@ def parse_dihedrals(self, diha, dihh): dihed = [] for i in itertools.chain(diha, dihh): if i[3] < 0: - improp.append(i[:2]+(abs(i[2]),)+(abs(i[3]),)) + improp.append(i[:2] + (abs(i[2]),) + (abs(i[3]),)) elif i[2] < 0: vals = i[:2] + (abs(i[2]),) + i[3:] dihed.append(vals) diff --git a/package/MDAnalysis/topology/TPRParser.py b/package/MDAnalysis/topology/TPRParser.py index 5c648016e8..6adac116e8 100644 --- a/package/MDAnalysis/topology/TPRParser.py +++ b/package/MDAnalysis/topology/TPRParser.py @@ -174,6 +174,7 @@ from ..core.topologyattrs import Resnums import logging + logger = logging.getLogger("MDAnalysis.topology.TPRparser") @@ -183,7 +184,8 @@ class TPRParser(TopologyReaderBase): .. _Gromacs: http://www.gromacs.org .. _TPR file: http://manual.gromacs.org/current/online/tpr.html """ - format = 'TPR' + + format = "TPR" def parse(self, tpr_resid_from_one=True, **kwargs): """Parse a Gromacs TPR file into a MDAnalysis internal topology structure. @@ -206,11 +208,11 @@ def parse(self, tpr_resid_from_one=True, **kwargs): .. versionchanged:: 2.0.0 Changed to ``tpr_resid_from_one=True`` by default. """ - with openany(self.filename, mode='rb') as infile: + with openany(self.filename, mode="rb") as infile: tprf = infile.read() data = tpr_utils.TPXUnpacker(tprf) try: - th = tpr_utils.read_tpxheader(data) # tpxheader + th = tpr_utils.read_tpxheader(data) # tpxheader except (EOFError, ValueError): msg = f"{self.filename}: Invalid tpr file or cannot be recognized" logger.critical(msg) @@ -232,18 +234,21 @@ def parse(self, tpr_resid_from_one=True, **kwargs): raise IOError(msg) data = tpr_utils.TPXUnpacker2020.from_unpacker(data) - state_ngtc = th.ngtc # done init_state() in src/gmxlib/tpxio.c + state_ngtc = th.ngtc # done init_state() in src/gmxlib/tpxio.c if th.bBox: tpr_utils.extract_box_info(data, th.fver) if state_ngtc > 0: - if th.fver < 69: # redundancy due to different versions + if th.fver < 69: # redundancy due to different versions tpr_utils.ndo_real(data, state_ngtc) - tpr_utils.ndo_real(data, state_ngtc) # relevant to Berendsen tcoupl_lambda + tpr_utils.ndo_real( + data, state_ngtc + ) # relevant to Berendsen tcoupl_lambda if th.bTop: - tpr_top = tpr_utils.do_mtop(data, th.fver, - tpr_resid_from_one=tpr_resid_from_one) + tpr_top = tpr_utils.do_mtop( + data, th.fver, tpr_resid_from_one=tpr_resid_from_one + ) else: msg = f"{self.filename}: No topology found in tpr file" logger.critical(msg) @@ -253,7 +258,6 @@ def parse(self, tpr_resid_from_one=True, **kwargs): return tpr_top - def _log_header(self, th): logger.info(f"Gromacs version : {th.ver_str}") logger.info(f"tpx version : {th.fver}") diff --git a/package/MDAnalysis/topology/TXYZParser.py b/package/MDAnalysis/topology/TXYZParser.py index 4b0d248e37..4752f1bf48 100644 --- a/package/MDAnalysis/topology/TXYZParser.py +++ b/package/MDAnalysis/topology/TXYZParser.py @@ -87,7 +87,8 @@ class TXYZParser(TopologyReaderBase): through universe.guess_TopologyAttrs() API). """ - format = ['TXYZ', 'ARC'] + + format = ["TXYZ", "ARC"] def parse(self, **kwargs): """Read the file and return the structure. @@ -97,7 +98,7 @@ def parse(self, **kwargs): MDAnalysis Topology object """ with openany(self.filename) as inf: - #header + # header natoms = int(inf.readline().split()[0]) atomids = np.zeros(natoms, dtype=int) @@ -121,7 +122,7 @@ def parse(self, **kwargs): # Can't infinitely read as XYZ files can be multiframe for i, line in zip(range(natoms), itertools.chain([fline], inf)): line = line.split() - atomids[i]= line[0] + atomids[i] = line[0] names[i] = line[1] types[i] = line[5] bonded_atoms = line[6:] @@ -130,24 +131,26 @@ def parse(self, **kwargs): if i < other_atom: bonds.append((i, other_atom)) - attrs = [Atomnames(names), - Atomids(atomids), - Atomtypes(types), - Bonds(tuple(bonds)), - Resids(np.array([1])), - Resnums(np.array([1])), - Segids(np.array(['SYSTEM'], dtype=object)), - ] + attrs = [ + Atomnames(names), + Atomids(atomids), + Atomtypes(types), + Bonds(tuple(bonds)), + Resids(np.array([1])), + Resnums(np.array([1])), + Segids(np.array(["SYSTEM"], dtype=object)), + ] if all(n.capitalize() in SYMB2Z for n in names): attrs.append(Elements(np.array(names, dtype=object))) else: - warnings.warn("Element information is missing, elements attribute " - "will not be populated. If needed these can be " - "guessed using universe.guess_TopologyAttrs(" - "to_guess=['elements']).") - - top = Topology(natoms, 1, 1, - attrs=attrs) + warnings.warn( + "Element information is missing, elements attribute " + "will not be populated. If needed these can be " + "guessed using universe.guess_TopologyAttrs(" + "to_guess=['elements'])." + ) + + top = Topology(natoms, 1, 1, attrs=attrs) return top diff --git a/package/MDAnalysis/topology/XYZParser.py b/package/MDAnalysis/topology/XYZParser.py index 956c93567b..45683a1a71 100644 --- a/package/MDAnalysis/topology/XYZParser.py +++ b/package/MDAnalysis/topology/XYZParser.py @@ -75,7 +75,8 @@ class XYZParser(TopologyReaderBase): through universe.guess_TopologyAttrs() API). """ - format = 'XYZ' + + format = "XYZ" def parse(self, **kwargs): """Read the file and return the structure. @@ -95,15 +96,15 @@ def parse(self, **kwargs): name = inf.readline().split()[0] names[i] = name + attrs = [ + Atomnames(names), + Atomids(np.arange(natoms) + 1), + Resids(np.array([1])), + Resnums(np.array([1])), + Segids(np.array(["SYSTEM"], dtype=object)), + Elements(names), + ] - attrs = [Atomnames(names), - Atomids(np.arange(natoms) + 1), - Resids(np.array([1])), - Resnums(np.array([1])), - Segids(np.array(['SYSTEM'], dtype=object)), - Elements(names)] - - top = Topology(natoms, 1, 1, - attrs=attrs) + top = Topology(natoms, 1, 1, attrs=attrs) return top diff --git a/package/MDAnalysis/topology/core.py b/package/MDAnalysis/topology/core.py index 7ee6121982..a0e28f9643 100644 --- a/package/MDAnalysis/topology/core.py +++ b/package/MDAnalysis/topology/core.py @@ -41,9 +41,15 @@ # Deprecated local imports from MDAnalysis.guesser import tables from .guessers import ( - guess_atom_element, guess_atom_type, - get_atom_mass, guess_atom_mass, guess_atom_charge, - guess_bonds, guess_angles, guess_dihedrals, guess_improper_dihedrals, + guess_atom_element, + guess_atom_type, + get_atom_mass, + guess_atom_mass, + guess_atom_charge, + guess_bonds, + guess_angles, + guess_dihedrals, + guess_improper_dihedrals, ) -#tumbleweed +# tumbleweed diff --git a/package/MDAnalysis/topology/guessers.py b/package/MDAnalysis/topology/guessers.py index d1485bad08..6ae316cd02 100644 --- a/package/MDAnalysis/topology/guessers.py +++ b/package/MDAnalysis/topology/guessers.py @@ -131,7 +131,9 @@ def guess_masses(atom_types): atom_masses : np.ndarray dtype float64 """ validate_atom_types(atom_types) - masses = np.array([get_atom_mass(atom_t) for atom_t in atom_types], dtype=np.float64) + masses = np.array( + [get_atom_mass(atom_t) for atom_t in atom_types], dtype=np.float64 + ) return masses @@ -158,7 +160,11 @@ def validate_atom_types(atom_types): try: tables.masses[atom_type.upper()] except KeyError: - warnings.warn("Failed to guess the mass for the following atom types: {}".format(atom_type)) + warnings.warn( + "Failed to guess the mass for the following atom types: {}".format( + atom_type + ) + ) @deprecate(release="2.8.0", remove="3.0.0", message=deprecation_msg) @@ -174,7 +180,9 @@ def guess_types(atom_names): ------- atom_types : np.ndarray dtype object """ - return np.array([guess_atom_element(name) for name in atom_names], dtype=object) + return np.array( + [guess_atom_element(name) for name in atom_names], dtype=object + ) @deprecate(release="2.8.0", remove="3.0.0", message=deprecation_msg) @@ -195,8 +203,8 @@ def guess_atom_type(atomname): return guess_atom_element(atomname) -NUMBERS = re.compile(r'[0-9]') # match numbers -SYMBOLS = re.compile(r'[*+-]') # match *, +, - +NUMBERS = re.compile(r"[0-9]") # match numbers +SYMBOLS = re.compile(r"[*+-]") # match *, +, - @deprecate(release="2.8.0", remove="3.0.0", message=deprecation_msg) @@ -216,19 +224,19 @@ def guess_atom_element(atomname): :func:`guess_atom_type` :mod:`MDAnalysis.topology.tables` """ - if atomname == '': - return '' + if atomname == "": + return "" try: return tables.atomelements[atomname.upper()] except KeyError: # strip symbols - no_symbols = re.sub(SYMBOLS, '', atomname) + no_symbols = re.sub(SYMBOLS, "", atomname) # split name by numbers no_numbers = re.split(NUMBERS, no_symbols) - no_numbers = list(filter(None, no_numbers)) #remove '' + no_numbers = list(filter(None, no_numbers)) # remove '' # if no_numbers is not empty, use the first element of no_numbers - name = no_numbers[0].upper() if no_numbers else '' + name = no_numbers[0].upper() if no_numbers else "" # just in case if name in tables.atomelements: @@ -317,10 +325,10 @@ def guess_bonds(atoms, coords, box=None, **kwargs): if len(atoms) != len(coords): raise ValueError("'atoms' and 'coord' must be the same length") - fudge_factor = kwargs.get('fudge_factor', 0.55) + fudge_factor = kwargs.get("fudge_factor", 0.55) vdwradii = tables.vdwradii.copy() # so I don't permanently change it - user_vdwradii = kwargs.get('vdwradii', None) + user_vdwradii = kwargs.get("vdwradii", None) if user_vdwradii: # this should make algo use their values over defaults vdwradii.update(user_vdwradii) @@ -329,13 +337,16 @@ def guess_bonds(atoms, coords, box=None, **kwargs): # check that all types have a defined vdw if not all(val in vdwradii for val in set(atomtypes)): - raise ValueError(("vdw radii for types: " + - ", ".join([t for t in set(atomtypes) if - not t in vdwradii]) + - ". These can be defined manually using the" + - " keyword 'vdwradii'")) + raise ValueError( + ( + "vdw radii for types: " + + ", ".join([t for t in set(atomtypes) if not t in vdwradii]) + + ". These can be defined manually using the" + + " keyword 'vdwradii'" + ) + ) - lower_bound = kwargs.get('lower_bound', 0.1) + lower_bound = kwargs.get("lower_bound", 0.1) if box is not None: box = np.asarray(box) @@ -347,13 +358,12 @@ def guess_bonds(atoms, coords, box=None, **kwargs): bonds = [] - pairs, dist = distances.self_capped_distance(coords, - max_cutoff=2.0*max_vdw, - min_cutoff=lower_bound, - box=box) + pairs, dist = distances.self_capped_distance( + coords, max_cutoff=2.0 * max_vdw, min_cutoff=lower_bound, box=box + ) for idx, (i, j) in enumerate(pairs): - d = (vdwradii[atomtypes[i]] + vdwradii[atomtypes[j]])*fudge_factor - if (dist[idx] < d): + d = (vdwradii[atomtypes[i]] + vdwradii[atomtypes[j]]) * fudge_factor + if dist[idx] < d: bonds.append((atoms[i].index, atoms[j].index)) return tuple(bonds) @@ -416,8 +426,9 @@ def guess_dihedrals(angles): a_tup = tuple([a.index for a in b]) # angle as tuple of numbers # if searching with b[0], want tuple of (b[2], b[1], b[0], +new) # search the first and last atom of each angle - for atom, prefix in zip([b.atoms[0], b.atoms[-1]], - [a_tup[::-1], a_tup]): + for atom, prefix in zip( + [b.atoms[0], b.atoms[-1]], [a_tup[::-1], a_tup] + ): for other_b in atom.bonds: if not other_b.partner(atom) in b: third_a = other_b.partner(atom) @@ -548,7 +559,9 @@ def guess_gasteiger_charges(atomgroup): """ mol = atomgroup.convert_to("RDKIT") from rdkit.Chem.rdPartialCharges import ComputeGasteigerCharges + ComputeGasteigerCharges(mol, throwOnParamFailure=True) - return np.array([atom.GetDoubleProp("_GasteigerCharge") - for atom in mol.GetAtoms()], - dtype=np.float32) + return np.array( + [atom.GetDoubleProp("_GasteigerCharge") for atom in mol.GetAtoms()], + dtype=np.float32, + ) diff --git a/package/MDAnalysis/topology/tpr/obj.py b/package/MDAnalysis/topology/tpr/obj.py index 6be8b40b74..93c5a83ee7 100644 --- a/package/MDAnalysis/topology/tpr/obj.py +++ b/package/MDAnalysis/topology/tpr/obj.py @@ -35,24 +35,59 @@ from ...guesser.tables import Z2SYMB TpxHeader = namedtuple( - "TpxHeader", [ - "ver_str", "precision", - "fver", "fgen", "file_tag", "natoms", "ngtc", "fep_state", "lamb", - "bIr", "bTop", "bX", "bV", "bF", "bBox", "sizeOfTprBody"]) + "TpxHeader", + [ + "ver_str", + "precision", + "fver", + "fgen", + "file_tag", + "natoms", + "ngtc", + "fep_state", + "lamb", + "bIr", + "bTop", + "bX", + "bV", + "bF", + "bBox", + "sizeOfTprBody", + ], +) Box = namedtuple("Box", "size rel v") Mtop = namedtuple("Mtop", "nmoltype moltypes nmolblock") Params = namedtuple("Params", "atnr ntypes functype reppow fudgeQQ") -Atom = namedtuple("Atom", ["m", "q", "mB", "qB", "tp", "typeB", "ptype", "resind", "atomnumber"]) +Atom = namedtuple( + "Atom", + ["m", "q", "mB", "qB", "tp", "typeB", "ptype", "resind", "atomnumber"], +) Atoms = namedtuple("Atoms", "atoms nr nres type typeB atomnames resnames") Ilist = namedtuple("Ilist", "nr ik, iatoms") -Molblock = namedtuple("Molblock", [ - "molb_type", "molb_nmol", "molb_natoms_mol", - "molb_nposres_xA", "molb_nposres_xB"]) +Molblock = namedtuple( + "Molblock", + [ + "molb_type", + "molb_nmol", + "molb_natoms_mol", + "molb_nposres_xA", + "molb_nposres_xB", + ], +) class MoleculeKind(object): - def __init__(self, name, atomkinds, bonds=None, angles=None, - dihe=None, impr=None, donors=None, acceptors=None): + def __init__( + self, + name, + atomkinds, + bonds=None, + angles=None, + dihe=None, + impr=None, + donors=None, + acceptors=None, + ): self.name = name # name of the molecule self.atomkinds = atomkinds self.bonds = bonds @@ -105,7 +140,8 @@ def remap_impr(self, atom_start_ndx): class AtomKind(object): def __init__( - self, id, name, type, resid, resname, mass, charge, atomic_number): + self, id, name, type, resid, resname, mass, charge, atomic_number + ): # id is only within the scope of a single molecule, not the whole system self.id = id self.name = name @@ -125,7 +161,7 @@ def element_symbol(self): is not recognized, which happens if a particle is not really an atom (e.g a coarse-grained particle), an empty string is returned. """ - return Z2SYMB.get(self.atomic_number, '') + return Z2SYMB.get(self.atomic_number, "") def __repr__(self): return ( @@ -152,4 +188,4 @@ def process(self, atom_ndx): # The format for all record is (type, atom1, atom2, ...) # but we are only interested in the atoms. for cursor in range(0, len(atom_ndx), self.natoms + 1): - yield atom_ndx[cursor + 1: cursor + 1 + self.natoms] + yield atom_ndx[cursor + 1 : cursor + 1 + self.natoms] diff --git a/package/MDAnalysis/topology/tpr/setting.py b/package/MDAnalysis/topology/tpr/setting.py index ebb749d588..1fd0a68283 100644 --- a/package/MDAnalysis/topology/tpr/setting.py +++ b/package/MDAnalysis/topology/tpr/setting.py @@ -38,8 +38,22 @@ """ #: Gromacs TPR file format versions that can be read by the TPRParser. -SUPPORTED_VERSIONS = (58, 73, 83, 100, 103, 110, 112, - 116, 119, 122, 127, 129, 133, 134) +SUPPORTED_VERSIONS = ( + 58, + 73, + 83, + 100, + 103, + 110, + 112, + 116, + 119, + 122, + 127, + 129, + 133, + 134, +) # Some constants STRLEN = 4096 @@ -50,7 +64,7 @@ NR_FOURDIHS = 4 # /src/gromacs/topology/idef.h egcNR = 10 # include/types/topolog.h TPX_TAG_RELEASE = "release" # /src/gromacs/fileio/tpxio.c -tpx_version = 103 # /src/gromacs/fileio/tpxio.c +tpx_version = 103 # /src/gromacs/fileio/tpxio.c tpx_generation = 27 # /src/gromacs/fileio/tpxio.c tpxv_RestrictedBendingAndCombinedAngleTorsionPotentials = 98 tpxv_GenericInternalParameters = 117 @@ -61,6 +75,7 @@ #: Function types from ``/include/types/idef.h`` +# fmt: off ( F_BONDS, F_G96BONDS, F_MORSE, F_CUBICBONDS, F_CONNBONDS, F_HARMONIC, F_FENEBONDS, F_TABBONDS, @@ -83,9 +98,12 @@ F_ETOT, F_ECONSERVED, F_TEMP, F_VTEMP_NOLONGERUSED, F_PDISPCORR, F_PRES, F_DHDL_CON, F_DVDL, F_DKDL, F_DVDL_COUL, F_DVDL_VDW, F_DVDL_BONDED, - F_DVDL_RESTRAINT, F_DVDL_TEMPERATURE, F_NRE) = list(range(95)) + F_DVDL_RESTRAINT, F_DVDL_TEMPERATURE, F_NRE +) = list(range(95)) +# fmt: on #: Function types from ``/src/gmxlib/tpxio.c`` +# fmt: off ftupd = [ (20, F_CUBICBONDS), (20, F_CONNBONDS), (20, F_HARMONIC), (34, F_FENEBONDS), (43, F_TABBONDS), (43, F_TABBONDSNC), (70, F_RESTRBONDS), @@ -110,6 +128,7 @@ (tpxv_VSite1, F_VSITE1), (tpxv_VSite2FD, F_VSITE2FD), ] +# fmt: on #: Interaction types from ``/gmxlib/ifunc.c`` interaction_types = [ @@ -206,5 +225,5 @@ ("DVV/DL", "dVvdw/dl", None), ("DVB/DL", "dVbonded/dl", None), ("DVR/DL", "dVrestraint/dl", None), - ("DVT/DL", "dVtemperature/dl", None) + ("DVT/DL", "dVtemperature/dl", None), ] diff --git a/package/pyproject.toml b/package/pyproject.toml index ae0d34422d..8be16c7f61 100644 --- a/package/pyproject.toml +++ b/package/pyproject.toml @@ -135,6 +135,7 @@ tables\.py | MDAnalysis/visualization/.*\.py | MDAnalysis/lib/.*\.py^ | MDAnalysis/transformations/.*\.py +| MDAnalysis/topology/.*\.py | MDAnalysis/analysis/.*\.py | MDAnalysis/guesser/.*\.py | MDAnalysis/converters/.*\.py diff --git a/testsuite/MDAnalysisTests/topology/base.py b/testsuite/MDAnalysisTests/topology/base.py index 6527ab8ae3..e7649d65f8 100644 --- a/testsuite/MDAnalysisTests/topology/base.py +++ b/testsuite/MDAnalysisTests/topology/base.py @@ -25,7 +25,7 @@ import MDAnalysis as mda from MDAnalysis.core.topology import Topology -mandatory_attrs = ['ids', 'resids', 'resnums', 'segids'] +mandatory_attrs = ["ids", "resids", "resnums", "segids"] class ParserBase(object): @@ -57,34 +57,56 @@ def test_mandatory_attributes(self, top): # attributes required as part of the API # ALL parsers must provide these for attr in mandatory_attrs: - assert hasattr(top, attr), 'Missing required attribute: {}'.format(attr) + assert hasattr(top, attr), "Missing required attribute: {}".format( + attr + ) def test_expected_attributes(self, top): # Extra attributes as declared in specific implementations for attr in self.expected_attrs: - assert hasattr(top, attr), 'Missing expected attribute: {}'.format(attr) + assert hasattr(top, attr), "Missing expected attribute: {}".format( + attr + ) def test_no_unexpected_attributes(self, top): - attrs = set(self.expected_attrs - + mandatory_attrs - + ['indices', 'resindices', 'segindices'] + self.guessed_attrs) + attrs = set( + self.expected_attrs + + mandatory_attrs + + ["indices", "resindices", "segindices"] + + self.guessed_attrs + ) for attr in top.attrs: - assert attr.attrname in attrs, 'Unexpected attribute: {}'.format(attr.attrname) + assert attr.attrname in attrs, "Unexpected attribute: {}".format( + attr.attrname + ) def test_size(self, top): """Check that the Topology is correctly sized""" - assert top.n_atoms == self.expected_n_atoms, '{} atoms read, {} expected in {}'.format( - top.n_atoms, self.expected_n_atoms, self.__class__.__name__) - - assert top.n_residues == self.expected_n_residues, '{} residues read, {} expected in {}'.format( - top.n_residues, self.expected_n_residues, self.__class__.__name__) - - assert top.n_segments == self.expected_n_segments, '{} segment read, {} expected in {}'.format( - top.n_segments, self.expected_n_segments, self.__class__.__name__) + assert ( + top.n_atoms == self.expected_n_atoms + ), "{} atoms read, {} expected in {}".format( + top.n_atoms, self.expected_n_atoms, self.__class__.__name__ + ) + + assert ( + top.n_residues == self.expected_n_residues + ), "{} residues read, {} expected in {}".format( + top.n_residues, self.expected_n_residues, self.__class__.__name__ + ) + + assert ( + top.n_segments == self.expected_n_segments + ), "{} segment read, {} expected in {}".format( + top.n_segments, self.expected_n_segments, self.__class__.__name__ + ) def test_tt_size(self, top): """Check that the transtable is appropriately sized""" - assert top.tt.size == (self.expected_n_atoms, self.expected_n_residues, self.expected_n_segments) + assert top.tt.size == ( + self.expected_n_atoms, + self.expected_n_residues, + self.expected_n_segments, + ) def test_creates_universe(self, filename): """Check that Universe works with this Parser""" @@ -95,8 +117,9 @@ def test_guessed_attributes(self, filename): """check that the universe created with certain parser have the same guessed attributes as when it was guessed inside the parser""" u = mda.Universe(filename) - u_guessed_attrs = [attr.attrname for attr - in u._topology.guessed_attributes] + u_guessed_attrs = [ + attr.attrname for attr in u._topology.guessed_attributes + ] for attr in self.guessed_attrs: assert hasattr(u.atoms, attr) assert attr in u_guessed_attrs diff --git a/testsuite/MDAnalysisTests/topology/test_altloc.py b/testsuite/MDAnalysisTests/topology/test_altloc.py index 1007c3e067..63b43c3213 100644 --- a/testsuite/MDAnalysisTests/topology/test_altloc.py +++ b/testsuite/MDAnalysisTests/topology/test_altloc.py @@ -50,7 +50,7 @@ def test_bonds(u): def test_write_read(u, tmpdir): - outfile = str(tmpdir.join('test.pdb')) + outfile = str(tmpdir.join("test.pdb")) u.select_atoms("all").write(outfile) u2 = Universe(outfile) assert len(u.atoms) == len(u2.atoms) diff --git a/testsuite/MDAnalysisTests/topology/test_crd.py b/testsuite/MDAnalysisTests/topology/test_crd.py index 7c9b0a7241..d7a8794c97 100644 --- a/testsuite/MDAnalysisTests/topology/test_crd.py +++ b/testsuite/MDAnalysisTests/topology/test_crd.py @@ -33,10 +33,16 @@ class TestCRDParser(ParserBase): parser = mda.topology.CRDParser.CRDParser ref_filename = CRD - expected_attrs = ['ids', 'names', 'tempfactors', - 'resids', 'resnames', 'resnums', - 'segids'] - guessed_attrs = ['masses', 'types'] + expected_attrs = [ + "ids", + "names", + "tempfactors", + "resids", + "resnames", + "resnums", + "segids", + ] + guessed_attrs = ["masses", "types"] expected_n_atoms = 3341 expected_n_residues = 214 @@ -44,10 +50,10 @@ class TestCRDParser(ParserBase): def test_guessed_masses(self, filename): u = mda.Universe(filename) - expected = [14.007, 1.008, 1.008, 1.008, 12.011, 1.008, 12.011] + expected = [14.007, 1.008, 1.008, 1.008, 12.011, 1.008, 12.011] assert_allclose(u.atoms.masses[:7], expected) def test_guessed_types(self, filename): u = mda.Universe(filename) - expected = ['N', 'H', 'H', 'H', 'C', 'H', 'C'] + expected = ["N", "H", "H", "H", "C", "H", "C"] assert (u.atoms.types[:7] == expected).all() diff --git a/testsuite/MDAnalysisTests/topology/test_dlpoly.py b/testsuite/MDAnalysisTests/topology/test_dlpoly.py index da1e871dcd..9ab731d439 100644 --- a/testsuite/MDAnalysisTests/topology/test_dlpoly.py +++ b/testsuite/MDAnalysisTests/topology/test_dlpoly.py @@ -34,7 +34,7 @@ DLP_HISTORY_order, DLP_HISTORY_minimal, DLP_HISTORY_minimal_cell, - DLP_HISTORY_classic + DLP_HISTORY_classic, ) @@ -50,8 +50,8 @@ def test_guessed_attributes(self, filename): class DLPBase2(DLPUniverse): - expected_attrs = ['ids', 'names'] - guessed_attrs = ['masses', 'types'] + expected_attrs = ["ids", "names"] + guessed_attrs = ["masses", "types"] expected_n_atoms = 216 expected_n_residues = 1 @@ -64,73 +64,72 @@ def test_guesssed_masses(self, filename): def test_guessed_types(self, filename): u = mda.Universe(filename, topology_format=self.format) - assert u.atoms.types[0] == 'K' - assert u.atoms.types[4] == 'CL' + assert u.atoms.types[0] == "K" + assert u.atoms.types[4] == "CL" def test_names(self, top): - assert top.names.values[0] == 'K+' - assert top.names.values[4] == 'Cl-' + assert top.names.values[0] == "K+" + assert top.names.values[4] == "Cl-" class TestDLPHistoryParser(DLPBase2): parser = mda.topology.DLPolyParser.HistoryParser ref_filename = DLP_HISTORY - format = 'HISTORY' + format = "HISTORY" class TestDLPConfigParser(DLPBase2): parser = mda.topology.DLPolyParser.ConfigParser ref_filename = DLP_CONFIG - format = 'CONFIG' + format = "CONFIG" class DLPBase(DLPUniverse): - expected_attrs = ['ids', 'names'] + expected_attrs = ["ids", "names"] expected_n_atoms = 3 expected_n_residues = 1 expected_n_segments = 1 def test_dlp_names(self, top): - assert_equal(top.names.values, - ['C', 'B', 'A']) + assert_equal(top.names.values, ["C", "B", "A"]) class TestDLPConfigMinimal(DLPBase): parser = mda.topology.DLPolyParser.ConfigParser ref_filename = DLP_CONFIG_minimal - format = 'CONFIG' + format = "CONFIG" class TestDLPConfigOrder(DLPBase): parser = mda.topology.DLPolyParser.ConfigParser ref_filename = DLP_CONFIG_order - format = 'CONFIG' + format = "CONFIG" class TestDLPHistoryMinimal(DLPBase): parser = mda.topology.DLPolyParser.HistoryParser ref_filename = DLP_HISTORY_minimal - format = 'HISTORY' + format = "HISTORY" class TestDLPHistoryMinimal(DLPBase): parser = mda.topology.DLPolyParser.HistoryParser ref_filename = DLP_HISTORY_minimal_cell - format = 'HISTORY' + format = "HISTORY" class TestDLPHistoryOrder(DLPBase): parser = mda.topology.DLPolyParser.HistoryParser ref_filename = DLP_HISTORY_order - format = 'HISTORY' + format = "HISTORY" class TestDLPHistoryClassic(DLPBase): parser = mda.topology.DLPolyParser.HistoryParser ref_filename = DLP_HISTORY_classic - format = 'HISTORY' + format = "HISTORY" def test_HISTORY_EOFError(): with pytest.raises(EOFError): - mda.Universe(DLP_CONFIG, topology_format='HISTORY') + mda.Universe(DLP_CONFIG, topology_format="HISTORY") diff --git a/testsuite/MDAnalysisTests/topology/test_dms.py b/testsuite/MDAnalysisTests/topology/test_dms.py index b1eb8d77b3..0465eb1de5 100644 --- a/testsuite/MDAnalysisTests/topology/test_dms.py +++ b/testsuite/MDAnalysisTests/topology/test_dms.py @@ -28,10 +28,19 @@ class TestDMSParser(ParserBase): parser = mda.topology.DMSParser.DMSParser ref_filename = DMS_DOMAINS - expected_attrs = ['ids', 'names', 'bonds', 'charges', - 'masses', 'resids', 'resnames', 'segids', - 'chainIDs', 'atomnums'] - guessed_attrs = ['types'] + expected_attrs = [ + "ids", + "names", + "bonds", + "charges", + "masses", + "resids", + "resnames", + "segids", + "chainIDs", + "atomnums", + ] + guessed_attrs = ["types"] expected_n_atoms = 3341 expected_n_residues = 214 expected_n_segments = 3 @@ -62,9 +71,9 @@ def test_atomsels(self, filename): assert len(s5) == 190 def test_guessed_types(self, filename): - u = mda.Universe(filename) - expected = ['N', 'H', 'H', 'H', 'C', 'H', 'C'] - assert (u.atoms.types[:7] == expected).all() + u = mda.Universe(filename) + expected = ["N", "H", "H", "H", "C", "H", "C"] + assert (u.atoms.types[:7] == expected).all() class TestDMSParserNoSegid(TestDMSParser): diff --git a/testsuite/MDAnalysisTests/topology/test_fhiaims.py b/testsuite/MDAnalysisTests/topology/test_fhiaims.py index b8bbc29e46..4086c107df 100644 --- a/testsuite/MDAnalysisTests/topology/test_fhiaims.py +++ b/testsuite/MDAnalysisTests/topology/test_fhiaims.py @@ -30,28 +30,25 @@ class TestFHIAIMS(ParserBase): parser = mda.topology.FHIAIMSParser.FHIAIMSParser - expected_attrs = ['names', 'elements'] - guessed_attrs = ['masses', 'types'] + expected_attrs = ["names", "elements"] + guessed_attrs = ["masses", "types"] expected_n_residues = 1 expected_n_segments = 1 expected_n_atoms = 6 ref_filename = FHIAIMS def test_names(self, top): - assert_equal(top.names.values, - ['O', 'H', 'H', 'O', 'H', 'H']) + assert_equal(top.names.values, ["O", "H", "H", "O", "H", "H"]) def test_guessed_types(self, filename): u = mda.Universe(filename) - assert_equal(u.atoms.types, - ['O', 'H', 'H', 'O', 'H', 'H']) + assert_equal(u.atoms.types, ["O", "H", "H", "O", "H", "H"]) def test_guessed_masses(self, filename): u = mda.Universe(filename) - assert_allclose(u.atoms.masses, - [15.999, 1.008, 1.008, 15.999, - 1.008, 1.008]) + assert_allclose( + u.atoms.masses, [15.999, 1.008, 1.008, 15.999, 1.008, 1.008] + ) def test_elements(self, top): - assert_equal(top.elements.values, - ['O', 'H', 'H', 'O', 'H', 'H']) + assert_equal(top.elements.values, ["O", "H", "H", "O", "H", "H"]) diff --git a/testsuite/MDAnalysisTests/topology/test_gms.py b/testsuite/MDAnalysisTests/topology/test_gms.py index 65935c14ba..8becdfda6f 100644 --- a/testsuite/MDAnalysisTests/topology/test_gms.py +++ b/testsuite/MDAnalysisTests/topology/test_gms.py @@ -34,8 +34,8 @@ class GMSBase(ParserBase): parser = mda.topology.GMSParser.GMSParser - expected_attrs = ['names', 'atomiccharges'] - guessed_attrs = ['masses', 'types'] + expected_attrs = ["names", "atomiccharges"] + guessed_attrs = ["masses", "types"] expected_n_residues = 1 expected_n_segments = 1 @@ -45,12 +45,10 @@ class TestGMSASYMOPT(GMSBase): ref_filename = GMS_ASYMOPT def test_names(self, top): - assert_equal(top.names.values, - ['O', 'H', 'H', 'O', 'H', 'H']) + assert_equal(top.names.values, ["O", "H", "H", "O", "H", "H"]) def test_types(self, top): - assert_equal(top.atomiccharges.values, - [8, 1, 1, 8, 1, 1]) + assert_equal(top.atomiccharges.values, [8, 1, 1, 8, 1, 1]) def test_guessed_masses(self, filename): u = mda.Universe(filename) @@ -59,7 +57,7 @@ def test_guessed_masses(self, filename): def test_guessed_types(self, filename): u = mda.Universe(filename) - expected = ['O', 'H', 'H', 'O', 'H', 'H'] + expected = ["O", "H", "H", "O", "H", "H"] assert (u.atoms.types == expected).all() @@ -68,12 +66,12 @@ class TestGMSSYMOPT(GMSBase): ref_filename = GMS_SYMOPT def test_names(self, top): - assert_equal(top.names.values, - ['CARBON', 'CARBON', 'HYDROGEN', 'HYDROGEN']) + assert_equal( + top.names.values, ["CARBON", "CARBON", "HYDROGEN", "HYDROGEN"] + ) def test_types(self, top): - assert_equal(top.atomiccharges.values, - [6, 6, 1, 1]) + assert_equal(top.atomiccharges.values, [6, 6, 1, 1]) class TestGMSASYMSURF(TestGMSASYMOPT): diff --git a/testsuite/MDAnalysisTests/topology/test_gro.py b/testsuite/MDAnalysisTests/topology/test_gro.py index f9d506fdba..3bd266b008 100644 --- a/testsuite/MDAnalysisTests/topology/test_gro.py +++ b/testsuite/MDAnalysisTests/topology/test_gro.py @@ -40,8 +40,8 @@ class TestGROParser(ParserBase): parser = mda.topology.GROParser.GROParser ref_filename = GRO - expected_attrs = ['ids', 'names', 'resids', 'resnames'] - guessed_attrs = ['masses', 'types'] + expected_attrs = ["ids", "names", "resids", "resnames"] + guessed_attrs = ["masses", "types"] expected_n_atoms = 47681 expected_n_residues = 11302 expected_n_segments = 1 @@ -54,17 +54,18 @@ def test_attr_size(self, top): def test_guessed_masses(self, filename): u = mda.Universe(filename) - expected = [14.007, 1.008, 1.008, 1.008, 12.011, 1.008, 12.011] + expected = [14.007, 1.008, 1.008, 1.008, 12.011, 1.008, 12.011] assert_allclose(u.atoms.masses[:7], expected) def test_guessed_types(self, filename): u = mda.Universe(filename) - expected = ['N', 'H', 'H', 'H', 'C', 'H', 'C'] + expected = ["N", "H", "H", "H", "C", "H", "C"] assert_equal(u.atoms.types[:7], expected) class TestGROWideBox(object): """Tests for Issue #548""" + def test_atoms(self): parser = mda.topology.GROParser.GROParser with parser(two_water_gro_widebox) as p: @@ -89,16 +90,23 @@ def test_parse_missing_atomname_IOerror(): class TestGroResidWrapping(object): # resid is 5 digit field, so is limited to 100k # check that parser recognises when resids have wrapped - names = ['MET', 'ARG', 'ILE', 'ILE', 'LEU', 'LEU', 'GLY'] + names = ["MET", "ARG", "ILE", "ILE", "LEU", "LEU", "GLY"] lengths = [19, 24, 19, 19, 19, 19, 7] parser = mda.topology.GROParser.GROParser - @pytest.mark.parametrize('parser, resids', ( - (GRO_residwrap, [1, 99999, 100000, 100001, 199999, 200000, 200001]), - (GRO_residwrap_0base, [0, 99999, 100000, 100001, 199999, 200000, - 200001]) - - )) + @pytest.mark.parametrize( + "parser, resids", + ( + ( + GRO_residwrap, + [1, 99999, 100000, 100001, 199999, 200000, 200001], + ), + ( + GRO_residwrap_0base, + [0, 99999, 100000, 100001, 199999, 200000, 200001], + ), + ), + ) def test_wrapping_resids(self, parser, resids): with self.parser(parser) as p: top = p.parse() @@ -116,7 +124,7 @@ def test_sameresid_diffresname(): with parser(GRO_sameresid_diffresname) as p: top = p.parse() resids = [9, 9] - resnames = ['GLN', 'POPC'] + resnames = ["GLN", "POPC"] for i, (resid, resname) in enumerate(zip(resids, resnames)): assert top.resids.values[i] == resid assert top.resnames.values[i] == resname diff --git a/testsuite/MDAnalysisTests/topology/test_gsd.py b/testsuite/MDAnalysisTests/topology/test_gsd.py index d183642013..54dbe49288 100644 --- a/testsuite/MDAnalysisTests/topology/test_gsd.py +++ b/testsuite/MDAnalysisTests/topology/test_gsd.py @@ -31,12 +31,23 @@ import os -@pytest.mark.skipif(not HAS_GSD, reason='gsd not installed') +@pytest.mark.skipif(not HAS_GSD, reason="gsd not installed") class GSDBase(ParserBase): parser = mda.topology.GSDParser.GSDParser - expected_attrs = ['ids', 'names', 'resids', 'resnames', 'masses', - 'charges', 'radii', 'types', - 'bonds', 'angles', 'dihedrals', 'impropers'] + expected_attrs = [ + "ids", + "names", + "resids", + "resnames", + "masses", + "charges", + "radii", + "types", + "bonds", + "angles", + "dihedrals", + "impropers", + ] expected_n_bonds = 0 expected_n_angles = 0 expected_n_dihedrals = 0 @@ -80,7 +91,7 @@ def test_impropers(self, top): assert top.impropers.values == [] -@pytest.mark.skipif(not HAS_GSD, reason='gsd not installed') +@pytest.mark.skipif(not HAS_GSD, reason="gsd not installed") class TestGSDParser(GSDBase): ref_filename = GSD expected_n_atoms = 5832 @@ -88,7 +99,7 @@ class TestGSDParser(GSDBase): expected_n_segments = 1 -@pytest.mark.skipif(not HAS_GSD, reason='gsd not installed') +@pytest.mark.skipif(not HAS_GSD, reason="gsd not installed") class TestGSDParserBonds(GSDBase): ref_filename = GSD_bonds expected_n_atoms = 490 @@ -102,16 +113,16 @@ def test_bonds_identity(self, top): vals = top.bonds.values for b in ((0, 1), (1, 2), (2, 3), (3, 4)): assert (b in vals) or (b[::-1] in vals) - assert ((0, 450) not in vals) + assert (0, 450) not in vals def test_angles_identity(self, top): vals = top.angles.values for b in ((0, 1, 2), (1, 2, 3), (2, 3, 4), (3, 4, 5)): assert (b in vals) or (b[::-1] in vals) - assert ((0, 350, 450) not in vals) + assert (0, 350, 450) not in vals def test_dihedrals_identity(self, top): vals = top.dihedrals.values for b in ((0, 1, 2, 3), (1, 2, 3, 4), (2, 3, 4, 5), (3, 4, 5, 6)): assert (b in vals) or (b[::-1] in vals) - assert ((0, 250, 350, 450) not in vals) + assert (0, 250, 350, 450) not in vals diff --git a/testsuite/MDAnalysisTests/topology/test_guessers.py b/testsuite/MDAnalysisTests/topology/test_guessers.py index 46581c6c99..69b1046f82 100644 --- a/testsuite/MDAnalysisTests/topology/test_guessers.py +++ b/testsuite/MDAnalysisTests/topology/test_guessers.py @@ -42,31 +42,38 @@ except ImportError: pass -requires_rdkit = pytest.mark.skipif(import_not_available("rdkit"), - reason="requires RDKit") +requires_rdkit = pytest.mark.skipif( + import_not_available("rdkit"), reason="requires RDKit" +) class TestGuessMasses(object): def test_guess_masses(self): - out = guessers.guess_masses(['C', 'C', 'H']) + out = guessers.guess_masses(["C", "C", "H"]) assert isinstance(out, np.ndarray) assert_equal(out, np.array([12.011, 12.011, 1.008])) def test_guess_masses_warn(self): - with pytest.warns(UserWarning, match='Failed to guess the mass'): - guessers.guess_masses(['X']) + with pytest.warns(UserWarning, match="Failed to guess the mass"): + guessers.guess_masses(["X"]) def test_guess_masses_miss(self): - out = guessers.guess_masses(['X', 'Z']) + out = guessers.guess_masses(["X", "Z"]) assert_equal(out, np.array([0.0, 0.0])) - @pytest.mark.parametrize('element, value', (('H', 1.008), ('XYZ', 0.0), )) + @pytest.mark.parametrize( + "element, value", + ( + ("H", 1.008), + ("XYZ", 0.0), + ), + ) def test_get_atom_mass(self, element, value): assert guessers.get_atom_mass(element) == value def test_guess_atom_mass(self): - assert guessers.guess_atom_mass('1H') == 1.008 + assert guessers.guess_atom_mass("1H") == 1.008 class TestGuessTypes(object): @@ -74,50 +81,53 @@ class TestGuessTypes(object): # guess_atom_type # guess_atom_element def test_guess_types(self): - out = guessers.guess_types(['MG2+', 'C12']) + out = guessers.guess_types(["MG2+", "C12"]) assert isinstance(out, np.ndarray) - assert_equal(out, np.array(['MG', 'C'], dtype=object)) + assert_equal(out, np.array(["MG", "C"], dtype=object)) def test_guess_atom_element(self): - assert guessers.guess_atom_element('MG2+') == 'MG' + assert guessers.guess_atom_element("MG2+") == "MG" def test_guess_atom_element_empty(self): - assert guessers.guess_atom_element('') == '' + assert guessers.guess_atom_element("") == "" def test_guess_atom_element_singledigit(self): - assert guessers.guess_atom_element('1') == '1' + assert guessers.guess_atom_element("1") == "1" def test_guess_atom_element_1H(self): - assert guessers.guess_atom_element('1H') == 'H' - assert guessers.guess_atom_element('2H') == 'H' - - @pytest.mark.parametrize('name, element', ( - ('AO5*', 'O'), - ('F-', 'F'), - ('HB1', 'H'), - ('OC2', 'O'), - ('1he2', 'H'), - ('3hg2', 'H'), - ('OH-', 'O'), - ('HO', 'H'), - ('he', 'H'), - ('zn', 'ZN'), - ('Ca2+', 'CA'), - ('CA', 'C'), - ('N0A', 'N'), - ('C0U', 'C'), - ('C0S', 'C'), - ('Na+', 'NA'), - ('Cu2+', 'CU') - )) + assert guessers.guess_atom_element("1H") == "H" + assert guessers.guess_atom_element("2H") == "H" + + @pytest.mark.parametrize( + "name, element", + ( + ("AO5*", "O"), + ("F-", "F"), + ("HB1", "H"), + ("OC2", "O"), + ("1he2", "H"), + ("3hg2", "H"), + ("OH-", "O"), + ("HO", "H"), + ("he", "H"), + ("zn", "ZN"), + ("Ca2+", "CA"), + ("CA", "C"), + ("N0A", "N"), + ("C0U", "C"), + ("C0S", "C"), + ("Na+", "NA"), + ("Cu2+", "CU"), + ), + ) def test_guess_element_from_name(self, name, element): assert guessers.guess_atom_element(name) == element def test_guess_charge(): # this always returns 0.0 - assert guessers.guess_atom_charge('this') == 0.0 + assert guessers.guess_atom_charge("this") == 0.0 def test_guess_bonds_Error(): @@ -141,42 +151,45 @@ def bond_sort(arr): # sort from low to high, also within a tuple # e.g. ([5, 4], [0, 1], [0, 3]) -> ([0, 1], [0, 3], [4, 5]) out = [] - for (i, j) in arr: + for i, j in arr: if i > j: i, j = j, i out.append((i, j)) return sorted(out) + def test_guess_bonds_water(): u = mda.Universe(datafiles.two_water_gro) - bonds = bond_sort(guessers.guess_bonds(u.atoms, u.atoms.positions, u.dimensions)) - assert_equal(bonds, ((0, 1), - (0, 2), - (3, 4), - (3, 5))) + bonds = bond_sort( + guessers.guess_bonds(u.atoms, u.atoms.positions, u.dimensions) + ) + assert_equal(bonds, ((0, 1), (0, 2), (3, 4), (3, 5))) + def test_guess_bonds_adk(): u = mda.Universe(datafiles.PSF, datafiles.DCD) u.atoms.types = guessers.guess_types(u.atoms.names) bonds = bond_sort(guessers.guess_bonds(u.atoms, u.atoms.positions)) - assert_equal(np.sort(u.bonds.indices, axis=0), - np.sort(bonds, axis=0)) + assert_equal(np.sort(u.bonds.indices, axis=0), np.sort(bonds, axis=0)) + def test_guess_bonds_peptide(): u = mda.Universe(datafiles.PSF_NAMD, datafiles.PDB_NAMD) u.atoms.types = guessers.guess_types(u.atoms.names) bonds = bond_sort(guessers.guess_bonds(u.atoms, u.atoms.positions)) - assert_equal(np.sort(u.bonds.indices, axis=0), - np.sort(bonds, axis=0)) - - -@pytest.mark.parametrize("smi", [ - "c1ccccc1", - "C1=CC=CC=C1", - "CCO", - "c1ccccc1Cc1ccccc1", - "CN1C=NC2=C1C(=O)N(C(=O)N2C)C", -]) + assert_equal(np.sort(u.bonds.indices, axis=0), np.sort(bonds, axis=0)) + + +@pytest.mark.parametrize( + "smi", + [ + "c1ccccc1", + "C1=CC=CC=C1", + "CCO", + "c1ccccc1Cc1ccccc1", + "CN1C=NC2=C1C(=O)N(C(=O)N2C)C", + ], +) @requires_rdkit def test_guess_aromaticities(smi): mol = Chem.MolFromSmiles(smi) @@ -187,20 +200,25 @@ def test_guess_aromaticities(smi): assert_equal(values, expected) -@pytest.mark.parametrize("smi", [ - "c1ccccc1", - "C1=CC=CC=C1", - "CCO", - "c1ccccc1Cc1ccccc1", - "CN1C=NC2=C1C(=O)N(C(=O)N2C)C", -]) +@pytest.mark.parametrize( + "smi", + [ + "c1ccccc1", + "C1=CC=CC=C1", + "CCO", + "c1ccccc1Cc1ccccc1", + "CN1C=NC2=C1C(=O)N(C(=O)N2C)C", + ], +) @requires_rdkit def test_guess_gasteiger_charges(smi): mol = Chem.MolFromSmiles(smi) mol = Chem.AddHs(mol) ComputeGasteigerCharges(mol, throwOnParamFailure=True) - expected = np.array([atom.GetDoubleProp("_GasteigerCharge") - for atom in mol.GetAtoms()], dtype=np.float32) + expected = np.array( + [atom.GetDoubleProp("_GasteigerCharge") for atom in mol.GetAtoms()], + dtype=np.float32, + ) u = mda.Universe(mol) values = guessers.guess_gasteiger_charges(u.atoms) assert_equal(values, expected) @@ -208,21 +226,24 @@ def test_guess_gasteiger_charges(smi): class TestDeprecationWarning: wmsg = ( - "MDAnalysis.topology.guessers is deprecated in favour of " - "the new Guessers API. " - "See MDAnalysis.guesser.default_guesser for more details." + "MDAnalysis.topology.guessers is deprecated in favour of " + "the new Guessers API. " + "See MDAnalysis.guesser.default_guesser for more details." ) - @pytest.mark.parametrize('func, arg', [ - [guessers.guess_masses, ['C']], - [guessers.validate_atom_types, ['C']], - [guessers.guess_types, ['CA']], - [guessers.guess_atom_type, 'CA'], - [guessers.guess_atom_element, 'CA'], - [guessers.get_atom_mass, 'C'], - [guessers.guess_atom_mass, 'CA'], - [guessers.guess_atom_charge, 'CA'], - ]) + @pytest.mark.parametrize( + "func, arg", + [ + [guessers.guess_masses, ["C"]], + [guessers.validate_atom_types, ["C"]], + [guessers.guess_types, ["CA"]], + [guessers.guess_atom_type, "CA"], + [guessers.guess_atom_element, "CA"], + [guessers.get_atom_mass, "C"], + [guessers.guess_atom_mass, "CA"], + [guessers.guess_atom_charge, "CA"], + ], + ) def test_mass_type_elements_deprecations(self, func, arg): with pytest.warns(DeprecationWarning, match=self.wmsg): func(arg) @@ -251,7 +272,7 @@ def test_angles_dihedral_deprecations(self): @requires_rdkit def test_rdkit_guessers_deprecations(self): - mol = Chem.MolFromSmiles('c1ccccc1') + mol = Chem.MolFromSmiles("c1ccccc1") mol = Chem.AddHs(mol) u = mda.Universe(mol) diff --git a/testsuite/MDAnalysisTests/topology/test_hoomdxml.py b/testsuite/MDAnalysisTests/topology/test_hoomdxml.py index 759a2aae78..091662abef 100644 --- a/testsuite/MDAnalysisTests/topology/test_hoomdxml.py +++ b/testsuite/MDAnalysisTests/topology/test_hoomdxml.py @@ -31,7 +31,14 @@ class TestHoomdXMLParser(ParserBase): parser = mda.topology.HoomdXMLParser.HoomdXMLParser ref_filename = HoomdXMLdata expected_attrs = [ - 'types', 'masses', 'charges', 'radii', 'bonds', 'angles', 'dihedrals', 'impropers' + "types", + "masses", + "charges", + "radii", + "bonds", + "angles", + "dihedrals", + "impropers", ] expected_n_atoms = 769 @@ -62,19 +69,19 @@ def test_bonds_identity(self, top): vals = top.bonds.values for b in ((0, 1), (1, 2), (2, 3), (3, 4)): assert (b in vals) or (b[::-1] in vals) - assert ((0, 450) not in vals) + assert (0, 450) not in vals def test_angles_identity(self, top): vals = top.angles.values for b in ((0, 1, 2), (1, 2, 3), (2, 3, 4), (3, 4, 5)): assert (b in vals) or (b[::-1] in vals) - assert ((0, 350, 450) not in vals) + assert (0, 350, 450) not in vals def test_dihedrals_identity(self, top): vals = top.dihedrals.values for b in ((0, 1, 2, 3), (1, 2, 3, 4), (2, 3, 4, 5), (3, 4, 5, 6)): assert (b in vals) or (b[::-1] in vals) - assert ((0, 250, 350, 450) not in vals) + assert (0, 250, 350, 450) not in vals def test_read_masses(self, top): assert_almost_equal(top.masses.values, 1.0) diff --git a/testsuite/MDAnalysisTests/topology/test_itp.py b/testsuite/MDAnalysisTests/topology/test_itp.py index 8711ac072a..5b3cf93411 100644 --- a/testsuite/MDAnalysisTests/topology/test_itp.py +++ b/testsuite/MDAnalysisTests/topology/test_itp.py @@ -45,13 +45,27 @@ class BaseITP(ParserBase): parser = mda.topology.ITPParser.ITPParser - expected_attrs = ['ids', 'names', 'types', 'masses', - 'charges', 'chargegroups', - 'resids', 'resnames', - 'segids', 'moltypes', 'molnums', - 'bonds', 'angles', 'dihedrals', 'impropers'] - - guessed_attrs = ['elements', ] + expected_attrs = [ + "ids", + "names", + "types", + "masses", + "charges", + "chargegroups", + "resids", + "resnames", + "segids", + "moltypes", + "molnums", + "bonds", + "angles", + "dihedrals", + "impropers", + ] + + guessed_attrs = [ + "elements", + ] expected_n_atoms = 63 expected_n_residues = 10 @@ -146,12 +160,26 @@ def test_impropers_type(self, universe): class TestITPNoMass(ParserBase): parser = mda.topology.ITPParser.ITPParser ref_filename = ITP_nomass - expected_attrs = ['ids', 'names', 'types', - 'charges', 'chargegroups', - 'resids', 'resnames', - 'segids', 'moltypes', 'molnums', - 'bonds', 'angles', 'dihedrals', 'impropers', 'masses', ] - guessed_attrs = ['elements', ] + expected_attrs = [ + "ids", + "names", + "types", + "charges", + "chargegroups", + "resids", + "resnames", + "segids", + "moltypes", + "molnums", + "bonds", + "angles", + "dihedrals", + "impropers", + "masses", + ] + guessed_attrs = [ + "elements", + ] expected_n_atoms = 60 expected_n_residues = 1 @@ -168,11 +196,23 @@ def test_mass_guess(self, universe): class TestITPAtomtypes(ParserBase): parser = mda.topology.ITPParser.ITPParser ref_filename = ITP_atomtypes - expected_attrs = ['ids', 'names', 'types', - 'charges', 'chargegroups', - 'resids', 'resnames', 'masses', - 'segids', 'moltypes', 'molnums', - 'bonds', 'angles', 'dihedrals', 'impropers'] + expected_attrs = [ + "ids", + "names", + "types", + "charges", + "chargegroups", + "resids", + "resnames", + "masses", + "segids", + "moltypes", + "molnums", + "bonds", + "angles", + "dihedrals", + "impropers", + ] expected_n_atoms = 4 expected_n_residues = 1 @@ -186,7 +226,7 @@ def test_charge_parse(self, universe): assert_allclose(universe.atoms[0].charge, 4) assert_allclose(universe.atoms[1].charge, 1.1) assert_allclose(universe.atoms[2].charge, -3.000) - assert_allclose(universe.atoms[3].charge, 1.) + assert_allclose(universe.atoms[3].charge, 1.0) def test_mass_parse_or_guess(self, universe): # read from [ atoms ] section @@ -202,12 +242,26 @@ def test_mass_parse_or_guess(self, universe): class TestITPCharges(ParserBase): parser = mda.topology.ITPParser.ITPParser ref_filename = ITP_charges - expected_attrs = ['ids', 'names', 'types', 'masses', - 'charges', 'chargegroups', - 'resids', 'resnames', - 'segids', 'moltypes', 'molnums', - 'bonds', 'angles', 'dihedrals', 'impropers'] - guessed_attrs = ['elements', ] + expected_attrs = [ + "ids", + "names", + "types", + "masses", + "charges", + "chargegroups", + "resids", + "resnames", + "segids", + "moltypes", + "molnums", + "bonds", + "angles", + "dihedrals", + "impropers", + ] + guessed_attrs = [ + "elements", + ] expected_n_atoms = 9 expected_n_residues = 3 @@ -221,7 +275,7 @@ def test_charge_parse(self, universe): assert_allclose(universe.atoms[0].charge, -1.0) assert_allclose(universe.atoms[1].charge, 0) assert_allclose(universe.atoms[2].charge, 0) - assert_allclose(universe.atoms[3].charge, -1.) + assert_allclose(universe.atoms[3].charge, -1.0) def test_masses_are_read(self, universe): assert_allclose(universe.atoms.masses, [100] * 9) @@ -252,12 +306,27 @@ def test_dihedrals_identity(self, universe): class TestITPNoKeywords(BaseITP): - expected_attrs = ['ids', 'names', 'types', - 'charges', 'chargegroups', - 'resids', 'resnames', - 'segids', 'moltypes', 'molnums', - 'bonds', 'angles', 'dihedrals', 'impropers', 'masses', ] - guessed_attrs = ['elements', 'masses', ] + expected_attrs = [ + "ids", + "names", + "types", + "charges", + "chargegroups", + "resids", + "resnames", + "segids", + "moltypes", + "molnums", + "bonds", + "angles", + "dihedrals", + "impropers", + "masses", + ] + guessed_attrs = [ + "elements", + "masses", + ] """ Test reading ITP files *without* defined keywords. @@ -285,7 +354,7 @@ class TestITPNoKeywords(BaseITP): def test_whether_settles_types(self, universe): for param in list(universe.bonds) + list(universe.angles): - assert param.type == 'settles' + assert param.type == "settles" def test_bonds_values(self, top): vals = top.bonds.values @@ -298,8 +367,9 @@ def test_defines(self, top): def test_guessed_masses(self, filename): u = mda.Universe(filename) - assert_allclose(u.atoms.masses, - [15.999, 15.999, 15.999, 15.999, 15.999]) + assert_allclose( + u.atoms.masses, [15.999, 15.999, 15.999, 15.999, 15.999] + ) class TestITPKeywords(TestITPNoKeywords): @@ -313,14 +383,20 @@ class TestITPKeywords(TestITPNoKeywords): @pytest.fixture def universe(self, filename): - return mda.Universe(filename, FLEXIBLE=True, EXTRA_ATOMS=True, - HW1_CHARGE=1, HW2_CHARGE=3) + return mda.Universe( + filename, + FLEXIBLE=True, + EXTRA_ATOMS=True, + HW1_CHARGE=1, + HW2_CHARGE=3, + ) @pytest.fixture() def top(self, filename): with self.parser(filename) as p: - yield p.parse(FLEXIBLE=True, EXTRA_ATOMS=True, - HW1_CHARGE=1, HW2_CHARGE=3) + yield p.parse( + FLEXIBLE=True, EXTRA_ATOMS=True, HW1_CHARGE=1, HW2_CHARGE=3 + ) def test_whether_settles_types(self, universe): for param in list(universe.bonds) + list(universe.angles): @@ -340,6 +416,7 @@ class TestNestedIfs(BaseITP): """ Test reading ITP files with nested ifdef/ifndef conditions. """ + ref_filename = ITP_spce expected_n_atoms = 7 expected_n_residues = 1 @@ -352,7 +429,9 @@ class TestNestedIfs(BaseITP): @pytest.fixture def universe(self, filename): - return mda.Universe(filename, HEAVY_H=True, EXTRA_ATOMS=True, HEAVY_SIX=True) + return mda.Universe( + filename, HEAVY_H=True, EXTRA_ATOMS=True, HEAVY_SIX=True + ) @pytest.fixture() def top(self, filename): @@ -386,7 +465,9 @@ def top(self, filename): @pytest.fixture() def universe(self, filename): - return mda.Universe(filename, topology_format='ITP', include_dir=GMX_DIR) + return mda.Universe( + filename, topology_format="ITP", include_dir=GMX_DIR + ) def test_output(self, filename): """Testing the call signature""" @@ -395,19 +476,21 @@ def test_output(self, filename): def test_creates_universe(self, filename): """Check that Universe works with this Parser""" - u = mda.Universe(filename, topology_format='ITP', include_dir=GMX_DIR) + u = mda.Universe(filename, topology_format="ITP", include_dir=GMX_DIR) def test_guessed_attributes(self, filename): """check that the universe created with certain parser have the same guessed attributes as when it was guessed inside the parser""" - u = mda.Universe(filename, topology_format='ITP', include_dir=GMX_DIR) + u = mda.Universe(filename, topology_format="ITP", include_dir=GMX_DIR) for attr in self.guessed_attrs: assert hasattr(u.atoms, attr) def test_sequential(self, universe): resids = np.array(list(range(2, 12)) + list(range(13, 23))) assert_equal(universe.residues.resids[:20], resids) - assert_equal(universe.residues.resindices, np.arange(self.expected_n_residues)) + assert_equal( + universe.residues.resindices, np.arange(self.expected_n_residues) + ) assert_equal(universe.atoms.chargegroups[-1], 63) @@ -442,7 +525,7 @@ def test_relstring(self, tmpdir): p2 = tmpdir.mkdir("sub2") p2.chdir() with p2.as_cwd() as pchange: - u = mda.Universe(str("../sub1/test.itp"), format='ITP') + u = mda.Universe(str("../sub1/test.itp"), format="ITP") def test_relpath(self, tmpdir): content = """ @@ -455,7 +538,7 @@ def test_relpath(self, tmpdir): p2.chdir() with p2.as_cwd() as pchange: relpath = Path("../sub1/test.itp") - u = mda.Universe(relpath, format='ITP') + u = mda.Universe(relpath, format="ITP") def test_relative_path(self, tmpdir): test_itp_content = '#include "../atoms.itp"' @@ -485,8 +568,10 @@ def test_missing_elements_no_attribute(): 1) a warning is raised if elements are missing 2) the elements attribute is not set """ - wmsg = ("Element information is missing, elements attribute " - "will not be populated. If needed these can be ") + wmsg = ( + "Element information is missing, elements attribute " + "will not be populated. If needed these can be " + ) with pytest.warns(UserWarning, match=wmsg): u = mda.Universe(ITP_atomtypes) with pytest.raises(AttributeError): diff --git a/testsuite/MDAnalysisTests/topology/test_lammpsdata.py b/testsuite/MDAnalysisTests/topology/test_lammpsdata.py index 7e76c2e7a3..c5f087b89b 100644 --- a/testsuite/MDAnalysisTests/topology/test_lammpsdata.py +++ b/testsuite/MDAnalysisTests/topology/test_lammpsdata.py @@ -43,8 +43,16 @@ class LammpsBase(ParserBase): parser = mda.topology.LAMMPSParser.DATAParser expected_n_segments = 1 - expected_attrs = ['types', 'resids', 'masses', 'charges', - 'bonds', 'angles', 'dihedrals', 'impropers'] + expected_attrs = [ + "types", + "resids", + "masses", + "charges", + "bonds", + "angles", + "dihedrals", + "impropers", + ] def test_n_atom_types(self, top): assert_equal(len(set(top.types.values)), self.expected_n_atom_types) @@ -53,7 +61,7 @@ def test_n_bonds(self, top): if self.ref_n_bonds: assert_equal(len(top.bonds.values), self.ref_n_bonds) else: - assert not hasattr(top, 'bonds') + assert not hasattr(top, "bonds") def test_bond_member(self, top): if self.ref_n_bonds: @@ -63,7 +71,7 @@ def test_n_angles(self, top): if self.ref_n_angles: assert_equal(len(top.angles.values), self.ref_n_angles) else: - assert not hasattr(self.top, 'angles') + assert not hasattr(self.top, "angles") def test_angle_member(self, top): if self.ref_n_angles: @@ -73,7 +81,7 @@ def test_n_dihedrals(self, top): if self.ref_n_dihedrals: assert_equal(len(top.dihedrals.values), self.ref_n_dihedrals) else: - assert not hasattr(self.top, 'dihedrals') + assert not hasattr(self.top, "dihedrals") def test_dihedral_member(self, top): if self.ref_n_dihedrals: @@ -83,17 +91,17 @@ def test_n_impropers(self, top): if self.ref_n_impropers: assert_equal(len(top.impropers.values), self.ref_n_impropers) else: - assert not hasattr(self.top, 'impropers') + assert not hasattr(self.top, "impropers") def test_improper_member(self, top): if self.ref_n_impropers: assert self.ref_improper in top.impropers.values def test_creates_universe(self, filename): - u = mda.Universe(filename, format='DATA') + u = mda.Universe(filename, format="DATA") def test_guessed_attributes(self, filename): - u = mda.Universe(filename, format='DATA') + u = mda.Universe(filename, format="DATA") for attr in self.guessed_attrs: assert hasattr(u.atoms, attr) @@ -104,6 +112,7 @@ class TestLammpsData(LammpsBase): The reading of coords and velocities is done separately in test_coordinates """ + ref_filename = LAMMPSdata expected_n_atoms = 18364 expected_n_atom_types = 10 @@ -165,24 +174,32 @@ class TestLAMMPSDeletedAtoms(LammpsBase): def test_atom_ids(self, filename): u = mda.Universe(filename) - assert_equal(u.atoms.ids, - [1, 10, 1002, 2003, 2004, 2005, 2006, 2007, 2008, 2009]) + assert_equal( + u.atoms.ids, + [1, 10, 1002, 2003, 2004, 2005, 2006, 2007, 2008, 2009], + ) def test_traj(self, filename): u = mda.Universe(filename) - assert_equal(u.atoms.positions, - np.array([[11.8998565674, 48.4455718994, 19.0971984863], - [14.5285415649, 50.6892776489, 19.9419136047], - [12.8466796875, 48.1473007202, 18.6461906433], - [11.0093536377, 48.7145767212, 18.5247917175], - [12.4033203125, 49.2582168579, 20.2825050354], - [13.0947723389, 48.8437194824, 21.0175533295], - [11.540184021, 49.6138534546, 20.8459072113], - [13.0085144043, 50.6062469482, 19.9141769409], - [12.9834518433, 51.1562423706, 18.9713554382], - [12.6588821411, 51.4160842896, 20.5548400879]], - dtype=np.float32)) + assert_equal( + u.atoms.positions, + np.array( + [ + [11.8998565674, 48.4455718994, 19.0971984863], + [14.5285415649, 50.6892776489, 19.9419136047], + [12.8466796875, 48.1473007202, 18.6461906433], + [11.0093536377, 48.7145767212, 18.5247917175], + [12.4033203125, 49.2582168579, 20.2825050354], + [13.0947723389, 48.8437194824, 21.0175533295], + [11.540184021, 49.6138534546, 20.8459072113], + [13.0085144043, 50.6062469482, 19.9141769409], + [12.9834518433, 51.1562423706, 18.9713554382], + [12.6588821411, 51.4160842896, 20.5548400879], + ], + dtype=np.float32, + ), + ) class TestLammpsDataPairIJ(LammpsBase): @@ -190,8 +207,15 @@ class TestLammpsDataPairIJ(LammpsBase): PairIJ Coeffs section """ - expected_attrs = ['types', 'resids', 'masses', - 'bonds', 'angles', 'dihedrals', 'impropers'] + expected_attrs = [ + "types", + "resids", + "masses", + "bonds", + "angles", + "dihedrals", + "impropers", + ] ref_filename = LAMMPSdata_PairIJ expected_n_atoms = 800 expected_n_atom_types = 2 @@ -228,47 +252,62 @@ class TestLammpsDataPairIJ(LammpsBase): 1 1 3.7151744275286681e+01 1.8684434743140471e+01 1.9285127961842125e+01 0 0 0 """ + def test_noresid(): - u = mda.Universe(StringIO(LAMMPS_NORESID), format='data', - atom_style='id type x y z') + u = mda.Universe( + StringIO(LAMMPS_NORESID), format="data", atom_style="id type x y z" + ) assert len(u.atoms) == 1 assert_equal(u.atoms[0].mass, 28.0) - assert_equal(u.atoms.positions, - np.array([[3.7151744275286681e+01, - 1.8684434743140471e+01, - 1.9285127961842125e+01]], dtype=np.float32)) + assert_equal( + u.atoms.positions, + np.array( + [ + [ + 3.7151744275286681e01, + 1.8684434743140471e01, + 1.9285127961842125e01, + ] + ], + dtype=np.float32, + ), + ) + def test_noresid_failure(): with pytest.raises( - ValueError, - match='.+?You can supply a description of the atom_style.+?', + ValueError, + match=".+?You can supply a description of the atom_style.+?", ): - u = mda.Universe(StringIO(LAMMPS_NORESID), format='data') + u = mda.Universe(StringIO(LAMMPS_NORESID), format="data") def test_interpret_atom_style(): style = mda.topology.LAMMPSParser.DATAParser._interpret_atom_style( - 'id charge type z y x') + "id charge type z y x" + ) assert isinstance(style, dict) - assert style['id'] == 0 - assert style['type'] == 2 - assert style['charge'] == 1 - assert style['x'] == 5 - assert style['y'] == 4 - assert style['z'] == 3 + assert style["id"] == 0 + assert style["type"] == 2 + assert style["charge"] == 1 + assert style["x"] == 5 + assert style["y"] == 4 + assert style["z"] == 3 def test_interpret_atom_style_missing(): - with pytest.raises(ValueError, - match='atom_style string missing required.+?'): + with pytest.raises( + ValueError, match="atom_style string missing required.+?" + ): style = mda.topology.LAMMPSParser.DATAParser._interpret_atom_style( - 'id charge z y x') + "id charge z y x" + ) class TestDumpParser(ParserBase): - expected_attrs = ['types', 'masses'] + expected_attrs = ["types", "masses"] expected_n_atoms = 24 expected_n_residues = 1 expected_n_segments = 1 @@ -277,7 +316,7 @@ class TestDumpParser(ParserBase): ref_filename = LAMMPSDUMP def test_creates_universe(self): - u = mda.Universe(self.ref_filename, format='LAMMPSDUMP') + u = mda.Universe(self.ref_filename, format="LAMMPSDUMP") assert isinstance(u, mda.Universe) assert len(u.atoms) == 24 @@ -286,30 +325,31 @@ def test_masses_warning(self): # masses are mandatory, but badly guessed # check that user is alerted with self.parser(self.ref_filename) as p: - with pytest.warns(UserWarning, match='Guessed all Masses to 1.0'): + with pytest.warns(UserWarning, match="Guessed all Masses to 1.0"): p.parse() def test_guessed_attributes(self, filename): - u = mda.Universe(filename, format='LAMMPSDUMP') + u = mda.Universe(filename, format="LAMMPSDUMP") for attr in self.guessed_attrs: assert hasattr(u.atoms, attr) def test_id_ordering(self): # ids are nonsequential in file, but should get rearranged - u = mda.Universe(self.ref_filename, format='LAMMPSDUMP') + u = mda.Universe(self.ref_filename, format="LAMMPSDUMP") # the 4th in file has id==13, but should have been sorted assert u.atoms[3].id == 4 def test_guessed_masses(self, filename): - u = mda.Universe(filename, format='LAMMPSDUMP') - expected = [1., 1., 1., 1., 1., 1., 1.] + u = mda.Universe(filename, format="LAMMPSDUMP") + expected = [1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0] assert_allclose(u.atoms.masses[:7], expected) def test_guessed_types(self, filename): - u = mda.Universe(filename, format='LAMMPSDUMP') - expected = ['2', '1', '1', '2', '1', '1', '2'] + u = mda.Universe(filename, format="LAMMPSDUMP") + expected = ["2", "1", "1", "2", "1", "1", "2"] assert (u.atoms.types[:7] == expected).all() + # this tests that topology can still be constructed if non-standard or uneven # column present. class TestDumpParserLong(TestDumpParser): diff --git a/testsuite/MDAnalysisTests/topology/test_minimal.py b/testsuite/MDAnalysisTests/topology/test_minimal.py index 1a1cee1d6a..2d0e1ffd87 100644 --- a/testsuite/MDAnalysisTests/topology/test_minimal.py +++ b/testsuite/MDAnalysisTests/topology/test_minimal.py @@ -41,7 +41,8 @@ working_readers = pytest.mark.parametrize( - 'filename,expected_n_atoms', [ + "filename,expected_n_atoms", + [ (DCD, 3341), (INPCRD, 5), (LAMMPSdcd2, 12421), @@ -49,7 +50,9 @@ (TRR, 47681), (XTC, 47681), (np.zeros((1, 10, 3)), 10), # memory reader default - ]) + ], +) + @working_readers def test_minimal_parser(filename, expected_n_atoms): @@ -65,14 +68,18 @@ def test_universe_with_minimal(filename, expected_n_atoms): assert len(u.atoms) == expected_n_atoms -nonworking_readers = pytest.mark.parametrize('filename,n_atoms', [ - (TRJ, 252), - (TRJncdf, 2661), - (TRZ, 8184), -]) +nonworking_readers = pytest.mark.parametrize( + "filename,n_atoms", + [ + (TRJ, 252), + (TRJncdf, 2661), + (TRZ, 8184), + ], +) + @nonworking_readers -def test_minimal_parser_fail(filename,n_atoms): +def test_minimal_parser_fail(filename, n_atoms): with MinimalParser(filename) as p: with pytest.raises(NotImplementedError): p.parse() @@ -89,15 +96,18 @@ def test_minimal_n_atoms_kwarg(filename, n_atoms): def memory_possibilities(): # iterate over all possible shapes for a MemoryReader array # number of frames, atoms and coordinates - n = {'f': 1, 'a': 10, 'c': 3} - for permutation in itertools.permutations('fac', 3): - order = ''.join(permutation) + n = {"f": 1, "a": 10, "c": 3} + for permutation in itertools.permutations("fac", 3): + order = "".join(permutation) array = np.zeros([n[val] for val in permutation]) yield array, order -memory_reader = pytest.mark.parametrize('array,order', list(memory_possibilities())) +memory_reader = pytest.mark.parametrize( + "array,order", list(memory_possibilities()) +) + @memory_reader def test_memory_minimal_parser(array, order): @@ -105,6 +115,7 @@ def test_memory_minimal_parser(array, order): top = p.parse(order=order) assert top.n_atoms == 10 + @memory_reader def test_memory_universe(array, order): u = mda.Universe(array, order=order, to_guess=()) diff --git a/testsuite/MDAnalysisTests/topology/test_mmtf.py b/testsuite/MDAnalysisTests/topology/test_mmtf.py index 9e2a85f778..7125d23f2d 100644 --- a/testsuite/MDAnalysisTests/topology/test_mmtf.py +++ b/testsuite/MDAnalysisTests/topology/test_mmtf.py @@ -12,16 +12,28 @@ class MMTFBase(ParserBase): expected_attrs = [ - 'ids', 'names', 'types', 'altLocs', 'tempfactors', 'occupancies', - 'charges', 'names', 'resnames', 'resids', 'resnums', 'icodes', - 'segids', 'bonds', 'models' + "ids", + "names", + "types", + "altLocs", + "tempfactors", + "occupancies", + "charges", + "names", + "resnames", + "resids", + "resnums", + "icodes", + "segids", + "bonds", + "models", ] class TestMMTFParser(MMTFBase): parser = mda.topology.MMTFParser.MMTFParser ref_filename = MMTF - guessed_attrs = ['masses'] + guessed_attrs = ["masses"] expected_n_atoms = 512 expected_n_residues = 124 expected_n_segments = 8 @@ -40,7 +52,7 @@ class TestMMTFSkinny(MMTFBase): # for all attributes often in MMTF, # check that we get expected error on access # (sort so pytest gets reliable order) - guessed_attrs = ['ids', 'masses', 'segids'] + guessed_attrs = ["ids", "masses", "segids"] expected_n_atoms = 660 expected_n_residues = 134 expected_n_segments = 2 @@ -49,7 +61,7 @@ class TestMMTFSkinny(MMTFBase): class TestMMTFSkinny2(MMTFBase): parser = mda.topology.MMTFParser.MMTFParser ref_filename = MMTF_skinny2 - guessed_attrs = ['ids', 'masses', 'segids'] + guessed_attrs = ["ids", "masses", "segids"] expected_n_atoms = 169 expected_n_residues = 44 expected_n_segments = 2 @@ -70,10 +82,10 @@ def test_names(self, u): assert_equal(u.atoms.names[:3], ["O5'", "C5'", "C4'"]) def test_resnames(self, u): - assert_equal(u.residues.resnames[:3], ['DG', 'DA', 'DA']) + assert_equal(u.residues.resnames[:3], ["DG", "DA", "DA"]) def test_segids(self, u): - assert_equal(u.segments[:3].segids, ['A', 'B', 'C']) + assert_equal(u.segments[:3].segids, ["A", "B", "C"]) def test_resids(self, u): assert_equal(u.residues.resids[-3:], [2008, 2009, 2010]) @@ -86,16 +98,16 @@ def test_bfactors(self, u): assert_equal(u.atoms.bfactors[:3], [9.48, 10.88, 10.88]) def test_types(self, u): - assert_equal(u.atoms.types[:3], ['O', 'C', 'C']) + assert_equal(u.atoms.types[:3], ["O", "C", "C"]) def test_models(self, u): assert all(u.atoms.models == 0) def test_icodes(self, u): - assert all(u.atoms.icodes == '') + assert all(u.atoms.icodes == "") def test_altlocs(self, u): - assert all(u.atoms.altLocs[:3] == '') + assert all(u.atoms.altLocs[:3] == "") def test_guessed_masses(self, u): expected = [15.999, 12.011, 12.011, 15.999, 12.011, 15.999, 12.011] @@ -143,33 +155,33 @@ def u(self): return mda.Universe(MMTF_gz) def test_model_selection(self, u): - m1 = u.select_atoms('model 0') - m2 = u.select_atoms('model 1') + m1 = u.select_atoms("model 0") + m2 = u.select_atoms("model 1") assert len(m1) == 570 assert len(m2) == 570 def test_model_multiple(self, u): - m2plus = u.select_atoms('model 1-10') + m2plus = u.select_atoms("model 1-10") assert len(m2plus) == 570 def test_model_multiple_2(self, u): - m2plus = u.select_atoms('model 1:10') + m2plus = u.select_atoms("model 1:10") assert len(m2plus) == 570 def test_model_multiple_3(self, u): - m1and2 = u.select_atoms('model 0-1') + m1and2 = u.select_atoms("model 0-1") assert len(m1and2) == 1140 def test_model_multiple_4(self, u): - m1and2 = u.select_atoms('model 0:1') + m1and2 = u.select_atoms("model 0:1") assert len(m1and2) == 1140 def test_model_multiple_5(self, u): - m1and2 = u.select_atoms('model 0 1') + m1and2 = u.select_atoms("model 0 1") assert len(m1and2) == 1140 diff --git a/testsuite/MDAnalysisTests/topology/test_mol2.py b/testsuite/MDAnalysisTests/topology/test_mol2.py index 604fbe6362..4157e8273a 100644 --- a/testsuite/MDAnalysisTests/topology/test_mol2.py +++ b/testsuite/MDAnalysisTests/topology/test_mol2.py @@ -176,11 +176,17 @@ class TestMOL2Base(ParserBase): parser = mda.topology.MOL2Parser.MOL2Parser expected_attrs = [ - 'ids', 'names', 'types', 'charges', 'resids', 'resnames', 'bonds', - 'elements', + "ids", + "names", + "types", + "charges", + "resids", + "resnames", + "bonds", + "elements", ] - guessed_attrs = ['masses'] + guessed_attrs = ["masses"] expected_n_atoms = 49 expected_n_residues = 1 expected_n_segments = 1 @@ -204,10 +210,12 @@ def filename(self, request): def test_bond_orders(): - ref_orders = ('am 1 1 2 1 2 1 1 am 1 1 am 2 2 ' - '1 1 1 1 1 1 1 1 1 1 1 1 1 1 ' - 'ar ar ar 1 ar 1 ar 1 ar 1 1 1 ' - '2 1 1 1 1 2 1 1 2 1 1').split() + ref_orders = ( + "am 1 1 2 1 2 1 1 am 1 1 am 2 2 " + "1 1 1 1 1 1 1 1 1 1 1 1 1 1 " + "ar ar ar 1 ar 1 ar 1 ar 1 1 1 " + "2 1 1 1 1 2 1 1 2 1 1" + ).split() u = mda.Universe(mol2_molecule) orders = [bond.order for bond in u.atoms.bonds] assert_equal(orders, ref_orders) @@ -217,8 +225,7 @@ def test_elements(): u = mda.Universe(mol2_molecule) assert_equal( - u.atoms.elements[:5], - np.array(["N", "S", "N", "N", "O"], dtype="U3") + u.atoms.elements[:5], np.array(["N", "S", "N", "N", "O"], dtype="U3") ) @@ -227,22 +234,19 @@ def test_elements_selection(): u = mda.Universe(mol2_molecule) ag = u.select_atoms("element S") - assert_equal( - ag.elements, - np.array(["S", "S"], dtype="U3") - ) + assert_equal(ag.elements, np.array(["S", "S"], dtype="U3")) def test_wrong_elements_warnings(): - with pytest.warns(UserWarning, match='Unknown elements found') as record: - u = mda.Universe(StringIO(mol2_wrong_element), format='MOL2') + with pytest.warns(UserWarning, match="Unknown elements found") as record: + u = mda.Universe(StringIO(mol2_wrong_element), format="MOL2") # One warning from invalid elements, one from masses PendingDeprecationWarning assert len(record) == 3 - expected_elements = np.array(['N', '', ''], dtype=object) + expected_elements = np.array(["N", "", ""], dtype=object) guseed_masses = np.array([14.007, 0.0, 0.0], dtype=float) - gussed_types = np.array(['N.am', 'X.o2', 'XX.am']) + gussed_types = np.array(["N.am", "X.o2", "XX.am"]) assert_equal(u.atoms.elements, expected_elements) assert_equal(u.atoms.types, gussed_types) @@ -250,29 +254,40 @@ def test_wrong_elements_warnings(): def test_all_wrong_elements_warnings(): - with pytest.warns(UserWarning, match='Unknown elements found'): - u = mda.Universe(StringIO(mol2_all_wrong_elements), format='MOL2') + with pytest.warns(UserWarning, match="Unknown elements found"): + u = mda.Universe(StringIO(mol2_all_wrong_elements), format="MOL2") - with pytest.raises(mda.exceptions.NoDataError, - match='This Universe does not contain element ' - 'information'): + with pytest.raises( + mda.exceptions.NoDataError, + match="This Universe does not contain element " "information", + ): u.atoms.elements def test_all_elements(): - with pytest.warns(UserWarning, match='Unknown elements found'): - u = mda.Universe(StringIO(mol2_fake), format='MOL2') - - expected = ["H"] * 2 + [""] + ["C"] * 5 + [""] + ["N"] * 4 + ["O"] * 5 + \ - ["S"] * 6 + ["P"] + ["Cr"] * 2 + ["Co"] + with pytest.warns(UserWarning, match="Unknown elements found"): + u = mda.Universe(StringIO(mol2_fake), format="MOL2") + + expected = ( + ["H"] * 2 + + [""] + + ["C"] * 5 + + [""] + + ["N"] * 4 + + ["O"] * 5 + + ["S"] * 6 + + ["P"] + + ["Cr"] * 2 + + ["Co"] + ) expected = np.array(expected, dtype=object) assert_equal(u.atoms.elements, expected) # Test for Issue #3385 / PR #3598 def test_wo_optional_columns(): - u = mda.Universe(StringIO(mol2_wo_opt_col), format='MOL2') + u = mda.Universe(StringIO(mol2_wo_opt_col), format="MOL2") assert_equal(u.atoms.resids, np.array([1, 1])) with pytest.raises(mda.exceptions.NoDataError): u.atoms.resnames @@ -281,7 +296,7 @@ def test_wo_optional_columns(): def test_partial_optional_columns(): - u = mda.Universe(StringIO(mol2_partial_opt_col), format='MOL2') + u = mda.Universe(StringIO(mol2_partial_opt_col), format="MOL2") assert_equal(u.atoms.resids, np.array([1, 2])) with pytest.raises(mda.exceptions.NoDataError): u.atoms.resnames @@ -290,27 +305,27 @@ def test_partial_optional_columns(): def test_mol2_wo_required_columns(): - with pytest.raises(ValueError, - match='The @ATOM block in mol2 file'): - u = mda.Universe(StringIO(mol2_wo_required_col), format='MOL2') + with pytest.raises( + ValueError, match="The @ATOM block in mol2 file" + ): + u = mda.Universe(StringIO(mol2_wo_required_col), format="MOL2") def test_mol2_no_charges(): - with pytest.raises(ValueError, - match='indicates no charges'): - u = mda.Universe(StringIO(mol2_no_charge_error1), format='MOL2') - with pytest.raises(ValueError, - match='indicates a charge model'): - u = mda.Universe(StringIO(mol2_no_charge_error2), format='MOL2') + with pytest.raises(ValueError, match="indicates no charges"): + u = mda.Universe(StringIO(mol2_no_charge_error1), format="MOL2") + with pytest.raises(ValueError, match="indicates a charge model"): + u = mda.Universe(StringIO(mol2_no_charge_error2), format="MOL2") def test_unformat(): - with pytest.raises(ValueError, - match='Some atoms in the mol2 file'): - u = mda.Universe(StringIO(mol2_resname_unformat), format='MOL2') + with pytest.raises(ValueError, match="Some atoms in the mol2 file"): + u = mda.Universe(StringIO(mol2_resname_unformat), format="MOL2") def test_guessed_masses(): u = mda.Universe(mol2_molecules) - assert_allclose(u.atoms.masses[:7], [14.007, 32.06, - 14.007, 14.007, 15.999, 15.999, 12.011]) + assert_allclose( + u.atoms.masses[:7], + [14.007, 32.06, 14.007, 14.007, 15.999, 15.999, 12.011], + ) diff --git a/testsuite/MDAnalysisTests/topology/test_pdb.py b/testsuite/MDAnalysisTests/topology/test_pdb.py index 51822e9671..146462969a 100644 --- a/testsuite/MDAnalysisTests/topology/test_pdb.py +++ b/testsuite/MDAnalysisTests/topology/test_pdb.py @@ -22,28 +22,28 @@ # from io import StringIO -import pytest -import numpy as np -from numpy.testing import assert_equal, assert_allclose import MDAnalysis as mda +import numpy as np +import pytest +from MDAnalysis import NoDataError +from MDAnalysis.guesser import tables +from MDAnalysis.topology.PDBParser import PDBParser +from numpy.testing import assert_allclose, assert_equal -from MDAnalysisTests.topology.base import ParserBase from MDAnalysisTests.datafiles import ( PDB, PDB_HOLE, - PDB_small, + PDB_chainidnewres, + PDB_charges, PDB_conect, PDB_conect2TER, - PDB_singleconect, - PDB_chainidnewres, - PDB_sameresid_diffresname, - PDB_helix, PDB_elements, - PDB_charges, + PDB_helix, + PDB_sameresid_diffresname, + PDB_singleconect, + PDB_small, ) -from MDAnalysis.topology.PDBParser import PDBParser -from MDAnalysis import NoDataError -from MDAnalysis.guesser import tables +from MDAnalysisTests.topology.base import ParserBase _PDBPARSER = mda.topology.PDBParser.PDBParser @@ -64,24 +64,34 @@ (" 24", 24), (" 645", 645), (" 4951", 4951), - ("10267", 10267) + ("10267", 10267), ] -@pytest.mark.parametrize('hybrid, integer', hybrid36) +@pytest.mark.parametrize("hybrid, integer", hybrid36) def test_hy36decode(hybrid, integer): assert mda.topology.PDBParser.hy36decode(5, hybrid) == integer class PDBBase(ParserBase): - expected_attrs = ['ids', 'names', 'record_types', 'resids', - 'resnames', 'altLocs', 'icodes', 'occupancies', - 'tempfactors', 'chainIDs'] - guessed_attrs = ['types', 'masses'] + expected_attrs = [ + "ids", + "names", + "record_types", + "resids", + "resnames", + "altLocs", + "icodes", + "occupancies", + "tempfactors", + "chainIDs", + ] + guessed_attrs = ["types", "masses"] class TestPDBParser(PDBBase): """This one has neither chainids or segids""" + parser = mda.topology.PDBParser.PDBParser ref_filename = PDB expected_n_atoms = 47681 @@ -91,6 +101,7 @@ class TestPDBParser(PDBBase): class TestPDBParserSegids(PDBBase): """Has segids""" + parser = mda.topology.PDBParser.PDBParser ref_filename = PDB_small expected_n_atoms = 3341 @@ -102,20 +113,24 @@ class TestPDBConect(object): """Testing PDB topology parsing (PDB)""" def test_conect_parser(self): - lines = ("CONECT1233212331", - "CONECT123331233112334", - "CONECT123341233312335", - "CONECT123351233412336", - "CONECT12336123271233012335", - "CONECT12337 7718 84081234012344", - "CONECT1233812339123401234112345") - results = ((12332, [12331]), - (12333, [12331, 12334]), - (12334, [12333, 12335]), - (12335, [12334, 12336]), - (12336, [12327, 12330, 12335]), - (12337, [7718, 8408, 12340, 12344]), - (12338, [12339, 12340, 12341, 12345])) + lines = ( + "CONECT1233212331", + "CONECT123331233112334", + "CONECT123341233312335", + "CONECT123351233412336", + "CONECT12336123271233012335", + "CONECT12337 7718 84081234012344", + "CONECT1233812339123401234112345", + ) + results = ( + (12332, [12331]), + (12333, [12331, 12334]), + (12334, [12333, 12335]), + (12335, [12334, 12336]), + (12336, [12327, 12330, 12335]), + (12337, [7718, 8408, 12340, 12344]), + (12338, [12339, 12340, 12341, 12345]), + ) for line, res in zip(lines, results): bonds = mda.topology.PDBParser._parse_conect(line) assert_equal(bonds[0], res[0]) @@ -124,8 +139,9 @@ def test_conect_parser(self): def test_conect_parser_runtime(self): with pytest.raises(RuntimeError): - mda.topology.PDBParser._parse_conect('CONECT12337 7718 ' - '84081234012344123') + mda.topology.PDBParser._parse_conect( + "CONECT12337 7718 " "84081234012344123" + ) def test_conect_topo_parser(self): """Check that the parser works as intended, @@ -145,7 +161,7 @@ def parse(): with pytest.warns(UserWarning): struc = parse() - assert hasattr(struc, 'bonds') + assert hasattr(struc, "bonds") assert len(struc.bonds.values) == 4 @@ -157,7 +173,7 @@ def parse(): with pytest.warns(UserWarning): struc = parse() - assert hasattr(struc, 'bonds') + assert hasattr(struc, "bonds") assert len(struc.bonds.values) == 2 @@ -169,7 +185,7 @@ def test_new_chainid_new_res(): assert len(u.residues) == 4 assert_equal(u.residues.resids, [1, 2, 3, 3]) assert len(u.segments) == 4 - assert_equal(u.segments.segids, ['A', 'B', 'C', 'D']) + assert_equal(u.segments.segids, ["A", "B", "C", "D"]) assert len(u.segments[0].atoms) == 5 assert len(u.segments[1].atoms) == 5 assert len(u.segments[2].atoms) == 5 @@ -180,7 +196,7 @@ def test_sameresid_diffresname(): with _PDBPARSER(PDB_sameresid_diffresname) as p: top = p.parse() resids = [9, 9] - resnames = ['GLN', 'POPC'] + resnames = ["GLN", "POPC"] for i, (resid, resname) in enumerate(zip(resids, resnames)): assert top.resids.values[i] == resid assert top.resnames.values[i] == resname @@ -189,11 +205,11 @@ def test_sameresid_diffresname(): def test_PDB_record_types(): u = mda.Universe(PDB_HOLE) - assert u.atoms[0].record_type == 'ATOM' - assert u.atoms[132].record_type == 'HETATM' + assert u.atoms[0].record_type == "ATOM" + assert u.atoms[132].record_type == "HETATM" - assert_equal(u.atoms[10:20].record_types, 'ATOM') - assert_equal(u.atoms[271:].record_types, 'HETATM') + assert_equal(u.atoms[10:20].record_types, "ATOM") + assert_equal(u.atoms[271:].record_types, "HETATM") PDB_noresid = """\ @@ -210,7 +226,7 @@ def test_PDB_record_types(): def test_PDB_no_resid(): - u = mda.Universe(StringIO(PDB_noresid), format='PDB') + u = mda.Universe(StringIO(PDB_noresid), format="PDB") assert len(u.atoms) == 4 assert len(u.residues) == 1 @@ -242,7 +258,7 @@ def test_PDB_no_resid(): def test_PDB_hex(): - u = mda.Universe(StringIO(PDB_hex), format='PDB') + u = mda.Universe(StringIO(PDB_hex), format="PDB") assert len(u.atoms) == 5 assert u.atoms[0].id == 1 assert u.atoms[1].id == 100000 @@ -253,7 +269,7 @@ def test_PDB_hex(): @pytest.mark.filterwarnings("error:Failed to guess the mass") def test_PDB_metals(): - u = mda.Universe(StringIO(PDB_metals), format='PDB') + u = mda.Universe(StringIO(PDB_metals), format="PDB") assert len(u.atoms) == 4 assert u.atoms[0].mass == pytest.approx(tables.masses["CU"]) @@ -266,11 +282,17 @@ def test_PDB_elements(): """The test checks whether elements attribute are assigned properly given a PDB file with valid elements record. """ - u = mda.Universe(PDB_elements, format='PDB') - element_list = np.array(['N', 'C', 'C', 'O', 'C', 'C', 'O', 'N', 'H', - 'H', 'H', 'H', 'H', 'H', 'H', 'H', 'Cu', 'Fe', - 'Mg', 'Ca', 'S', 'O', 'C', 'C', 'S', 'O', 'C', - 'C'], dtype=object) + u = mda.Universe(PDB_elements, format="PDB") + # fmt: off + element_list = np.array( + [ + 'N', 'C', 'C', 'O', 'C', 'C', 'O', 'N', 'H', 'H', 'H', 'H', 'H', + 'H', 'H', 'H', 'Cu', 'Fe', 'Mg', 'Ca', 'S', 'O', 'C', 'C', 'S', + 'O', 'C', 'C' + ], + dtype=object + ) + # fmt: on assert_equal(u.atoms.elements, element_list) @@ -280,8 +302,10 @@ def test_missing_elements_noattribute(): 1) a warning is raised if elements are missing 2) the elements attribute is not set """ - wmsg = ("Element information is missing, elements attribute will not be " - "populated") + wmsg = ( + "Element information is missing, elements attribute will not be " + "populated" + ) with pytest.warns(UserWarning, match=wmsg): u = mda.Universe(PDB_small) with pytest.raises(AttributeError): @@ -308,14 +332,21 @@ def test_wrong_elements_warnings(): """The test checks whether there are invalid elements in the elements column which have been parsed and returns an appropriate warning. """ - with pytest.warns(UserWarning, match='Unknown element XX found'): - u = mda.Universe(StringIO(PDB_wrong_ele,), format='PDB') - - expected_elements = np.array(['N', '', 'C', 'O', '', 'Cu', 'Fe', 'Mg'], - dtype=object) - gussed_types = np.array(['N', '', 'C', 'O', 'XX', 'CU', 'Fe', 'MG']) - guseed_masses = np.array([14.007, 0.0, 12.011, 15.999, 0.0, - 63.546, 55.847, 24.305], dtype=float) + with pytest.warns(UserWarning, match="Unknown element XX found"): + u = mda.Universe( + StringIO( + PDB_wrong_ele, + ), + format="PDB", + ) + + expected_elements = np.array( + ["N", "", "C", "O", "", "Cu", "Fe", "Mg"], dtype=object + ) + gussed_types = np.array(["N", "", "C", "O", "XX", "CU", "Fe", "MG"]) + guseed_masses = np.array( + [14.007, 0.0, 12.011, 15.999, 0.0, 63.546, 55.847, 24.305], dtype=float + ) assert_equal(u.atoms.elements, expected_elements) assert_equal(u.atoms.types, gussed_types) @@ -324,12 +355,22 @@ def test_wrong_elements_warnings(): def test_guessed_masses_and_types_values(): """Test that guessed masses and types have the expected values for universe - constructed from PDB file. + constructed from PDB file. """ - u = mda.Universe(PDB, format='PDB') - gussed_types = np.array(['N', 'H', 'H', 'H', 'C', 'H', 'C', 'H', 'H', 'C']) - guseed_masses = [14.007, 1.008, 1.008, 1.008, - 12.011, 1.008, 12.011, 1.008, 1.008, 12.011] + u = mda.Universe(PDB, format="PDB") + gussed_types = np.array(["N", "H", "H", "H", "C", "H", "C", "H", "H", "C"]) + guseed_masses = [ + 14.007, + 1.008, + 1.008, + 1.008, + 12.011, + 1.008, + 12.011, + 1.008, + 1.008, + 12.011, + ] failed_type_guesses = u.atoms.types == "" assert_allclose(u.atoms.masses[:10], guseed_masses) @@ -353,9 +394,15 @@ def test_PDB_charges(): properly given a PDB file with a valid formal charges record. """ u = mda.Universe(PDB_charges) - formal_charges = np.array([0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0], dtype=int) + # fmt: off + formal_charges = np.array( + [ + 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + ], + dtype=int + ) + # fmt: on assert_equal(u.atoms.formalcharges, formal_charges) @@ -377,10 +424,10 @@ def test_PDB_charges(): """ -@pytest.mark.parametrize('infile,entry', [ - [PDB_charges_nosign, r'2'], - [PDB_charges_invertsign, r'\+2'] -]) +@pytest.mark.parametrize( + "infile,entry", + [[PDB_charges_nosign, r"2"], [PDB_charges_invertsign, r"\+2"]], +) def test_PDB_bad_charges(infile, entry): """ Test that checks that a warning is raised and formal charges are not set: @@ -389,5 +436,5 @@ def test_PDB_bad_charges(infile, entry): """ wmsg = f"Unknown entry {entry} encountered in formal charge field." with pytest.warns(UserWarning, match=wmsg): - u = mda.Universe(StringIO(infile), format='PDB') - assert not hasattr(u, 'formalcharges') + u = mda.Universe(StringIO(infile), format="PDB") + assert not hasattr(u, "formalcharges") diff --git a/testsuite/MDAnalysisTests/topology/test_pdbqt.py b/testsuite/MDAnalysisTests/topology/test_pdbqt.py index 9578c1e948..02b326b5f5 100644 --- a/testsuite/MDAnalysisTests/topology/test_pdbqt.py +++ b/testsuite/MDAnalysisTests/topology/test_pdbqt.py @@ -51,15 +51,16 @@ class TestPDBQT(ParserBase): "tempfactors", ] - guessed_attrs = ['masses'] + guessed_attrs = ["masses"] expected_n_atoms = 1805 expected_n_residues = 199 # resids go 2-102 then 2-99 expected_n_segments = 2 # res2-102 are A, 2-99 are B def test_guessed_masses(self, filename): u = mda.Universe(filename) - assert_allclose(u.atoms.masses[:7], [14.007, 0., - 0., 12.011, 12.011, 0., 12.011]) + assert_allclose( + u.atoms.masses[:7], [14.007, 0.0, 0.0, 12.011, 12.011, 0.0, 12.011] + ) def test_footnote(): diff --git a/testsuite/MDAnalysisTests/topology/test_pqr.py b/testsuite/MDAnalysisTests/topology/test_pqr.py index fa35171efe..df3790b7a7 100644 --- a/testsuite/MDAnalysisTests/topology/test_pqr.py +++ b/testsuite/MDAnalysisTests/topology/test_pqr.py @@ -34,10 +34,18 @@ class TestPQRParser(ParserBase): parser = mda.topology.PQRParser.PQRParser ref_filename = PQR - expected_attrs = ['ids', 'names', 'charges', 'radii', 'record_types', - 'resids', 'resnames', 'icodes', - 'segids'] - guessed_attrs = ['masses', 'types'] + expected_attrs = [ + "ids", + "names", + "charges", + "radii", + "record_types", + "resids", + "resnames", + "icodes", + "segids", + ] + guessed_attrs = ["masses", "types"] expected_n_atoms = 3341 expected_n_residues = 214 expected_n_segments = 1 @@ -51,8 +59,8 @@ def test_attr_size(self, top): assert len(top.resnames) == top.n_residues assert len(top.segids) == top.n_segments - expected_masses = [14.007, 1.008, 1.008, 1.008, 12.011, 1.008, 12.011] - expected_types = ['N', 'H', 'H', 'H', 'C', 'H', 'C'] + expected_masses = [14.007, 1.008, 1.008, 1.008, 12.011, 1.008, 12.011] + expected_types = ["N", "H", "H", "H", "C", "H", "C"] def test_guessed_masses(self, filename): u = mda.Universe(filename) @@ -69,20 +77,20 @@ class TestPQRParser2(TestPQRParser): expected_n_residues = 474 expected_masses = [14.007, 12.011, 12.011, 15.999, 12.011, 12.011, 12.011] - expected_types = ['N', 'C', 'C', 'O', 'C', 'C', 'C'] + expected_types = ["N", "C", "C", "O", "C", "C", "C"] def test_record_types(): u = mda.Universe(PQR_icodes) - assert u.atoms[4052].record_type == 'ATOM' - assert u.atoms[4053].record_type == 'HETATM' + assert u.atoms[4052].record_type == "ATOM" + assert u.atoms[4053].record_type == "HETATM" - assert_equal(u.atoms[:10].record_types, 'ATOM') - assert_equal(u.atoms[4060:4070].record_types, 'HETATM') + assert_equal(u.atoms[:10].record_types, "ATOM") + assert_equal(u.atoms[4060:4070].record_types, "HETATM") -GROMACS_PQR = ''' +GROMACS_PQR = """ REMARK The B-factors in this file hold atomic radii REMARK The occupancy in this file hold atomic charges TITLE system @@ -92,17 +100,19 @@ def test_record_types(): ATOM 1 O ZR 1 15.710 17.670 23.340 -0.67 1.48 O TER ENDMDL -''' +""" def test_gromacs_flavour(): - u = mda.Universe(StringIO(GROMACS_PQR), format='PQR') + u = mda.Universe(StringIO(GROMACS_PQR), format="PQR") assert len(u.atoms) == 1 # topology things - assert u.atoms[0].type == 'O' - assert u.atoms[0].segid == 'SYSTEM' + assert u.atoms[0].type == "O" + assert u.atoms[0].segid == "SYSTEM" assert_almost_equal(u.atoms[0].radius, 1.48, decimal=5) assert_almost_equal(u.atoms[0].charge, -0.67, decimal=5) # coordinatey things - assert_almost_equal(u.atoms[0].position, [15.710, 17.670, 23.340], decimal=4) + assert_almost_equal( + u.atoms[0].position, [15.710, 17.670, 23.340], decimal=4 + ) diff --git a/testsuite/MDAnalysisTests/topology/test_psf.py b/testsuite/MDAnalysisTests/topology/test_psf.py index ccfbb0bddd..c86e6a781c 100644 --- a/testsuite/MDAnalysisTests/topology/test_psf.py +++ b/testsuite/MDAnalysisTests/topology/test_psf.py @@ -37,34 +37,45 @@ XYZ, ) + class PSFBase(ParserBase): parser = mda.topology.PSFParser.PSFParser - expected_attrs = ['ids', 'names', 'types', 'masses', - 'charges', - 'resids', 'resnames', - 'segids', - 'bonds', 'angles', 'dihedrals', 'impropers'] + expected_attrs = [ + "ids", + "names", + "types", + "masses", + "charges", + "resids", + "resnames", + "segids", + "bonds", + "angles", + "dihedrals", + "impropers", + ] class TestPSFParser(PSFBase): """ Based on small PDB with AdK (:data:`PDB_small`). """ + ref_filename = PSF expected_n_atoms = 3341 expected_n_residues = 214 expected_n_segments = 1 - @pytest.fixture(params=['uncompressed', 'bz2']) + @pytest.fixture(params=["uncompressed", "bz2"]) def filename(self, request, tmpdir): - if request.param == 'uncompressed': + if request.param == "uncompressed": return self.ref_filename else: - fn = str(tmpdir.join('file.psf.bz2')) - with open(self.ref_filename, 'rb') as f: + fn = str(tmpdir.join("file.psf.bz2")) + with open(self.ref_filename, "rb") as f: stuff = f.read() buf = bz2.compress(stuff) - with open(fn, 'wb') as out: + with open(fn, "wb") as out: out.write(buf) return fn @@ -114,6 +125,7 @@ class TestNAMDPSFParser(PSFBase): https://github.com/MDAnalysis/mdanalysis/issues/107 """ + ref_filename = PSF_NAMD expected_n_atoms = 130 expected_n_residues = 6 @@ -134,6 +146,7 @@ def test_as_universe_resids(self): for seg in u.segments: assert_equal(seg.residues.resids[:4], [380, 381, 382, 383]) + class TestPSFParserNoTop(PSFBase): ref_filename = PSF_notop expected_n_atoms = 3341 @@ -152,6 +165,7 @@ def test_dihedrals_total_counts(self, top): def test_impropers_total_counts(self, top): assert len(top.impropers.values) == 0 + def test_psf_nosegid(): """Issue #121""" u = mda.Universe(PSF_nosegid) @@ -159,7 +173,8 @@ def test_psf_nosegid(): assert u.atoms.n_atoms == 98 assert_equal(u.segments.segids, ["SYSTEM"]) + def test_psf_inscode(): """Issue #2053 and #4189""" u = mda.Universe(PSF_inscode) - assert_equal(u.residues.resids[:3], [1, 1, 1]) \ No newline at end of file + assert_equal(u.residues.resids[:3], [1, 1, 1]) diff --git a/testsuite/MDAnalysisTests/topology/test_tables.py b/testsuite/MDAnalysisTests/topology/test_tables.py index 37246ad186..6b19c71587 100644 --- a/testsuite/MDAnalysisTests/topology/test_tables.py +++ b/testsuite/MDAnalysisTests/topology/test_tables.py @@ -32,4 +32,3 @@ def test_moved_to_guessers_warning(): wmsg = "has been moved to MDAnalysis.guesser.tables" with pytest.warns(DeprecationWarning, match=wmsg): reload(tables) - diff --git a/testsuite/MDAnalysisTests/topology/test_top.py b/testsuite/MDAnalysisTests/topology/test_top.py index 3a8227227c..a67c9283b7 100644 --- a/testsuite/MDAnalysisTests/topology/test_top.py +++ b/testsuite/MDAnalysisTests/topology/test_top.py @@ -20,37 +20,48 @@ # MDAnalysis: A Toolkit for the Analysis of Molecular Dynamics Simulations. # J. Comput. Chem. 32 (2011), 2319--2327, doi:10.1002/jcc.21787 # -import sys import platform +import sys import warnings + import MDAnalysis as mda -import pytest import numpy as np +import pytest from numpy.testing import assert_equal -from MDAnalysisTests.topology.base import ParserBase + +from MDAnalysisTests.datafiles import PRM # ache.prmtop +from MDAnalysisTests.datafiles import PRM7 # tz2.truncoct.parm7.bz2 +from MDAnalysisTests.datafiles import PRM12 # anti.top +from MDAnalysisTests.datafiles import PRM_chainid_bz2 # multi_anche.prmtop.bz2 from MDAnalysisTests.datafiles import ( - PRM, # ache.prmtop - PRM_chainid_bz2, # multi_anche.prmtop.bz2 - PRM12, # anti.top - PRM7, # tz2.truncoct.parm7.bz2 - PRMpbc, + PRM19SBOPC, PRMNCRST, PRMNEGATIVE, + PRM_UreyBradley, PRMErr1, PRMErr2, PRMErr3, PRMErr4, PRMErr5, - PRM_UreyBradley, - PRM19SBOPC, + PRMpbc, ) +from MDAnalysisTests.topology.base import ParserBase class TOPBase(ParserBase): parser = mda.topology.TOPParser.TOPParser expected_attrs = [ - "names", "types", "type_indices", "charges", "masses", "resnames", - "bonds", "angles", "dihedrals", "impropers", "elements" + "names", + "types", + "type_indices", + "charges", + "masses", + "resnames", + "bonds", + "angles", + "dihedrals", + "impropers", + "elements", ] expected_n_segments = 1 @@ -79,14 +90,18 @@ def test_angles_atom_counts(self, filename): def test_dihedrals_atom_counts(self, filename): u = mda.Universe(filename) assert len(u.atoms[[0]].dihedrals) == self.expected_n_zero_dihedrals - assert len(u.atoms[[self.atom_i]].dihedrals) == \ - self.expected_n_i_dihedrals + assert ( + len(u.atoms[[self.atom_i]].dihedrals) + == self.expected_n_i_dihedrals + ) def test_impropers_atom_counts(self, filename): u = mda.Universe(filename) assert len(u.atoms[[0]].impropers) == self.expected_n_zero_impropers - assert len(u.atoms[[self.atom_i]].impropers) == \ - self.expected_n_i_impropers + assert ( + len(u.atoms[[self.atom_i]].impropers) + == self.expected_n_i_impropers + ) def test_bonds_identity(self, top): vals = top.bonds.values @@ -134,8 +149,12 @@ def test_improper_atoms_bonded(self, top): forward = ((imp[0], imp[2]), (imp[1], imp[2]), (imp[2], imp[3])) backward = ((imp[0], imp[1]), (imp[1], imp[2]), (imp[1], imp[3])) for a, b in zip(forward, backward): - assert ((b in vals) or (b[::-1] in vals) or - (a in vals) or (a[::-1] in vals)) + assert ( + (b in vals) + or (b[::-1] in vals) + or (a in vals) + or (a[::-1] in vals) + ) def test_elements(self, top): """Tests elements attribute. @@ -147,18 +166,29 @@ def test_elements(self, top): if self.expected_elems: for erange, evals in zip(self.elems_ranges, self.expected_elems): - assert_equal(top.elements.values[erange[0]:erange[1]], evals, - "unexpected element match") + assert_equal( + top.elements.values[erange[0] : erange[1]], + evals, + "unexpected element match", + ) else: - assert not hasattr(top, 'elements'), 'Unexpected elements attr' + assert not hasattr(top, "elements"), "Unexpected elements attr" class TestPRMParser(TOPBase): ref_filename = PRM # Does not contain an ATOMIC_NUMBER record, so no elements expected_attrs = [ - "names", "types", "type_indices", "charges", "masses", "resnames", - "bonds", "angles", "dihedrals", "impropers" + "names", + "types", + "type_indices", + "charges", + "masses", + "resnames", + "bonds", + "angles", + "dihedrals", + "impropers", ] expected_n_atoms = 252 expected_n_residues = 14 @@ -177,30 +207,68 @@ class TestPRMParser(TOPBase): expected_n_i_impropers = 4 atom_zero_bond_values = ((0, 4), (0, 1), (0, 2), (0, 3)) atom_i_bond_values = ((79, 80), (79, 83), (77, 79)) - atom_zero_angle_values = ((0, 4, 6), (0, 4, 10), (3, 0, 4), - (2, 0, 3), (2, 0, 4), (1, 0, 2), - (1, 0, 3), (1, 0, 4), (0, 4, 5)) - atom_i_angle_values = ((80, 79, 83), (77, 79, 80), (77, 79, 83), - (74, 77, 79), (79, 80, 81), (79, 80, 82), - (79, 83, 84), (79, 83, 85), (78, 77, 79)) - atom_zero_dihedral_values = ((0, 4, 10, 11), (0, 4, 10, 12), - (3, 0, 4, 5), (3, 0, 4, 6), - (3, 0, 4, 10), (2, 0, 4, 5), - (2, 0, 4, 6), (2, 0, 4, 10), - (1, 0, 4, 5), (1, 0, 4, 6), - (1, 0, 4, 10), (0, 4, 6, 7), - (0, 4, 6, 8), (0, 4, 6, 9)) - atom_i_dihedral_values = ((71, 74, 77, 79), (74, 77, 79, 80), - (74, 77, 79, 83), (75, 74, 77, 79), - (76, 74, 77, 79), (77, 79, 80, 81), - (77, 79, 80, 82), (77, 79, 83, 84), - (77, 79, 83, 85), (78, 77, 79, 80), - (78, 77, 79, 83), (80, 79, 83, 84), - (80, 79, 83, 85), (81, 80, 79, 83), - (82, 80, 79, 83)) + atom_zero_angle_values = ( + (0, 4, 6), + (0, 4, 10), + (3, 0, 4), + (2, 0, 3), + (2, 0, 4), + (1, 0, 2), + (1, 0, 3), + (1, 0, 4), + (0, 4, 5), + ) + atom_i_angle_values = ( + (80, 79, 83), + (77, 79, 80), + (77, 79, 83), + (74, 77, 79), + (79, 80, 81), + (79, 80, 82), + (79, 83, 84), + (79, 83, 85), + (78, 77, 79), + ) + atom_zero_dihedral_values = ( + (0, 4, 10, 11), + (0, 4, 10, 12), + (3, 0, 4, 5), + (3, 0, 4, 6), + (3, 0, 4, 10), + (2, 0, 4, 5), + (2, 0, 4, 6), + (2, 0, 4, 10), + (1, 0, 4, 5), + (1, 0, 4, 6), + (1, 0, 4, 10), + (0, 4, 6, 7), + (0, 4, 6, 8), + (0, 4, 6, 9), + ) + atom_i_dihedral_values = ( + (71, 74, 77, 79), + (74, 77, 79, 80), + (74, 77, 79, 83), + (75, 74, 77, 79), + (76, 74, 77, 79), + (77, 79, 80, 81), + (77, 79, 80, 82), + (77, 79, 83, 84), + (77, 79, 83, 85), + (78, 77, 79, 80), + (78, 77, 79, 83), + (80, 79, 83, 84), + (80, 79, 83, 85), + (81, 80, 79, 83), + (82, 80, 79, 83), + ) atom_zero_improper_values = () - atom_i_improper_values = ((74, 79, 77, 78), (77, 80, 79, 83), - (79, 81, 80, 82), (79, 84, 83, 85)) + atom_i_improper_values = ( + (74, 79, 77, 78), + (77, 80, 79, 83), + (79, 81, 80, 82), + (79, 84, 83, 85), + ) expected_elems = None @@ -305,76 +373,25 @@ class TestPRMChainidParser(TOPBase): expected_elems = [ np.array( - [ - "N", - "H", - "H", - "H", - "C", - "H", - "C", - "H", - "H", - ], + ["N", "H", "H", "H", "C", "H", "C", "H", "H"], dtype=object, ), np.array( - [ - "O", - "O", - "N", - "H", - "H", - "H", - "C", - ], + ["O", "O", "N", "H", "H", "H", "C"], dtype=object, ), np.array(["H", "C", "O", "O", "N", "H", "H", "H"], dtype=object), ] + # fmt: off expected_chainIDs = np.array( [ - "A", - "A", - "A", - "A", - "A", - "A", - "A", - "A", - "A", - "A", - "A", - "A", - "A", - "A", - "B", - "B", - "B", - "B", - "B", - "B", - "B", - "B", - "B", - "B", - "B", - "B", - "B", - "B", - "C", - "C", - "C", - "C", - "C", - "C", - "C", - "C", - "C", - "C", + "A", "A", "A", "A", "A", "A", "A", "A", "A", "A", "A", "A", "A", "A", + "B", "B", "B", "B", "B", "B", "B", "B", "B", "B", "B", "B", "B", "B", + "C", "C", "C", "C", "C", "C", "C", "C", "C", "C", ] ) + # fmt: on def test_chainIDs(self, filename): """Tests chainIDs attribute. @@ -386,7 +403,9 @@ def test_chainIDs(self, filename): u = mda.Universe(filename) if hasattr(self, "expected_chainIDs"): - reschainIDs = [atomchainIDs[0] for atomchainIDs in u.residues.chainIDs] + reschainIDs = [ + atomchainIDs[0] for atomchainIDs in u.residues.chainIDs + ] assert_equal( reschainIDs, self.expected_chainIDs, "unexpected element match" ) @@ -413,54 +432,94 @@ class TestPRM12Parser(TOPBase): atom_i = 335 ref_proteinatoms = 0 atom_zero_bond_values = ((0, 1),) - atom_i_bond_values = ((335, 337), (335, 354), - (334, 335), (335, 336)) + atom_i_bond_values = ((335, 337), (335, 354), (334, 335), (335, 336)) atom_zero_angle_values = ((0, 1, 2),) - atom_i_angle_values = ((337, 335, 354), (335, 337, 338), - (335, 337, 351), (335, 354, 352), - (334, 335, 337), (334, 335, 354), - (332, 334, 335), (336, 335, 337), - (336, 335, 354), (335, 354, 355), - (335, 354, 356), (334, 335, 336)) - atom_zero_dihedral_values = ((0, 1, 2, 3), (0, 1, 2, 4), - (0, 1, 2, 5)) - atom_i_dihedral_values = ((329, 332, 334, 335), (332, 334, 335, 336), - (332, 334, 335, 337), (332, 334, 335, 354), - (332, 352, 354, 335), (333, 332, 334, 335), - (334, 335, 337, 338), (334, 335, 337, 351), - (334, 335, 354, 352), (334, 335, 354, 355), - (334, 335, 354, 356), (335, 334, 332, 352), - (335, 337, 338, 339), (335, 337, 338, 340), - (335, 337, 351, 341), (335, 337, 351, 350), - (335, 354, 352, 353), (335, 354, 352, 357), - (336, 335, 337, 338), (336, 335, 337, 351), - (336, 335, 354, 352), (336, 335, 354, 355), - (336, 335, 354, 356), (337, 335, 354, 352), - (337, 335, 354, 355), (337, 335, 354, 356), - (338, 337, 335, 354), (351, 337, 335, 354)) + atom_i_angle_values = ( + (337, 335, 354), + (335, 337, 338), + (335, 337, 351), + (335, 354, 352), + (334, 335, 337), + (334, 335, 354), + (332, 334, 335), + (336, 335, 337), + (336, 335, 354), + (335, 354, 355), + (335, 354, 356), + (334, 335, 336), + ) + atom_zero_dihedral_values = ((0, 1, 2, 3), (0, 1, 2, 4), (0, 1, 2, 5)) + atom_i_dihedral_values = ( + (329, 332, 334, 335), + (332, 334, 335, 336), + (332, 334, 335, 337), + (332, 334, 335, 354), + (332, 352, 354, 335), + (333, 332, 334, 335), + (334, 335, 337, 338), + (334, 335, 337, 351), + (334, 335, 354, 352), + (334, 335, 354, 355), + (334, 335, 354, 356), + (335, 334, 332, 352), + (335, 337, 338, 339), + (335, 337, 338, 340), + (335, 337, 351, 341), + (335, 337, 351, 350), + (335, 354, 352, 353), + (335, 354, 352, 357), + (336, 335, 337, 338), + (336, 335, 337, 351), + (336, 335, 354, 352), + (336, 335, 354, 355), + (336, 335, 354, 356), + (337, 335, 354, 352), + (337, 335, 354, 355), + (337, 335, 354, 356), + (338, 337, 335, 354), + (351, 337, 335, 354), + ) atom_zero_improper_values = () atom_i_improper_values = ((335, 337, 338, 351),) elems_ranges = [[0, 36], [351, 403]] - expected_elems = [np.array(["H", "O", "C", "H", "H", "C", "H", "O", "C", - "H", "N", "C", "H", "N", "C", "C", "O", "N", - "H", "C", "N", "H", "H", "N", "C", "C", "H", - "C", "H", "H", "O", "P", "O", "O", "O", "C"], - dtype=object), - np.array(["C", "C", "H", "C", "H", "H", "O", "P", "O", - "O", "O", "C", "H", "H", "C", "H", "O", "C", - "H", "N", "C", "H", "N", "C", "C", "O", "N", - "H", "C", "N", "H", "H", "N", "C", "C", "H", - "C", "H", "H", "O", "H", "Na", "Na", "Na", - "Na", "Na", "Na", "Na", "Na", "O", "H", "H"], - dtype=object)] + # fmt: off + expected_elems = [ + np.array( + [ + "H", "O", "C", "H", "H", "C", "H", "O", "C", "H", "N", "C", + "H", "N", "C", "C", "O", "N", "H", "C", "N", "H", "H", "N", + "C", "C", "H", "C", "H", "H", "O", "P", "O", "O", "O","C", + ], + dtype=object, + ), + np.array( + [ + "C", "C", "H", "C", "H", "H", "O", "P", "O", "O", "O", "C", + "H", "H", "C", "H", "O", "C", "H", "N", "C", "H", "N", "C", + "C", "O", "N", "H", "C", "N", "H", "H", "N", "C", "C", "H", + "C", "H", "H", "O", "H", "Na", "Na", "Na", "Na", "Na", "Na", + "Na", "Na", "O", "H", "H", + ], + dtype=object, + ), + ] + # fmt: on class TestParm7Parser(TOPBase): ref_filename = PRM7 # Does not contain an ATOMIC_NUMBER record, so no elements expected_attrs = [ - "names", "types", "type_indices", "charges", "masses", "resnames", - "bonds", "angles", "dihedrals", "impropers" + "names", + "types", + "type_indices", + "charges", + "masses", + "resnames", + "bonds", + "angles", + "dihedrals", + "impropers", ] expected_n_atoms = 5827 expected_n_residues = 1882 @@ -478,39 +537,78 @@ class TestParm7Parser(TOPBase): expected_n_zero_impropers = 0 expected_n_i_impropers = 2 atom_zero_bond_values = ((0, 4), (0, 1), (0, 2), (0, 3)) - atom_i_bond_values = ((135, 137), (135, 155), (133, 135), - (135, 136)) - atom_zero_angle_values = ((0, 4, 6), (0, 4, 11), (3, 0, 4), - (2, 0, 3), (2, 0, 4), (1, 0, 2), - (1, 0, 3), (1, 0, 4), (0, 4, 5)) - atom_i_angle_values = ((131, 133, 135), (137, 135, 155), - (135, 137, 140), (135, 155, 156), - (135, 155, 157), (133, 135, 137), - (133, 135, 155), (136, 135, 137), - (136, 135, 155), (135, 137, 138), - (135, 137, 139), (134, 133, 135), - (133, 135, 136)) - atom_zero_dihedral_values = ((0, 4, 6, 7), (0, 4, 6, 8), - (0, 4, 6, 9), (0, 4, 11, 12), - (0, 4, 11, 13), (1, 0, 4, 5), - (1, 0, 4, 6), (1, 0, 4, 11), - (2, 0, 4, 5), (2, 0, 4, 6), - (2, 0, 4, 11), (3, 0, 4, 5), - (3, 0, 4, 6), (3, 0, 4, 11)) - atom_i_dihedral_values = ((113, 131, 133, 135), (131, 133, 135, 136), - (131, 133, 135, 137), (131, 133, 135, 155), - (132, 131, 133, 135), (133, 135, 137, 138), - (133, 135, 137, 139), (133, 135, 137, 140), - (133, 135, 155, 156), (133, 135, 155, 157), - (134, 133, 135, 136), (134, 133, 135, 137), - (134, 133, 135, 155), (135, 137, 140, 141), - (135, 137, 140, 154), (135, 155, 157, 158), - (135, 155, 157, 159), (136, 135, 137, 138), - (136, 135, 137, 139), (136, 135, 137, 140), - (136, 135, 155, 156), (136, 135, 155, 157), - (137, 135, 155, 156), (137, 135, 155, 157), - (138, 137, 135, 155), (139, 137, 135, 155), - (140, 137, 135, 155)) + atom_i_bond_values = ((135, 137), (135, 155), (133, 135), (135, 136)) + atom_zero_angle_values = ( + (0, 4, 6), + (0, 4, 11), + (3, 0, 4), + (2, 0, 3), + (2, 0, 4), + (1, 0, 2), + (1, 0, 3), + (1, 0, 4), + (0, 4, 5), + ) + atom_i_angle_values = ( + (131, 133, 135), + (137, 135, 155), + (135, 137, 140), + (135, 155, 156), + (135, 155, 157), + (133, 135, 137), + (133, 135, 155), + (136, 135, 137), + (136, 135, 155), + (135, 137, 138), + (135, 137, 139), + (134, 133, 135), + (133, 135, 136), + ) + atom_zero_dihedral_values = ( + (0, 4, 6, 7), + (0, 4, 6, 8), + (0, 4, 6, 9), + (0, 4, 11, 12), + (0, 4, 11, 13), + (1, 0, 4, 5), + (1, 0, 4, 6), + (1, 0, 4, 11), + (2, 0, 4, 5), + (2, 0, 4, 6), + (2, 0, 4, 11), + (3, 0, 4, 5), + (3, 0, 4, 6), + (3, 0, 4, 11), + ) + atom_i_dihedral_values = ( + (113, 131, 133, 135), + (131, 133, 135, 136), + (131, 133, 135, 137), + (131, 133, 135, 155), + (132, 131, 133, 135), + (133, 135, 137, 138), + (133, 135, 137, 139), + (133, 135, 137, 140), + (133, 135, 155, 156), + (133, 135, 155, 157), + (134, 133, 135, 136), + (134, 133, 135, 137), + (134, 133, 135, 155), + (135, 137, 140, 141), + (135, 137, 140, 154), + (135, 155, 157, 158), + (135, 155, 157, 159), + (136, 135, 137, 138), + (136, 135, 137, 139), + (136, 135, 137, 140), + (136, 135, 155, 156), + (136, 135, 155, 157), + (137, 135, 155, 156), + (137, 135, 155, 157), + (138, 137, 135, 155), + (139, 137, 135, 155), + (140, 137, 135, 155), + ) atom_zero_improper_values = () atom_i_improper_values = ((131, 135, 133, 134), (135, 157, 155, 156)) expected_elems = None @@ -520,8 +618,16 @@ class TestPRM2(TOPBase): ref_filename = PRMpbc # Does not contain an ATOMIC_NUMBER record, so no elements expected_attrs = [ - "names", "types", "type_indices", "charges", "masses", "resnames", - "bonds", "angles", "dihedrals", "impropers" + "names", + "types", + "type_indices", + "charges", + "masses", + "resnames", + "bonds", + "angles", + "dihedrals", + "impropers", ] expected_n_atoms = 5071 expected_n_residues = 1686 @@ -542,19 +648,37 @@ class TestPRM2(TOPBase): atom_zero_bond_values = ((0, 1),) atom_i_bond_values = ((14, 15), (14, 16), (8, 14)) atom_zero_angle_values = ((0, 1, 2), (0, 1, 3), (0, 1, 4)) - atom_i_angle_values = ((15, 14, 16), (14, 16, 18), (10, 8, 14), - (8, 14, 15), (8, 14, 16), (6, 8, 14), - (14, 16, 17), (9, 8, 14)) + atom_i_angle_values = ( + (15, 14, 16), + (14, 16, 18), + (10, 8, 14), + (8, 14, 15), + (8, 14, 16), + (6, 8, 14), + (14, 16, 17), + (9, 8, 14), + ) atom_zero_dihedral_values = ((0, 1, 4, 5), (0, 1, 4, 6)) - atom_i_dihedral_values = ((4, 6, 8, 14), (6, 8, 14, 15), - (6, 8, 14, 16), (7, 6, 8, 14), - (8, 14, 16, 17), (8, 14, 16, 18), - (9, 8, 14, 15), (9, 8, 14, 16), - (10, 8, 14, 15), (10, 8, 14, 16), - (11, 10, 8, 14), (12, 10, 8, 14), - (13, 10, 8, 14), (14, 16, 18, 19), - (14, 16, 18, 20), (14, 16, 18, 21), - (15, 14, 16, 17), (15, 14, 16, 18)) + atom_i_dihedral_values = ( + (4, 6, 8, 14), + (6, 8, 14, 15), + (6, 8, 14, 16), + (7, 6, 8, 14), + (8, 14, 16, 17), + (8, 14, 16, 18), + (9, 8, 14, 15), + (9, 8, 14, 16), + (10, 8, 14, 15), + (10, 8, 14, 16), + (11, 10, 8, 14), + (12, 10, 8, 14), + (13, 10, 8, 14), + (14, 16, 18, 19), + (14, 16, 18, 20), + (14, 16, 18, 21), + (15, 14, 16, 17), + (15, 14, 16, 18), + ) atom_zero_improper_values = () atom_i_improper_values = ((8, 16, 14, 15), (14, 18, 16, 17)) expected_elems = None @@ -587,8 +711,12 @@ class TestPRMNCRST(TOPBase): atom_i_dihedral_values = ((0, 1, 4, 5), (2, 1, 4, 5), (3, 1, 4, 5)) atom_zero_improper_values = () atom_i_improper_values = () - elems_ranges = [[0, 6], ] - expected_elems = [np.array(["H", "C", "H", "H", "C", "O"], dtype=object), ] + elems_ranges = [ + [0, 6], + ] + expected_elems = [ + np.array(["H", "C", "H", "H", "C", "O"], dtype=object), + ] class TestPRMNCRST_negative(TOPBase): @@ -618,8 +746,12 @@ class TestPRMNCRST_negative(TOPBase): atom_i_dihedral_values = ((0, 1, 4, 5), (2, 1, 4, 5), (3, 1, 4, 5)) atom_zero_improper_values = () atom_i_improper_values = () - elems_ranges = [[0, 6], ] - expected_elems = [np.array(["H", "", "H", "H", "C", ""], dtype=object), ] + elems_ranges = [ + [0, 6], + ] + expected_elems = [ + np.array(["H", "", "H", "H", "C", ""], dtype=object), + ] class TestPRMEP(TOPBase): @@ -650,17 +782,15 @@ class TestPRMEP(TOPBase): atom_zero_improper_values = () atom_i_improper_values = () elems_ranges = [[0, 8], [20, 28]] - expected_elems = [np.array(["H", "C", "H", "H", "C", "O", "N", "H"], - dtype=object), - np.array(["H", "H", "O", "H", "H", "", "O", "H"], - dtype=object)] + expected_elems = [ + np.array(["H", "C", "H", "H", "C", "O", "N", "H"], dtype=object), + np.array(["H", "H", "O", "H", "H", "", "O", "H"], dtype=object), + ] class TestErrorsAndWarnings(object): - ATOMIC_NUMBER_MSG = ( - "ATOMIC_NUMBER record not found, elements attribute will not be populated" - ) + ATOMIC_NUMBER_MSG = "ATOMIC_NUMBER record not found, elements attribute will not be populated" MISSING_ELEM_MSG = ( "Unknown ATOMIC_NUMBER value found for some atoms, " "these have been given an empty element record" diff --git a/testsuite/MDAnalysisTests/topology/test_topology_base.py b/testsuite/MDAnalysisTests/topology/test_topology_base.py index 0a6099b17d..33d03258f9 100644 --- a/testsuite/MDAnalysisTests/topology/test_topology_base.py +++ b/testsuite/MDAnalysisTests/topology/test_topology_base.py @@ -6,16 +6,18 @@ class TestSquash(object): atom_resids = np.array([2, 2, 1, 1, 5, 5, 4, 4]) - atom_resnames = np.array(['A', 'A', 'B', 'B', 'C', 'C', 'D', 'D'], - dtype=object) + atom_resnames = np.array( + ["A", "A", "B", "B", "C", "C", "D", "D"], dtype=object + ) def test_squash(self): - atom_residx, resids, (resnames, ) = squash_by(self.atom_resids, - self.atom_resnames) + atom_residx, resids, (resnames,) = squash_by( + self.atom_resids, self.atom_resnames + ) assert_equal(atom_residx, np.array([1, 1, 0, 0, 3, 3, 2, 2])) assert_equal(resids, np.array([1, 2, 4, 5])) - assert_equal(resnames, np.array(['B', 'A', 'D', 'C'])) + assert_equal(resnames, np.array(["B", "A", "D", "C"])) class TestChangeSquash(object): @@ -24,21 +26,22 @@ def test_resid_squash(self): # Residues 1 & 2 are Segid A, Residue 3 is Segid B # Resid 2 is repeated twice! Should be detected as 2 distinct residues resids = np.array([2, 2, 3, 3, 2, 2]) - resnames = np.array(['RsA', 'RsA', 'RsB', 'RsB', 'RsC', 'RsC']) - segids = np.array(['A', 'A', 'A', 'A', 'B', 'B']) + resnames = np.array(["RsA", "RsA", "RsB", "RsB", "RsC", "RsC"]) + segids = np.array(["A", "A", "A", "A", "B", "B"]) residx, (new_resids, new_resnames, new_segids) = change_squash( - (resids, ), (resids, resnames, segids)) + (resids,), (resids, resnames, segids) + ) assert_equal(residx, np.array([0, 0, 1, 1, 2, 2])) assert_equal(new_resids, np.array([2, 3, 2])) - assert_equal(new_resnames, np.array(['RsA', 'RsB', 'RsC'])) - assert_equal(new_segids, np.array(['A', 'A', 'B'])) + assert_equal(new_resnames, np.array(["RsA", "RsB", "RsC"])) + assert_equal(new_segids, np.array(["A", "A", "B"])) def test_segid_squash(self): - segids = np.array(['A', 'A', 'B']) + segids = np.array(["A", "A", "B"]) - segidx, (new_segids, ) = change_squash((segids, ), (segids, )) + segidx, (new_segids,) = change_squash((segids,), (segids,)) assert_equal(segidx, np.array([0, 0, 1])) - assert_equal(new_segids, np.array(['A', 'B'])) + assert_equal(new_segids, np.array(["A", "B"])) diff --git a/testsuite/MDAnalysisTests/topology/test_topology_str_types.py b/testsuite/MDAnalysisTests/topology/test_topology_str_types.py index 66bf89b3e0..2827dc4eec 100644 --- a/testsuite/MDAnalysisTests/topology/test_topology_str_types.py +++ b/testsuite/MDAnalysisTests/topology/test_topology_str_types.py @@ -43,40 +43,47 @@ HoomdXMLdata, XPDB_small, XYZ_mini, - DLP_HISTORY_minimal, ) + DLP_HISTORY_minimal, +) -@pytest.mark.parametrize('prop', [ - 'name', - 'resname', - 'type', - 'segid', - 'moltype', -]) +@pytest.mark.parametrize( + "prop", + [ + "name", + "resname", + "type", + "segid", + "moltype", + ], +) # topology formats curated from values available in # MDAnalysis._PARSERS -@pytest.mark.parametrize( 'top_format, top', [ - ('CONFIG', DLP_CONFIG_minimal), - ('CRD', CRD), - ('DATA', LAMMPSdata), - ('DMS', DMS), - ('GMS', GMS_SYMOPT), - ('GRO', GRO), - ('HISTORY', DLP_HISTORY_minimal), - ('MMTF', MMTF), - ('MOL2', mol2_molecule), - ('PARM7', PRM7), - ('PDB', PDB_small), - ('PDBQT', PDBQT_input), - ('PQR', PQR), - ('PRMTOP', PRM), - ('PSF', PSF), - ('TOP', PRM12), - ('TPR', TPR), - ('XML', HoomdXMLdata), - ('XPDB', XPDB_small), - ('XYZ', XYZ_mini) -]) +@pytest.mark.parametrize( + "top_format, top", + [ + ("CONFIG", DLP_CONFIG_minimal), + ("CRD", CRD), + ("DATA", LAMMPSdata), + ("DMS", DMS), + ("GMS", GMS_SYMOPT), + ("GRO", GRO), + ("HISTORY", DLP_HISTORY_minimal), + ("MMTF", MMTF), + ("MOL2", mol2_molecule), + ("PARM7", PRM7), + ("PDB", PDB_small), + ("PDBQT", PDBQT_input), + ("PQR", PQR), + ("PRMTOP", PRM), + ("PSF", PSF), + ("TOP", PRM12), + ("TPR", TPR), + ("XML", HoomdXMLdata), + ("XPDB", XPDB_small), + ("XYZ", XYZ_mini), + ], +) def test_str_types(top_format, top, prop): # Python 2/3 topology string type checking # Related to Issue #1336 diff --git a/testsuite/MDAnalysisTests/topology/test_tprparser.py b/testsuite/MDAnalysisTests/topology/test_tprparser.py index 208769bd61..f2b93e0fcb 100644 --- a/testsuite/MDAnalysisTests/topology/test_tprparser.py +++ b/testsuite/MDAnalysisTests/topology/test_tprparser.py @@ -20,32 +20,38 @@ # MDAnalysis: A Toolkit for the Analysis of Molecular Dynamics Simulations. # J. Comput. Chem. 32 (2011), 2319--2327, doi:10.1002/jcc.21787 # -import pytest -from numpy.testing import assert_equal import functools +import MDAnalysis as mda +import MDAnalysis.topology.TPRParser import numpy as np +import pytest +# fmt: off +from MDAnalysis.tests.datafiles import (TPR, TPR400, TPR402, TPR403, TPR404, + TPR405, TPR406, TPR407, TPR450, TPR451, + TPR452, TPR453, TPR454, TPR455, TPR460, + TPR461, TPR502, TPR504, TPR505, TPR510, + TPR2016, TPR2018, TPR2019B3, TPR2020, + TPR2020B2, TPR2021, TPR2022RC1, + TPR2023, TPR2024, TPR2024_4, + TPR_EXTRA_407, TPR_EXTRA_2016, + TPR_EXTRA_2018, TPR_EXTRA_2020, + TPR_EXTRA_2021, TPR_EXTRA_2022RC1, + TPR_EXTRA_2023, TPR_EXTRA_2024, + TPR_EXTRA_2024_4, XTC, TPR334_bonded, + TPR455Double, TPR510_bonded, + TPR2016_bonded, TPR2018_bonded, + TPR2019B3_bonded, TPR2020_bonded, + TPR2020_double_bonded, + TPR2020B2_bonded, TPR2020Double, + TPR2021_bonded, TPR2021_double_bonded, + TPR2021Double, TPR2022RC1_bonded, + TPR2023_bonded, TPR2024_4_bonded, + TPR2024_bonded) +from numpy.testing import assert_equal -from MDAnalysis.tests.datafiles import ( - TPR, - TPR400, TPR402, TPR403, TPR404, TPR405, TPR406, TPR407, - TPR450, TPR451, TPR452, TPR453, TPR454, TPR455, TPR455Double, - TPR460, TPR461, TPR502, TPR504, TPR505, TPR510, TPR510_bonded, - TPR2016, TPR2018, TPR2019B3, TPR2020B2, TPR2020, TPR2020Double, - TPR2021, TPR2021Double, TPR2022RC1, TPR2023, TPR2024, TPR2024_4, - TPR2016_bonded, TPR2018_bonded, TPR2019B3_bonded, - TPR2020B2_bonded, TPR2020_bonded, TPR2020_double_bonded, - TPR2021_bonded, TPR2021_double_bonded, TPR334_bonded, - TPR2022RC1_bonded, TPR2023_bonded, TPR2024_bonded, - TPR2024_4_bonded, - TPR_EXTRA_2021, TPR_EXTRA_2020, TPR_EXTRA_2018, - TPR_EXTRA_2016, TPR_EXTRA_407, TPR_EXTRA_2022RC1, - TPR_EXTRA_2023, TPR_EXTRA_2024, TPR_EXTRA_2024_4, - XTC, -) +# fmt: on from MDAnalysisTests.topology.base import ParserBase -import MDAnalysis.topology.TPRParser -import MDAnalysis as mda BONDED_TPRS = ( TPR510_bonded, @@ -103,18 +109,22 @@ def test_molnums(self, top): def test_chainIDs(self, top): if hasattr(self, "ref_chainIDs"): - assert_equal(self.ref_chainIDs, getattr(top, "chainIDs").name_lookup) + assert_equal( + self.ref_chainIDs, getattr(top, "chainIDs").name_lookup + ) class TestTPR(TPRAttrs): """ this test the data/adk_oplsaa.tpr which is of tpx version 58 """ + expected_n_atoms = 47681 expected_n_residues = 11302 expected_n_segments = 3 - ref_moltypes = np.array(['AKeco'] * 214 + ['SOL'] * 11084 + ['NA+'] * 4, - dtype=object) + ref_moltypes = np.array( + ["AKeco"] * 214 + ["SOL"] * 11084 + ["NA+"] * 4, dtype=object + ) ref_molnums = np.array([0] * 214 + list(range(1, 1 + 11084 + 4))) @pytest.fixture() @@ -129,16 +139,19 @@ class TestTPRGromacsVersions(TPRAttrs): expected_n_atoms = 2263 expected_n_residues = 230 expected_n_segments = 2 - ref_moltypes = np.array(['Protein_A'] * 129 + ['SOL'] * 101, dtype=object) + ref_moltypes = np.array(["Protein_A"] * 129 + ["SOL"] * 101, dtype=object) ref_molnums = np.array([0] * 129 + list(range(1, 1 + 101))) ref_chainIDs = ["Protein_A", "SOL"] - @pytest.fixture(params=[TPR400, TPR402, TPR403, TPR404, TPR405, TPR406, - TPR407, TPR450, TPR451, TPR452, TPR453, TPR454, - TPR455, TPR502, TPR504, TPR505, TPR510, TPR2016, - TPR2018, TPR2019B3, TPR2020, TPR2020Double, - TPR2021, TPR2021Double, TPR2022RC1, TPR2023, - TPR2024, TPR2024_4]) + # fmt: off + @pytest.fixture(params=[ + TPR400, TPR402, TPR403, TPR404, TPR405, TPR406, TPR407, TPR450, + TPR451, TPR452, TPR453, TPR454, TPR455, TPR502, TPR504, TPR505, + TPR510, TPR2016, TPR2018, TPR2019B3, TPR2020, TPR2020Double, + TPR2021, TPR2021Double, TPR2022RC1, TPR2023, TPR2024, TPR2024_4 + ] + ) + # fmt: on def filename(self, request): return request.param @@ -147,10 +160,16 @@ class TestTPRDouble(TPRAttrs): expected_n_atoms = 21692 expected_n_residues = 4352 expected_n_segments = 7 - ref_moltypes = np.array(['DOPC'] * 21 + ['DPPC'] * 10 + ['CHOL'] * 3 - + ['DOPC'] * 21 + ['DPPC'] * 10 + ['CHOL'] * 3 - + ['SOL'] * 4284, - dtype=object) + ref_moltypes = np.array( + ["DOPC"] * 21 + + ["DPPC"] * 10 + + ["CHOL"] * 3 + + ["DOPC"] * 21 + + ["DPPC"] * 10 + + ["CHOL"] * 3 + + ["SOL"] * 4284, + dtype=object, + ) ref_molnums = np.arange(4352) @pytest.fixture() @@ -162,13 +181,25 @@ class TestTPR46x(TPRAttrs): expected_n_atoms = 44052 expected_n_residues = 10712 expected_n_segments = 8 - ref_moltypes = np.array(['Protein_A'] * 27 + ['Protein_B'] * 27 - + ['Protein_C'] * 27 + ['Protein_D'] * 27 - + ['Protein_E'] * 27 - + ['SOL'] * 10530 + ['NA+'] * 26 + ['CL-'] * 21, - dtype=object) - ref_molnums = np.array([0] * 27 + [1] * 27 + [2] * 27 + [3] * 27 + [4] * 27 - + list(range(5, 5 + 10530 + 26 + 21))) + ref_moltypes = np.array( + ["Protein_A"] * 27 + + ["Protein_B"] * 27 + + ["Protein_C"] * 27 + + ["Protein_D"] * 27 + + ["Protein_E"] * 27 + + ["SOL"] * 10530 + + ["NA+"] * 26 + + ["CL-"] * 21, + dtype=object, + ) + ref_molnums = np.array( + [0] * 27 + + [1] * 27 + + [2] * 27 + + [3] * 27 + + [4] * 27 + + list(range(5, 5 + 10530 + 26 + 21)) + ) @pytest.fixture(params=[TPR460, TPR461]) def filename(self, request): @@ -180,7 +211,11 @@ def _test_is_in_topology(name, elements, topology_path, topology_section): Test if an interaction appears as expected in the topology """ post_40_potentials = { - 'RESTRAINTPOT', 'RESTRANGLES', 'RESTRDIHS', 'CBTDIHS', 'PIDIHS', + "RESTRAINTPOT", + "RESTRANGLES", + "RESTRDIHS", + "CBTDIHS", + "PIDIHS", } if name in post_40_potentials and topology_path == TPR_EXTRA_407: # The potential is not yet implemented in this version of gromacs @@ -188,85 +223,102 @@ def _test_is_in_topology(name, elements, topology_path, topology_section): parser = MDAnalysis.topology.TPRParser.TPRParser(topology_path) top = parser.parse() for element in elements: - assert element in getattr(top, topology_section).values, \ - 'Interaction type "{}" not found'.format(name) - - -@pytest.mark.parametrize('topology', BONDED_TPRS) -@pytest.mark.parametrize('bond', ( - ('BONDS', [(0, 1)]), - ('G96BONDS', [(1, 2)]), - ('MORSE', [(2, 3)]), - ('CUBICBONDS', [(3, 4)]), - ('CONNBONDS', [(4, 5)]), - ('HARMONIC', [(5, 6)]), - ('FENEBONDS', [(6, 7)]), - ('RESTRAINTPOT', [(7, 8)]), - ('TABBONDS', [(8, 9)]), - ('TABBONDSNC', [(9, 10)]), - ('CONSTR', [(10, 11)]), - ('CONSTRNC', [(11, 12)]), -)) + assert ( + element in getattr(top, topology_section).values + ), 'Interaction type "{}" not found'.format(name) + + +@pytest.mark.parametrize("topology", BONDED_TPRS) +@pytest.mark.parametrize( + "bond", + ( + ("BONDS", [(0, 1)]), + ("G96BONDS", [(1, 2)]), + ("MORSE", [(2, 3)]), + ("CUBICBONDS", [(3, 4)]), + ("CONNBONDS", [(4, 5)]), + ("HARMONIC", [(5, 6)]), + ("FENEBONDS", [(6, 7)]), + ("RESTRAINTPOT", [(7, 8)]), + ("TABBONDS", [(8, 9)]), + ("TABBONDSNC", [(9, 10)]), + ("CONSTR", [(10, 11)]), + ("CONSTRNC", [(11, 12)]), + ), +) def test_all_bonds(topology, bond): """Test that all bond types are parsed as expected""" - bond_type_in_topology = functools.partial(_test_is_in_topology, - topology_section='bonds') + bond_type_in_topology = functools.partial( + _test_is_in_topology, topology_section="bonds" + ) bond_type, elements = bond bond_type_in_topology(bond_type, elements, topology) -@pytest.mark.parametrize('topology', BONDED_TPRS) -@pytest.mark.parametrize('angle', ( - ('ANGLES', [(0, 1, 2)]), - ('G96ANGLES', [(1, 2, 3)]), - ('CROSS_BOND_BOND', [(2, 3, 4)]), - ('CROSS_BOND_ANGLE', [(3, 4, 5)]), - ('UREY_BRADLEY', [(4, 5, 6)]), - ('QANGLES', [(5, 6, 7)]), - ('RESTRANGLES', [(6, 7, 8)]), - ('TABANGLES', [(7, 8, 9)]), -)) +@pytest.mark.parametrize("topology", BONDED_TPRS) +@pytest.mark.parametrize( + "angle", + ( + ("ANGLES", [(0, 1, 2)]), + ("G96ANGLES", [(1, 2, 3)]), + ("CROSS_BOND_BOND", [(2, 3, 4)]), + ("CROSS_BOND_ANGLE", [(3, 4, 5)]), + ("UREY_BRADLEY", [(4, 5, 6)]), + ("QANGLES", [(5, 6, 7)]), + ("RESTRANGLES", [(6, 7, 8)]), + ("TABANGLES", [(7, 8, 9)]), + ), +) def test_all_angles(topology, angle): - angle_type_in_topology = functools.partial(_test_is_in_topology, - topology_section='angles') + angle_type_in_topology = functools.partial( + _test_is_in_topology, topology_section="angles" + ) angle_type, elements = angle angle_type_in_topology(angle_type, elements, topology) -@pytest.mark.parametrize('topology', BONDED_TPRS) -@pytest.mark.parametrize('dih', ( - ('PDIHS', [(0, 1, 2, 3), (1, 2, 3, 4), (7, 8, 9, 10)]), - ('RBDIHS', [(4, 5, 6, 7)]), - ('RESTRDIHS', [(8, 9, 10, 11)]), - ('CBTDIHS', [(9, 10, 11, 12)]), - ('FOURDIHS', [(6, 7, 8, 9)]), - ('TABDIHS', [(10, 11, 12, 13)]), -)) +@pytest.mark.parametrize("topology", BONDED_TPRS) +@pytest.mark.parametrize( + "dih", + ( + ("PDIHS", [(0, 1, 2, 3), (1, 2, 3, 4), (7, 8, 9, 10)]), + ("RBDIHS", [(4, 5, 6, 7)]), + ("RESTRDIHS", [(8, 9, 10, 11)]), + ("CBTDIHS", [(9, 10, 11, 12)]), + ("FOURDIHS", [(6, 7, 8, 9)]), + ("TABDIHS", [(10, 11, 12, 13)]), + ), +) def test_all_dihedrals(topology, dih): - dih_type_in_topology = functools.partial(_test_is_in_topology, - topology_section='dihedrals') + dih_type_in_topology = functools.partial( + _test_is_in_topology, topology_section="dihedrals" + ) dih_type, elements = dih dih_type_in_topology(dih_type, elements, topology) -@pytest.mark.parametrize('topology', BONDED_TPRS) -@pytest.mark.parametrize('impr', ( - ('IDIHS', [(2, 3, 4, 5), (3, 4, 5, 6)]), - ('PIDIHS', [(5, 6, 7, 8)]) -)) +@pytest.mark.parametrize("topology", BONDED_TPRS) +@pytest.mark.parametrize( + "impr", + (("IDIHS", [(2, 3, 4, 5), (3, 4, 5, 6)]), ("PIDIHS", [(5, 6, 7, 8)])), +) def test_all_impropers(topology, impr): - impr_type_in_topology = functools.partial(_test_is_in_topology, - topology_section='impropers') + impr_type_in_topology = functools.partial( + _test_is_in_topology, topology_section="impropers" + ) impr_type, elements = impr impr_type_in_topology(impr_type, elements, topology) +# fmt: off @pytest.fixture(params=( - TPR400, TPR402, TPR403, TPR404, TPR405, TPR406, TPR407, TPR450, TPR451, - TPR452, TPR453, TPR454, TPR502, TPR504, TPR505, TPR510, TPR2016, TPR2018, - TPR2023, TPR2024, TPR2024_4, -)) + TPR400, TPR402, TPR403, TPR404, TPR405, TPR406, TPR407, TPR450, + TPR451, TPR452, TPR453, TPR454, TPR502, TPR504, TPR505, TPR510, + TPR2016, TPR2018, TPR2023, TPR2024, TPR2024_4, + ) +) +# fmt: on def bonds_water(request): parser = MDAnalysis.topology.TPRParser.TPRParser(request.param).parse() # The index of the first water atom is 1960 @@ -286,19 +338,22 @@ def test_settle(bonds_water): assert bonds_water[-1][1] == 2262 -@pytest.mark.parametrize('tpr_path, expected_exception', ( - (TPR2020B2, IOError), # Gromacs 2020 beta see issue #2428 - (TPR2020B2_bonded, IOError), # Gromacs 2020 beta see issue #2428 - (TPR334_bonded, NotImplementedError), # Too old - (XTC, IOError), # Not a TPR file -)) +@pytest.mark.parametrize( + "tpr_path, expected_exception", + ( + (TPR2020B2, IOError), # Gromacs 2020 beta see issue #2428 + (TPR2020B2_bonded, IOError), # Gromacs 2020 beta see issue #2428 + (TPR334_bonded, NotImplementedError), # Too old + (XTC, IOError), # Not a TPR file + ), +) def test_fail_for_unsupported_files(tpr_path, expected_exception): parser = MDAnalysis.topology.TPRParser.TPRParser(tpr_path) with pytest.raises(expected_exception): parser.parse() -@pytest.mark.parametrize('tpr_path', BONDED_TPRS) +@pytest.mark.parametrize("tpr_path", BONDED_TPRS) def test_no_elements(tpr_path): """ If the TPR does not contain element information, the element topology @@ -314,26 +369,40 @@ def test_elements(): tpr_path = TPR parser = MDAnalysis.topology.TPRParser.TPRParser(tpr_path) topology = parser.parse() - reference = np.array(( - 'H,C,H,H,C,H,H,H,C,H,H,H,C,O,N,H,C,H,C,H,H,C,H,C,H,H,H,C,H,H,' - 'H,C,O,N,H,C,H,H,C,O,O,O,H,H,,O,H,H,,O,H,H,,O,H,H,,O,H,H,,O,H' - ',H,,O,H,H,,O,H,H,,O,H,H,,O,H,H,,O,H,H,,O,H,H,,O,H,H,,O,H,H,,' - 'O,H,H' - ).split(','), dtype=object) + reference = np.array( + ( + "H,C,H,H,C,H,H,H,C,H,H,H,C,O,N,H,C,H,C,H,H,C,H,C,H,H,H,C,H,H," + "H,C,O,N,H,C,H,H,C,O,O,O,H,H,,O,H,H,,O,H,H,,O,H,H,,O,H,H,,O,H" + ",H,,O,H,H,,O,H,H,,O,H,H,,O,H,H,,O,H,H,,O,H,H,,O,H,H,,O,H,H,," + "O,H,H" + ).split(","), + dtype=object, + ) assert_equal(topology.elements.values[3300:3400], reference) - reference = np.array([ - 'O', 'H', 'H', '', 'O', 'H', 'H', '', 'O', 'H', 'H', '', 'O', 'H', - 'H', '', 'Na', 'Na', 'Na', 'Na', - ], dtype=object) + # fmt: off + reference = np.array( + [ + "O", "H", "H", "", "O", "H", "H", "", "O", "H", "H", "", "O", "H", + "H", "", "Na", "Na", "Na", "Na", + ], + dtype=object, + ) + # fmt: on assert_equal(topology.elements.values[-20:], reference) -@pytest.mark.parametrize("resid_from_one,resid_addition", [ - (False, 0), - (True, 1), # status quo for 2.x - ]) +@pytest.mark.parametrize( + "resid_from_one,resid_addition", + [ + (False, 0), + (True, 1), # status quo for 2.x + ], +) def test_resids(resid_from_one, resid_addition): u = mda.Universe(TPR, tpr_resid_from_one=resid_from_one) resids = np.arange(len(u.residues)) + resid_addition - assert_equal(u.residues.resids, resids, - err_msg="tpr_resid_from_one kwarg not switching resids") + assert_equal( + u.residues.resids, + resids, + err_msg="tpr_resid_from_one kwarg not switching resids", + ) diff --git a/testsuite/MDAnalysisTests/topology/test_txyz.py b/testsuite/MDAnalysisTests/topology/test_txyz.py index 06c2e757e0..5615bdc985 100644 --- a/testsuite/MDAnalysisTests/topology/test_txyz.py +++ b/testsuite/MDAnalysisTests/topology/test_txyz.py @@ -31,8 +31,8 @@ class TestTXYZParser(ParserBase): parser = mda.topology.TXYZParser.TXYZParser - guessed_attrs = ['masses'] - expected_attrs = ['ids', 'names', 'bonds', 'types', 'elements'] + guessed_attrs = ["masses"] + expected_attrs = ["ids", "names", "bonds", "types", "elements"] expected_n_residues = 1 expected_n_atoms = 9 @@ -55,12 +55,15 @@ def test_atom_type_type(self, top): type_is_str = [isinstance(atom_type, str) for atom_type in types] assert all(type_is_str) + def test_TXYZ_elements(): """The test checks whether elements attribute are assigned properly given a TXYZ file with valid elements record. """ - u = mda.Universe(TXYZ, format='TXYZ') - element_list = np.array(['C', 'H', 'H', 'O', 'H', 'C', 'H', 'H', 'H'], dtype=object) + u = mda.Universe(TXYZ, format="TXYZ") + element_list = np.array( + ["C", "H", "H", "O", "H", "C", "H", "H", "H"], dtype=object + ) assert_equal(u.atoms.elements, element_list) @@ -70,8 +73,10 @@ def test_missing_elements_noattribute(): 1) a warning is raised if elements are missing 2) the elements attribute is not set """ - wmsg = ("Element information is missing, elements attribute " - "will not be populated. If needed these can be ") + wmsg = ( + "Element information is missing, elements attribute " + "will not be populated. If needed these can be " + ) with pytest.warns(UserWarning, match=wmsg): u = mda.Universe(ARC_PBC) with pytest.raises(AttributeError): @@ -80,6 +85,15 @@ def test_missing_elements_noattribute(): def test_guessed_masses(): u = mda.Universe(TXYZ) - expected = [12.011, 1.008, 1.008, 15.999, 1.008, 12.011, - 1.008, 1.008, 1.008] + expected = [ + 12.011, + 1.008, + 1.008, + 15.999, + 1.008, + 12.011, + 1.008, + 1.008, + 1.008, + ] assert_allclose(u.atoms.masses, expected) diff --git a/testsuite/MDAnalysisTests/topology/test_xpdb.py b/testsuite/MDAnalysisTests/topology/test_xpdb.py index 2be72fb8e7..ba55580acd 100644 --- a/testsuite/MDAnalysisTests/topology/test_xpdb.py +++ b/testsuite/MDAnalysisTests/topology/test_xpdb.py @@ -33,10 +33,19 @@ class TestXPDBParser(ParserBase): parser = mda.topology.ExtendedPDBParser.ExtendedPDBParser ref_filename = XPDB_small - expected_attrs = ['ids', 'names', 'record_types', 'resids', - 'resnames', 'altLocs', 'icodes', 'occupancies', - 'tempfactors', 'chainIDs'] - guessed_attrs = ['masses', 'types'] + expected_attrs = [ + "ids", + "names", + "record_types", + "resids", + "resnames", + "altLocs", + "icodes", + "occupancies", + "tempfactors", + "chainIDs", + ] + guessed_attrs = ["masses", "types"] expected_n_atoms = 5 expected_n_residues = 5 expected_n_segments = 1 @@ -48,5 +57,5 @@ def test_guessed_masses(self, filename): def test_guessed_types(self, filename): u = mda.Universe(filename) - expected = ['O', 'O', 'O', 'O', 'O'] + expected = ["O", "O", "O", "O", "O"] assert_equal(u.atoms.types, expected) diff --git a/testsuite/MDAnalysisTests/topology/test_xyz.py b/testsuite/MDAnalysisTests/topology/test_xyz.py index 07b0159d6d..4574e4edb4 100644 --- a/testsuite/MDAnalysisTests/topology/test_xyz.py +++ b/testsuite/MDAnalysisTests/topology/test_xyz.py @@ -37,8 +37,8 @@ class XYZBase(ParserBase): parser = mda.topology.XYZParser.XYZParser expected_n_residues = 1 expected_n_segments = 1 - expected_attrs = ['names', 'elements'] - guessed_attrs = ['masses', 'types'] + expected_attrs = ["names", "elements"] + guessed_attrs = ["masses", "types"] class TestXYZMini(XYZBase): @@ -60,5 +60,5 @@ def test_guessed_masses(self, filename): def test_guessed_types(self, filename): u = mda.Universe(filename) - expected = ['H', 'H', 'H', 'H', 'H', 'H', 'H'] + expected = ["H", "H", "H", "H", "H", "H", "H"] assert_equal(u.atoms.types[:7], expected) diff --git a/testsuite/pyproject.toml b/testsuite/pyproject.toml index 4a9a4bf625..63450efa74 100644 --- a/testsuite/pyproject.toml +++ b/testsuite/pyproject.toml @@ -162,6 +162,7 @@ setup\.py | MDAnalysisTests/auxiliary/.*\.py | MDAnalysisTests/lib/.*\.py | MDAnalysisTests/transformations/.*\.py +| MDAnalysisTests/topology/.*\.py | MDAnalysisTests/analysis/.*\.py | MDAnalysisTests/guesser/.*\.py | MDAnalysisTests/converters/.*\.py