XYZ trajectory files
********************

class XYZTrajectoryFile(filename=None, mode='r', fileobject=None, ntap=None)

   Class representing an XYZ file containing a molecular trajectory

   An instance of this class has the following attributes:

   * "file_object" -- A Python "file" object, referring to the actual
     XYZ file

   * "position"    -- The frame to which the cursor is currently
     pointing in the XYZ file

   * "mode"        -- Designates whether the file is in read or write
     mode ('r' or 'w')

   * "ntap"        -- The number of atoms in the molecular system
     (needs to be constant throughout)

   * "elements"    -- The elements of the atoms in the system (needs
     to be constant throughout)

   An "XYZTrajectoryFile" object behaves very similar to a regular
   file object. It has read and write methods ("read_next()" and
   "write_next()") that read and write from/to the position of the
   cursor in the "file_object" attribute. If the file is in read mode,
   an additional method "read_frame()" can be used that moves the
   cursor to any frame in the file and reads from there. The amount of
   information stored in memory is kept to a minimum, as only
   information from the current frame is ever stored.

   Reading and writing to and from the files can be done as follows:

      >>> from scm.plams import XYZTrajectoryFile

      >>> xyz = XYZTrajectoryFile('old.xyz')
      >>> mol = xyz.get_plamsmol()

      >>> xyzout = XYZTrajectoryFile('new.xyz',mode='w')

      >>> for i in range(xyz.get_length()) :
      >>>     crd,cell = xyz.read_frame(i,molecule=mol)
      >>>     xyzout.write_next(molecule=mol)

   The above script reads information from the XYZ file "old.xyz" into
   the "Molecule" object "mol" in a step-by-step manner. The
   "Molecule" object is then passed to the "write_next()" method of
   the new "XYZTrajectoryFile" object corresponding to the new xyz
   file "new.xyz".

   The exact same result can also be achieved by iterating over the
   instance as a callable

   >>> xyz = XYZTrajectoryFile('old.xyz')
   >>> mol = xyz.get_plamsmol()

   >>> xyzout = XYZTrajectoryFile('new.xyz',mode='w')

   >>> for crd,cell in xyz(mol) :
   >>>     xyzout.write_next(molecule=mol)

   This procedure requires all coordinate information to be passed to
   and from the "Molecule" object for each frame, which can be time-
   consuming. It is therefore also possible to bypass the "Molecule"
   object when reading through the frames:

      >>> xyz = XYZTrajectoryFile('old.xyz')

      >>> xyzout = XYZTrajectoryFile('new.xyz',mode='w')
      >>> xyzout.set_elements(xyz.get_elements())

      >>> for crd,cell in xyz :
      >>>     xyzout.write_next(coords=crd)
      >>> xyzout.close()

   By default the write mode will create a minimal version of the XYZ
   file, containing only elements and coordinates. Additional
   information can be written to the file by supplying additional
   arguments to the "write_next()" method. The additional keywords
   *step* and *energy* trigger the writing of a remark containing the
   molecule name, the step number, the energy, and the lattice
   vectors.

   >>> mol = Molecule('singleframe.xyz')

   >>> xyzout = XYZTrajectoryFile('new.xyz',mode='w')
   >>> xyzout.set_name('MyMol')

   >>> xyzout.write_next(molecule=mol, step=0, energy=5.)

   __init__(filename=None, mode='r', fileobject=None, ntap=None)

      Initiates an XYZTrajectoryFile object

      * "filename"   -- The path to the XYZ file

      * "mode"       -- The mode in which to open the XYZ file ('r' or
        'w')

      * "fileobject" -- Optionally, a file object can be passed
        instead (filename needs to be set to None)

      * "ntap"       -- If the file is in write mode, the number of
        atoms needs to be passed here

   property style

      Returns the style/formatting of this XYZ file

      Note: Can be one of ["extended", "scm"]

   store_historydata()

      Additional data should be read from/written to file

   set_name(name)

      Sets the name of the system, in case an extensive write is
      requested

      * "name" -- A string containing the name of the molecule

   read_next(molecule=None, read=True)

      Reads coordinates from the current position of the cursor and
      returns it

      * "molecule" -- "Molecule" object in which the new coordinates
        need to be stored

      * "read"     -- If set to False the cursor will move to the next
        frame without reading

   write_next(coords=None, molecule=None, cell=[0.0, 0.0, 0.0], conect=None, historydata=None)

      Write frame to next position in trajectory file

      * "coords"   -- A list or numpy array of ("ntap",3) containing
        the system coordinates

      * "molecule" -- A molecule object to read the molecular data
        from

      * "cell"     -- A set of lattice vectors or cell diameters

      * "conect"   -- A dictionary containing connectivity info (not
        used)

      * "historydata" -- A dictionary containing additional variables
        to be written to the comment line

      The "historydata" dictionary can contain for example:
      ('Step','Energy'), the frame number and the energy respectively

      Note:

        Either "coords" or "molecule" are mandatory arguments

   __call__(molecule=None, read=True)

      Magic method that makes an instance of this class into a
      callable

   _move_cursor_to_append_pos()

      Get file ready to append

   _set_fileobject(filename, fileobject, mode)

      Set the file object and mode (read/write)

   close()

      Close the file

   property connection_table

      Symmetrize the connection table and remove bond orders

   get_elements()

      Get the elements attribute

   get_length()

      Get the number of frames in the file

   get_plamsmol()

      Extracts a PLAMS molecule object from the XYZ trajectory file

   property molecule

      Returns the current molecule, which exists only when the object
      is used as an iterator

   read_frame(frame, molecule=None)

      Reads the relevant info from frame "frame" and returns it, or
      stores it in "molecule"

      * "frame"    -- The frame number to be read from the file

      * "molecule" -- "Molecule" object in which the new coordinates
        need to be stored

   read_last_frame(molecule=None)

      Reads the last frame from the file

   rewind(nframes=None)

      Rewind the file either by "nframes" or to the first frame

      * "nframes" -- The number of frames to rewind

   set_elements(elements)

      Sets the elements attribute (needed in write mode).

      * "elements" -- A list containing the element symbol of each
        atom


XYZ history files
=================

This subsection describes the API of the "XYZHistoryFile" class, which
can read and write the results from simulations with changing numbers
of atoms. The majority of molecular simulations explore a subspace of
the canonical, micro-canonical, or isothermal-isobaric ensembles, in
which the number of atoms N remains constant. However, a Grand
Canonical Monte Carlo simulation is one of the exceptions in which the
number of atoms in the system does change. The "XYZTrajectoryFile"
object cannot read and write the resulting simulation history, and the
derived class "XYZHistoryFile" was developed to handle these atypical
trajectories. While the methods in this class will be slower than the
ones in the parent class, the API is nearly identical. The only
exception is the "write_next()" method, which has an additional
argument "elements".

class XYZHistoryFile(filename=None, mode='r', fileobject=None, ntap=None)

   Class representing an XYZ file containing a molecular simulation
   history with varying numbers of atoms

   An instance of this class has the following attributes:

   * "file_object" -- A Python "file" object, referring to the actual
     XYZ file

   * "position"    -- The frame to which the cursor is currently
     pointing in the XYZ file

   * "mode"        -- Designates whether the file is in read or write
     mode ('r' or 'w')

   * "elements"    -- The elements of the atoms in the system at the
     current frame

   An "XYZHistoryFile" object behaves very similar to a regular file
   object. It has read and write methods ("read_next()" and
   "write_next()") that read and write from/to the position of the
   cursor in the "file_object" attribute. If the file is in read mode,
   an additional method "read_frame()" can be used that moves the
   cursor to any frame in the file and reads from there. The amount of
   information stored in memory is kept to a minimum, as only
   information from the current frame is ever stored.

   Reading and writing to and from the files can be done as follows:

      >>> from scm.plams import XYZHistoryFile

      >>> xyz = XYZHistoryFile('old.xyz')
      >>> mol = xyz.get_plamsmol()

      >>> xyzout = XYZHistoryFile('new.xyz',mode='w')

      >>> for i in range(xyz.get_length()) :
      >>>     crd,cell = xyz.read_frame(i,molecule=mol)
      >>>     xyzout.write_next(molecule=mol)

   The above script reads information from the XYZ file "old.xyz" into
   the "Molecule" object "mol" in a step-by-step manner. The
   "Molecule" object is then passed to the "write_next()" method of
   the new "XYZHistoryFile" object corresponding to the new xyz file
   "new.xyz".

   The exact same result can also be achieved by iterating over the
   instance as a callable

   >>> xyz = XYZHistoryFile('old.xyz')
   >>> mol = xyz.get_plamsmol()

   >>> xyzout = XYZHistoryFile('new.xyz',mode='w')

   >>> for crd,cell in xyz(mol) :
   >>>     xyzout.write_next(molecule=mol)

   This procedure requires all coordinate information to be passed to
   and from the "Molecule" object for each frame, which can be time-
   consuming. It is therefore also possible to bypass the "Molecule"
   object when reading through the frames:

      >>> xyz = XYZHistoryFile('old.xyz')

      >>> xyzout = XYZHistoryFile('new.xyz',mode='w')

      >>> for crd,cell in xyz :
      >>>     xyzout.write_next(coords=crd,elements=xyz.elements)

   By default the write mode will create a minimal version of the XYZ
   file, containing only elements and coordinates. Additional
   information can be written to the file by supplying additional
   arguments to the "write_next()" method. The additional keywords
   *step* and *energy* trigger the writing of a remark containing the
   molecule name, the step number, the energy, and the lattice
   vectors.

   >>> mol = Molecule('singleframe.xyz')

   >>> xyzout = XYZHistoryFile('new.xyz',mode='w')
   >>> xyzout.set_name('MyMol')

   >>> xyzout.write_next(molecule=mol, step=0, energy=5.)

   __init__(filename=None, mode='r', fileobject=None, ntap=None)

      Initiates an XYZHistoryFile object

      * "filename"   -- The path to the XYZ file

      * "mode"       -- The mode in which to open the XYZ file ('r' or
        'w')

      * "fileobject" -- Optionally, a file object can be passed
        instead (filename needs to be set to None)

      * "ntap"       -- If the file is in write mode, the number of
        atoms can be passed here

   write_next(coords=None, molecule=None, elements=None, cell=[0.0, 0.0, 0.0], conect=None, historydata=None)

      Write frame to next position in trajectory file

      * "coords"   -- A list or numpy array of ("ntap",3) containing
        the system coordinates

      * "molecule" -- A molecule object to read the molecular data
        from

      * "elements" -- The element symbols of the atoms in the system

      * "cell"     -- A set of lattice vectors or cell diameters

      * "energy"   -- An energy value to be written to the remark line

      * "conect"   -- A dictionary containing connectivity info (not
        used)

      * "historydata" -- A dictionary containing additional variables
        to be written to the comment line

      The "historydata" dictionary can contain for example:
      ('Step','Energy'), the frame number and the energy respectively

      Note:

        Either "coords" and "elements" or "molecule" are mandatory
        arguments
