PyFrag
Public Member Functions | Public Attributes | List of all members
qmworks.plams.core.basemol.Molecule Class Reference
Inheritance diagram for qmworks.plams.core.basemol.Molecule:
Inheritance graph
[legend]
Collaboration diagram for qmworks.plams.core.basemol.Molecule:
Collaboration graph
[legend]

Public Member Functions

def __init__ (self, filename=None, inputformat=None, geometry=1, other)
 
def copy (self, atoms=None)
 
def add_atom (self, atom, adjacent=None)
 
def delete_atom (self, atom)
 
def add_bond (self, arg1, arg2=None, order=1)
 
def delete_bond (self, arg1, arg2=None)
 
def delete_all_bonds (self)
 
def find_bond (self, atom1, atom2)
 
def set_atoms_id (self)
 
def unset_atoms_id (self)
 
def neighbors (self, atom)
 
def separate (self)
 
def guess_bonds (self)
 
def translate (self, vector, unit='angstrom')
 
def rotate (self, matrix)
 
def rotate_bond (self, bond, atom, angle, unit='radian')
 
def closest_atom (self, point, unit='angstrom')
 
def distance_to_point (self, point, unit='angstrom', result_unit='angstrom')
 
def distance_to_mol (self, other, result_unit='angstrom', return_atoms=False)
 
def wrap (self, length, angle=2 *math.pi, length_unit='angstrom', angle_unit='radian')
 
def get_center_of_mass (self, unit='angstrom')
 
def get_mass (self)
 
def get_formula (self)
 
def __len__ (self)
 
def __str__ (self)
 
def __iter__ (self)
 
def __getitem__ (self, key)
 
def __add__ (self, other)
 
def __iadd__ (self, other)
 
def __copy__ (self)
 
def readxyz (self, f, frame)
 
def writexyz (self, f)
 
def readmol (self, f, frame)
 
def writemol (self, f)
 
def readmol2 (self, f, frame)
 
def writemol2 (self, f)
 
def readpdb (self, f, frame)
 
def writepdb (self, f)
 
def read (self, filename, inputformat=None, frame=1)
 
def write (self, filename, outputformat=None)
 
def as_dict (self)
 
def from_dict (cls, atomBlock, bondBlock, properties)
 

Public Attributes

 atoms
 
 bonds
 
 lattice
 
 properties
 

Detailed Description

A class representing basic molecule object.

An instance of this class has the following attributes:
    *   ``atoms`` -- a list of |Atom| objects that belong to this molecule
    *   ``bonds`` -- a list of |Bond| objects between atoms listed in ``atoms``
    *   ``lattice`` -- a list of lattice vectors, in case of periodic structures
    *   ``properties`` -- a |Settings| instance storing all other information about this molecule

.. note::

    Each |Atom| in ``atoms`` list and each |Bond| in ``bonds`` list has a reference to the parent molecule. Moreover, each atom stores the list of bonds it's a part of and each bond stores references to atoms it bonds. That creates a complex net of references between objects that are part of a molecule. Consistency of this data is crucial for proper functioning of many methods. Because of that it is advised not to modify contents of ``atoms`` and ``bonds`` by hand. When you need to alter your molecule, methods :meth:`add_atom`, :meth:`delete_atom`, :meth:`add_bond` and :meth:`delete_bond` can be used to ensure that all these references are updated properly.

Creating a |Molecule| object for your calculation can be done in two ways. You can start with an empty molecule and manually add all atoms (and bonds, if needed)::

    >>> mol = Molecule()
    >>> mol.add_atom(Atom(atnum=1, coords=(0,0,0)))
    >>> mol.add_atom(Atom(atnum=1, coords=(d,0,0)))

This approach can be useful for building small molecules, especially if you wish to parametrize some of atomic coordinates (like in :ref:`simple_example`), but in general it's not very practical. Usually one wants to import atomic coordinates from some external file::

    >>> mol = Molecule('xyz/Benzene.xyz')

Constructor of a |Molecule| object accepts three arguments that can be used to supply this information from a file in your filesystem. *filename* should be a string with a path (absolute or relative) to such a file. *inputformat* describes the format of the file. Currently, the following formats are supported: ``xyz``, ``mol``, ``mol2`` and ``pdb``. If *inputformat* argument is not supplied, PLAMS will try to deduce it by examining the extension of the provided file, so in most of cases it is not needed to use *inputformat*, if only the file has the proper extension. Some formats (``xyz`` and ``pdb``) allow to store more than one geometry of a particular molecule within a single file. In such cases *geometry* argument can be used to indicate which (in order of appearance in the file) geometry to import. *other* keyword arguments passed to the constructor are used to populate ``properties`` |Settings|.

If a |Molecule| is initialized from an external file, the path to this file (*filename* argument) is stored in ``properties.source``. The base name of the file without extension is kept in ``properties.name``.

It is also possible to write a molecule to a file in one of the formats mentioned above. See :meth:`write` for details.

``lattice`` attribute is used to store information about lattice vectors in case of periodic structures. Some job types (|BANDJob|, |DFTBJob|) will automatically use that data while constructing input files. ``lattice`` should be a list of up to 3 vectors (for different types of periodicity: chain, slab or bulk), each of which needs to be a list or a tuple of 3 numbers.

Lattice vectors can be directly read and written to ``xyz`` files using the following convention (please mind the fact that this is an unofficial extension to the XYZ format)::

    3

        H      0.000000      0.765440     -0.008360
        O      0.000000      0.000000      0.593720
        H      0.000000     -0.765440     -0.008360
    VEC1       3.000000      0.000000      0.000000
    VEC2       0.000000      3.000000      0.000000
    VEC3       0.000000      0.000000      3.000000

For 1D (2D) periodicity please supply only ``VEC1`` (``VEC1`` and ``VEC2``). Writing lattice vectors to ``xyz`` files can be disabled by simply reseting the ``lattice`` attribute::

    >>> mol.lattice = []

|hspace|

Below the detailed description of available methods is presented. Many of these methods require passing atoms belonging to the molecule as arguments. It can by done by using a reference to an |Atom| object present it ``atoms`` list, but not by passing a number of an atom (its position within ``atoms`` list). Unlike some other tools, PLAMS does not use integer numbers as primary identifiers of atoms. It is done to prevent problems when atoms within a molecule are reordered or some atoms are deleted. References to |Atom| or |Bond| objects can be obtained directly from ``atoms`` or ``bonds`` lists, or with dictionary-like bracket notation::

    >>> mol = Molecule('xyz/Ammonia.xyz')
    >>> mol.guess_bonds()
    >>> print mol
      Atoms:
        1         H      0.942179      0.000000     -0.017370
        2         H     -0.471089      0.815951     -0.017370
        3         N      0.000000      0.000000      0.383210
        4         H     -0.471089     -0.815951     -0.017370
      Bonds:
       (1)--1.0--(3)
       (2)--1.0--(3)
       (3)--1.0--(4)
    >>> at = mol[1]
    >>> print at
             H      0.942179      0.000000     -0.017370
    >>> b = mol[(1,3)]
    >>> print b
    (         H      0.942179      0.000000     -0.017370 )--1.0--(         N      0.000000      0.000000      0.383210 )
    >>> b = mol[(1,4)]
    >>> print b
    None

.. note::

    Numbering of atoms within a molecule starts with 1.


However, if you feel more familiar with identifying atoms by natural numbers, you can use :meth:`set_atoms_id` to equip each atom of the molecule with ``id`` attribute equal to atom's position within ``atoms`` list. This method can also be helpful to track changes in your molecule during tasks that can reorder atoms.

Member Function Documentation

◆ __add__()

def qmworks.plams.core.basemol.Molecule.__add__ (   self,
  other 
)
Create a new molecule that is a sum of this molecule and *other*::

>>> newmol = mol1 + mol2

The new molecule has atoms, bonds and all other elements distinct from both components. ``properties`` of ``newmol`` are ``properties`` of ``mol1`` :meth:`soft_updated<scm.plams.settings.Settings.soft_update>` with ``properties`` of ``mol2``.
Here is the call graph for this function:

◆ __getitem__()

def qmworks.plams.core.basemol.Molecule.__getitem__ (   self,
  key 
)
Bracket notation can be used to access atoms or bonds directly.

If *key* is a single int (``mymol[i]``), return i-th atom of this molecule. If *key* is a pair of ints (``mymol[(i,j)]``), return bond between i-th and j-th atom (``None`` if such a bond does not exist).

This method is read only (things like ``mymol[3] = Atom(...)`` are forbidden). Numbering of atoms withing a molecule starts with 1.
Here is the call graph for this function:

◆ __iadd__()

def qmworks.plams.core.basemol.Molecule.__iadd__ (   self,
  other 
)
Add *other* molecule to this one::

>>> protein += water

All atoms and bonds present in *other* are copied and copies are added to this molecule. ``properties`` of this molecule are :meth:`soft_updated<scm.plams.settings.Settings.soft_update>` with ``properties`` of *other*.
Here is the call graph for this function:

◆ __iter__()

def qmworks.plams.core.basemol.Molecule.__iter__ (   self)
Iterate over atoms.

◆ __len__()

def qmworks.plams.core.basemol.Molecule.__len__ (   self)
Length of a molecule is the number of atoms.

◆ __str__()

def qmworks.plams.core.basemol.Molecule.__str__ (   self)
Return string representation of this molecule.

Information about atoms are printed in ``xyz`` format fashion -- each atom in a separate, enumerated line. Then, if the molecule contains any bonds, they are printed. Each bond is printed in a separate line, with information about both atoms and bond order. Example::

  Atoms:
    1         N       0.00000       0.00000       0.38321
    2         H       0.94218       0.00000      -0.01737
    3         H      -0.47109       0.81595      -0.01737
    4         H      -0.47109      -0.81595      -0.01737
  Bonds:
    (1)----1----(2)
    (1)----1----(3)
    (1)----1----(4)

◆ add_atom()

def qmworks.plams.core.basemol.Molecule.add_atom (   self,
  atom,
  adjacent = None 
)
Add new *atom* to this molecule.

*atom* should be an |Atom| instance that does not belong to the molecule. Bonds between the new atom and other atoms of the molecule can be automatically added based on *adjacent* argument. It should be a list describing atoms of the molecule that the new atom is connected to. Each element of *adjacent* list can either be a pair ``(Atom, order)`` to indicate new bond's order (use ``Bond.AR`` for aromatic bonds) or an |Atom| instance (a single bond is inserted in this case).

Example::

    >>> mol = Molecule() #create an empty molecule
    >>> h1 = Atom(symbol='H', coords=(1.0, 0.0, 0.0))
    >>> h2 = Atom(symbol='H', coords=(-1.0, 0.0, 0.0))
    >>> o = Atom(symbol='O', coords=(0.0, 1.0, 0.0))
    >>> mol.add_atom(h1)
    >>> mol.add_atom(h2)
    >>> mol.add_atom(o)
    >>> mol.add_atom(Atom(symbol='C', coords=(0.0, 0.0, 0.0)), adjacent=[h1, h2, (o,2)])
Here is the call graph for this function:
Here is the caller graph for this function:

◆ add_bond()

def qmworks.plams.core.basemol.Molecule.add_bond (   self,
  arg1,
  arg2 = None,
  order = 1 
)
Add new bond to this molecule.

This method can be used in two different ways. You can call it with just one argument being a |Bond| instance (other arguments are then ignored)::

    >>> b = Bond(mol[2], mol[4], order=Bond.AR) #create aromatic bond between 2nd and 4th atom
    >>> mol.add_bond(b)

Other way is to pass two atoms (and possibly bond order) and new |Bond| object will be created automatically::

    >>> mol.add_bond(mol[2], mol[4], order=Bond.AR)

In both cases atoms that are to be bond have to belong to the molecule, otherwise an exception is raised.
Here is the caller graph for this function:

◆ as_dict()

def qmworks.plams.core.basemol.Molecule.as_dict (   self)
The Molecule information is stored in a dict based on the mol
`file format <http://onlinelibrarystatic.wiley.com/marvin/help/FF/19693841.html>`
:returns: JSON object

◆ closest_atom()

def qmworks.plams.core.basemol.Molecule.closest_atom (   self,
  point,
  unit = 'angstrom' 
)
Return the atom of this molecule that is the closest one to some *point* in space.

*point* should be an iterable container of length 3 (for example: tuple, |Atom|, list, numpy array). *unit* describes unit of values stored in *point*.
Here is the caller graph for this function:

◆ copy()

def qmworks.plams.core.basemol.Molecule.copy (   self,
  atoms = None 
)
Return a copy of this molecule. New molecule has atoms, bonds and all other components distinct from original molecule (it is so called "deep copy").

By default the entire molecule is copied. It is also possible to copy only some part of the molecule, indicated by *atoms* argument. It should be a list of atoms that **belong to this molecule**. Only these atoms, together with any bonds between them, are copied and included in the returned molecule.
Here is the caller graph for this function:

◆ delete_all_bonds()

def qmworks.plams.core.basemol.Molecule.delete_all_bonds (   self)
Delete all bonds from the molecule.
Here is the call graph for this function:
Here is the caller graph for this function:

◆ delete_atom()

def qmworks.plams.core.basemol.Molecule.delete_atom (   self,
  atom 
)
Delete *atom* from this molecule.

*atom* should be an |Atom| instance that belongs to the molecule. All bonds containing this atom are removed too.

Examples::

    >>> #delete all hydrogens
    >>> mol = Molecule('protein.pdb')
    >>> hydrogens = [atom for atom in mol if atom.atnum == 1]
    >>> for i in hydrogens: mol.delete_atom(i)

::

    >>> #delete first two atoms
    >>> mol = Molecule('geom.xyz')
    >>> mol.delete_atom(mol[1])
    >>> mol.delete_atom(mol[1]) #since the second atom of original molecule is now the first
Here is the call graph for this function:

◆ delete_bond()

def qmworks.plams.core.basemol.Molecule.delete_bond (   self,
  arg1,
  arg2 = None 
)
Delete bond from this molecule

Just like :meth:`add_bond`, this method accepts either a single argument that is a |Bond| instance, or two arguments being instances of |Atom|. In both cases objects used as arguments have to belong to the molecule.
Here is the call graph for this function:
Here is the caller graph for this function:

◆ distance_to_mol()

def qmworks.plams.core.basemol.Molecule.distance_to_mol (   self,
  other,
  result_unit = 'angstrom',
  return_atoms = False 
)
Calculate the distance between this molecule and some *other* molecule.

The distance is measured as the smallest distance between a pair of atoms, one belonging to each of the molecules. Returned distance is expressed in *result_unit*.

If *return_atoms* is ``False``, only a single number is returned.  If *return_atoms* is ``True``, this method returns a tuple ``(distance, atom1, atom2)`` where ``atom1`` and ``atom2`` are atoms fulfilling the minimal distance, with atom1 belonging to this molecule and atom2 to *other*.

◆ distance_to_point()

def qmworks.plams.core.basemol.Molecule.distance_to_point (   self,
  point,
  unit = 'angstrom',
  result_unit = 'angstrom' 
)
Calculate the distance between this molecule and some *point* in space (distance between *point* and :meth:`closest_atom`).

*point* should be an iterable container of length 3 (for example: tuple, |Atom|, list, numpy array). *unit* describes unit of values stored in *point*. Returned value is expressed in *result_unit*.
Here is the call graph for this function:

◆ find_bond()

def qmworks.plams.core.basemol.Molecule.find_bond (   self,
  atom1,
  atom2 
)
Find and return a bond between *atom1* and *atom2*. Both atoms have to belong to the molecule. If a bond between chosen atoms does not exist, ``None`` is returned.
Here is the caller graph for this function:

◆ from_dict()

def qmworks.plams.core.basemol.Molecule.from_dict (   cls,
  atomBlock,
  bondBlock,
  properties 
)
Generate a new Molecule instance using the data stored
in the dictionary representing the JSON serialized data
:parameter ds: Dict containing the JSON serialized molecule
:type      ds: Dict
:returns: |Molecule|

◆ get_center_of_mass()

def qmworks.plams.core.basemol.Molecule.get_center_of_mass (   self,
  unit = 'angstrom' 
)
Return the center of mass of this molecule (as a tuple). Returned coordinates are expressed in *unit*.

◆ get_formula()

def qmworks.plams.core.basemol.Molecule.get_formula (   self)
Calculate the molecular formula for this molecule.

Returned value is a single string. It contains simple molecular formula (it only includes atom types and total number of atoms of each type).

◆ get_mass()

def qmworks.plams.core.basemol.Molecule.get_mass (   self)
Return mass of the molecule, expressed in atomic units.

◆ guess_bonds()

def qmworks.plams.core.basemol.Molecule.guess_bonds (   self)
Try to guess bonds in the molecule based on types and positions of atoms.

All previously existing bonds are removed. New bonds are generated based on interatomic distances and information about maximal number of bonds for each atom type (``connectors`` property, taken from |PeriodicTable|).

The problem of finding molecular bonds for a given set of atoms in space does not have a general solution, especially considering the fact the chemical bond is itself not a precisely defined concept. For every method, no matter how sophisticated, there will always be corner cases for which the method produces disputable results. Moreover, depending on the context (area of application) the desired solution for a particular geometry may vary. Please do not treat this method as an oracle always providing proper solution. Algorithm used here gives very good results for geometries that are not very far from optimal geometry, especially consisting of lighter atoms. All kinds of organic molecules, including aromatic ones, usually work very well. Problematic results can emerge for transition metal complexes, transition states, incomplete molecules etc.

The algorithm used scales as *n log n* where *n* is the number of atoms.

.. warning::

    This method works reliably only for geometries representing complete molecules. If some atoms are missing (for example, a protein without hydrogens) the resulting set of bonds would usually contain more bonds or bonds with higher order than expected.
Here is the call graph for this function:

◆ neighbors()

def qmworks.plams.core.basemol.Molecule.neighbors (   self,
  atom 
)
Return a list of neighbors of *atom* within this molecule.

*atom* has to belong to the molecule. Returned list follows the same order as ``bonds`` list of *atom*.

◆ read()

def qmworks.plams.core.basemol.Molecule.read (   self,
  filename,
  inputformat = None,
  frame = 1 
)
Read molecular coordinates from file.

*filename* should be a string with a path to the file. If *inputformat* is not ``None``, it should be one of supported formats (keys occurring in class attribute ``_readformat``). Otherwise, format of the file is deduced from file's extension (for files without extension `xyz` format is assumed).

If chosen format allows multiple geometries in a single file, *frame* can be used to pick one of them.
Here is the caller graph for this function:

◆ rotate()

def qmworks.plams.core.basemol.Molecule.rotate (   self,
  matrix 
)
Rotate this molecule according to rotation *matrix*.

*matrix* should be a container with 9 numerical values. It can be a list (tuple, numpy array etc.) listing matrix elements row-wise, either flat (``[1,2,3,4,5,6,7,8,9]``) or in two-level fashion (``[[1,2,3],[4,5,6],[7,8,9]]``).

.. note::

    This method does not check if supplied matrix is a proper rotation matrix.

◆ rotate_bond()

def qmworks.plams.core.basemol.Molecule.rotate_bond (   self,
  bond,
  atom,
  angle,
  unit = 'radian' 
)
Rotate given *bond* by an *angle* expressed in *unit*.

*bond* should be chosen in such a way, that it divides the molecule into two parts (using a bond being part of a ring results in an error). *atom* has to belong to *bond* and is used to pick which "half" of the molecule is rotated. Positive angle denotes counterclockwise rotation (looking along the bond, from the stationary part of the molecule).

◆ separate()

def qmworks.plams.core.basemol.Molecule.separate (   self)
Separate this molecule into connected components.

Returned is a list of new |Molecule| objects (all atoms and bonds are disjoint with original molecule). Each element of this list is identical to one connected component of the base molecule. A connected component is a subset of atoms such that there exists a path (along one or more bonds) between any two atoms.

Example::

    >>> mol = Molecule('/xyz_dimers/NH3-H2O.xyz')
    >>> mol.guess_bonds()
    >>> print(mol)
      Atoms:
1         N     -1.395591     -0.021564      0.000037
2         H     -1.629811      0.961096     -0.106224
3         H     -1.862767     -0.512544     -0.755974
4         H     -1.833547     -0.330770      0.862307
5         O      1.568501      0.105892      0.000005
6         H      0.606736     -0.033962     -0.000628
7         H      1.940519     -0.780005      0.000222
      Bonds:
       (5)--1.0--(7)
       (5)--1.0--(6)
       (1)--1.0--(3)
       (1)--1.0--(4)
       (1)--1.0--(2)
    >>> x = mol.separate()
    >>> for i in x: print(i)
      Atoms:
1         N     -1.395591     -0.021564      0.000037
2         H     -1.629811      0.961096     -0.106224
3         H     -1.862767     -0.512544     -0.755974
4         H     -1.833547     -0.330770      0.862307
      Bonds:
       (1)--1.0--(3)
       (1)--1.0--(4)
       (1)--1.0--(2)

      Atoms:
1         O      1.568501      0.105892      0.000005
2         H      0.606736     -0.033962     -0.000628
3         H      1.940519     -0.780005      0.000222
      Bonds:
       (1)--1.0--(3)
       (1)--1.0--(2)
Here is the call graph for this function:

◆ set_atoms_id()

def qmworks.plams.core.basemol.Molecule.set_atoms_id (   self)
Equip each atom of this molecule with ``id`` attribute equal to its position within ``atoms`` list.
Here is the caller graph for this function:

◆ translate()

def qmworks.plams.core.basemol.Molecule.translate (   self,
  vector,
  unit = 'angstrom' 
)
Move this molecule in space by *vector*, expressed in *unit*.

*vector* should be an iterable container of length 3 (usually tuple, list or numpy array). *unit* describes unit of values stored in *vector*.

◆ unset_atoms_id()

def qmworks.plams.core.basemol.Molecule.unset_atoms_id (   self)
Delete ``id`` attributes of all atoms.
Here is the caller graph for this function:

◆ wrap()

def qmworks.plams.core.basemol.Molecule.wrap (   self,
  length,
  angle = 2*math.pi,
  length_unit = 'angstrom',
  angle_unit = 'radian' 
)
wrap(self, length, angle=2*pi, length_unit='angstrom', angle_unit='radian')

Transform the molecule wrapping its x-axis around z-axis. This method is useful for building nanotubes or molecular wedding rings.

Atomic coordinates are transformed in the following way:
    *   zzzzz coordinates remain untouched
    *   x axis gets wrapped around the circle centered in the origin of new coordinate system. Each segment of x axis of length *length* ends up as an arc of a circle subtended by an angle *angle*. The radius of this circle is R = *length*/*angle*.
    *   part of the plane between the x axis and the line y=R is transformed into the interior of the circle, with line y=R being squashed into a single point - the center of the circle.
    *   part of the plane above line y=R is dropped
    *   part of the plane below x axis is transformed into outside of the circle
    *   transformation is done in such a way that distances along y axis are preserved

Before:

.. image:: _static/wrap.*

After:

.. image:: _static/wrap2.*

◆ write()

def qmworks.plams.core.basemol.Molecule.write (   self,
  filename,
  outputformat = None 
)
Write molecular coordinates to a file.

*filename* should be a string with a path to the file. If *outputformat* is not ``None``, it should be one of supported formats (keys occurring in class attribute ``_writeformat``). Otherwise, format of the file is deduced from file's extension (for files without extension `xyz` format is assumed).
Here is the caller graph for this function:

The documentation for this class was generated from the following file: