From 3ffaf75bc768a66ef4677e6c988e164b9ef102a9 Mon Sep 17 00:00:00 2001 From: Lily Wang Date: Tue, 25 Jun 2019 11:07:42 +1000 Subject: [PATCH 01/19] added universe classmethods --- package/MDAnalysis/core/universe.py | 238 ++++++++++++++++------------ 1 file changed, 133 insertions(+), 105 deletions(-) diff --git a/package/MDAnalysis/core/universe.py b/package/MDAnalysis/core/universe.py index 6bc96327ad0..14dfc0db32d 100644 --- a/package/MDAnalysis/core/universe.py +++ b/package/MDAnalysis/core/universe.py @@ -246,113 +246,141 @@ class Universe(object): """ - def __init__(self, *args, **kwargs): - # Store the segments for the deprecated instant selector feature. - # This attribute has to be defined early to avoid recursion in - # __getattr__. - self._instant_selectors = {} - # hold on to copy of kwargs; used by external libraries that - # reinitialize universes - self._kwargs = copy.deepcopy(kwargs) - - # managed attribute holding Reader - self._trajectory = None - self._cache = {} - - if not args: - # create an empty universe - self._topology = None - self.atoms = None + @classmethod + def from_streams(cls, topology, *args, **kwargs): + if isinstance(topology, NamedStream): + filename = topology + elif isstream(topology): + if hasattr(topology, 'name'): + _name = topology.name + else: + _name = None + filename = NamedStream(topology, _name) else: - topology_format = kwargs.pop('topology_format', None) - if len(args) == 1: - # special hacks to treat a coordinate file as a coordinate AND - # topology file - if kwargs.get('format', None) is None: - kwargs['format'] = topology_format - elif topology_format is None: - topology_format = kwargs.get('format', None) - - # if we're given a Topology object, we don't need to parse anything - if isinstance(args[0], Topology): - self._topology = args[0] - self.filename = None + raise ValueError('topology parameter must be a stream') + return cls.from_files(filename, *args, **kwargs) + + @classmethod + def from_files(cls, topology_file, *coordinates, topology_format=None, + format=None, all_coordinates=False, **kwargs): + """ + Parameters + ---------- + topology : str + A CHARMM/XPLOR PSF topology file, PDB file or Gromacs GRO file; used to + define the list of atoms. If the file includes bond information, + partial charges, atom masses, ... then these data will be available to + MDAnalysis. A "structure" file (PSF, PDB or GRO, in the sense of a + topology) is always required. + """ + + if not coordinates: + if format is None: + format = topology_format + elif topology_format is None: + topology_format = format + + parser = get_parser_for(topology_file, format=topology_format) + try: + with parser(topology_file) as p: + topology = p.parse(**kwargs) + except (IOError, OSError) as err: + # There are 2 kinds of errors that might be raised here: + # one because the file isn't present + # or the permissions are bad, second when the parser fails + if (err.errno is not None and + errno.errorcode[err.errno] in ['ENOENT', 'EACCES']): + # Runs if the error is propagated due to no permission / file not found + six.reraise(*sys.exc_info()) else: - if isinstance(args[0], NamedStream): - self.filename = args[0] - elif isstream(args[0]): - filename = None - if hasattr(args[0], 'name'): - filename = args[0].name - self.filename = NamedStream(args[0], filename) - else: - self.filename = args[0] - parser = get_parser_for(self.filename, format=topology_format) - try: - with parser(self.filename) as p: - self._topology = p.parse(**kwargs) - except (IOError, OSError) as err: - # There are 2 kinds of errors that might be raised here: - # one because the file isn't present - # or the permissions are bad, second when the parser fails - if (err.errno is not None and - errno.errorcode[err.errno] in ['ENOENT', 'EACCES']): - # Runs if the error is propagated due to no permission / file not found - six.reraise(*sys.exc_info()) - else: - # Runs when the parser fails - six.raise_from(IOError( - "Failed to load from the topology file {0}" - " with parser {1}.\n" - "Error: {2}".format(self.filename, parser, err)), - None) - except (ValueError, NotImplementedError) as err: - six.raise_from(ValueError( - "Failed to construct topology from file {0}" - " with parser {1}.\n" - "Error: {2}".format(self.filename, parser, err)), None) - - # generate and populate Universe version of each class - self._generate_from_topology() - - # Load coordinates - if len(args) == 1 or kwargs.get('all_coordinates', False): - if self.filename is None: - # If we got the topology as a Topology object, then we - # cannot read coordinates from it. - coordinatefile = args[1:] - else: - # Can the topology file also act as coordinate file? - try: - _ = get_reader_for(self.filename, - format=kwargs.get('format', None)) - except ValueError: - coordinatefile = args[1:] - else: - coordinatefile = (self.filename,) + args[1:] + # Runs when the parser fails + raise IOError("Failed to load from the topology file {0}" + " with parser {1}.\n" + "Error: {2}".format(topology_file, parser, err)) + except (ValueError, NotImplementedError) as err: + raise ValueError( + "Failed to construct topology from file {0}" + " with parser {1}.\n" + "Error: {2}".format(topology_file, parser, err)) + + if all_coordinates or not coordinates: + try: + get_reader_for(topology_file, format) + except ValueError: + warnings.warn('No coordinate reader found for {}. Skipping ' + 'this file.'.format(topology_file)) else: - coordinatefile = args[1:] - - if not coordinatefile: - coordinatefile = None - - self.load_new(coordinatefile, **kwargs) - # parse transformations - trans_arg = kwargs.pop('transformations', None) - if trans_arg: - transforms =[trans_arg] if callable(trans_arg) else trans_arg - self.trajectory.add_transformations(*transforms) - - # Check for guess_bonds - if kwargs.pop('guess_bonds', False): - self.atoms.guess_bonds(vdwradii=kwargs.pop('vdwradii', None)) - - # None causes generic hash to get used. - # We store the name ieven if is_anchor is False in case the user later - # wants to make the universe an anchor. - self._anchor_name = kwargs.get('anchor_name', None) - # Universes are anchors by default - self.is_anchor = kwargs.get('is_anchor', True) + coordinates = (topology_file,) + coordinates + + # trajectory = load_new(coordinates, format=format, n_atoms=topology.n_atoms) + + obj = cls(topology, *coordinates, topology_file=topology_file, format=format, + topology_format=topology_format, + all_coordinates=all_coordinates, **kwargs) + return obj + + def __new__(cls, topology, *coordinates, **kwargs): + _type = type(Topology) + if _type is not Topology: + raise DeprecationWarning('The new API only supports passing' + 'MDAnalysis.Topology objects through ' + '__init__. Invalid type: {}. Please ' + 'use Universe.from_files or ' + 'Universe.from_streams'.format(_type)) + if _type is str: + return cls.from_files(topology, *coordinates, **kwargs) + else: + return cls.from_streams(topology, *coordinates, **kwargs) + return super(Universe, cls).__new__(cls, topology, *coordinates, **kwargs) + + + + def __init__(self, topology, *coordinates, topology_file=None, + transformations=None, guess_bonds=False, vdwradii=None, + anchor_name=None, is_anchor=False, **kwargs): + """ + Parameters + ---------- + + anchor_name: str, None + None causes generic hash to get used. + is_anchor: bool + Universe are anchors by default. + """ + + self._instant_selectors = {} # for storing segments. Deprecated? + self._trajectory = None # managed attribute holding Reader + self._cache = {} + self._anchor_name = anchor_name + self._is_anchor = is_anchor + self.atoms = None + self.residues = None + self.segments = None + self._topology = topology + self.filename = topology_file + + + if topology: + self._generate_from_topology() # make real atoms, res, segments + + self.load_new(coordinates, **kwargs) + + if transformations: + if callable(transformations): + transformations = [transformations] + self._trajectory.add_transformations(*transformations) + + if guess_bonds: + self.atoms.guess_bonds(vdwradii=vdwradii) + + self._kwargs = { + 'transformations': transformations, + 'guess_bonds': guess_bonds, + 'vdwradii': vdwradii, + 'anchor_name': anchor_name, + 'is_anchor': is_anchor, + } + self._kwargs.update(kwargs) def copy(self): @@ -591,7 +619,7 @@ def load_new(self, filename, format=None, in_memory=False, **kwargs): and detected file type. """ # filename==None happens when only a topology is provided - if filename is None: + if not filename: return self if len(util.asiterable(filename)) == 1: From 7d0da8c2f97fb980d48359e3ed3bbbe2d29a9737 Mon Sep 17 00:00:00 2001 From: Lily Wang Date: Thu, 27 Jun 2019 09:01:31 +1000 Subject: [PATCH 02/19] added class decorator --- .vscode/settings.json | 3 + package/MDAnalysis/analysis/diffusionmap.py | 3 +- package/MDAnalysis/core/AtomGroup.py | 11 +- package/MDAnalysis/core/universe.py | 206 +++++++++++++------- 4 files changed, 141 insertions(+), 82 deletions(-) create mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000000..20545edda3f --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "python.pythonPath": "/Users/lily/anaconda3/envs/mda/bin/python" +} \ No newline at end of file diff --git a/package/MDAnalysis/analysis/diffusionmap.py b/package/MDAnalysis/analysis/diffusionmap.py index 407e0244c58..8278123d084 100644 --- a/package/MDAnalysis/analysis/diffusionmap.py +++ b/package/MDAnalysis/analysis/diffusionmap.py @@ -306,7 +306,8 @@ def __init__(self, u, epsilon=1, **kwargs): Parameters to be passed for the initialization of a :class:`DistanceMatrix`. """ - if isinstance(u, Universe): + # if isinstance(u, Universe): # doesn't work with reformat_universe wrapper + if hasattr(u, 'trajectory') and hasattr(u, 'select_atoms'): self._dist_matrix = DistanceMatrix(u, **kwargs) elif isinstance(u, DistanceMatrix): self._dist_matrix = u diff --git a/package/MDAnalysis/core/AtomGroup.py b/package/MDAnalysis/core/AtomGroup.py index 3967fe7a9ec..03f4447d0fe 100644 --- a/package/MDAnalysis/core/AtomGroup.py +++ b/package/MDAnalysis/core/AtomGroup.py @@ -38,12 +38,11 @@ def __init__(self, *args, **kwargs): return new_class - -Universe = deprecate_class( - universe.Universe, - "MDAnalysis.core.AtomGroup.Universe has been removed." - "Please use MDAnalysis.Universe." - "This stub will be removed in 1.0") +def Universe(*args, **kwargs): + warnings.warn("MDAnalysis.core.AtomGroup.Universe has been removed." + "Please use MDAnalysis.Universe." + "This stub will be removed in 1.0") + return universe.Universe(*args, **kwargs) _group_message = ("MDAnalysis.core.AtomGroup.{0} has been removed." "Please use MDAnalysis.groups.{0}" diff --git a/package/MDAnalysis/core/universe.py b/package/MDAnalysis/core/universe.py index 14dfc0db32d..c4a42efdf7c 100644 --- a/package/MDAnalysis/core/universe.py +++ b/package/MDAnalysis/core/universe.py @@ -131,7 +131,27 @@ logger = logging.getLogger("MDAnalysis.core.universe") +def reformat_universe(cls): + class Wrapper(cls): # Must return class for deprecate_class subclassing + def __new__(klass, topology, *coordinates, **kwargs): + _type = type(topology) + if _type is not Topology: + warnings.warn('Universes should be created with an explicit' + 'classmethod, eg. Universe.from_files or ' + 'Universe.from_streams. Only MDAnalysis.Topology ' + 'objects should be passed to Universe(); {} ' + 'passed instead'.format(_type), DeprecationWarning) + if _type is str: + return cls.from_files(topology, *coordinates, **kwargs) + else: + return cls.from_streams(topology, *coordinates, **kwargs) + + else: + return cls(topology, *coordinates, **kwargs) + + return Wrapper +@reformat_universe class Universe(object): """The MDAnalysis Universe contains all the information describing the system. @@ -171,62 +191,63 @@ class Universe(object): Parameters ---------- - topology : str, Topology object or stream - A CHARMM/XPLOR PSF topology file, PDB file or Gromacs GRO file; used to - define the list of atoms. If the file includes bond information, - partial charges, atom masses, ... then these data will be available to - MDAnalysis. A "structure" file (PSF, PDB or GRO, in the sense of a - topology) is always required. Alternatively, an existing - :class:`MDAnalysis.core.topology.Topology` instance may also be given. - topology_format + topology : `~MDAnalysis.core.topology.Topology` + The topology defines the list of atoms. + coordinates : str, stream, list of str, list of stream (optional) + Coordinates can be provided as streams of filenames either of + a single frame (eg a PDB, CRD, or GRO file); a list of single + frames; or a trajectory file (in CHARMM/NAMD/LAMMPS DCD, Gromacs + XTC/TRR, or generic XYZ format). The coordinates have to be + ordered in the same way as the list of atoms in the topology. + See :ref:`Supported coordinate formats` for what can be read + as coordinates. + topology_format: str, ``None``, default ``None`` Provide the file format of the topology file; ``None`` guesses it from - the file extension [``None``] Can also pass a subclass of + the file extension. Can also pass a subclass of :class:`MDAnalysis.topology.base.TopologyReaderBase` to define a custom reader to be used on the topology file. - format + format: str, ``None``, default ``None`` Provide the file format of the coordinate or trajectory file; ``None`` guesses it from the file extension. Note that this keyword has no effect if a list of file names is supplied because the "chained" reader has to guess the file format for each individual list member. - [``None``] Can also pass a subclass of - :class:`MDAnalysis.coordinates.base.ProtoReader` to define a custom - reader to be used on the trajectory file. - all_coordinates : bool + Can also pass a subclass of :class:`MDAnalysis.coordinates.base.ProtoReader` + to define a custom reader to be used on the trajectory file. + all_coordinates : bool, default ``False`` If set to ``True`` specifies that if more than one filename is passed they are all to be used, if possible, as coordinate files (employing a - :class:`MDAnalysis.coordinates.chain.ChainReader`). [``False``] The + :class:`MDAnalysis.coordinates.chain.ChainReader`). The default behavior is to take the first file as a topology and the remaining as coordinates. The first argument will always always be used - to infer a topology regardless of *all_coordinates*. This parameter is - ignored if only one argument is passed. - guess_bonds : bool, optional + to infer a topology regardless of *all_coordinates*. + guess_bonds : bool, default ``False`` Once Universe has been loaded, attempt to guess the connectivity - between atoms. This will populate the .bonds .angles and .dihedrals + between atoms. This will populate the .bonds, .angles, and .dihedrals attributes of the Universe. - vdwradii : dict, optional + vdwradii : dict, ``None``, default ``None`` For use with *guess_bonds*. Supply a dict giving a vdwradii for each atom type which are used in guessing bonds. - is_anchor : bool, optional + is_anchor : bool, default ``True`` When unpickling instances of :class:`MDAnalysis.core.groups.AtomGroup` existing Universes are searched for one where to anchor those atoms. Set to ``False`` to - prevent this Universe from being considered. [``True``] - anchor_name : str, optional + prevent this Universe from being considered. + anchor_name : str, ``None``, default ``None`` Setting to other than ``None`` will cause :class:`MDAnalysis.core.groups.AtomGroup` instances pickled from the Universe to only unpickle if a compatible Universe with matching *anchor_name* is found. Even if *anchor_name* is set *is_anchor* will still be honored when unpickling. - transformations: function or list, optional + transformations: function or list, ``None``, default ``None`` Provide a list of transformations that you wish to apply to the trajectory upon reading. Transformations can be found in :mod:`MDAnalysis.transformations`, or can be user-created. - in_memory + in_memory: bool, default ``False`` After reading in the trajectory, transfer it to an in-memory representations, which allow for manipulation of coordinates. - in_memory_step + in_memory_step: int, default 1 Only read every nth frame into in-memory representation. - continuous : bool, optional + continuous : bool, default ``False`` The `continuous` option is used by the :mod:`ChainReader`, which contains the functionality to treat independent trajectory files as a single virtual @@ -247,7 +268,29 @@ class Universe(object): """ @classmethod - def from_streams(cls, topology, *args, **kwargs): + def from_streams(cls, topology, *coordinates, **kwargs): + """ + Construct a Universe with the topology from a stream. + + Parameters + ---------- + topology : stream + A stream of a CHARMM/XPLOR PSF topology file, PDB file or Gromacs GRO file; used to + define the list of atoms. If the file includes bond information, + partial charges, atom masses, ... then these data will be available to + MDAnalysis. A "structure" file (PSF, PDB or GRO, in the sense of a + topology) is always required. + coordinates : str, stream, list of str, list of stream (optional) + Coordinates can be provided either as a single frame (eg a PDB, CRD, + or GRO file); a list of single frames; or a trajectory file (in + CHARMM/NAMD/LAMMPS DCD, Gromacs XTC/TRR, or generic XYZ format). + The coordinates have to be ordered in the same way as the list of + atoms in the topology. See :ref:`Supported coordinate formats` + for what can be read as coordinates. + kwargs: + Optional arguments for file formats, transformations, vdwradii and + anchoring are passed to :func:`~MDAnalysis.Universe.from_files`. + """ if isinstance(topology, NamedStream): filename = topology elif isstream(topology): @@ -257,12 +300,13 @@ def from_streams(cls, topology, *args, **kwargs): _name = None filename = NamedStream(topology, _name) else: - raise ValueError('topology parameter must be a stream') - return cls.from_files(filename, *args, **kwargs) + raise ValueError('topology parameter must be a stream. ' + 'Given: {}'.format(type(topology))) + return cls.from_files(filename, *coordinates, **kwargs) @classmethod def from_files(cls, topology_file, *coordinates, topology_format=None, - format=None, all_coordinates=False, **kwargs): + format=None, **kwargs): """ Parameters ---------- @@ -272,6 +316,36 @@ def from_files(cls, topology_file, *coordinates, topology_format=None, partial charges, atom masses, ... then these data will be available to MDAnalysis. A "structure" file (PSF, PDB or GRO, in the sense of a topology) is always required. + coordinates : str, stream, list of str, list of stream (optional) + Coordinates can be provided as streams of filenames either of + a single frame (eg a PDB, CRD, or GRO file); a list of single + frames; or a trajectory file (in CHARMM/NAMD/LAMMPS DCD, Gromacs + XTC/TRR, or generic XYZ format). The coordinates have to be + ordered in the same way as the list of atoms in the topology. + See :ref:`Supported coordinate formats` for what can be read + as coordinates. + topology_format: str, ``None``, default ``None`` + Provide the file format of the topology file; ``None`` guesses it from + the file extension. Can also pass a subclass of + :class:`MDAnalysis.topology.base.TopologyReaderBase` to define a custom + reader to be used on the topology file. + format: str, ``None``, default ``None`` + Provide the file format of the coordinate or trajectory file; ``None`` + guesses it from the file extension. Note that this keyword has no + effect if a list of file names is supplied because the "chained" reader + has to guess the file format for each individual list member. + Can also pass a subclass of :class:`MDAnalysis.coordinates.base.ProtoReader` + to define a custom reader to be used on the trajectory file. + all_coordinates : bool, default ``False`` + If set to ``True`` specifies that if more than one filename is passed + they are all to be used, if possible, as coordinate files (employing a + :class:`MDAnalysis.coordinates.chain.ChainReader`). The + default behavior is to take the first file as a topology and the + remaining as coordinates. The first argument will always always be used + to infer a topology regardless of *all_coordinates*. + kwargs: + Optional arguments for transformations, vdwradii and + anchoring are passed to :func:`~MDAnalysis.Universe`. """ if not coordinates: @@ -303,50 +377,23 @@ def from_files(cls, topology_file, *coordinates, topology_format=None, " with parser {1}.\n" "Error: {2}".format(topology_file, parser, err)) - if all_coordinates or not coordinates: - try: - get_reader_for(topology_file, format) - except ValueError: - warnings.warn('No coordinate reader found for {}. Skipping ' - 'this file.'.format(topology_file)) - else: - coordinates = (topology_file,) + coordinates - - # trajectory = load_new(coordinates, format=format, n_atoms=topology.n_atoms) - obj = cls(topology, *coordinates, topology_file=topology_file, format=format, - topology_format=topology_format, - all_coordinates=all_coordinates, **kwargs) + topology_format=topology_format, **kwargs) return obj - def __new__(cls, topology, *coordinates, **kwargs): - _type = type(Topology) - if _type is not Topology: - raise DeprecationWarning('The new API only supports passing' - 'MDAnalysis.Topology objects through ' - '__init__. Invalid type: {}. Please ' - 'use Universe.from_files or ' - 'Universe.from_streams'.format(_type)) - if _type is str: - return cls.from_files(topology, *coordinates, **kwargs) - else: - return cls.from_streams(topology, *coordinates, **kwargs) - return super(Universe, cls).__new__(cls, topology, *coordinates, **kwargs) - - - def __init__(self, topology, *coordinates, topology_file=None, - transformations=None, guess_bonds=False, vdwradii=None, - anchor_name=None, is_anchor=False, **kwargs): - """ - Parameters - ---------- - - anchor_name: str, None - None causes generic hash to get used. - is_anchor: bool - Universe are anchors by default. - """ + def __init__(self, topology, *coordinates, topology_file=None, + all_coordinates=False, + format=None, transformations=None, guess_bonds=False, + vdwradii=None, anchor_name=None, is_anchor=False, + in_memory=False, in_memory_step=1, **kwargs): + + print('INITING, ', topology) + _type = type(topology) + if _type is not Topology: + raise ValueError('Only MDAnalysis.Topology objects should be ' + 'passed to Universe(); {} passed ' + 'instead.'.format(_type)) self._instant_selectors = {} # for storing segments. Deprecated? self._trajectory = None # managed attribute holding Reader @@ -359,11 +406,19 @@ def __init__(self, topology, *coordinates, topology_file=None, self._topology = topology self.filename = topology_file - if topology: self._generate_from_topology() # make real atoms, res, segments - self.load_new(coordinates, **kwargs) + if all_coordinates or not coordinates and topology_file: + try: + get_reader_for(topology_file, format) + except ValueError: + warnings.warn('No coordinate reader found for {}. Skipping ' + 'this file.'.format(topology_file)) + else: + coordinates = (topology_file,) + coordinates + + self.load_new(coordinates, format=format, in_memory=in_memory, in_memory_step=in_memory_step) if transformations: if callable(transformations): @@ -563,7 +618,8 @@ def universe(self): # It is also cleaner than a weakref. return self - def load_new(self, filename, format=None, in_memory=False, **kwargs): + def load_new(self, filename, format=None, in_memory=False, + in_memory_step=1, **kwargs): """Load coordinates from `filename`. The file format of `filename` is autodetected from the file name suffix @@ -649,7 +705,7 @@ def load_new(self, filename, format=None, in_memory=False, **kwargs): trj_n_atoms=self.trajectory.n_atoms)) if in_memory: - self.transfer_to_memory(step=kwargs.get("in_memory_step", 1)) + self.transfer_to_memory(step=in_memory_step) return self From 05b4bf2cece624ecc67f96d74f84bdd979a037f1 Mon Sep 17 00:00:00 2001 From: Lily Wang Date: Thu, 27 Jun 2019 18:25:50 +1000 Subject: [PATCH 03/19] passing tests except empty --- package/MDAnalysis/core/AtomGroup.py | 11 ++++++----- package/MDAnalysis/core/universe.py | 22 +++++++++++++--------- 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/package/MDAnalysis/core/AtomGroup.py b/package/MDAnalysis/core/AtomGroup.py index 03f4447d0fe..3967fe7a9ec 100644 --- a/package/MDAnalysis/core/AtomGroup.py +++ b/package/MDAnalysis/core/AtomGroup.py @@ -38,11 +38,12 @@ def __init__(self, *args, **kwargs): return new_class -def Universe(*args, **kwargs): - warnings.warn("MDAnalysis.core.AtomGroup.Universe has been removed." - "Please use MDAnalysis.Universe." - "This stub will be removed in 1.0") - return universe.Universe(*args, **kwargs) + +Universe = deprecate_class( + universe.Universe, + "MDAnalysis.core.AtomGroup.Universe has been removed." + "Please use MDAnalysis.Universe." + "This stub will be removed in 1.0") _group_message = ("MDAnalysis.core.AtomGroup.{0} has been removed." "Please use MDAnalysis.groups.{0}" diff --git a/package/MDAnalysis/core/universe.py b/package/MDAnalysis/core/universe.py index c4a42efdf7c..29cfdc05dfc 100644 --- a/package/MDAnalysis/core/universe.py +++ b/package/MDAnalysis/core/universe.py @@ -141,13 +141,17 @@ def __new__(klass, topology, *coordinates, **kwargs): 'Universe.from_streams. Only MDAnalysis.Topology ' 'objects should be passed to Universe(); {} ' 'passed instead'.format(_type), DeprecationWarning) - if _type is str: - return cls.from_files(topology, *coordinates, **kwargs) + if isinstance(topology, NamedStream) or isstream(topology): + return klass.from_streams(topology, *coordinates, **kwargs) else: - return cls.from_streams(topology, *coordinates, **kwargs) + return klass.from_files(topology, *coordinates, **kwargs) else: - return cls(topology, *coordinates, **kwargs) + return super(Wrapper, klass).__new__(klass) + + def __init__(self, topology, *coordinates, **kwargs): + if not self._topology: # ugly ugly ugly but passes tests + super(Wrapper, self).__init__(topology, *coordinates, **kwargs) return Wrapper @@ -266,6 +270,7 @@ class Universe(object): master ConnectivityGroups for each connectivity type """ + _topology = None @classmethod def from_streams(cls, topology, *coordinates, **kwargs): @@ -387,8 +392,6 @@ def __init__(self, topology, *coordinates, topology_file=None, format=None, transformations=None, guess_bonds=False, vdwradii=None, anchor_name=None, is_anchor=False, in_memory=False, in_memory_step=1, **kwargs): - - print('INITING, ', topology) _type = type(topology) if _type is not Topology: raise ValueError('Only MDAnalysis.Topology objects should be ' @@ -406,10 +409,10 @@ def __init__(self, topology, *coordinates, topology_file=None, self._topology = topology self.filename = topology_file - if topology: + if topology is not None: self._generate_from_topology() # make real atoms, res, segments - if all_coordinates or not coordinates and topology_file: + if all_coordinates or not len(coordinates) and topology_file is not None: try: get_reader_for(topology_file, format) except ValueError: @@ -418,7 +421,8 @@ def __init__(self, topology, *coordinates, topology_file=None, else: coordinates = (topology_file,) + coordinates - self.load_new(coordinates, format=format, in_memory=in_memory, in_memory_step=in_memory_step) + self.load_new(coordinates, format=format, in_memory=in_memory, + in_memory_step=in_memory_step, **kwargs) if transformations: if callable(transformations): From f167c750fff5a86e01effd095cdca2a5f07ea1ab Mon Sep 17 00:00:00 2001 From: Lily Wang Date: Tue, 25 Jun 2019 11:07:42 +1000 Subject: [PATCH 04/19] added universe classmethods --- package/MDAnalysis/core/universe.py | 127 ++++++++++------------------ 1 file changed, 47 insertions(+), 80 deletions(-) diff --git a/package/MDAnalysis/core/universe.py b/package/MDAnalysis/core/universe.py index 29cfdc05dfc..e65b21a94f0 100644 --- a/package/MDAnalysis/core/universe.py +++ b/package/MDAnalysis/core/universe.py @@ -273,29 +273,7 @@ class Universe(object): _topology = None @classmethod - def from_streams(cls, topology, *coordinates, **kwargs): - """ - Construct a Universe with the topology from a stream. - - Parameters - ---------- - topology : stream - A stream of a CHARMM/XPLOR PSF topology file, PDB file or Gromacs GRO file; used to - define the list of atoms. If the file includes bond information, - partial charges, atom masses, ... then these data will be available to - MDAnalysis. A "structure" file (PSF, PDB or GRO, in the sense of a - topology) is always required. - coordinates : str, stream, list of str, list of stream (optional) - Coordinates can be provided either as a single frame (eg a PDB, CRD, - or GRO file); a list of single frames; or a trajectory file (in - CHARMM/NAMD/LAMMPS DCD, Gromacs XTC/TRR, or generic XYZ format). - The coordinates have to be ordered in the same way as the list of - atoms in the topology. See :ref:`Supported coordinate formats` - for what can be read as coordinates. - kwargs: - Optional arguments for file formats, transformations, vdwradii and - anchoring are passed to :func:`~MDAnalysis.Universe.from_files`. - """ + def from_streams(cls, topology, *args, **kwargs): if isinstance(topology, NamedStream): filename = topology elif isstream(topology): @@ -305,13 +283,12 @@ def from_streams(cls, topology, *coordinates, **kwargs): _name = None filename = NamedStream(topology, _name) else: - raise ValueError('topology parameter must be a stream. ' - 'Given: {}'.format(type(topology))) - return cls.from_files(filename, *coordinates, **kwargs) + raise ValueError('topology parameter must be a stream') + return cls.from_files(filename, *args, **kwargs) @classmethod def from_files(cls, topology_file, *coordinates, topology_format=None, - format=None, **kwargs): + format=None, all_coordinates=False, **kwargs): """ Parameters ---------- @@ -321,36 +298,6 @@ def from_files(cls, topology_file, *coordinates, topology_format=None, partial charges, atom masses, ... then these data will be available to MDAnalysis. A "structure" file (PSF, PDB or GRO, in the sense of a topology) is always required. - coordinates : str, stream, list of str, list of stream (optional) - Coordinates can be provided as streams of filenames either of - a single frame (eg a PDB, CRD, or GRO file); a list of single - frames; or a trajectory file (in CHARMM/NAMD/LAMMPS DCD, Gromacs - XTC/TRR, or generic XYZ format). The coordinates have to be - ordered in the same way as the list of atoms in the topology. - See :ref:`Supported coordinate formats` for what can be read - as coordinates. - topology_format: str, ``None``, default ``None`` - Provide the file format of the topology file; ``None`` guesses it from - the file extension. Can also pass a subclass of - :class:`MDAnalysis.topology.base.TopologyReaderBase` to define a custom - reader to be used on the topology file. - format: str, ``None``, default ``None`` - Provide the file format of the coordinate or trajectory file; ``None`` - guesses it from the file extension. Note that this keyword has no - effect if a list of file names is supplied because the "chained" reader - has to guess the file format for each individual list member. - Can also pass a subclass of :class:`MDAnalysis.coordinates.base.ProtoReader` - to define a custom reader to be used on the trajectory file. - all_coordinates : bool, default ``False`` - If set to ``True`` specifies that if more than one filename is passed - they are all to be used, if possible, as coordinate files (employing a - :class:`MDAnalysis.coordinates.chain.ChainReader`). The - default behavior is to take the first file as a topology and the - remaining as coordinates. The first argument will always always be used - to infer a topology regardless of *all_coordinates*. - kwargs: - Optional arguments for transformations, vdwradii and - anchoring are passed to :func:`~MDAnalysis.Universe`. """ if not coordinates: @@ -382,21 +329,50 @@ def from_files(cls, topology_file, *coordinates, topology_format=None, " with parser {1}.\n" "Error: {2}".format(topology_file, parser, err)) + if all_coordinates or not coordinates: + try: + get_reader_for(topology_file, format) + except ValueError: + warnings.warn('No coordinate reader found for {}. Skipping ' + 'this file.'.format(topology_file)) + else: + coordinates = (topology_file,) + coordinates + + # trajectory = load_new(coordinates, format=format, n_atoms=topology.n_atoms) + obj = cls(topology, *coordinates, topology_file=topology_file, format=format, - topology_format=topology_format, **kwargs) + topology_format=topology_format, + all_coordinates=all_coordinates, **kwargs) return obj - - def __init__(self, topology, *coordinates, topology_file=None, - all_coordinates=False, - format=None, transformations=None, guess_bonds=False, - vdwradii=None, anchor_name=None, is_anchor=False, - in_memory=False, in_memory_step=1, **kwargs): - _type = type(topology) + def __new__(cls, topology, *coordinates, **kwargs): + _type = type(Topology) if _type is not Topology: - raise ValueError('Only MDAnalysis.Topology objects should be ' - 'passed to Universe(); {} passed ' - 'instead.'.format(_type)) + raise DeprecationWarning('The new API only supports passing' + 'MDAnalysis.Topology objects through ' + '__init__. Invalid type: {}. Please ' + 'use Universe.from_files or ' + 'Universe.from_streams'.format(_type)) + if _type is str: + return cls.from_files(topology, *coordinates, **kwargs) + else: + return cls.from_streams(topology, *coordinates, **kwargs) + return super(Universe, cls).__new__(cls, topology, *coordinates, **kwargs) + + + + def __init__(self, topology, *coordinates, topology_file=None, + transformations=None, guess_bonds=False, vdwradii=None, + anchor_name=None, is_anchor=False, **kwargs): + """ + Parameters + ---------- + + anchor_name: str, None + None causes generic hash to get used. + is_anchor: bool + Universe are anchors by default. + """ self._instant_selectors = {} # for storing segments. Deprecated? self._trajectory = None # managed attribute holding Reader @@ -409,20 +385,11 @@ def __init__(self, topology, *coordinates, topology_file=None, self._topology = topology self.filename = topology_file - if topology is not None: + + if topology: self._generate_from_topology() # make real atoms, res, segments - if all_coordinates or not len(coordinates) and topology_file is not None: - try: - get_reader_for(topology_file, format) - except ValueError: - warnings.warn('No coordinate reader found for {}. Skipping ' - 'this file.'.format(topology_file)) - else: - coordinates = (topology_file,) + coordinates - - self.load_new(coordinates, format=format, in_memory=in_memory, - in_memory_step=in_memory_step, **kwargs) + self.load_new(coordinates, **kwargs) if transformations: if callable(transformations): From 14446c3a16031d25d6485465eb7360870557b573 Mon Sep 17 00:00:00 2001 From: Lily Wang Date: Thu, 27 Jun 2019 09:01:31 +1000 Subject: [PATCH 05/19] added class decorator --- package/MDAnalysis/core/AtomGroup.py | 11 +-- package/MDAnalysis/core/universe.py | 138 ++++++++++++++++----------- 2 files changed, 89 insertions(+), 60 deletions(-) diff --git a/package/MDAnalysis/core/AtomGroup.py b/package/MDAnalysis/core/AtomGroup.py index 3967fe7a9ec..03f4447d0fe 100644 --- a/package/MDAnalysis/core/AtomGroup.py +++ b/package/MDAnalysis/core/AtomGroup.py @@ -38,12 +38,11 @@ def __init__(self, *args, **kwargs): return new_class - -Universe = deprecate_class( - universe.Universe, - "MDAnalysis.core.AtomGroup.Universe has been removed." - "Please use MDAnalysis.Universe." - "This stub will be removed in 1.0") +def Universe(*args, **kwargs): + warnings.warn("MDAnalysis.core.AtomGroup.Universe has been removed." + "Please use MDAnalysis.Universe." + "This stub will be removed in 1.0") + return universe.Universe(*args, **kwargs) _group_message = ("MDAnalysis.core.AtomGroup.{0} has been removed." "Please use MDAnalysis.groups.{0}" diff --git a/package/MDAnalysis/core/universe.py b/package/MDAnalysis/core/universe.py index e65b21a94f0..83c1b9181d9 100644 --- a/package/MDAnalysis/core/universe.py +++ b/package/MDAnalysis/core/universe.py @@ -141,17 +141,13 @@ def __new__(klass, topology, *coordinates, **kwargs): 'Universe.from_streams. Only MDAnalysis.Topology ' 'objects should be passed to Universe(); {} ' 'passed instead'.format(_type), DeprecationWarning) - if isinstance(topology, NamedStream) or isstream(topology): - return klass.from_streams(topology, *coordinates, **kwargs) + if _type is str: + return cls.from_files(topology, *coordinates, **kwargs) else: - return klass.from_files(topology, *coordinates, **kwargs) + return cls.from_streams(topology, *coordinates, **kwargs) else: - return super(Wrapper, klass).__new__(klass) - - def __init__(self, topology, *coordinates, **kwargs): - if not self._topology: # ugly ugly ugly but passes tests - super(Wrapper, self).__init__(topology, *coordinates, **kwargs) + return cls(topology, *coordinates, **kwargs) return Wrapper @@ -273,7 +269,29 @@ class Universe(object): _topology = None @classmethod - def from_streams(cls, topology, *args, **kwargs): + def from_streams(cls, topology, *coordinates, **kwargs): + """ + Construct a Universe with the topology from a stream. + + Parameters + ---------- + topology : stream + A stream of a CHARMM/XPLOR PSF topology file, PDB file or Gromacs GRO file; used to + define the list of atoms. If the file includes bond information, + partial charges, atom masses, ... then these data will be available to + MDAnalysis. A "structure" file (PSF, PDB or GRO, in the sense of a + topology) is always required. + coordinates : str, stream, list of str, list of stream (optional) + Coordinates can be provided either as a single frame (eg a PDB, CRD, + or GRO file); a list of single frames; or a trajectory file (in + CHARMM/NAMD/LAMMPS DCD, Gromacs XTC/TRR, or generic XYZ format). + The coordinates have to be ordered in the same way as the list of + atoms in the topology. See :ref:`Supported coordinate formats` + for what can be read as coordinates. + kwargs: + Optional arguments for file formats, transformations, vdwradii and + anchoring are passed to :func:`~MDAnalysis.Universe.from_files`. + """ if isinstance(topology, NamedStream): filename = topology elif isstream(topology): @@ -283,12 +301,13 @@ def from_streams(cls, topology, *args, **kwargs): _name = None filename = NamedStream(topology, _name) else: - raise ValueError('topology parameter must be a stream') - return cls.from_files(filename, *args, **kwargs) + raise ValueError('topology parameter must be a stream. ' + 'Given: {}'.format(type(topology))) + return cls.from_files(filename, *coordinates, **kwargs) @classmethod def from_files(cls, topology_file, *coordinates, topology_format=None, - format=None, all_coordinates=False, **kwargs): + format=None, **kwargs): """ Parameters ---------- @@ -298,6 +317,36 @@ def from_files(cls, topology_file, *coordinates, topology_format=None, partial charges, atom masses, ... then these data will be available to MDAnalysis. A "structure" file (PSF, PDB or GRO, in the sense of a topology) is always required. + coordinates : str, stream, list of str, list of stream (optional) + Coordinates can be provided as streams of filenames either of + a single frame (eg a PDB, CRD, or GRO file); a list of single + frames; or a trajectory file (in CHARMM/NAMD/LAMMPS DCD, Gromacs + XTC/TRR, or generic XYZ format). The coordinates have to be + ordered in the same way as the list of atoms in the topology. + See :ref:`Supported coordinate formats` for what can be read + as coordinates. + topology_format: str, ``None``, default ``None`` + Provide the file format of the topology file; ``None`` guesses it from + the file extension. Can also pass a subclass of + :class:`MDAnalysis.topology.base.TopologyReaderBase` to define a custom + reader to be used on the topology file. + format: str, ``None``, default ``None`` + Provide the file format of the coordinate or trajectory file; ``None`` + guesses it from the file extension. Note that this keyword has no + effect if a list of file names is supplied because the "chained" reader + has to guess the file format for each individual list member. + Can also pass a subclass of :class:`MDAnalysis.coordinates.base.ProtoReader` + to define a custom reader to be used on the trajectory file. + all_coordinates : bool, default ``False`` + If set to ``True`` specifies that if more than one filename is passed + they are all to be used, if possible, as coordinate files (employing a + :class:`MDAnalysis.coordinates.chain.ChainReader`). The + default behavior is to take the first file as a topology and the + remaining as coordinates. The first argument will always always be used + to infer a topology regardless of *all_coordinates*. + kwargs: + Optional arguments for transformations, vdwradii and + anchoring are passed to :func:`~MDAnalysis.Universe`. """ if not coordinates: @@ -329,50 +378,23 @@ def from_files(cls, topology_file, *coordinates, topology_format=None, " with parser {1}.\n" "Error: {2}".format(topology_file, parser, err)) - if all_coordinates or not coordinates: - try: - get_reader_for(topology_file, format) - except ValueError: - warnings.warn('No coordinate reader found for {}. Skipping ' - 'this file.'.format(topology_file)) - else: - coordinates = (topology_file,) + coordinates - - # trajectory = load_new(coordinates, format=format, n_atoms=topology.n_atoms) - obj = cls(topology, *coordinates, topology_file=topology_file, format=format, - topology_format=topology_format, - all_coordinates=all_coordinates, **kwargs) + topology_format=topology_format, **kwargs) return obj - def __new__(cls, topology, *coordinates, **kwargs): - _type = type(Topology) - if _type is not Topology: - raise DeprecationWarning('The new API only supports passing' - 'MDAnalysis.Topology objects through ' - '__init__. Invalid type: {}. Please ' - 'use Universe.from_files or ' - 'Universe.from_streams'.format(_type)) - if _type is str: - return cls.from_files(topology, *coordinates, **kwargs) - else: - return cls.from_streams(topology, *coordinates, **kwargs) - return super(Universe, cls).__new__(cls, topology, *coordinates, **kwargs) - - - def __init__(self, topology, *coordinates, topology_file=None, - transformations=None, guess_bonds=False, vdwradii=None, - anchor_name=None, is_anchor=False, **kwargs): - """ - Parameters - ---------- - - anchor_name: str, None - None causes generic hash to get used. - is_anchor: bool - Universe are anchors by default. - """ + def __init__(self, topology, *coordinates, topology_file=None, + all_coordinates=False, + format=None, transformations=None, guess_bonds=False, + vdwradii=None, anchor_name=None, is_anchor=False, + in_memory=False, in_memory_step=1, **kwargs): + + print('INITING, ', topology) + _type = type(topology) + if _type is not Topology: + raise ValueError('Only MDAnalysis.Topology objects should be ' + 'passed to Universe(); {} passed ' + 'instead.'.format(_type)) self._instant_selectors = {} # for storing segments. Deprecated? self._trajectory = None # managed attribute holding Reader @@ -385,11 +407,19 @@ def __init__(self, topology, *coordinates, topology_file=None, self._topology = topology self.filename = topology_file - if topology: self._generate_from_topology() # make real atoms, res, segments - self.load_new(coordinates, **kwargs) + if all_coordinates or not coordinates and topology_file: + try: + get_reader_for(topology_file, format) + except ValueError: + warnings.warn('No coordinate reader found for {}. Skipping ' + 'this file.'.format(topology_file)) + else: + coordinates = (topology_file,) + coordinates + + self.load_new(coordinates, format=format, in_memory=in_memory, in_memory_step=in_memory_step) if transformations: if callable(transformations): From fb3b56cfc840b0800e1aec59d41216fd1b60e209 Mon Sep 17 00:00:00 2001 From: Lily Wang Date: Thu, 27 Jun 2019 18:25:50 +1000 Subject: [PATCH 06/19] passing tests except empty --- package/MDAnalysis/core/AtomGroup.py | 11 ++++++----- package/MDAnalysis/core/universe.py | 21 ++++++++++++--------- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/package/MDAnalysis/core/AtomGroup.py b/package/MDAnalysis/core/AtomGroup.py index 03f4447d0fe..3967fe7a9ec 100644 --- a/package/MDAnalysis/core/AtomGroup.py +++ b/package/MDAnalysis/core/AtomGroup.py @@ -38,11 +38,12 @@ def __init__(self, *args, **kwargs): return new_class -def Universe(*args, **kwargs): - warnings.warn("MDAnalysis.core.AtomGroup.Universe has been removed." - "Please use MDAnalysis.Universe." - "This stub will be removed in 1.0") - return universe.Universe(*args, **kwargs) + +Universe = deprecate_class( + universe.Universe, + "MDAnalysis.core.AtomGroup.Universe has been removed." + "Please use MDAnalysis.Universe." + "This stub will be removed in 1.0") _group_message = ("MDAnalysis.core.AtomGroup.{0} has been removed." "Please use MDAnalysis.groups.{0}" diff --git a/package/MDAnalysis/core/universe.py b/package/MDAnalysis/core/universe.py index 83c1b9181d9..29cfdc05dfc 100644 --- a/package/MDAnalysis/core/universe.py +++ b/package/MDAnalysis/core/universe.py @@ -141,13 +141,17 @@ def __new__(klass, topology, *coordinates, **kwargs): 'Universe.from_streams. Only MDAnalysis.Topology ' 'objects should be passed to Universe(); {} ' 'passed instead'.format(_type), DeprecationWarning) - if _type is str: - return cls.from_files(topology, *coordinates, **kwargs) + if isinstance(topology, NamedStream) or isstream(topology): + return klass.from_streams(topology, *coordinates, **kwargs) else: - return cls.from_streams(topology, *coordinates, **kwargs) + return klass.from_files(topology, *coordinates, **kwargs) else: - return cls(topology, *coordinates, **kwargs) + return super(Wrapper, klass).__new__(klass) + + def __init__(self, topology, *coordinates, **kwargs): + if not self._topology: # ugly ugly ugly but passes tests + super(Wrapper, self).__init__(topology, *coordinates, **kwargs) return Wrapper @@ -388,8 +392,6 @@ def __init__(self, topology, *coordinates, topology_file=None, format=None, transformations=None, guess_bonds=False, vdwradii=None, anchor_name=None, is_anchor=False, in_memory=False, in_memory_step=1, **kwargs): - - print('INITING, ', topology) _type = type(topology) if _type is not Topology: raise ValueError('Only MDAnalysis.Topology objects should be ' @@ -407,10 +409,10 @@ def __init__(self, topology, *coordinates, topology_file=None, self._topology = topology self.filename = topology_file - if topology: + if topology is not None: self._generate_from_topology() # make real atoms, res, segments - if all_coordinates or not coordinates and topology_file: + if all_coordinates or not len(coordinates) and topology_file is not None: try: get_reader_for(topology_file, format) except ValueError: @@ -419,7 +421,8 @@ def __init__(self, topology, *coordinates, topology_file=None, else: coordinates = (topology_file,) + coordinates - self.load_new(coordinates, format=format, in_memory=in_memory, in_memory_step=in_memory_step) + self.load_new(coordinates, format=format, in_memory=in_memory, + in_memory_step=in_memory_step, **kwargs) if transformations: if callable(transformations): From f8f1f48d01d193e3c7b5b2e02319d626bc862460 Mon Sep 17 00:00:00 2001 From: Lily Wang Date: Fri, 9 Aug 2019 08:28:55 +1000 Subject: [PATCH 07/19] removed vscode --- .vscode/settings.json | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index 20545edda3f..00000000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "python.pythonPath": "/Users/lily/anaconda3/envs/mda/bin/python" -} \ No newline at end of file From 205cf98162fdfc846ea0427701e2f0c31fb892ab Mon Sep 17 00:00:00 2001 From: Lily Wang Date: Mon, 19 Aug 2019 12:26:27 +1000 Subject: [PATCH 08/19] updated empty and warning tests --- .../MDAnalysisTests/core/test_universe.py | 12 ++++---- .../MDAnalysisTests/topology/test_top.py | 29 ++++++++++--------- 2 files changed, 21 insertions(+), 20 deletions(-) diff --git a/testsuite/MDAnalysisTests/core/test_universe.py b/testsuite/MDAnalysisTests/core/test_universe.py index 9d980f89baf..4431fc6b923 100644 --- a/testsuite/MDAnalysisTests/core/test_universe.py +++ b/testsuite/MDAnalysisTests/core/test_universe.py @@ -98,12 +98,12 @@ def test_load_trajectory_stringio(self): u = mda.Universe(StringIO(CHOL_GRO), StringIO(CHOL_GRO), format='GRO', topology_format='GRO') assert_equal(len(u.atoms), 8, "Loading universe from StringIO failed somehow") - def test_make_universe_no_args(self): - # universe creation without args should work - u = mda.Universe() + # def test_make_universe_no_args(self): + # # universe creation without args should work + # u = mda.Universe() - assert isinstance(u, mda.Universe) - assert u.atoms is None + # assert isinstance(u, mda.Universe) + # assert u.atoms is None def test_make_universe_stringio_no_format(self): # Loading from StringIO without format arg should raise TypeError @@ -187,7 +187,7 @@ def test_Universe_invalidpermissionfile_IE_msg(self): temp_dir.dissolve() def test_load_new_VE(self): - u = mda.Universe() + u = mda.Universe.empty() with pytest.raises(TypeError): u.load_new('thisfile', format = 'soup') diff --git a/testsuite/MDAnalysisTests/topology/test_top.py b/testsuite/MDAnalysisTests/topology/test_top.py index 96f20785446..8b015c00b3c 100644 --- a/testsuite/MDAnalysisTests/topology/test_top.py +++ b/testsuite/MDAnalysisTests/topology/test_top.py @@ -38,7 +38,9 @@ PRM_UreyBradley ) - +ATOMIC_NUMBER_MSG = ("ATOMIC_NUMBER record not found, guessing atom elements " + "based on their atom types") +COORDINATE_READER_MSG = ("No coordinate reader found") class TOPBase(ParserBase): parser = mda.topology.TOPParser.TOPParser expected_attrs = [ @@ -180,10 +182,9 @@ def test_warning(self, filename): with pytest.warns(UserWarning) as record: u = mda.Universe(filename) - assert len(record) == 1 - wmsg = ("ATOMIC_NUMBER record not found, guessing atom elements " - "based on their atom types") - assert str(record[0].message.args[0]) == wmsg + assert len(record) == 2 + assert str(record[0].message.args[0]) == ATOMIC_NUMBER_MSG + assert COORDINATE_READER_MSG in str(record[1].message.args[0]) class TestPRM12Parser(TOPBase): @@ -293,10 +294,9 @@ def test_warning(self, filename): with pytest.warns(UserWarning) as record: u = mda.Universe(filename) - assert len(record) == 1 - wmsg = ("ATOMIC_NUMBER record not found, guessing atom elements " - "based on their atom types") - assert str(record[0].message.args[0]) == wmsg + assert len(record) == 2 + assert str(record[0].message.args[0]) == ATOMIC_NUMBER_MSG + assert COORDINATE_READER_MSG in str(record[1].message.args[0]) class TestPRM2(TOPBase): @@ -341,10 +341,10 @@ def test_warning(self, filename): with pytest.warns(UserWarning) as record: u = mda.Universe(filename) - assert len(record) == 1 - wmsg = ("ATOMIC_NUMBER record not found, guessing atom elements " - "based on their atom types") - assert str(record[0].message.args[0]) == wmsg + assert len(record) == 2 + assert str(record[0].message.args[0]) == ATOMIC_NUMBER_MSG + assert COORDINATE_READER_MSG in str(record[1].message.args[0]) + class TestPRMNCRST(TOPBase): @@ -413,13 +413,14 @@ def test_warning(self, filename): with pytest.warns(UserWarning) as record: u = mda.Universe(filename) - assert len(record) == 2 + assert len(record) == 3 wmsg1 = ("Unknown ATOMIC_NUMBER value found, guessing atom element " "from type: CT assigned to C") wmsg2 = ("Unknown ATOMIC_NUMBER value found, guessing atom element " "from type: O assigned to O") assert str(record[0].message.args[0]) == wmsg1 assert str(record[1].message.args[0]) == wmsg2 + assert COORDINATE_READER_MSG in str(record[2].message.args[0]) class TestErrors(object): From 8d8edd7145f937ab137e6d60dfe951b672a623f8 Mon Sep 17 00:00:00 2001 From: Lily Wang Date: Mon, 19 Aug 2019 12:26:51 +1000 Subject: [PATCH 09/19] removed old changes --- package/MDAnalysis/analysis/diffusionmap.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/package/MDAnalysis/analysis/diffusionmap.py b/package/MDAnalysis/analysis/diffusionmap.py index 8278123d084..407e0244c58 100644 --- a/package/MDAnalysis/analysis/diffusionmap.py +++ b/package/MDAnalysis/analysis/diffusionmap.py @@ -306,8 +306,7 @@ def __init__(self, u, epsilon=1, **kwargs): Parameters to be passed for the initialization of a :class:`DistanceMatrix`. """ - # if isinstance(u, Universe): # doesn't work with reformat_universe wrapper - if hasattr(u, 'trajectory') and hasattr(u, 'select_atoms'): + if isinstance(u, Universe): self._dist_matrix = DistanceMatrix(u, **kwargs) elif isinstance(u, DistanceMatrix): self._dist_matrix = u From 5ccdc258cb1968d59b8a561a99c57ab8554bfa73 Mon Sep 17 00:00:00 2001 From: Lily Wang Date: Mon, 19 Aug 2019 12:30:41 +1000 Subject: [PATCH 10/19] removed classmethods --- package/MDAnalysis/core/universe.py | 306 ++++++++++------------------ 1 file changed, 111 insertions(+), 195 deletions(-) diff --git a/package/MDAnalysis/core/universe.py b/package/MDAnalysis/core/universe.py index 29cfdc05dfc..ea4bd602050 100644 --- a/package/MDAnalysis/core/universe.py +++ b/package/MDAnalysis/core/universe.py @@ -131,31 +131,72 @@ logger = logging.getLogger("MDAnalysis.core.universe") -def reformat_universe(cls): - class Wrapper(cls): # Must return class for deprecate_class subclassing - def __new__(klass, topology, *coordinates, **kwargs): - _type = type(topology) - if _type is not Topology: - warnings.warn('Universes should be created with an explicit' - 'classmethod, eg. Universe.from_files or ' - 'Universe.from_streams. Only MDAnalysis.Topology ' - 'objects should be passed to Universe(); {} ' - 'passed instead'.format(_type), DeprecationWarning) - if isinstance(topology, NamedStream) or isstream(topology): - return klass.from_streams(topology, *coordinates, **kwargs) - else: - return klass.from_files(topology, *coordinates, **kwargs) - else: - return super(Wrapper, klass).__new__(klass) - - def __init__(self, topology, *coordinates, **kwargs): - if not self._topology: # ugly ugly ugly but passes tests - super(Wrapper, self).__init__(topology, *coordinates, **kwargs) - - return Wrapper -@reformat_universe +def _check_file_like(topology): + if isstream(topology): + if hasattr(topology, 'name'): + _name = topology.name + else: + _name = None + return NamedStream(topology, _name) + + return topology + + # raise ValueError('topology parameter must be a stream, file, ' + # 'numpy array, or MDAnalysis.Topology object. ' + # 'Given: {}'.format(type(topology))) + + +def _topology_from_file_like(topology_file, topology_format=None, + **kwargs): + parser = get_parser_for(topology_file, format=topology_format) + + try: + with parser(topology_file) as p: + topology = p.parse(**kwargs) + except (IOError, OSError) as err: + # There are 2 kinds of errors that might be raised here: + # one because the file isn't present + # or the permissions are bad, second when the parser fails + if (err.errno is not None and + errno.errorcode[err.errno] in ['ENOENT', 'EACCES']): + # Runs if the error is propagated due to no permission / file not found + six.reraise(*sys.exc_info()) + else: + # Runs when the parser fails + raise IOError("Failed to load from the topology file {0}" + " with parser {1}.\n" + "Error: {2}".format(topology_file, parser, err)) + except (ValueError, NotImplementedError) as err: + raise ValueError( + "Failed to construct topology from file {0}" + " with parser {1}.\n" + "Error: {2}".format(topology_file, parser, err)) + return topology + +def _resolve_formats(*coordinates, format=None, topology_format=None): + if not coordinates: + if format is None: + format = topology_format + elif topology_format is None: + topology_format = format + return format, topology_format + +def _resolve_coordinates(filename, *coordinates, format=None, + all_coordinates=False): + if all_coordinates or not coordinates and filename is not None: + try: + get_reader_for(filename, format=format) + except ValueError: + warnings.warn('No coordinate reader found for {}. Skipping ' + 'this file.'.format(filename)) + else: + coordinates = (filename,) + coordinates + return coordinates + + + class Universe(object): """The MDAnalysis Universe contains all the information describing the system. @@ -195,8 +236,10 @@ class Universe(object): Parameters ---------- - topology : `~MDAnalysis.core.topology.Topology` - The topology defines the list of atoms. + topology : `~MDAnalysis.core.topology.Topology`, str, stream, `np.ndarray`, None + The topology defines the list of atoms. This can be provided + as filenames, streams of filenames, numpy arrays, or None for + an empty universe. coordinates : str, stream, list of str, list of stream (optional) Coordinates can be provided as streams of filenames either of a single frame (eg a PDB, CRD, or GRO file); a list of single @@ -272,156 +315,56 @@ class Universe(object): """ _topology = None - @classmethod - def from_streams(cls, topology, *coordinates, **kwargs): - """ - Construct a Universe with the topology from a stream. - - Parameters - ---------- - topology : stream - A stream of a CHARMM/XPLOR PSF topology file, PDB file or Gromacs GRO file; used to - define the list of atoms. If the file includes bond information, - partial charges, atom masses, ... then these data will be available to - MDAnalysis. A "structure" file (PSF, PDB or GRO, in the sense of a - topology) is always required. - coordinates : str, stream, list of str, list of stream (optional) - Coordinates can be provided either as a single frame (eg a PDB, CRD, - or GRO file); a list of single frames; or a trajectory file (in - CHARMM/NAMD/LAMMPS DCD, Gromacs XTC/TRR, or generic XYZ format). - The coordinates have to be ordered in the same way as the list of - atoms in the topology. See :ref:`Supported coordinate formats` - for what can be read as coordinates. - kwargs: - Optional arguments for file formats, transformations, vdwradii and - anchoring are passed to :func:`~MDAnalysis.Universe.from_files`. - """ - if isinstance(topology, NamedStream): - filename = topology - elif isstream(topology): - if hasattr(topology, 'name'): - _name = topology.name - else: - _name = None - filename = NamedStream(topology, _name) - else: - raise ValueError('topology parameter must be a stream. ' - 'Given: {}'.format(type(topology))) - return cls.from_files(filename, *coordinates, **kwargs) - - @classmethod - def from_files(cls, topology_file, *coordinates, topology_format=None, - format=None, **kwargs): - """ - Parameters - ---------- - topology : str - A CHARMM/XPLOR PSF topology file, PDB file or Gromacs GRO file; used to - define the list of atoms. If the file includes bond information, - partial charges, atom masses, ... then these data will be available to - MDAnalysis. A "structure" file (PSF, PDB or GRO, in the sense of a - topology) is always required. - coordinates : str, stream, list of str, list of stream (optional) - Coordinates can be provided as streams of filenames either of - a single frame (eg a PDB, CRD, or GRO file); a list of single - frames; or a trajectory file (in CHARMM/NAMD/LAMMPS DCD, Gromacs - XTC/TRR, or generic XYZ format). The coordinates have to be - ordered in the same way as the list of atoms in the topology. - See :ref:`Supported coordinate formats` for what can be read - as coordinates. - topology_format: str, ``None``, default ``None`` - Provide the file format of the topology file; ``None`` guesses it from - the file extension. Can also pass a subclass of - :class:`MDAnalysis.topology.base.TopologyReaderBase` to define a custom - reader to be used on the topology file. - format: str, ``None``, default ``None`` - Provide the file format of the coordinate or trajectory file; ``None`` - guesses it from the file extension. Note that this keyword has no - effect if a list of file names is supplied because the "chained" reader - has to guess the file format for each individual list member. - Can also pass a subclass of :class:`MDAnalysis.coordinates.base.ProtoReader` - to define a custom reader to be used on the trajectory file. - all_coordinates : bool, default ``False`` - If set to ``True`` specifies that if more than one filename is passed - they are all to be used, if possible, as coordinate files (employing a - :class:`MDAnalysis.coordinates.chain.ChainReader`). The - default behavior is to take the first file as a topology and the - remaining as coordinates. The first argument will always always be used - to infer a topology regardless of *all_coordinates*. - kwargs: - Optional arguments for transformations, vdwradii and - anchoring are passed to :func:`~MDAnalysis.Universe`. - """ - - if not coordinates: - if format is None: - format = topology_format - elif topology_format is None: - topology_format = format - - parser = get_parser_for(topology_file, format=topology_format) - try: - with parser(topology_file) as p: - topology = p.parse(**kwargs) - except (IOError, OSError) as err: - # There are 2 kinds of errors that might be raised here: - # one because the file isn't present - # or the permissions are bad, second when the parser fails - if (err.errno is not None and - errno.errorcode[err.errno] in ['ENOENT', 'EACCES']): - # Runs if the error is propagated due to no permission / file not found - six.reraise(*sys.exc_info()) - else: - # Runs when the parser fails - raise IOError("Failed to load from the topology file {0}" - " with parser {1}.\n" - "Error: {2}".format(topology_file, parser, err)) - except (ValueError, NotImplementedError) as err: - raise ValueError( - "Failed to construct topology from file {0}" - " with parser {1}.\n" - "Error: {2}".format(topology_file, parser, err)) - - obj = cls(topology, *coordinates, topology_file=topology_file, format=format, - topology_format=topology_format, **kwargs) - return obj - - def __init__(self, topology, *coordinates, topology_file=None, - all_coordinates=False, - format=None, transformations=None, guess_bonds=False, - vdwradii=None, anchor_name=None, is_anchor=False, - in_memory=False, in_memory_step=1, **kwargs): - _type = type(topology) - if _type is not Topology: - raise ValueError('Only MDAnalysis.Topology objects should be ' - 'passed to Universe(); {} passed ' - 'instead.'.format(_type)) + def __init__(self, topology, *coordinates, all_coordinates=False, + format=None, topology_format=None, transformations=None, + guess_bonds=False, vdwradii=None, anchor_name=None, + is_anchor=True, in_memory=False, in_memory_step=1, + **kwargs): self._instant_selectors = {} # for storing segments. Deprecated? self._trajectory = None # managed attribute holding Reader self._cache = {} self._anchor_name = anchor_name - self._is_anchor = is_anchor + self.is_anchor = is_anchor self.atoms = None self.residues = None self.segments = None + self.filename = None + + self._kwargs = { + 'transformations': transformations, + 'guess_bonds': guess_bonds, + 'vdwradii': vdwradii, + 'anchor_name': anchor_name, + 'is_anchor': is_anchor, + 'in_memory': in_memory, + 'in_memory_step': in_memory_step, + 'format': format, + 'topology_format': topology_format, + 'all_coordinates': all_coordinates + } + self._kwargs.update(kwargs) + + format, topology_format = _resolve_formats(*coordinates, format=format, + topology_format=topology_format) + + if not isinstance(topology, Topology) and not topology is None: + self.filename = _check_file_like(topology) + topology = _topology_from_file_like(self.filename, + topology_format=topology_format, + **kwargs) + self._topology = topology - self.filename = topology_file if topology is not None: self._generate_from_topology() # make real atoms, res, segments - if all_coordinates or not len(coordinates) and topology_file is not None: - try: - get_reader_for(topology_file, format) - except ValueError: - warnings.warn('No coordinate reader found for {}. Skipping ' - 'this file.'.format(topology_file)) - else: - coordinates = (topology_file,) + coordinates + coordinates = _resolve_coordinates(self.filename, *coordinates, + format=format, + all_coordinates=all_coordinates) - self.load_new(coordinates, format=format, in_memory=in_memory, + self.load_new(coordinates, format=format, in_memory=in_memory, in_memory_step=in_memory_step, **kwargs) if transformations: @@ -432,15 +375,6 @@ def __init__(self, topology, *coordinates, topology_file=None, if guess_bonds: self.atoms.guess_bonds(vdwradii=vdwradii) - self._kwargs = { - 'transformations': transformations, - 'guess_bonds': guess_bonds, - 'vdwradii': vdwradii, - 'anchor_name': anchor_name, - 'is_anchor': is_anchor, - } - self._kwargs.update(kwargs) - def copy(self): """Return an independent copy of this Universe""" @@ -500,7 +434,7 @@ def _generate_from_topology(self): self._instant_selectors[name] = segment @classmethod - def empty(cls, n_atoms, n_residues=None, n_segments=None, + def empty(cls, n_atoms=0, n_residues=1, n_segments=1, atom_resindex=None, residue_segindex=None, trajectory=False, velocities=False, forces=False): """Create a blank Universe @@ -516,9 +450,9 @@ def empty(cls, n_atoms, n_residues=None, n_segments=None, ---------- n_atoms : int number of Atoms in the Universe - n_residues : int, optional + n_residues : int, default 1 number of Residues in the Universe, defaults to 1 - n_segments : int, optional + n_segments : int, default 1 number of Segments in the Universe, defaults to 1 atom_resindex : array like, optional mapping of atoms to residues, e.g. with 6 atoms, @@ -556,35 +490,17 @@ def empty(cls, n_atoms, n_residues=None, n_segments=None, Universes can now be created with 0 atoms """ if not n_atoms: - n_residues = 0 - n_segments = 0 + return cls(None) - if n_residues is None and n_atoms: - n_residues = 1 - elif atom_resindex is None: + if atom_resindex is None: warnings.warn( - 'Multiple residues specified but no atom_resindex given. ' + 'Residues specified but no atom_resindex given. ' 'All atoms will be placed in first Residue.', UserWarning) - if n_segments is None and n_atoms: - n_segments = 1 - elif residue_segindex is None: + if residue_segindex is None: warnings.warn( - 'Multiple segments specified but no segment_resindex given. ' - 'All residues will be placed in first Segment', - UserWarning) - - top = Topology(n_atoms, n_residues, n_segments, - atom_resindex=atom_resindex, - residue_segindex=residue_segindex, - ) - - u = cls(top) - - if trajectory: - coords = np.zeros((1, n_atoms, 3), dtype=np.float32) + 'Segments specified but no segment_resindex given. ' dims = np.zeros(6, dtype=np.float32) - vels = np.zeros_like(coords) if velocities else None forces = np.zeros_like(coords) if forces else None # grab and attach a MemoryReader From cf30e8b5dc35fe9ecc90c18ae6c8637d66129b5b Mon Sep 17 00:00:00 2001 From: Lily Wang Date: Wed, 21 Aug 2019 12:53:30 +1000 Subject: [PATCH 11/19] removed commented stuff --- package/MDAnalysis/core/universe.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/package/MDAnalysis/core/universe.py b/package/MDAnalysis/core/universe.py index ea4bd602050..fb9fecd6fe8 100644 --- a/package/MDAnalysis/core/universe.py +++ b/package/MDAnalysis/core/universe.py @@ -140,13 +140,7 @@ def _check_file_like(topology): else: _name = None return NamedStream(topology, _name) - return topology - - # raise ValueError('topology parameter must be a stream, file, ' - # 'numpy array, or MDAnalysis.Topology object. ' - # 'Given: {}'.format(type(topology))) - def _topology_from_file_like(topology_file, topology_format=None, **kwargs): From f53d6f9ab125ddb3df202c351ce14079763df675 Mon Sep 17 00:00:00 2001 From: Lily Wang Date: Thu, 22 Aug 2019 09:41:06 +1000 Subject: [PATCH 12/19] updated docstrings --- package/MDAnalysis/core/universe.py | 22 +++++++++++-------- .../MDAnalysisTests/core/test_universe.py | 7 ------ 2 files changed, 13 insertions(+), 16 deletions(-) diff --git a/package/MDAnalysis/core/universe.py b/package/MDAnalysis/core/universe.py index fb9fecd6fe8..32b6b6b8015 100644 --- a/package/MDAnalysis/core/universe.py +++ b/package/MDAnalysis/core/universe.py @@ -230,18 +230,21 @@ class Universe(object): Parameters ---------- - topology : `~MDAnalysis.core.topology.Topology`, str, stream, `np.ndarray`, None - The topology defines the list of atoms. This can be provided - as filenames, streams of filenames, numpy arrays, or None for - an empty universe. + topology : str, stream, `~MDAnalysis.core.topology.Topology`, `np.ndarray`, None + A CHARMM/XPLOR PSF topology file, PDB file or Gromacs GRO file; used to + define the list of atoms. If the file includes bond information, + partial charges, atom masses, ... then these data will be available to + MDAnalysis. Alternatively, an existing + :class:`MDAnalysis.core.topology.Topology` instance may be given, + numpy coordinates, or None for an empty universe. coordinates : str, stream, list of str, list of stream (optional) - Coordinates can be provided as streams of filenames either of + Coordinates can be provided as files of a single frame (eg a PDB, CRD, or GRO file); a list of single frames; or a trajectory file (in CHARMM/NAMD/LAMMPS DCD, Gromacs - XTC/TRR, or generic XYZ format). The coordinates have to be + XTC/TRR, or generic XYZ format). The coordinates must be ordered in the same way as the list of atoms in the topology. See :ref:`Supported coordinate formats` for what can be read - as coordinates. + as coordinates. Alternatively, streams can be given. topology_format: str, ``None``, default ``None`` Provide the file format of the topology file; ``None`` guesses it from the file extension. Can also pass a subclass of @@ -306,8 +309,9 @@ class Universe(object): bonds, angles, dihedrals master ConnectivityGroups for each connectivity type + .. versionchanged:: 0.20.0 + Universe() now raises an error. Use Universe(None) or :func:`Universe.empty()` instead. """ - _topology = None def __init__(self, topology, *coordinates, all_coordinates=False, @@ -442,7 +446,7 @@ def empty(cls, n_atoms=0, n_residues=1, n_segments=1, Parameters ---------- - n_atoms : int + n_atoms : int, default 0 number of Atoms in the Universe n_residues : int, default 1 number of Residues in the Universe, defaults to 1 diff --git a/testsuite/MDAnalysisTests/core/test_universe.py b/testsuite/MDAnalysisTests/core/test_universe.py index 4431fc6b923..5ff99dc7291 100644 --- a/testsuite/MDAnalysisTests/core/test_universe.py +++ b/testsuite/MDAnalysisTests/core/test_universe.py @@ -98,13 +98,6 @@ def test_load_trajectory_stringio(self): u = mda.Universe(StringIO(CHOL_GRO), StringIO(CHOL_GRO), format='GRO', topology_format='GRO') assert_equal(len(u.atoms), 8, "Loading universe from StringIO failed somehow") - # def test_make_universe_no_args(self): - # # universe creation without args should work - # u = mda.Universe() - - # assert isinstance(u, mda.Universe) - # assert u.atoms is None - def test_make_universe_stringio_no_format(self): # Loading from StringIO without format arg should raise TypeError with pytest.raises(TypeError): From 1f9eb597a711ee8a004ad907d28e2746c8ed3cd5 Mon Sep 17 00:00:00 2001 From: Lily Wang Date: Sun, 22 Sep 2019 16:25:29 +1000 Subject: [PATCH 13/19] moved gen_from_top out --- package/MDAnalysis/core/universe.py | 187 ++++++++++++++-------------- 1 file changed, 92 insertions(+), 95 deletions(-) diff --git a/package/MDAnalysis/core/universe.py b/package/MDAnalysis/core/universe.py index 32b6b6b8015..3aef42d8cf4 100644 --- a/package/MDAnalysis/core/universe.py +++ b/package/MDAnalysis/core/universe.py @@ -189,7 +189,56 @@ def _resolve_coordinates(filename, *coordinates, format=None, coordinates = (filename,) + coordinates return coordinates - +def _generate_from_topology(universe): + # generate Universe version of each class + # AG, RG, SG, A, R, S + universe._class_bases, universe._classes = groups.make_classes() + + # Put Group level stuff from topology into class + for attr in universe._topology.attrs: + universe._process_attr(attr) + + # Generate atoms, residues and segments. + # These are the first such groups generated for this universe, so + # there are no cached merged classes yet. Otherwise those could be + # used directly to get a (very) small speedup. (Only really pays off + # the readability loss if instantiating millions of AtomGroups at + # once.) + universe.atoms = AtomGroup(np.arange(universe._topology.n_atoms), universe) + + universe.residues = ResidueGroup( + np.arange(universe._topology.n_residues), universe) + + universe.segments = SegmentGroup( + np.arange(universe._topology.n_segments), universe) + + # Update Universe namespace with segids + # Many segments can have same segid, so group together first + # + # DEPRECATED in 0.16.2 + # REMOVE in 1.0 + # See https://github.com/MDAnalysis/mdanalysis/issues/1377 + try: + # returns dict of segid:segment + segids = universe.segments.groupby('segids') + except AttributeError: + # no segids, don't do this step + pass + else: + for segid, segment in segids.items(): + if not segid: # ignore blank segids + continue + + # cannot start attribute with number + if segid[0].isdigit(): + # prefix 's' if starts with number + name = 's' + segid + else: + name = segid + # if len 1 SegmentGroup, convert to Segment + if len(segment) == 1: + segment = segment[0] + universe._instant_selectors[name] = segment class Universe(object): """The MDAnalysis Universe contains all the information describing the system. @@ -230,14 +279,14 @@ class Universe(object): Parameters ---------- - topology : str, stream, `~MDAnalysis.core.topology.Topology`, `np.ndarray`, None + topology: str, stream, `~MDAnalysis.core.topology.Topology`, `np.ndarray`, None A CHARMM/XPLOR PSF topology file, PDB file or Gromacs GRO file; used to define the list of atoms. If the file includes bond information, partial charges, atom masses, ... then these data will be available to MDAnalysis. Alternatively, an existing :class:`MDAnalysis.core.topology.Topology` instance may be given, numpy coordinates, or None for an empty universe. - coordinates : str, stream, list of str, list of stream (optional) + coordinates: str, stream, list of str, list of stream (optional) Coordinates can be provided as files of a single frame (eg a PDB, CRD, or GRO file); a list of single frames; or a trajectory file (in CHARMM/NAMD/LAMMPS DCD, Gromacs @@ -257,26 +306,26 @@ class Universe(object): has to guess the file format for each individual list member. Can also pass a subclass of :class:`MDAnalysis.coordinates.base.ProtoReader` to define a custom reader to be used on the trajectory file. - all_coordinates : bool, default ``False`` + all_coordinates: bool, default ``False`` If set to ``True`` specifies that if more than one filename is passed they are all to be used, if possible, as coordinate files (employing a :class:`MDAnalysis.coordinates.chain.ChainReader`). The default behavior is to take the first file as a topology and the remaining as coordinates. The first argument will always always be used to infer a topology regardless of *all_coordinates*. - guess_bonds : bool, default ``False`` + guess_bonds: bool, default ``False`` Once Universe has been loaded, attempt to guess the connectivity between atoms. This will populate the .bonds, .angles, and .dihedrals attributes of the Universe. - vdwradii : dict, ``None``, default ``None`` + vdwradii: dict, ``None``, default ``None`` For use with *guess_bonds*. Supply a dict giving a vdwradii for each atom type which are used in guessing bonds. - is_anchor : bool, default ``True`` + is_anchor: bool, default ``True`` When unpickling instances of :class:`MDAnalysis.core.groups.AtomGroup` existing Universes are searched for one where to anchor those atoms. Set to ``False`` to prevent this Universe from being considered. - anchor_name : str, ``None``, default ``None`` + anchor_name: str, ``None``, default ``None`` Setting to other than ``None`` will cause :class:`MDAnalysis.core.groups.AtomGroup` instances pickled from the Universe to only unpickle if a compatible Universe with matching @@ -291,7 +340,7 @@ class Universe(object): representations, which allow for manipulation of coordinates. in_memory_step: int, default 1 Only read every nth frame into in-memory representation. - continuous : bool, default ``False`` + continuous: bool, default ``False`` The `continuous` option is used by the :mod:`ChainReader`, which contains the functionality to treat independent trajectory files as a single virtual @@ -319,7 +368,6 @@ def __init__(self, topology, *coordinates, all_coordinates=False, guess_bonds=False, vdwradii=None, anchor_name=None, is_anchor=True, in_memory=False, in_memory_step=1, **kwargs): - self._instant_selectors = {} # for storing segments. Deprecated? self._trajectory = None # managed attribute holding Reader self._cache = {} @@ -356,7 +404,7 @@ def __init__(self, topology, *coordinates, all_coordinates=False, self._topology = topology if topology is not None: - self._generate_from_topology() # make real atoms, res, segments + _generate_from_topology(self) # make real atoms, res, segments coordinates = _resolve_coordinates(self.filename, *coordinates, format=format, @@ -380,56 +428,6 @@ def copy(self): new.trajectory = self.trajectory.copy() return new - def _generate_from_topology(self): - # generate Universe version of each class - # AG, RG, SG, A, R, S - self._class_bases, self._classes = groups.make_classes() - - # Put Group level stuff from topology into class - for attr in self._topology.attrs: - self._process_attr(attr) - - # Generate atoms, residues and segments. - # These are the first such groups generated for this universe, so - # there are no cached merged classes yet. Otherwise those could be - # used directly to get a (very) small speedup. (Only really pays off - # the readability loss if instantiating millions of AtomGroups at - # once.) - self.atoms = AtomGroup(np.arange(self._topology.n_atoms), self) - - self.residues = ResidueGroup( - np.arange(self._topology.n_residues), self) - - self.segments = SegmentGroup( - np.arange(self._topology.n_segments), self) - - # Update Universe namespace with segids - # Many segments can have same segid, so group together first - # - # DEPRECATED in 0.16.2 - # REMOVE in 1.0 - # See https://github.com/MDAnalysis/mdanalysis/issues/1377 - try: - # returns dict of segid:segment - segids = self.segments.groupby('segids') - except AttributeError: - # no segids, don't do this step - pass - else: - for segid, segment in segids.items(): - if not segid: # ignore blank segids - continue - - # cannot start attribute with number - if segid[0].isdigit(): - # prefix 's' if starts with number - name = 's' + segid - else: - name = segid - # if len 1 SegmentGroup, convert to Segment - if len(segment) == 1: - segment = segment[0] - self._instant_selectors[name] = segment @classmethod def empty(cls, n_atoms=0, n_residues=1, n_segments=1, @@ -446,24 +444,24 @@ def empty(cls, n_atoms=0, n_residues=1, n_segments=1, Parameters ---------- - n_atoms : int, default 0 + n_atoms: int, default 0 number of Atoms in the Universe - n_residues : int, default 1 + n_residues: int, default 1 number of Residues in the Universe, defaults to 1 - n_segments : int, default 1 + n_segments: int, default 1 number of Segments in the Universe, defaults to 1 - atom_resindex : array like, optional + atom_resindex: array like, optional mapping of atoms to residues, e.g. with 6 atoms, `atom_resindex=[0, 0, 1, 1, 2, 2]` would put 2 atoms into each of 3 residues. - residue_segindex : array like, optional + residue_segindex: array like, optional mapping of residues to segments - trajectory : bool, optional + trajectory: bool, optional if True, attaches a :class:`MDAnalysis.coordinates.memory.MemoryReader` allowing coordinates to be set and written. Default is False - velocities : bool, optional + velocities: bool, optional include velocities in the :class:`MDAnalysis.coordinates.memory.MemoryReader` - forces : bool, optional + forces: bool, optional include forces in the :class:`MDAnalysis.coordinates.memory.MemoryReader` Returns @@ -548,10 +546,10 @@ def load_new(self, filename, format=None, in_memory=False, Parameters ---------- - filename : str or list + filename: str or list the coordinate file (single frame or trajectory) *or* a list of filenames, which are read one after another. - format : str or list or object (optional) + format: str or list or object (optional) provide the file format of the coordinate or trajectory file; ``None`` guesses it from the file extension. Note that this keyword has no effect if a list of file names is supplied because @@ -559,19 +557,19 @@ def load_new(self, filename, format=None, in_memory=False, individual list member [``None``]. Can also pass a subclass of :class:`MDAnalysis.coordinates.base.ProtoReader` to define a custom reader to be used on the trajectory file. - in_memory : bool (optional) + in_memory: bool (optional) Directly load trajectory into memory with the :class:`~MDAnalysis.coordinates.memory.MemoryReader` .. versionadded:: 0.16.0 - **kwargs : dict + **kwargs: dict Other kwargs are passed to the trajectory reader (only for advanced use) Returns ------- - universe : Universe + universe: Universe Raises ------ @@ -641,9 +639,9 @@ def transfer_to_memory(self, start=None, stop=None, step=None, start reading from the nth frame. stop: int, optional read upto and excluding the nth frame. - step : int, optional + step: int, optional Read in every nth frame. [1] - verbose : bool, optional + verbose: bool, optional Will print the progress of loading trajectory to memory, if set to True. Default value is False. @@ -730,15 +728,6 @@ def impropers(self): @property def anchor_name(self): - return self._gen_anchor_hash() - - @anchor_name.setter - def anchor_name(self, name): - self.remove_anchor() # clear any old anchor - self._anchor_name = str(name) if not name is None else name - self.make_anchor() # add anchor again - - def _gen_anchor_hash(self): # hash used for anchoring. # Try and use anchor_name, else use (and store) uuid if self._anchor_name is not None: @@ -751,10 +740,18 @@ def _gen_anchor_hash(self): self._anchor_uuid = uuid.uuid4() return self._anchor_uuid + @anchor_name.setter + def anchor_name(self, name): + self.remove_anchor() # clear any old anchor + self._anchor_name = str(name) if not name is None else name + self.make_anchor() # add anchor again + + + @property def is_anchor(self): """Is this Universe an anchoring for unpickling AtomGroups""" - return self._gen_anchor_hash() in _ANCHOR_UNIVERSES + return self.anchor_name in _ANCHOR_UNIVERSES @is_anchor.setter def is_anchor(self, new): @@ -765,10 +762,10 @@ def is_anchor(self, new): def remove_anchor(self): """Remove this Universe from the possible anchor list for unpickling""" - _ANCHOR_UNIVERSES.pop(self._gen_anchor_hash(), None) + _ANCHOR_UNIVERSES.pop(self.anchor_name, None) def make_anchor(self): - _ANCHOR_UNIVERSES[self._gen_anchor_hash()] = self + _ANCHOR_UNIVERSES[self.anchor_name] = self def __repr__(self): # return "".format( @@ -845,10 +842,10 @@ def add_TopologyAttr(self, topologyattr, values=None): Parameters ---------- - topologyattr : TopologyAttr or string + topologyattr: TopologyAttr or string Either a MDAnalysis TopologyAttr object or the name of a possible topology attribute. - values : np.ndarray, optional + values: np.ndarray, optional If initiating an attribute from a string, the initial values to use. If not supplied, the new TopologyAttribute will have empty or zero values. @@ -929,10 +926,10 @@ def add_Residue(self, segment=None, **attrs): Parameters ---------- - segment : MDAnalysis.Segment + segment: MDAnalysis.Segment If there are multiple segments, then the Segment that the new Residue will belong in must be specified. - attrs : dict + attrs: dict For each Residue attribute, the value for the new Residue must be specified @@ -975,7 +972,7 @@ def add_Segment(self, **attrs): Parameters ---------- - attrs : dict + attrs: dict For each Segment attribute as a key, give the value in the new Segment @@ -1361,12 +1358,12 @@ def Merge(*args): Parameters ---------- - *args : :class:`~MDAnalysis.core.groups.AtomGroup` + *args: :class:`~MDAnalysis.core.groups.AtomGroup` One or more AtomGroups. Returns ------- - universe : :class:`Universe` + universe: :class:`Universe` Raises ------ From f7c929720b994dfdc4f3066f70ab93d02225e2af Mon Sep 17 00:00:00 2001 From: Lily Wang Date: Mon, 23 Sep 2019 03:38:11 +1000 Subject: [PATCH 14/19] updated CHANGELOG --- package/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/package/CHANGELOG b/package/CHANGELOG index 87bef9ee407..61f5dd5b119 100644 --- a/package/CHANGELOG +++ b/package/CHANGELOG @@ -108,10 +108,13 @@ Changes * Removed `MDAnalysis.migration` (Issue #2490, PR #2496) * The fasta2select now always assumes that the gap character in a sequence is "-" (Issue #2448, PR #2457) + * refactored Universe.__init__ with clear arguments. + Universe.empty() now has n_atoms=0 as default. (Issue #2282) Deprecations * analysis.hbonds.HydrogenBondAnalysis is deprecated in 1.0 (remove in 2.0) + 09/05/19 IAlibay, richardjgowers * 0.20.1 From 4d0ccdfb2166468bf31eb11edcc85b245f9a260e Mon Sep 17 00:00:00 2001 From: Lily Wang Date: Fri, 14 Feb 2020 10:19:59 +1100 Subject: [PATCH 15/19] changed topology to kwarg --- package/MDAnalysis/core/universe.py | 4 +++- testsuite/MDAnalysisTests/core/test_universe.py | 5 +++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/package/MDAnalysis/core/universe.py b/package/MDAnalysis/core/universe.py index 3aef42d8cf4..8f3fb6c0066 100644 --- a/package/MDAnalysis/core/universe.py +++ b/package/MDAnalysis/core/universe.py @@ -363,7 +363,7 @@ class Universe(object): """ - def __init__(self, topology, *coordinates, all_coordinates=False, + def __init__(self, topology=None, *coordinates, all_coordinates=False, format=None, topology_format=None, transformations=None, guess_bonds=False, vdwradii=None, anchor_name=None, is_anchor=True, in_memory=False, in_memory_step=1, @@ -405,6 +405,8 @@ def __init__(self, topology, *coordinates, all_coordinates=False, if topology is not None: _generate_from_topology(self) # make real atoms, res, segments + else: + self.atoms = None coordinates = _resolve_coordinates(self.filename, *coordinates, format=format, diff --git a/testsuite/MDAnalysisTests/core/test_universe.py b/testsuite/MDAnalysisTests/core/test_universe.py index 5ff99dc7291..259bc271fde 100644 --- a/testsuite/MDAnalysisTests/core/test_universe.py +++ b/testsuite/MDAnalysisTests/core/test_universe.py @@ -1147,3 +1147,8 @@ def test_empty_no_atoms(self): assert len(u.atoms) == 0 assert len(u.residues) == 0 assert len(u.segments) == 0 + + def test_empty_creation(self): + u = mda.Universe() + assert u.atoms is None + assert u._topology is None From 9250f315cb6a49af7dad65a79d6914e501086bdc Mon Sep 17 00:00:00 2001 From: Lily Wang Date: Fri, 14 Feb 2020 10:41:31 +1100 Subject: [PATCH 16/19] rebased and corrected for new tests --- package/MDAnalysis/core/universe.py | 24 +++++++++++++++---- .../MDAnalysisTests/core/test_universe.py | 6 +++-- 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/package/MDAnalysis/core/universe.py b/package/MDAnalysis/core/universe.py index 8f3fb6c0066..cf8e2fcad66 100644 --- a/package/MDAnalysis/core/universe.py +++ b/package/MDAnalysis/core/universe.py @@ -401,12 +401,12 @@ def __init__(self, topology=None, *coordinates, all_coordinates=False, topology_format=topology_format, **kwargs) - self._topology = topology - if topology is not None: - _generate_from_topology(self) # make real atoms, res, segments + self._topology = topology else: - self.atoms = None + self._topology = Topology(0, 0, 0) + + _generate_from_topology(self) # make real atoms, res, segments coordinates = _resolve_coordinates(self.filename, *coordinates, format=format, @@ -488,17 +488,31 @@ def empty(cls, n_atoms=0, n_residues=1, n_segments=1, Universes can now be created with 0 atoms """ if not n_atoms: - return cls(None) + return cls() if atom_resindex is None: warnings.warn( 'Residues specified but no atom_resindex given. ' 'All atoms will be placed in first Residue.', UserWarning) + if residue_segindex is None: warnings.warn( 'Segments specified but no segment_resindex given. ' + 'All residues will be placed in first Segment', + UserWarning) + + top = Topology(n_atoms, n_residues, n_segments, + atom_resindex=atom_resindex, + residue_segindex=residue_segindex, + ) + + u = cls(top) + + if trajectory: + coords = np.zeros((1, n_atoms, 3), dtype=np.float32) dims = np.zeros(6, dtype=np.float32) + vels = np.zeros_like(coords) if velocities else None forces = np.zeros_like(coords) if forces else None # grab and attach a MemoryReader diff --git a/testsuite/MDAnalysisTests/core/test_universe.py b/testsuite/MDAnalysisTests/core/test_universe.py index 259bc271fde..6705310c719 100644 --- a/testsuite/MDAnalysisTests/core/test_universe.py +++ b/testsuite/MDAnalysisTests/core/test_universe.py @@ -1150,5 +1150,7 @@ def test_empty_no_atoms(self): def test_empty_creation(self): u = mda.Universe() - assert u.atoms is None - assert u._topology is None + assert len(u.atoms) == 0 + assert len(u.residues) == 0 + assert len(u.segments) == 0 + \ No newline at end of file From 354ee9593fcf9175e57928802283cf1b59ccb677 Mon Sep 17 00:00:00 2001 From: Lily Wang Date: Fri, 14 Feb 2020 13:37:40 +1100 Subject: [PATCH 17/19] added error to Universe.__init__ --- package/CHANGELOG | 5 +++-- package/MDAnalysis/core/universe.py | 19 ++++++++++++------- .../MDAnalysisTests/core/test_universe.py | 11 +++++------ 3 files changed, 20 insertions(+), 15 deletions(-) diff --git a/package/CHANGELOG b/package/CHANGELOG index 61f5dd5b119..ed00fbb0099 100644 --- a/package/CHANGELOG +++ b/package/CHANGELOG @@ -84,6 +84,9 @@ Enhancements * Improve the distance search in water bridge analysis with capped_distance (PR #2480) Changes + * refactored Universe.__init__ with clear arguments. Calling Universe() + now raises a TypeError advising you to use Universe.empty. + Universe.empty() now no longer has n_atoms=0 as default. (Issue #2282) * Removes support for setting `start`/`stop`/`step` trajecotry slicing keywords on :class:`HOLEtraj` construction. Also removes undocumented support for passing :class:`HOLE` parameters to :meth:`HOLEtraj.run` @@ -108,8 +111,6 @@ Changes * Removed `MDAnalysis.migration` (Issue #2490, PR #2496) * The fasta2select now always assumes that the gap character in a sequence is "-" (Issue #2448, PR #2457) - * refactored Universe.__init__ with clear arguments. - Universe.empty() now has n_atoms=0 as default. (Issue #2282) Deprecations * analysis.hbonds.HydrogenBondAnalysis is deprecated in 1.0 (remove in 2.0) diff --git a/package/MDAnalysis/core/universe.py b/package/MDAnalysis/core/universe.py index cf8e2fcad66..ad45a7e77e8 100644 --- a/package/MDAnalysis/core/universe.py +++ b/package/MDAnalysis/core/universe.py @@ -404,7 +404,10 @@ def __init__(self, topology=None, *coordinates, all_coordinates=False, if topology is not None: self._topology = topology else: - self._topology = Topology(0, 0, 0) + # point to Universe.empty instead of making empty universe + raise TypeError('Topology argument required to make Universe. ' + 'Try Universe.empty(n_atoms, ...) to construct ' + 'your own Universe.') _generate_from_topology(self) # make real atoms, res, segments @@ -412,8 +415,9 @@ def __init__(self, topology=None, *coordinates, all_coordinates=False, format=format, all_coordinates=all_coordinates) - self.load_new(coordinates, format=format, in_memory=in_memory, - in_memory_step=in_memory_step, **kwargs) + if coordinates: + self.load_new(coordinates, format=format, in_memory=in_memory, + in_memory_step=in_memory_step, **kwargs) if transformations: if callable(transformations): @@ -432,7 +436,7 @@ def copy(self): @classmethod - def empty(cls, n_atoms=0, n_residues=1, n_segments=1, + def empty(cls, n_atoms, n_residues=1, n_segments=1, atom_resindex=None, residue_segindex=None, trajectory=False, velocities=False, forces=False): """Create a blank Universe @@ -446,7 +450,7 @@ def empty(cls, n_atoms=0, n_residues=1, n_segments=1, Parameters ---------- - n_atoms: int, default 0 + n_atoms: int number of Atoms in the Universe n_residues: int, default 1 number of Residues in the Universe, defaults to 1 @@ -488,7 +492,8 @@ def empty(cls, n_atoms=0, n_residues=1, n_segments=1, Universes can now be created with 0 atoms """ if not n_atoms: - return cls() + n_residues = 0 + n_segments = 0 if atom_resindex is None: warnings.warn( @@ -607,7 +612,7 @@ def load_new(self, filename, format=None, in_memory=False, and detected file type. """ # filename==None happens when only a topology is provided - if not filename: + if filename is None: return self if len(util.asiterable(filename)) == 1: diff --git a/testsuite/MDAnalysisTests/core/test_universe.py b/testsuite/MDAnalysisTests/core/test_universe.py index 6705310c719..bde306efec5 100644 --- a/testsuite/MDAnalysisTests/core/test_universe.py +++ b/testsuite/MDAnalysisTests/core/test_universe.py @@ -180,7 +180,7 @@ def test_Universe_invalidpermissionfile_IE_msg(self): temp_dir.dissolve() def test_load_new_VE(self): - u = mda.Universe.empty() + u = mda.Universe.empty(0) with pytest.raises(TypeError): u.load_new('thisfile', format = 'soup') @@ -1148,9 +1148,8 @@ def test_empty_no_atoms(self): assert len(u.residues) == 0 assert len(u.segments) == 0 - def test_empty_creation(self): - u = mda.Universe() - assert len(u.atoms) == 0 - assert len(u.residues) == 0 - assert len(u.segments) == 0 + def test_empty_creation_raises_error(self): + with pytest.raises(TypeError) as exc: + u = mda.Universe() + assert 'Universe.empty' in str(exc.value) \ No newline at end of file From d28ef331744f876ba1bdcfc89363e0d287bf84a1 Mon Sep 17 00:00:00 2001 From: richardjgowers Date: Sun, 16 Feb 2020 11:31:03 +0000 Subject: [PATCH 18/19] butchered function signatures for py2 --- package/MDAnalysis/core/universe.py | 46 +++++++++++++++++++++-------- 1 file changed, 34 insertions(+), 12 deletions(-) diff --git a/package/MDAnalysis/core/universe.py b/package/MDAnalysis/core/universe.py index ad45a7e77e8..15869d08b50 100644 --- a/package/MDAnalysis/core/universe.py +++ b/package/MDAnalysis/core/universe.py @@ -169,7 +169,11 @@ def _topology_from_file_like(topology_file, topology_format=None, "Error: {2}".format(topology_file, parser, err)) return topology -def _resolve_formats(*coordinates, format=None, topology_format=None): +# py3 TODO +#def _resolve_formats(*coordinates, format=None, topology_format=None): +def _resolve_formats(*coordinates, **kwargs): + format = kwargs.get('format', None) + topology_format = kwargs.get('topology_format', None) if not coordinates: if format is None: format = topology_format @@ -177,8 +181,15 @@ def _resolve_formats(*coordinates, format=None, topology_format=None): topology_format = format return format, topology_format -def _resolve_coordinates(filename, *coordinates, format=None, - all_coordinates=False): +# py3 TODO +#def _resolve_coordinates(filename, *coordinates, format=None, +# all_coordinates=False): +def _resolve_coordinates(*args, **kwargs): + filename = args[0] + coordinates = args[1:] + format = kwargs.get('format', None) + all_coordinates = kwargs.get('all_coordinates', False) + if all_coordinates or not coordinates and filename is not None: try: get_reader_for(filename, format=format) @@ -358,16 +369,29 @@ class Universe(object): bonds, angles, dihedrals master ConnectivityGroups for each connectivity type - .. versionchanged:: 0.20.0 + .. versionchanged:: 0.21.0 Universe() now raises an error. Use Universe(None) or :func:`Universe.empty()` instead. """ +# Py3 TODO +# def __init__(self, topology=None, *coordinates, all_coordinates=False, +# format=None, topology_format=None, transformations=None, +# guess_bonds=False, vdwradii=None, anchor_name=None, +# is_anchor=True, in_memory=False, in_memory_step=1, +# **kwargs): + def __init__(self, *args, **kwargs): + topology = args[0] if args else None + coordinates = args[1:] + all_coordinates = kwargs.pop('all_coordinates', False) + format = kwargs.pop('format', None) + topology_format = kwargs.pop('topology_format', None) + transformations = kwargs.pop('transformations', None) + guess_bonds = kwargs.pop('guess_bonds', False) + vdwradii = kwargs.pop('vdwradii', None) + anchor_name = kwargs.pop('anchor_name', None) + is_anchor = kwargs.pop('is_anchor', True) + in_memory = kwargs.pop('in_memory', False) + in_memory_step = kwargs.pop('in_memory_step', 1) - - def __init__(self, topology=None, *coordinates, all_coordinates=False, - format=None, topology_format=None, transformations=None, - guess_bonds=False, vdwradii=None, anchor_name=None, - is_anchor=True, in_memory=False, in_memory_step=1, - **kwargs): self._instant_selectors = {} # for storing segments. Deprecated? self._trajectory = None # managed attribute holding Reader self._cache = {} @@ -426,7 +450,6 @@ def __init__(self, topology=None, *coordinates, all_coordinates=False, if guess_bonds: self.atoms.guess_bonds(vdwradii=vdwradii) - def copy(self): """Return an independent copy of this Universe""" @@ -434,7 +457,6 @@ def copy(self): new.trajectory = self.trajectory.copy() return new - @classmethod def empty(cls, n_atoms, n_residues=1, n_segments=1, atom_resindex=None, residue_segindex=None, From b3a85250eb54d9df69de4236b213b619ec193c0d Mon Sep 17 00:00:00 2001 From: richardjgowers Date: Sun, 16 Feb 2020 11:44:36 +0000 Subject: [PATCH 19/19] update changelog --- package/CHANGELOG | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/package/CHANGELOG b/package/CHANGELOG index ed00fbb0099..ea8fa72f97e 100644 --- a/package/CHANGELOG +++ b/package/CHANGELOG @@ -84,9 +84,8 @@ Enhancements * Improve the distance search in water bridge analysis with capped_distance (PR #2480) Changes - * refactored Universe.__init__ with clear arguments. Calling Universe() - now raises a TypeError advising you to use Universe.empty. - Universe.empty() now no longer has n_atoms=0 as default. (Issue #2282) + * Calling Universe() now raises a TypeError advising you to use Universe.empty. + Universe.empty() now no longer has n_atoms=0 as default. (Issue #2527) * Removes support for setting `start`/`stop`/`step` trajecotry slicing keywords on :class:`HOLEtraj` construction. Also removes undocumented support for passing :class:`HOLE` parameters to :meth:`HOLEtraj.run`