Atom
****

class Atom(atnum=0, symbol=None, coords=None, unit='angstrom', bonds=None, mol=None, **other)

   A class representing a single atom in three dimensional space.

   An instance of this class has the following attributes:

   * "atnum" -- atomic number (zero for "dummy atoms")

   * "coords" -- tuple of length 3 storing spatial coordinates

   * "bonds" -- list of bonds (see "Bond") this atom is a part of

   * "mol" -- "Molecule" this atom belongs to

   * "properties" -- "Settings" instance storing all other information
     about this atom (initially it is populated with ***other*)

   The above attributes can be accessed either directly or using one
   of the following properties:

   * "x", "y", "z" -- allow to read or modify each coordinate
     separately

   * "symbol" -- allows to read or write the atomic symbol directly.
     Atomic symbol is not stored as an attribute, instead of that the
     atomic number ("atnum") indicates the type of atom. In fact,
     "symbol" this is just a wrapper around "atnum" that uses
     "PeriodicTable" as a translator:

        >>> a = Atom(atnum=8)
        >>> print(a.symbol)
        O
        >>> a.symbol = 'Ca'
        >>> print(a.atnum)
        20

   * "mass" -- atomic mass, obtained from "PeriodicTable", read only

   * "radius" -- atomic radius, obtained from "PeriodicTable", read
     only

   * "connectors" -- number of connectors, obtained from
     "PeriodicTable", read only

   Note:

     When creating a new atom, its type can be chosen either by
     setting an atomic number or a symbol ("atnum" and "symbol"
     constructor arguments). The "symbol" argument takes precedence --
     if it is supplied, the "atnum" argument is ignored.

   Values stored in "coords" tuple do not necessarily have to be
   numeric, you can also store any string there. This might come handy
   for programs that allow parametrization of coordinates in the input
   file (to enforce some geometry constraints for example):

      >>> a = Atom(symbol='C', coords=(1,2,3))
      >>> print(a)
               C       1.00000       2.00000       3.00000
      >>> a.y = 'param1'
      >>> print(a)
               C       1.00000        param1       3.00000

   However, non-numerical coordinates cannot be used together with
   some methods (for example "distance_to()" or "translate()"). An
   attempt to do this raises an exception.

   Internally, atomic coordinates are always expressed in angstroms.
   Most of methods that read or modify atomic coordinates accept a
   keyword argument "unit" allowing to choose unit in which results
   and/or arguments are expressed (see "Units" for details).
   Throughout the entire code angstrom is the default length unit. If
   you don't specify "unit" parameter in any place of your script, all
   the automatic unit handling described above boils down to
   occasional multiplication/division by 1.0.

   Parameters:
      * **atnum** (*int*) --

      * **symbol** (*str** | **None*) --

      * **coords** (*Sequence**[**ConvertibleToFloat**] **| **None*)
        --

      * **unit** (*str*) --

      * **bonds** (*List**[**Bond**] **| **None*) --

      * **mol** (*Molecule** | **None*) --

      * **other** (*Any*) --

   __init__(atnum=0, symbol=None, coords=None, unit='angstrom', bonds=None, mol=None, **other)

      Parameters:
         * **atnum** (*int*) --

         * **symbol** (*str** | **None*) --

         * **coords** (*Sequence**[**ConvertibleToFloat**] **|
           **None*) --

         * **unit** (*str*) --

         * **bonds** (*List**[**Bond**] **| **None*) --

         * **mol** (*Molecule** | **None*) --

         * **other** (*Any*) --

   str(symbol=True, suffix='', suffix_dict=None, unit='angstrom', space=14, decimal=6)

      Return a string representation of this atom.

      Returned string is a single line (no newline characters) that
      always contains atomic coordinates (and maybe more). Each atomic
      coordinate is printed using *space* characters, with *decimal*
      characters reserved for decimal digits. Coordinates values are
      expressed in *unit*.

      If *symbol* is "True", atomic symbol is added at the beginning
      of the line. If *symbol* is a string, this exact string is
      printed there.

      *suffix* is an arbitrary string that is appended at the end of
      returned line. It can contain identifiers in curly brackets
      (like for example "f={fragment}") that will be replaced by
      values of corresponding keys from *suffix_dict* dictionary. See
      Format String Syntax for details.

      -[ Example ]-

      >>> a = Atom(atnum=6, coords=(1,1.5,2))
      >>> print(a.str())
               C      1.000000      1.500000      2.000000
      >>> print(a.str(unit='bohr'))
               C      1.889726      2.834589      3.779452
      >>> print(a.str(symbol=False))
            1.000000      1.500000      2.000000
      >>> print(a.str(symbol='C2.13'))
           C2.13      1.000000      1.500000      2.000000
      >>> print(a.str(suffix='protein1'))
               C      1.000000      1.500000      2.000000 protein1
      >>> a.properties.info = 'membrane'
      >>> print(a.str(suffix='subsystem={info}', suffix_dict=a.properties))
               C      1.000000      1.500000      2.000000 subsystem=membrane

      Parameters:
         * **symbol** (*bool** | **str*) --

         * **suffix** (*str*) --

         * **suffix_dict** (*Dict** | **None*) --

         * **unit** (*str*) --

         * **space** (*int*) --

         * **decimal** (*int*) --

      Return type:
         str

   __str__()

      Return a string representation of this atom. Simplified version
      of "str()" to work as a magic method.

      Return type:
         str

   __iter__()

      Iteration through atom yields coordinates. Thanks to that
      instances of "Atom" can be passed to any method requiring as an
      argument a point or a vector in 3D space.

      Return type:
         *Iterator*

   translate(vector, unit='angstrom')

      Move this atom 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*.

      This method requires all atomic coordinates to be numerical
      values, "TypeError" is raised otherwise.

      Parameters:
         * **vector** (*Iterable**[**float**]*) --

         * **unit** (*str*) --

      Return type:
         None

   move_to(point, unit='angstrom')

      Move this atom to a given *point* in space, expressed in *unit*.

      *point* should be a container of length 3 (for example: tuple,
      "Atom", list, numpy array). *unit* describes unit of values
      stored in *point*.

      This method requires all atomic coordinates to be numerical
      values, "TypeError" is raised otherwise.

      Parameters:
         * **point** (*Sequence**[**float**]*) --

         * **unit** (*str*) --

      Return type:
         None

   distance_to(point, unit='angstrom', result_unit='angstrom')

      Measure the distance between this atom and *point*.

      *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*.

      This method requires all atomic coordinates to be numerical
      values, "TypeError" is raised otherwise.

      Parameters:
         * **point** (*Iterable**[**float**]*) --

         * **unit** (*str*) --

         * **result_unit** (*str*) --

      Return type:
         float

   vector_to(point, unit='angstrom', result_unit='angstrom')

      Calculate a vector from this atom to *point*.

      *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*.

      This method requires all atomic coordinates to be numerical
      values, "TypeError" is raised otherwise.

      Parameters:
         * **point** (*Iterable**[**float**]*) --

         * **unit** (*str*) --

         * **result_unit** (*str*) --

      Return type:
         *Tuple*[float, float, float]

   angle(point1, point2, point1unit='angstrom', point2unit='angstrom', result_unit='radian')

      Calculate an angle between vectors pointing from this atom to
      *point1* and *point2*.

      *point1* and *point2* should be iterable containers of length 3
      (for example: tuple, "Atom", list, numpy array). Values stored
      in them are expressed in, respectively, *point1unit* and
      *point2unit*. Returned value is expressed in *result_unit*.

      This method requires all atomic coordinates to be numerical
      values, "TypeError" is raised otherwise.

      Parameters:
         * **point1** (*Iterable**[**float**]*) --

         * **point2** (*Iterable**[**float**]*) --

         * **point1unit** (*str*) --

         * **point2unit** (*str*) --

         * **result_unit** (*str*) --

      Return type:
         float

   rotate(matrix)

      Rotate this atom according to a 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 *matrix* is a proper rotation
        matrix.

      Parameters:
         **matrix** (*Iterable**[**float**] **|
         **Iterable**[**Iterable**[**float**]**]*) --

      Return type:
         None

   neighbors()

      Return a list of neighbors of this atom within the molecule. The
      list follows the same order as the "bonds" attribute.

      Return type:
         *List*[*Atom*]


Bond
****

class Bond(atom1=None, atom2=None, order=1, mol=None, **other)

   A class representing a bond between two atoms.

   An instance of this class has the following attributes:

   * "atom1" and "atom2" -- two instances of "Atom" that form this
     bond

   * "order" -- order of the bond. It is either an integer number or
     the floating point value stored in "Bond.AR", indicating an
     aromatic bond

   * "mol" -- "Molecule" this bond belongs to

   * "properties" -- "Settings" instance storing all other
     information about this bond (initially it is populated with
     ***other*)

   Note:

     Newly created bond is **not** added to "atom1.bonds" or
     "atom2.bonds". Storing information about "Bond" in "Atom" is
     relevant only in the context of the whole "Molecule", so this
     information is updated by "add_bond()".

   Parameters:
      * **atom1** (*Atom*) --

      * **atom2** (*Atom*) --

      * **order** (*float*) --

      * **mol** (*Molecule** | **None*) --

      * **other** (*Any*) --

   __init__(atom1=None, atom2=None, order=1, mol=None, **other)

      Parameters:
         * **atom1** (*Atom*) --

         * **atom2** (*Atom*) --

         * **order** (*float*) --

         * **mol** (*Molecule** | **None*) --

         * **other** (*Any*) --

   __str__()

      Return a string representation of this bond.

      Return type:
         str

   __iter__()

      Iterate over bonded atoms ("atom1" first, then "atom2").

      Return type:
         *Iterator*[Atom]

   is_aromatic()

      Check if this bond is aromatic.

      Return type:
         bool

   length(unit='angstrom')

      Return bond length, expressed in *unit*.

      Parameters:
         **unit** (*str*) --

      Return type:
         float

   as_vector(start=None, unit='angstrom')

      Return a vector between two atoms that form this bond. *start*
      can be used to indicate which atom should be the beginning of
      that vector. If not specified, "self.atom1" is used. Returned
      value if a tuple of length 3, expressed in *unit*.

      Parameters:
         * **start** (*Atom** | **None*) --

         * **unit** (*str*) --

      Return type:
         *Tuple*[float, float, float]

   other_end(atom)

      Return the atom on the other end of this bond with respect to
      *atom*. *atom* has to be one of the atoms forming this bond,
      otherwise an exception is raised.

      Parameters:
         **atom** (*Atom*) --

      Return type:
         Atom

   resize(moving_atom, length, unit='angstrom')

      Change the length of this bond to *length* expressed in *unit*
      by moving *moving_atom*.

      *moving_atom* should be one of the atoms that form this bond.
      This atom is moved along the bond axis in such a way that new
      bond length equals *length*. If this bond is a part of a
      "Molecule" the whole part connected to *moving_atom* is moved.

      Note:

        Calling this method on a bond that forms a ring within a
        molecule raises a "MoleculeError".

      Parameters:
         * **moving_atom** (*Atom*) --

         * **length** (*float*) --

         * **unit** (*str*) --

      Return type:
         None

   rotate(moving_atom, angle, unit='radian')

      Rotate part of the molecule containing *moving_atom* along axis
      defined by this bond by an *angle* expressed in *unit*.

      Calling this method makes sense only if this bond is a part of a
      "Molecule". *moving_atom* should be one of the atoms that form
      this bond and it indicates which part of the molecule is
      rotated. A positive value of *angle* denotes counterclockwise
      rotation (when looking along the bond, from the stationary part
      of the molecule).

      Note:

        Calling this method on a bond that forms a ring raises a
        "MoleculeError".

      Parameters:
         * **moving_atom** (*Atom*) --

         * **angle** (*float*) --

         * **unit** (*str*) --

      Return type:
         None
