コード例 #1
0
    def _nest_directories(self, processes):
        """Helper function to nest processes inside the runner's working
           directory.

           Parameters
           ----------

           processes : [:class:`Process <BioSimSpace.Process>`]
               A list of process objects.

           Returns
           -------

           new_processes : [:class:`Process <BioSimSpace.Process>`]
               A list of procesess with updated working directories.
        """

        # Create the list of new processes.
        new_processes = []

        # Loop over each process.
        for process in processes:
            # Create the new working directory name.
            new_dir = "%s/%s" % (self._work_dir, process._work_dir)

            # Create a new process object using the nested directory.
            if process._package_name == "SOMD":
                new_processes.append(
                    type(process)(_System(process._system), process._protocol,
                                  process._exe, process._name,
                                  process._platform, new_dir, process._seed,
                                  process._property_map))
            else:
                new_processes.append(
                    type(process)(_System(process._system), process._protocol,
                                  process._exe, process._name, new_dir,
                                  process._seed, process._property_map))

        return new_processes
コード例 #2
0
def saveMolecules(filebase, system, fileformat, property_map={}):
    """Save a molecular system to file.

       Parameters
       ----------

       filebase : str
           The base name of the output file.

       system : :class:`System <BioSimSpace._SireWrappers.System>`, \
                :class:`Molecule< BioSimSpace._SireWrappers.Molecule>` \
                :class:`Molecule< BioSimSpace._SireWrappers.Molecules>`
           The molecular system.

       fileformat : str, [str]
           The file format (or formats) to save to.

       property_map : dict
           A dictionary that maps system "properties" to their user
           defined values. This allows the user to refer to properties
           with their own naming scheme, e.g. { "charge" : "my-charge" }

       Returns
       -------

       files : [str]
           The list of files that were generated.

       Examples
       --------

       Load a molecular system from AMBER coordinate and topology files then
       try to save it to all supported file formats.

       >>> import BioSimSpace as BSS
       >>> system = BSS.IO.readMolecules(["ala.rst7", "ala.prm7"])
       >>> for format in BSS.IO.fileFormats():
       ...     try:
       ...         BSS.IO.saveMolecules("test", system, format)
       ...     except:
       ...         print("Could not convert to format: '%s'" % format)

       Load a molecular system from AMBER coordinate and topology files then
       try to save it to GROMACS format, mapping and un-mapping the charge
       property along the way.

       >>> import BioSimSpace as BSS
       >>> system = BSS.IO.readMolecules(["ala.rst7", "ala.prm7"], property_map={"charge" : "my-charge"})
       >>> BSS.IO.saveMolecules("test", system, ["gro87", "grotop"], property_map={"charge" : "my-charge"})
    """

    global _has_gmx_warned
    if _gromacs_path is None and not _has_gmx_warned:
        _warn(
            "BioSimSpace.IO: Please install GROMACS (http://www.gromacs.org) "
            "for GROMACS topology file support.")
        _has_gmx_warned = True

    # Check that the filebase is a string.
    if type(filebase) is not str:
        raise TypeError("'filebase' must be of type 'str'")

    # Check that that the system is of the correct type.

    # A System object.
    if type(system) is _System:
        pass
    # A Molecule object.
    elif type(system) is _Molecule:
        system = _System(system)
    elif type(system) is _Molecules:
        system = system.toSystem()
    # A list of Molecule objects.
    elif type(system) is list and all(
            isinstance(x, _Molecule) for x in system):
        system = _System(system)
    # Invalid type.
    else:
        raise TypeError(
            "'system' must be of type 'BioSimSpace.SireWrappers.System', "
            "'BioSimSpace._SireWrappers.Molecule, 'BioSimSpace._SireWrappers.Molecules' "
            "or a list of 'BiSimSpace._SireWrappers.Molecule' types.")

    # Check that fileformat argument is of the correct type.

    # Convert to a list if a single string is passed.
    # We split on ',' since the user might pass system.fileFormats() as the argument.
    if type(fileformat) is str:
        fileformat = fileformat.split(",")
    # Lists and tuples are okay!
    elif type(fileformat) is list:
        pass
    elif type(fileformat) is tuple:
        pass
    # Invalid.
    else:
        raise TypeError(
            "'fileformat' must be a 'str' or a 'list' of 'str' types.")

    # Make sure all items in list or tuple are strings.
    if not all(isinstance(x, str) for x in fileformat):
        raise TypeError(
            "'fileformat' must be a 'str' or a 'list' of 'str' types.")

    # Make a list of the matched file formats.
    formats = []

    # Make sure that all of the formats are valid.
    for format in fileformat:
        try:
            f = _formats_dict[format.replace(" ", "").upper()][0]
            formats.append(f)
        except KeyError:
            raise ValueError("Unsupported file format '%s'. Supported formats "
                             "are: %s." % (format, str(_formats)))

    # Validate the map.
    if type(property_map) is not dict:
        raise TypeError("'property_map' must be of type 'dict'")

    # Copy the map.
    _property_map = property_map.copy()

    # Add the GROMACS topology file path.
    if _gromacs_path is not None and ("GROMACS_PATH" not in _property_map):
        _property_map["GROMACS_PATH"] = _gromacs_path

    # Get the directory name.
    dirname = _os.path.dirname(filebase)

    # If the user has passed a directory, make sure that is exists.
    if _os.path.basename(filebase) != filebase:
        # Create the directory if it doesn't already exist.
        if not _os.path.isdir(dirname):
            _os.makedirs(dirname, exist_ok=True)

    # Store the current working directory.
    dir = _os.getcwd()

    # Change to the working directory for the process.
    # This avoid problems with relative paths.
    if dirname != "":
        _os.chdir(dirname)

    # A list of the files that have been written.
    files = []

    # Save the system using each file format.
    for format in formats:
        # Add the file format to the property map.
        _property_map["fileformat"] = _SireBase.wrap(format)

        # Write the file.
        try:
            file = _SireIO.MoleculeParser.save(system._getSireObject(),
                                               filebase, _property_map)
            files += file
        except Exception as e:
            if dirname != "":
                _os.chdir(dir)
            msg = "Failed to save system to format: '%s'" % format
            if _isVerbose():
                raise IOError(msg) from e
            else:
                raise IOError(msg) from None

    # Change back to the original directory.
    if dirname != "":
        _os.chdir(dir)

    # Return the list of files.
    return files
コード例 #3
0
def readMolecules(files, property_map={}):
    """Read a molecular system from file.

       Parameters
       ----------

       files : str, [str]
           A file name, or a list of file names.

       property_map : dict
           A dictionary that maps system "properties" to their user defined
           values. This allows the user to refer to properties with their
           own naming scheme, e.g. { "charge" : "my-charge" }

       Returns
       -------

       system : :class:`System <BioSimSpace._SireWrappers.System>`
           A molecular system.

       Examples
       --------

       Load a molecular system from AMBER coordinate and topology files.

       >>> import BioSimSpace as BSS
       >>> system = BSS.IO.readMolecules(["ala.rst7", "ala.prm7"])

       Load the same system, but map the "charge" property to the key "my-charge".

       >>> import BioSimSpace as BSS
       >>> system = BSS.IO.readMolecules(["ala.rst7", "ala.prm7"], property_map={"charge" : "my-charge"})

       >>> import BioSimSpace as BSS
       >>> system = BSS.IO.readMolecules(["ala.rst7", "ala.prm7"])

       Load a molecular system from all of the files contained within a directory.

       >>> import BioSimSpace as BSS
       >>> system = BSS.IO.readMolecules(BSS.IO.glob("dir/*"))

       Load a molecular system from GROMACS coordinate and topology files using
       a custom GROMACS topology directory.

       >>> import BioSimSpace as BSS
       >>> system = BSS.IO.readMolecules(["mol.gro87", "mol.grotop"], property_map={"GROMACS_PATH" : "/path/to/gromacs/topology"})
    """

    global _has_gmx_warned
    if _gromacs_path is None and not _has_gmx_warned:
        _warn(
            "BioSimSpace.IO: Please install GROMACS (http://www.gromacs.org) "
            "for GROMACS topology file support.")
        _has_gmx_warned = True

    # Convert to a list.
    if type(files) is str:
        files = [files]

    # Check that all arguments are of type 'str'.
    if type(files) is list:
        if not all(isinstance(x, str) for x in files):
            raise TypeError("'files' must be a list of 'str' types.")
        if len(files) == 0:
            raise ValueError("The list of input files is empty!")
    else:
        raise TypeError(
            "'files' must be of type 'str', or a list of 'str' types.")

    # Validate the map.
    if type(property_map) is not dict:
        raise TypeError("'property_map' must be of type 'dict'")

    # Add the GROMACS topology file path.
    if _gromacs_path is not None and ("GROMACS_PATH" not in property_map):
        property_map["GROMACS_PATH"] = _gromacs_path

    # Check that the files exist.
    for file in files:
        if not _os.path.isfile(file):
            raise IOError("Missing input file: '%s'" % file)

    # Try to read the files and return a molecular system.
    try:
        system = _SireIO.MoleculeParser.read(files, property_map)
    except Exception as e:
        if "There are no lead parsers!" in str(e):
            msg = (
                "Failed to read molecules from %s. "
                "It looks like you failed to include a topology file.") % files
            if _isVerbose():
                raise IOError(msg) from e
            else:
                raise IOError(msg) from None
        else:
            if "Incompatibility" in str(e):
                msg = "Incompatibility between molecular information in files: %s" % files
                if _isVerbose():
                    raise IOError(msg) from e
                else:
                    raise IOError(msg) from None
            else:
                msg = "Failed to read molecules from: %s" % files
                if _isVerbose():
                    raise IOError(msg) from e
                else:
                    raise IOError(msg) from None

    return _System(system)
コード例 #4
0
ファイル: _solvent.py プロジェクト: naveen584/BioSimSpace
def _validate_input(molecule, box, shell, ion_conc, is_neutral, work_dir,
                    property_map):
    """Internal function to validate function arguments.

       Parameters
       ----------

       molecule : :class:`Molecule <BioSimSpace._SireWrappers.Molecule>`, \
                  :class:`Molecule <BioSimSpace._SireWrappers.Molecules>`, \
                  :class:`System <BioSimSpace._SireWrappers.System>`
           A molecule, or container/system of molecules.

       box : [:class:`Length <BioSimSpace.Types.Length>`]
           A list containing the box size in each dimension.

       shell : :class:`Length` <BioSimSpace.Types.Length>`
           Thickness of the water shell around the solute. Note that the
           base length of the resulting box must be at least twice as large
           as the cutoff used by the chosen molecular dynamics engine. As such,
           the shell option is often unsuitable for small molecules.

       ion_conc : float
           The ion concentration in (mol per litre).

       is_neutral : bool
           Whether to neutralise the system.

       work_dir : str
           The working directory for the process.

       property_map : dict
           A dictionary that maps system "properties" to their user defined
           values. This allows the user to refer to properties with their
           own naming scheme, e.g. { "charge" : "my-charge" }

       Returns
       -------

       (molecule, box, shell, work_dir, property_map) : tuple
           The validated input arguments.
    """

    # Whether to check the box size.
    check_box = True

    # Validate the molecule and create a local copy called _molecule to ensure
    # that the passed molecule is preserved.
    if molecule is not None:
        if type(molecule) is _Molecule:
            _molecule = _Molecule(molecule)
        elif type(molecule) is _Molecules:
            _molecule = molecule.toSystem()
        elif type(molecule) is _System:
            _molecule = _System(molecule)
        else:
            raise TypeError(
                "'molecule' must be of type 'BioSimSpace._SireWrappers.Molecule' "
                "'BioSimSpace._SireWrappers.Molecules', or 'BioSimSpace._SireWrappers.System'"
            )

        # Try to extract the box dimensions from the system.
        if type(molecule) is _System and box is None and shell is None:
            try:
                check_box = False
                prop = property_map.get("space", "space")
                box = molecule._sire_object.property(prop).dimensions()
                # Convert to a list of Length objects.
                box = [
                    _Length(box[0], "A"),
                    _Length(box[1], "A"),
                    _Length(box[2], "A")
                ]
            except:
                raise ValueError(
                    "The system has no box information. Please use "
                    "the 'box' keyword argument.")
        else:
            if box is None and shell is None:
                raise ValueError("Missing 'box' keyword argument!")
    else:
        _molecule = None

        if box is None:
            raise ValueError("Missing 'box' keyword argument!")

        if shell is not None:
            _warnings.warn(
                "Ignoring 'shell' keyword argument as solute is missing.")
            shell = None

    if box is not None:
        # Convert tuple to list.
        if type(box) is tuple:
            box = list(box)

        # Convert Coordinate to list.
        if type(box) is _Coordinate:
            box = [box.x(), box.y(), box.z()]

        # Validate.
        if len(box) != 3:
            raise ValueError(
                "The 'box' must have x, y, and z size information.")
        else:
            if not all(isinstance(x, _Length) for x in box):
                raise ValueError(
                    "The box dimensions must be of type 'BioSimSpace.Types.Length'"
                )

    if shell is not None:
        if type(shell) is not _Length:
            raise ValueError(
                "'shell' must must be of type 'BioSimSpace.Types.Length'")

        if box is not None:
            _warnings.warn(
                "Ignoring 'box' keyword argument as 'shell' takes precendence."
            )

        # Work out the box size based on axis-aligned bounding box.
        # We take the maximum dimension as the base length of our box.
        base_length = max(2 * molecule._getAABox().halfExtents())

        # Now add the shell thickness.
        base_length = _Length(base_length, "A") + shell

        # If we need to add ions, make sure the box is at least 2.54 nanometers
        # wide, i.e. twice the rlist cutoff used by GROMACS protocols.
        if base_length < _Length(2.54, "nm"):
            base_length = _Length(2.54, "nm")

        # Create the dimensions for a cubic box.
        box = 3 * [base_length]

    # Check that the ion concentration is valid.
    if type(ion_conc) is not float and type(ion_conc) is not int:
        raise TypeError("'ion_conc' must be of type 'int' or 'float'.")
    elif ion_conc < 0:
        raise ValueError("'ion_conc' cannot be negative!")

    if type(is_neutral) is not bool:
        raise TypeError("'is_neutral' must be of type 'bool'.")

    # Check that the working directory is valid.
    if work_dir is not None and type(work_dir) is not str:
        raise TypeError("'work_dir' must be of type 'str'")

    # Check that the property map is valid.
    if type(property_map) is not dict:
        raise TypeError("'property_map' must be of type 'dict'")

    # Check that the box is large enough to hold the molecule.
    if check_box:
        if molecule is not None and shell is None and not _check_box_size(
                molecule, box, property_map):
            raise ValueError(
                "The 'box' is not large enough to hold the 'molecule'")

    return (_molecule, box, shell, work_dir, property_map)
コード例 #5
0
ファイル: _somd.py プロジェクト: jamesmkrieger/BioSimSpace
    def _setup(self):
        """Setup the input files and working directory ready for simulation."""

        # Create the input files...

        # First create a copy of the system.
        system = self._system.copy()

        # If the we are performing a free energy simulation, then check that
        # the system contains a single perturbable molecule. If so, then create
        # and write a perturbation file to the work directory.
        if type(self._protocol) is _Protocol.FreeEnergy:
            if system.nPerturbableMolecules() == 1:
                # Extract the perturbable molecule.
                pert_mol = system.getPerturbableMolecules()[0]

                # Write the perturbation file and get the molecule corresponding
                # to the lambda = 0 state.
                pert_mol = pert_mol._toPertFile(
                    self._pert_file, property_map=self._property_map)
                self._input_files.append(self._pert_file)

                # Remove the perturbable molecule.
                system._sire_object.remove(pert_mol.number())

                # Recreate the system, putting the perturbable molecule with
                # renamed properties first.
                updated_system = _System(pert_mol) + _System(system)

                # Copy across all of the properties from the orginal system.
                for prop in system._sire_object.propertyKeys():
                    updated_system._sire_object.setProperty(
                        prop, system._sire_object.property(prop))

                # Copy the updated system object across.
                system = updated_system

            else:
                raise ValueError("'BioSimSpace.Protocol.FreeEnergy' requires a single "
                                 "perturbable molecule. The system has %d" \
                                  % system.nPerturbableMolecules())

        # If this is a different protocol and the system still contains a
        # perturbable molecule, then we'll warn the user and simulate the
        # lambda = 0 state.
        else:
            if system.nPerturbableMolecules() > 0:
                if not "is_lambda1" in self._property_map:
                    is_lambda1 = False
                    _warnings.warn(
                        "The system contains a perturbable molecule but "
                        "this isn't a 'FreeEnergy' protocol. We will assume "
                        "that you intend to simulate the lambda = 0 state. "
                        "If you want to simulate the lambda = 1 state, then "
                        "pass {'is_lambda1' : True} in the 'property_map' "
                        "argument.")
                else:
                    is_lambda1 = self._property_map["is_lambda1"]
                    self._property_map.pop("is_lambda1")

                # Loop over all perturbable molecules in the system and replace them
                # with a regular molecule and the chosen end state.
                for mol in system.getPerturbableMolecules():
                    system.updateMolecules(
                        mol._toRegularMolecule(property_map=self._property_map,
                                               is_lambda1=is_lambda1))

                # Copy across the properties from the original system.
                for prop in self._system._sire_object.propertyKeys():
                    system._sire_object.setProperty(
                        prop, self._system._sire_object.property(prop))

        # RST file (coordinates).
        try:
            rst = _SireIO.AmberRst7(system._sire_object, self._property_map)
            rst.writeToFile(self._rst_file)
        except Exception as e:
            msg = "Failed to write system to 'RST7' format."
            if _isVerbose():
                raise IOError(msg) from e
            else:
                raise IOError(msg) from None

        # PRM file (topology).
        try:
            prm = _SireIO.AmberPrm(system._sire_object, self._property_map)
            prm.writeToFile(self._top_file)
        except Exception as e:
            msg = "Failed to write system to 'PRM7' format."
            if _isVerbose():
                raise IOError(msg) from e
            else:
                raise IOError(msg) from None

        # Generate the SOMD configuration file.
        # Skip if the user has passed a custom config.
        if type(self._protocol) is _Protocol.Custom:
            self.setConfig(self._protocol.getConfig())
        else:
            self._generate_config()
        self.writeConfig(self._config_file)

        # Generate the dictionary of command-line arguments.
        self._generate_args()

        # Return the list of input files.
        return self._input_files
コード例 #6
0
def getFrame(trajectory, topology, index):
    """Extract a single frame from a trajectory file.

       Parameters
       ----------

       trajectory : str
           A trajectory file.

       topology : str
           A topology file.

       index : int
          The index of the frame.

       Returns
       -------

       frame : :class:`System <BioSimSpace._SireWrappers.System>`
           The System object of the corresponding frame.
    """

    if type(trajectory) is not str:
        raise TypeError("'trajectory' must be of type 'str'")

    if type(topology) is not str:
        raise TypeError("'topology' must be of type 'str'")

    if type(index) is not int:
        raise TypeError("'index' must be of type 'int'")

    # Try to load the frame.
    try:
        frame = _mdtraj.load_frame(trajectory, index, top=topology)
    except:
        # Get the file format of the topology file.
        try:
            # Load the topology file to determine the file format.
            file_format = _IO.readMolecules(topology).fileFormat()

            # Set the extension.
            extension = _extensions.get(file_format, file_format.lower())

            # Set the path to the temporary topology file.
            top_file = _os.getcwd() + "/.topology." + extension

            # Copy the topology to a file with the correct extension.
            _shutil.copyfile(topology, top_file)

            frame = _mdtraj.load_frame(trajectory, index, top=top_file)
        except:
            _os.remove(top_file)
            raise IOError(
                "MDTraj failed to read frame %d from: traj=%s, top=%s" %
                (index, trajectory, topology))

        # Remove the temporary topology file.
        _os.remove(top_file)

    # The name of the frame coordinate file.
    frame_file = ".frame.nc"

    # Save the coordinates to file.
    frame.save(frame_file)

    # Load the frame into a System object.
    try:
        system = _System(_SireIO.MoleculeParser.read([topology, frame_file]))
    except Exception as e:
        _os.remove(frame_file)
        msg = "Failed to read trajectory frame: '%s'" % frame_file
        if _isVerbose():
            raise IOError(msg) from e
        else:
            raise IOError(msg) from None

    # Remove the temporary frame coordinate file.
    _os.remove(frame_file)

    # Return the system.
    return system
コード例 #7
0
    def getFrames(self, indices=None):
        """Get trajectory frames as a list of System objects.

           Parameters
           ----------

           indices : [int], [:class:`Time <BioSimSpace.Types.Time>`]
               A list of trajectory frame indices, or time stamps (in ns).

           Returns
           -------

           frames : [:class:`System <BioSimSpace._SireWrappers.System>`]
               The list of System objects.
        """

        # The process is running. Grab the latest trajectory.
        if self._process is not None and self._process.isRunning():
            self._trajectory = self.getTrajectory()

            # There is no trajectory.
            if self._trajectory is None:
                return None

        # Store the number of frames.
        n_frames = self._trajectory.n_frames

        # Work out the frame spacing in nanoseconds.
        # TODO:
        # How can we do this in a robust way if the trajectory is loaded from file?
        # Some formats do not store time information as part of the trajectory.
        if n_frames > 1:
            if self._process is not None:
                time_interval = self._process._protocol.getRunTime(
                ) / self._process._protocol.getFrames()
            else:
                time_interval = self._trajectory.timestep / 1000

        # Create the indices array.

        # Default to all frames.
        if indices is None:
            indices = [x for x in range(0, self._trajectory.n_frames)]

        # A single frame index.
        elif type(indices) is int:
            indices = [indices]

        # A single time stamp.
        elif type(indices) is _Time:
            if n_frames > 1:
                # Round time stamp to nearest frame index.
                indices = [
                    round(indices.nanoseconds().magnitude() / time_interval) -
                    1
                ]
            else:
                raise _IncompatibleError(
                    "Cannot determine time stamps for a trajectory "
                    "with only one frame!")

        # A list of frame indices.
        elif all(isinstance(x, int) for x in indices):
            pass

        # A list of time stamps.
        elif all(isinstance(x, _Time) for x in indices):
            if n_frames <= 1:
                raise _IncompatibleError(
                    "Cannot determine time stamps for a trajectory "
                    "with only one frame!")

            # Round time stamps to nearest frame indices.
            indices = [
                round(x.nanoseconds().magnitude() / time_interval) - 1
                for x in indices
            ]

        # Unsupported argument.
        else:
            raise ValueError(
                "Unsupported argument. Indices or time stamps "
                "must be an 'int' or 'BioSimSpace.Types.Time', or list of 'int' or "
                "'BioSimSpace.Types.Time' types.")

        # Intialise the list of frames.
        frames = []

        # Loop over all indices.
        for x in indices:

            # Make sure the frame index is within range.
            if x > 0 and x >= n_frames:
                raise ValueError("Frame index (%d) of of range (0 to %d)." %
                                 (x, n_frames - 1))
            elif x < -n_frames:
                raise ValueError("Frame index (%d) of of range (-1 to -%d)." %
                                 (x, n_frames))

            # The name of the frame coordinate file.
            frame_file = ".frame.nc"

            # Write the current frame as a NetCDF file.
            self._trajectory[x].save(frame_file)

            # Load the frame and create a System object.
            try:
                system = _System(
                    _SireIO.MoleculeParser.read([self._top_file, frame_file]))
            except Exception as e:
                _os.remove(frame_file)
                msg = "Failed to read trajectory frame: '%s'" % frame_file
                if _isVerbose():
                    raise IOError(msg) from e
                else:
                    raise IOError(msg) from None

            # Append the system to the list of frames.
            frames.append(system)

        # Remove the temporary frame coordinate file.
        _os.remove(frame_file)

        # Return the frames.
        return frames