AMS driver and engines
**********************

The AMS driver is a new program introduced in the 2018 release that
unifies the way in which different computational engines of the
Amsterdam Modeling Suite are called. You can find more information
about the AMS driver in the corresponding part of the documentation.


Preparing input
===============

Tip:

  Starting with AMS2024, you can also use PISA (Python Input System
  for AMS) to specify the input to AMS.However, almost all PLAMS
  examples still use the input description described on this page.

Note:

  Input files handling in the AMS driver is case insensitive.

The input file for the AMS driver consists of keys and values
organized in blocks and subblocks:

   Task GeometryOptimization

   GeometryOptimization
       Convergence
           Gradients 1.0e-4
       End
   End

   Properties
      NormalModes true
   End

   System
       Atoms
           C       0.00000000       0.00000000       0.00000000
           H       0.63294000      -0.63294000      -0.63294000
           H      -0.63294000       0.63294000      -0.63294000
           H       0.63294000       0.63294000       0.63294000
           H      -0.63294000      -0.63294000       0.63294000
       End
   End

   Engine DFTB
       Model DFTB3
       ResourcesDir DFTB.org/3ob-3-1
   EndEngine

Such a structure can be reflected in a natural way by a multi-level
character of "Settings". The example input file presented above can be
generated by:

   s = Settings()
   #AMS driver input
   s.input.ams.Task = 'GeometryOptimization'
   s.input.ams.GeometryOptimization.Convergence.Gradients = 1.0e-4
   s.input.ams.Properties.NormalModes = 'true'
   #DFTB engine input
   s.input.DFTB.Model = 'DFTB3'
   s.input.DFTB.ResourcesDir = 'DFTB.org/3ob-3-1'

   m = Molecule('methane.xyz')
   j = AMSJob(molecule=m, settings=s)
   j.run()

If an entry is a regular key-value pair it is printed in one line
(like "Task GeometryOptimization" above). If an entry is a nested
"Settings" instance it is printed as a block and entries inside this
instance correspond to the contents of that block.

One of the blocks is special: the engine block. It defines the
computational engine used to perform the task defined in all other
blocks. The contents of the engine block are not processed by the AMS
driver, but rather passed to the corresponding engine instead. Because
of that every AMS input file can be seen as composed of two distinct
parts: the engine input (everything **inside** the engine block) and
the driver input (everything apart from the engine block). That
distinction is reflected by how a "Settings" instance for "AMSJob" is
structured. As we can see, the "input" branch of job settings is
divided into two branches: the "ams" branch for the driver input and
the "DFTB" branch for the engine input.

Note:

  In general, PLAMS will use all the contents of the "ams" branch
  (spelling not case-sensitive) to construct the driver input and the
  contents of every other branch to construct a separate engine block
  with the same name as the branch (like "DFTB" in the example above).
  At present, only applications with a single engine block are
  implemented in the AMS driver, but that will most likely change in
  the near future.

The contents of each branch of "myjob.settings.input" are translated
to a string using the same logic:

* Entries within each block (including the top level) are listed in
  the alphabetical order.

* Both keys and values are kept in their original case.

* Strings used as values can contain spaces and all kinds of special
  characters, including new lines. They are printed in an unchanged
  form in the input file.

* If you need to put a key without any value, you can use "True" or an
  empty string as a value:

     s.input.ams.block.key = True
     s.input.ams.otherkey = ''

     ### translates to:

     block
       key
     end

     otherkey

* If a value of a key is "False" or "None" the key is omitted.

* An empty "Settings" instance produces an empty block:

     s.input.ams.emptyblock = Settings()
     s.input.ams.otherblock #short syntax equivalent to the line above

     ### translates to:

     emptyblock
     end

     otherblock
     end

* More instances of the same key within one block can be achieved by
  using a list of values instead of a single value:

     s.input.ams.constraints.atom = [1,5,4]
     s.input.ams.constraints.block = ['ligand', 'residue']

     ### translates to:

     constraints
       atom 1
       atom 5
       atom 4
       block ligand
       block residue
     end

* Some blocks require (or allow) something to be put in the header
  line, next to the block name. Special key "_h" is helpful in these
  situations:

     s.input.ams.block._h = 'header=very important'
     s.input.ams.block.key1 = 'value1'
     s.input.ams.block.key2 = 'value2'

     ### translates to:

     someblock header=very important
       key1 value1
       key2 value2
     end

* Another kind of special key can be used to override the default
  alphabetical ordering of entries within a block, or just to insert
  arbitrary strings into the block:

     s.input.ams.block._1 = 'entire line that has to be the first line of block'
     s.input.ams.block._2 = 'second line'
     s.input.ams.block._4 = 'I will not be printed'
     s.input.ams.block.key1 = 'value1'
     s.input.ams.block.key2 = 'value2'

     ### translates to:

     block
       entire line that has to be the first line of block
       second line
       key1 value1
       key2 value2
     end

* If a value of a key needs to be a path to some KF file with results
  of a previous AMS calculation, an instance of "AMSJob" or
  "AMSResults" (or directly "KFFile") can be used (see
  "AMSJob.get_input()" for details):

     oldjob = AMSJob(...)
     oldjob.run()
     newjob = AMSJob(...)
     newjob.settings.input.ams.loadsystem.file = oldjob
     newjob.settings.input.ams.loadengine = (oldjob, 'dftb')

     ### translates to:

     loadengine /home/user/plams_workdir/oldjob/dftb.rkf

     loadsystem
       file = /home/user/plams_workdir/oldjob/ams.rkf
     end

* Convert AMS text-style input to a Settings object (this requires
  that the SCM python package is installed):

     text = '''
     Task GeometryOptimization
     Engine DFTB
        Model GFN1-xTB
     EndEngine
     '''

     sett = AMSJob.from_input(text).settings
     print(sett)

     # output:

     input:
           dftb:
                model:       GFN1-xTB
           ams:
                task:        GeometryOptimization

Note:

  The algorithm translating "Settings" contents into an input file
  does not check the correctness of the given data - it simply takes
  keys and values from "Settings" and prints them in the text file.
  Due to that you are not going to be warned if you make a typo, use a
  wrong keyword or improper syntax.


Preparing runscript
===================

Runscripts for the AMS driver are very simple (see
"AMSJob.get_runscript()"). The only adjustable option (apart from
usual "pre", "post", "shebang" and "stdout_redirect" which are common
for all single jobs) is "myjob.settings.runscript.nproc", indicating
the number of parallel processes to run AMS with (like with "-n" flag
or "NSCM" environmental variable).


Molecule handling
=================

There are several ways in which the description of the simulated
system can be supplied to "AMSJob". The most convenient one is simply
by passing a "Molecule" instance:

   mol = Molecule('/path/to/some/file.xyz')
   myjob = AMSJob(name='test', molecule=mol, settings=...)

or:

   mol = Molecule('/path/to/some/file.xyz')
   myjob = AMSJob(...)
   myjob.molecule = mol

Note:

  Instead of passing a "Molecule" object to "AMSJob", you have the
  option to use a Chemical System as well.

A "Molecule" instance stored as the "molecule" attribute is
automatically processed during the input file preparation and printed
in the proper format (see AMS manual for details). Various details of
this process can be adjusted based on attributes of the supplied
"Molecule". If "mol.lattice" is nonempty, the information about
periodicity vectors is printed to the "lattice" subblock of the
"system" block. If the supplied lattice consists of 1 or 2 vectors
that do not follow the convention required by AMS (1D -- vector
aligned with X axis; 2D -- vectors aligned with XY plane) the whole
system is rotated to meet these criteria. If "mol.properties.charge"
exists, it is used as the "charge" key in the "system" block.

Moreover, each "Atom" present in the supplied "Molecule" has its own
"properties" attribute that can be used to adjust the details of the
line generated for this atom in the "atoms" block:

* The atomic symbol is generated based on the atomic number stored in
  the "atnum" attribute of the "Atom". The atomic number of 0
  corresponds to the "dummy atom" for which the symbol is empty.

* If "atom.properties.ghost" exists and is "True", the atomic symbol
  is prefixed with "Gh.".

* If "atom.properties.name" exists, the name is added after the atomic
  symbol, separated by a single dot.

* Leading, trailing and double dots are removed from the atomic
  symbol.

* If "atom.properties.suffix" exists, it is placed at the end of the
  line, after the numerical coordinates (it should ba a string)

Example:

   mol = Molecule('xyz/Ethanol.xyz')
   mol[1].properties.ghost = True
   mol[2].properties.name = 'D'
   mol[3].properties.ghost = True
   mol[3].properties.name = 'T'
   mol[4].properties.atnum = 0
   mol[4].properties.name = 'J.XYZ'
   mol[5].properties.atnum = 0
   mol[5].properties.name = 'J.ASD'
   mol[5].properties.ghost = True
   mol[6].properties.suffix = 'whatever text'
   myjob = AMSJob(molecule=mol)

The corresponding fragment of the input file produced by the above
code:

   system
     atoms
         1      Gh.C       0.01247       0.02254       1.08262
         2       C.D      -0.00894      -0.01624      -0.43421
         3    Gh.H.T      -0.49334       0.93505       1.44716
         4     J.XYZ       1.05522       0.04512       1.44808
         5  Gh.J.ASD      -0.64695      -1.12346       2.54219
         6         H       0.50112      -0.91640      -0.80440   whatever text
         7         H       0.49999       0.86726      -0.84481
         8         H      -1.04310      -0.02739      -0.80544
         9         O      -0.66442      -1.15471       1.56909
     end
   end

Another, more cumbersome way to provide the system information to
"AMSJob" is to manually populate the "system" block in job settings:

   s = Settings()
   s.input.ams.system.atoms._1 = 'H     0.0    0.0     0.0'
   s.input.ams.system.atoms._2 = 'O     1.0    0.0     0.0'
   s.input.ams.system.charge = 1.0
   #other settings adjustments
   myjob = AMSJob(settings=s)

An alternative way of supplying molecular coordinates is to use the
"GeometryFile" key in the "system" block:

   s = Settings()
   s.input.ams.system.geometryfile = '/path/to/some/file.xyz'
   #other settings adjustments
   myjob = AMSJob(settings=s)

Currently only the extended XYZ format is supported.

Finally, one could use the "LoadSystem" top-level key and point to an
existing ".rkf" file with results of some previous calculation:

   s = Settings()
   s.input.loadsystem = '/path/to/some/ams.rkf'
   #other settings adjustments
   myjob = AMSJob(settings=s)


Multiple molecules
------------------

The AMS driver allows multiple occurrences of the "system" block in
the input file. Different "system" blocks are distinguished by their
names defined in the header of the block:

   system protein
       atoms
       ...
       end
   end
   system ligand
       atoms
       ...
       end
   end

The system without such a name is considered the main system.

Multiple systems can be used in "AMSJob" by setting the "molecule"
attribute to a dictionary, instead of a single "Molecule". Such a
dictionary should have strings as keys and "Molecule" instances as
values. The main system should have "''" (an empty string) as a key.

Other methods of providing the contents of the "system" block
mentioned above can also be used to provide multiple "system" blocks.
"myjob.settings.input.ams.system" can be a list containing multiple
"Settings" instances, one for each system. Each such instance can be
have manually filled "atoms" block or use the "geometryfile" key.
Special header "_h" key can be used to set headers and hence names of
different "system" blocks. Multiple instances of the "LoadSystem" key
(also provided as a list, also with "_h" headers) can also be used.

All the methods mentioned above ("molecule" attribute, "GeometryFile",
"LoadSystem", manual "system" block preparation) can be combined in
any configuration. In case of a conflict, the data stored in
"settings.input.ams.system" takes precedence over "molecule". It is,
however, the user's responsibility to make sure that among all the
systems provided there is exactly one main system (without a name).


AMSJob API
==========

class AMSJob(name='plamsjob', molecule=None, settings=None, depend=None)

   A class representing a single computation with AMS driver. The
   corresponding results type is "AMSResults".

   Parameters:
      * **molecule** (*Molecule** | **Dict**[**str**, **Molecule**]
        **| **ChemicalSystem** | **Dict**[**str**, **ChemicalSystem**]
        **| **None*) --

      * **args** (*Any*) --

      * **kwargs** (*Any*) --

   __init__(molecule=None, *args, **kwargs)

      Parameters:
         * **molecule** (*Molecule** | **Dict**[**str**, **Molecule**]
           **| **ChemicalSystem** | **Dict**[**str**,
           **ChemicalSystem**] **| **None*) --

         * **args** (*Any*) --

         * **kwargs** (*Any*) --

   run(jobrunner=None, jobmanager=None, watch=False, **kwargs)

      Run the job using *jobmanager* and *jobrunner* (or defaults, if
      "None").

      If *watch* is set to "True", the contents of the AMS driver
      logfile will be forwarded line by line to the PLAMS logfile (and
      stdout), allowing for an easier monitoring of the running job.
      Not that the forwarding of the AMS driver logfile will make the
      call to this method block until the job's execution has
      finished, even when using a parallel "JobRunner".

      Other keyword arguments (***kwargs*) are stored in "run" branch
      of job's settings.

      Returned value is the "AMSResults" instance associated with this
      job.

      Parameters:
         * **jobrunner** (*JobRunner** | **None*) --

         * **jobmanager** (*JobManager** | **None*) --

         * **watch** (*bool*) --

         * **kwargs** (*Any*) --

      Return type:
         *AMSResults*

   get_input()

      Generate the input file. This method is just a wrapper around
      "_serialize_input()".

      Each instance of "AMSJob" or "AMSResults" present as a value in
      "settings.input" branch is replaced with an absolute path to
      "ams.rkf" file of that job.

      If you need to use a path to some engine specific ".rkf" file
      rather than the main "ams.rkf" file, you can to it by supplying
      a tuple "(x, name)" where "x" is an instance of "AMSJob" or
      "AMSResults" and "name" is a string with the name of the ".rkf"
      file you want. For example, "(myjob, 'dftb')" will transform to
      the absolute path to "dftb.rkf" file in "myjob"'s folder, if
      such a file is present.

      Instances of "KFFile" are replaced with absolute paths to
      corresponding files.

      Return type:
         str

   get_runscript()

      Generate the runscript. Returned string is of the form:

         unset AMS_SWITCH_LOGFILE_AND_STDOUT
         AMS_JOBNAME=jobname AMS_RESULTSDIR=. $AMSBIN/ams [-n nproc] --input=jobname.in [>jobname.out]

      "-n" flag is added if "settings.runscript.nproc" exists.
      "[>jobname.out]" is used based on
      "settings.runscript.stdout_redirect". If
      "settings.runscript.preamble_lines" exists, those lines will be
      added to the runscript verbatim before the execution of AMS. If
      "settings.runscript.postamble_lines" exists, those lines will be
      added to the runscript verbatim after the execution of AMS.

      Return type:
         str

   check()

      Check if "termination status" variable from "General" section of
      main KF file equals "NORMAL TERMINATION".

      Return type:
         bool

   get_errormsg()

      Tries to get an error message for a failed job. This method
      returns "None" for successful jobs.

      Return type:
         str | None

   hash_input()

      Calculate the hash of the input file.

      All instances of "AMSJob" or "AMSResults" present as values in
      "settings.input" branch are replaced with hashes of
      corresponding job's inputs. Instances of "KFFile" are replaced
      with absolute paths to corresponding files.

      Return type:
         str

   get_task()

      Returns the AMS Task from the job's settings. If it does not
      exist, returns None.

      Return type:
         str | None

   classmethod load_external(path, settings=None, molecule=None, finalize=False, fmt='ams')

      Load an external job from *path*.

      In this context an "external job" is an execution of some
      external binary that was not managed by PLAMS, and hence does
      not have a ".dill" file. It can also be used in situations where
      the execution was started with PLAMS, but the Python process was
      terminated before the execution finished, resulting in steps
      9-12 of Running a job not happening.

      All the files produced by your computation should be placed in
      one folder and *path* should be the path to this folder or a
      file in this folder. The name of the folder is used as a job
      name. Input, output, error and runscript files, if present,
      should have names defined in "_filenames" class attribute
      (usually "[jobname].in", "[jobname].out", "[jobname].err" and
      "[jobname].run"). It is not required to supply all these files,
      but in most cases one would like to use at least the output
      file, in order to use methods like "grep_output()" or
      "get_output_chunk()". If *path* is an instance of an AMSJob,
      that instance is returned.

      This method is a class method, so it is called via class object
      and it returns an instance of that class:

         >>> a = AMSJob.load_external(path='some/path/jobname')
         >>> type(a)
         scm.plams.interfaces.adfsuite.ams.AMSJob

      You can supply "Settings" and "Molecule" instances as *settings*
      and *molecule* parameters, they will end up attached to the
      returned job instance. If you don't do this, PLAMS will try to
      recreate them automatically using methods "recreate_settings()"
      and "recreate_molecule()" of the corresponding "Results"
      subclass. If no "Settings" instance is obtained in either way,
      the defaults from "config.job" are copied.

      You can set the *finalize* parameter to "True" if you wish to
      run the whole "_finalize()" on the newly created job. In that
      case PLAMS will perform the usual "check()" to determine the job
      status (*successful* or *failed*), followed by cleaning of the
      job folder (Cleaning job folder), "postrun()" and pickling
      (Pickling). If *finalize* is "False", the status of the returned
      job is *copied*.

      fmt : str
         One of 'ams', 'qe', 'vasp', or 'any'.

         'ams': load a finished AMS job

         'qe': convert a Quantum ESPRESSO .out file to ams.rkf and
         qe.rkf and load from those files

         'vasp': convert a VASP OUTCAR file to ams.rkf and vasp.rkf
         and load from those files (see more below)

         'any': auto-detect the format.

      This method can also be used to convert a finished VASP job to
      an AMSJob. If you supply the path to a folder containing OUTCAR,
      then a subdirectory will be created in this folder called
      AMSJob. In the AMSJob subdirectory, two files will be created:
      ams.rkf and vasp.rkf, that contain some of the results from the
      VASP calculation. If the AMSJob subdirectory already exists, the
      existing ams.rkf and vasp.rkf files will be reused. NOTE: the
      purpose of loading VASP data this way is to let you call for
      example job.results.get_energy() etc., not to run new VASP
      calculations!

      Parameters:
         * **path** (*str*) --

         * **settings** (*Settings** | **None*) --

         * **molecule** (*Molecule** | **None*) --

         * **finalize** (*bool*) --

         * **fmt** (*str*) --

      Return type:
         *AMSJob*

   classmethod from_input(text_input, **kwargs)

      Creates an AMSJob from AMS-style text input. This function
      requires that the SCM Python package is installed (if not, it
      will raise an ImportError).

      text_input : a multi-line string

      Returns: An AMSJob instance

      Example:

         text = '''
         Task GeometryOptimization
         Engine DFTB
             Model GFN1-xTB
         EndEngine
         '''

         job = AMSJob.from_input(text)

      Note:

        If *molecule* is included in the keyword arguments to this
        method, the *text_input* may not contain any System blocks. In
        other words, the molecules to be used either need to come from
        the *text_input*, or the keyword argument, but not both.If
        *settings* is included in the keyword arguments to this
        method, the "Settings" created from the *text_input* will be
        soft updated with the settings from the keyword argument. In
        other word, the *text_input* takes precedence over the
        *settings* keyword argument.

      Parameters:
         * **text_input** (*str*) --

         * **kwargs** (*Any*) --

      Return type:
         *AMSJob*

   classmethod from_inputfile(filename, heredoc_delimit='eor', **kwargs)

      Construct an "AMSJob" instance from an AMS inputfile or runfile.

      If a runscript is provide than this method will attempt to
      extract the input file based on the heredoc delimiter (see
      *heredoc_delimit*).

      Parameters:
         * **filename** (*str*) --

         * **heredoc_delimit** (*str*) --

         * **kwargs** (*Any*) --

      Return type:
         *AMSJob*

   static settings_to_mol(s)

      Pop the *s.input.ams.system* block from a settings instance and
      convert it into a dictionary of molecules.

      The provided settings should be in the same style as the ones
      produced by the SCM input parser. Dictionary keys are taken from
      the header of each system block. The existing
      *s.input.ams.system* block is removed in the process, assuming
      it was present in the first place.

      Parameters:
         **s** (*Settings*) --

      Return type:
         *Dict*[str, *Molecule*] | None


AMSResults API
==============

class AMSResults(*args, **kwargs)

   A specialized "Results" subclass for accessing the results of
   "AMSJob".

   Parameters:
      * **args** (*Any*) --

      * **kwargs** (*Any*) --

   collect()

      Collect files present in the job folder. Use parent method from
      "Results", then create an instance of "KFFile" for each ".rkf"
      file present in the job folder. Collect these files in "rkfs"
      dictionary, with keys being filenames without ".rkf" extension.

      The information about ".rkf" files generated by engines is taken
      from the main "ams.rkf" file.

      This method is called automatically during the final part of the
      job execution and there is no need to call it manually.

      Return type:
         None

   refresh()

      Refresh the contents of "files" list.

      Use the parent method from "Results", then look at "KFFile"
      instances present in "rkfs" dictionary and check if they point
      to existing files. If not, try to reinstantiate them with
      current job path (that can happen while loading a pickled job
      after the entire job folder was moved).

      Return type:
         None

   engine_names()

      Return a list of all names of engine specific ".rkf" files. The
      identifier of the main result file ("'ams'") is not present in
      the returned list, only engine specific names are listed.

      Return type:
         *List*[str]

   get_main_engine_name()

      Returns the main engine name.

      For geometry optimizations, this means that it will return the
      engine.rkf file and not any of the GOStep*.rkf files.

      For molecular dynamics, this means that it will return the last
      MDStep rkf.

      Raises ValueError if it cannot determine a unique main engine
      file or if no engine file is present.

      Return type:
         str

   read_hybrid_term_rkf(section, variable, term, file='engine')

      Reads a Hybrid-termX-subengine.rkf file.

      The engine.rkf file contains a section EngineResults with
      Files(1), Files(2) etc. that point to the individual term .rkf
      files.

      This method reads the corresponding individual term .rkf file.

      Example: job.results.read_hybrid_term_rkf("AMSResults",
      "Energy", file="engine", term=1)

      Parameters:
         * **section** (*str*) --

         * **variable** (*str*) --

         * **term** (*int*) --

         * **file** (*str*) --

      Return type:
         TRead

   rkfpath(file='ams')

      Return the absolute path of a chosen ".rkf" file.

      The *file* argument should be the identifier of the file to
      read. It defaults to "'ams'". To access a file called
      "something.rkf" you need to call this function with
      "file='something'". If there exists only one engine results
      ".rkf" file, you can call this function with "file='engine'" to
      access this file.

      Parameters:
         **file** (*str*) --

      Return type:
         str

   readrkf(section, variable, file='ams')

      Read data from *section*/*variable* of a chosen ".rkf" file.

      The *file* argument should be the identifier of the file to
      read. It defaults to "'ams'". To access a file called
      "something.rkf" you need to call this function with
      "file='something'". If there exists only one engine results
      ".rkf" file, you can call this function with "file='engine'" to
      access this file.

      The type of the returned value depends on the type of *variable*
      defined inside KF file. It can be: single int, list of ints,
      single float, list of floats, single boolean, list of booleans
      or string.

      Note:

        If arguments *section* or *variable* are incorrect (not
        present in the chosen file), the returned value is "None".
        Please mind the fact that KF files are case sensitive.

      Parameters:
         * **section** (*str*) --

         * **variable** (*str*) --

         * **file** (*str*) --

      Return type:
         TRead

   read_rkf_section(section, file='ams')

      Return a dictionary with all variables from a given *section* of
      a chosen ".rkf" file.

      The *file* argument should be the identifier of the file to
      read. It defaults to "'ams'". To access a file called
      "something.rkf" you need to call this function with
      "file='something'". If there exists only one engine results
      ".rkf" file, you can call this function with "file='engine'" to
      access this file.

      Note:

        If *section* is not present in the chosen file, the returned
        value is an empty dictionary. Please mind the fact that KF
        files are case sensitive.

      Parameters:
         * **section** (*str*) --

         * **file** (*str*) --

      Return type:
         *Dict*[str, TRead]

   get_rkf_skeleton(file='ams')

      Return a dictionary with the structure of a chosen ".rkf" file.
      Each key corresponds to a section name with the value being a
      set of variable names present in that section.

      The *file* argument should be the identifier of the file to
      read. It defaults to "'ams'". To access a file called
      "something.rkf" you need to call this function with
      "file='something'". If there exists only one engine results
      ".rkf" file, you can call this function with "file='engine'" to
      access this file.

      Parameters:
         **file** (*str*) --

      Return type:
         *Dict*[str, *Set*[str]]

   get_molecule(section, file='ams')

      Return a "Molecule" instance stored in a given *section* of a
      chosen ".rkf" file.

      The *file* argument should be the identifier of the file to
      read. It defaults to "'ams'". To access a file called
      "something.rkf" you need to call this function with
      "file='something'". If there exists only one engine results
      ".rkf" file, you can call this function with "file='engine'" to
      access this file.

      All data used by this method is taken from the chosen ".rkf"
      file. The "molecule" attribute of the corresponding job is
      ignored.

      Parameters:
         * **section** (*str*) --

         * **file** (*str*) --

      Return type:
         *Molecule*

   get_system(section, file='ams')

      Return a "ChemicalSystem" instance stored in a given *section*
      of a chosen ".rkf" file.

      The *file* argument should be the identifier of the file to
      read. It defaults to "'ams'". To access a file called
      "something.rkf" you need to call this function with
      "file='something'". If there exists only one engine results
      ".rkf" file, you can call this function with "file='engine'" to
      access this file.

      All data used by this method is taken from the chosen ".rkf"
      file. The "molecule" attribute of the corresponding job is
      ignored.

      Note that "ChemicalSystem" is only available within AMS python.
      If unavailable, the call will raise an error.

      Parameters:
         * **section** (*str*) --

         * **file** (*str*) --

      Return type:
         *ChemicalSystem*

   get_input_molecule()

      Return a "Molecule" instance with the initial coordinates.

      All data used by this method is taken from "ams.rkf" file. The
      "molecule" attribute of the corresponding job is ignored.

      Return type:
         *Molecule*

   get_input_system()

      Return a "ChemicalSystem" instance with the initial coordinates.

      All data used by this method is taken from "ams.rkf" file. The
      "molecule" attribute of the corresponding job is ignored.

      Note that "ChemicalSystem" is only available within AMS python.
      If unavailable, the call will raise an error.

      Return type:
         *ChemicalSystem*

   get_input_molecules()

      Return a dictionary mapping the name strings from the AMS input
      file to "Molecule" instances.

      The main molecule (aka the one that did not have a string in the
      block header in the input file) will be returned under the key
      of the empty string "". All data used by this method is taken
      from "ams.rkf" file. The "molecule" attribute of the
      corresponding job is ignored.

      Return type:
         *Dict*[str, *Molecule*]

   get_main_molecule()

      Return a "Molecule" instance with the final coordinates.

      All data used by this method is taken from "ams.rkf" file. The
      "molecule" attribute of the corresponding job is ignored.

      Return type:
         *Molecule*

   get_main_system()

      Return a "ChemicalSystem" instance with the final coordinates.

      All data used by this method is taken from "ams.rkf" file. The
      "molecule" attribute of the corresponding job is ignored.

      Note that "ChemicalSystem" is only available within AMS python.
      If unavailable, the call will raise an error.

      Return type:
         *ChemicalSystem*

   get_main_ase_atoms(get_results=False)

      Return an ase.Atoms instance with the final coordinates.

      An alternative is to call toASE(results.get_main_molecule()) to
      convert a Molecule to ASE Atoms.

      get_results : bool
         If True, the returned Atoms will have a SinglePointCalculator
         as their calculator, allowing you to call
         .get_potential_energy(), .get_forces(), and .get_stress() on
         the returned Atoms object.

      Parameters:
         **get_results** (*bool*) --

      Return type:
         AseAtoms

   get_history_molecule(step)

      Return a "Molecule" instance with coordinates taken from a
      particular *step* in the "History" section of "ams.rkf" file.

      All data used by this method is taken from "ams.rkf" file. The
      "molecule" attribute of the corresponding job is ignored.

      Parameters:
         **step** (*int*) --

      Return type:
         *Molecule* | None

   is_valid_stepnumber(main, step)

      Check if the requested step number is in the results file

      Parameters:
         * **main** (*KFFile*) --

         * **step** (*int*) --

      Return type:
         bool

   get_system_version(main, step)

      Determine which Molecule version is requested

      Parameters:
         * **main** (*KFFile*) --

         * **step** (*int*) --

      Return type:
         int | None

   get_history_variables(history_section='History')

      Return a set of keynames stored in the specified history section
      of the "ams.rkf" file.

      The *history_section argument should be a string representing
      the name of the history section (``History`` or ``MDHistory``)*

      Parameters:
         **history_section** (*str*) --

      Return type:
         *Set*[str] | None

   get_history_length(history_section='History')

      Returns the number of entries (nEntries) in the history section
      on the ams.rkf file.

      Parameters:
         **history_section** (*str*) --

      Return type:
         int

   get_history_property(varname, history_section='History')

      Return the values of *varname* in the history section
      *history_section*.

      Parameters:
         * **varname** (*str*) --

         * **history_section** (*str*) --

      Return type:
         *List* | None

   get_property_at_step(step, varname, history_section='History')

      Return the value of *varname* in the history section
      *history_section at step *step*.

      Parameters:
         * **step** (*int*) --

         * **varname** (*str*) --

         * **history_section** (*str*) --

      Return type:
         TRead | None

   get_atomic_temperatures_at_step(step, history_section='MDHistory')

      Get all the atomic temperatures for step *step*

      Note: Numbering of steps starts at 1

      Parameters:
         * **step** (*int*) --

         * **history_section** (*str*) --

      Return type:
         *ndarray* | None

   get_band_structure(bands=None, unit='hartree', only_high_symmetry_points=False)

      Extracts the electronic band structure from DFTB, BAND or
      QuantumEspresso calculations. The returned data can be plotted
      with "plot_band_structure".

      Note: for unrestricted calculations bands 0, 2, 4, ... are spin-
      up and bands 1, 3, 5, ... are spin-down.

      Returns: "x", "y_spin_up", "y_spin_down", "labels",
      "fermi_energy"

      "x": 1D array of float

      "y_spin_up": 2D array of shape (len(x), len(bands)). Every
      column is a separate band. In units of "unit"

      "y_spin_down": 2D array of shape (len(x), len(bands)). Every
      column is a separate band. In units of "unit". If the
      calculation was restricted this is identical to y_spin_up.

      "labels": 1D list of str of length len(x). If a point is not a
      high-symmetry point then the label is an empty string.

      "fermi_energy": float. The Fermi energy (in units of "unit").

      Arguments below.

      bands: list of int or None
         If None, all bands are returned. Note: the band indices start
         with 0.

      unit: str
         Unit of the returned band energies

      only_high_symmetry_points: bool
         Return only the first point of each edge.

      Parameters:
         * **bands** (*Sequence**[**int**] **| **None*) --

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

         * **only_high_symmetry_points** (*bool*) --

      Return type:
         *Tuple*[*ndarray*, *ndarray*, *ndarray*, *List*[str], float]

   get_phonons_dos(unit='hartree')

      Extracts the phonons density of states (DOS) as a function of
      the energy from a phonons calculation. The returned data can be
      plotted with "plot_phonons_dos".

      Returns: "energy", "total_dos", "dos_per_atom",
      "dos_per_species"

      "energy": 1D array of float.

      "total_dos": 1D array of float.

      "dos_per_atom": List of 1D float arrays.

      "dos_per_species": Dictionary of 1D float arrays. Keys are the
      species.

      Arguments below.

      unit: str
         Unit of the returned energy. All DOS are returned in units of
         inverse of "unit".

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

      Return type:
         *Tuple*[*ndarray*, *ndarray*, *Dict*[str, *ndarray*],
         *Dict*[str, *ndarray*]]

   get_phonons_band_structure(bands=None, unit='hartree', only_high_symmetry_points=False)

      Extracts the electronic band structure from DFTB, BAND or
      QuantumEspresso calculations. The returned data can be plotted
      with "plot_phonons_band_structure".

      Returns: "x", "y", "labels"

      "x": 1D array of float

      "y": 2D array of shape (len(x), len(bands)). Every column is a
      separate band. In units of "unit"

      "labels": 1D list of str of length len(x). If a point is not a
      high-symmetry point then the label is an empty string.

      Arguments below.

      bands: list of int or None
         If None, all bands are returned. Note: the band indices start
         with 0.

      unit: str
         Unit of the returned band energies

      only_high_symmetry_points: bool
         Return only the first point of each edge.

      Parameters:
         * **bands** (*Sequence**[**int**] **| **None*) --

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

         * **only_high_symmetry_points** (*bool*) --

      Return type:
         *Tuple*[*ndarray*, *ndarray*, *ndarray*, *List*[str], float]

   get_phonons_thermodynamic_properties(temperature_unit='K', properties_unit=['hartree', 'kB'])

      Extracts the thermodynamic properties as a function of the
      temperature from a phonons calculations. The returned data can
      be plotted with "plot_phonons_thermodynamic_properties".

      Returns: "temperature", "properties", "units"

      "temperature": 1D array of float. In units of "temperature_unit"

      "properties": Dictionary of 1D float arrays in units of
      "properties_unit". Available keys: "Internal Energy", "Free
      Energy", "Specific Heat", "Entropy".

      "units": Dictionary of str. Selected units for properties.
      Available keys: "Internal Energy", "Free Energy", "Specific
      Heat", "Entropy".

      Arguments below.

      temperature_unit: str
         Unit of the returned temperature

      property_unit: List[str]
         Unit of the returned properties. The first element,
         representing units for 'Internal Energy' and 'Free Energy',
         accepts any standard energy unit. The second element,
         specifying units for 'Specific Heat' and 'Entropy', accepts
         kB, J/K, J/K/mol, eV/K, or eV/K/mol. kB means boltzman
         constant units.

      Parameters:
         * **temperature_unit** (*str*) --

         * **properties_unit** (*List**[**str**]*) --

      Return type:
         *Tuple*[*ndarray*, *Dict*[str, *ndarray*], *Dict*[str, str]]

   get_engine_results(engine=None)

      Return a dictionary with contents of "AMSResults" section from
      an engine results ".rkf" file.

      The *engine* argument should be the identifier of the file you
      wish to read. To access a file called "something.rkf" you need
      to call this function with "engine='something'". The *engine*
      argument can be omitted if there's only one engine results file
      in the job folder.

      Parameters:
         **engine** (*str** | **None*) --

      Return type:
         *Dict*

   get_engine_properties(engine=None)

      Return a dictionary with all the entries from "Properties"
      section from an engine results ".rkf" file.

      The *engine* argument should be the identifier of the file you
      wish to read. To access a file called "something.rkf" you need
      to call this function with "engine='something'". The *engine*
      argument can be omitted if there's only one engine results file
      in the job folder.

      Parameters:
         **engine** (*str** | **None*) --

      Return type:
         *Dict*

   get_energy(unit='hartree', engine=None)

      Return final energy, expressed in *unit*. The final energy is
      found in AMSResults%Energy of the engine rkf file. You can find
      the meaning of final energy in the engine documentation.

      The *engine* argument should be the identifier of the file you
      wish to read. To access a file called "something.rkf" you need
      to call this function with "engine='something'". The *engine*
      argument can be omitted if there's only one engine results file
      in the job folder.

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

         * **engine** (*str** | **None*) --

      Return type:
         float

   get_energy_uncertainty(unit='hartree', engine=None)

      Return final energy uncertainty, expressed in *unit*. The final
      energy is found in AMSResults%EnergyU of the engine rkf file.
      You can find the meaning of final energy uncertainty in the
      engine documentation.

      The *engine* argument should be the identifier of the file you
      wish to read. To access a file called "something.rkf" you need
      to call this function with "engine='something'". The *engine*
      argument can be omitted if there's only one engine results file
      in the job folder.

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

         * **engine** (*str** | **None*) --

      Return type:
         float

   get_gradients(energy_unit='hartree', dist_unit='bohr', engine=None)

      Return the gradients of the final energy, expressed in
      *energy_unit* / *dist_unit*.

      The *engine* argument should be the identifier of the file you
      wish to read. To access a file called "something.rkf" you need
      to call this function with "engine='something'". The *engine*
      argument can be omitted if there's only one engine results file
      in the job folder.

      Parameters:
         * **energy_unit** (*str*) --

         * **dist_unit** (*str*) --

         * **engine** (*str** | **None*) --

      Return type:
         *ndarray*

   get_gradients_uncertainty(energy_unit='hartree', dist_unit='bohr', engine=None)

      Return the uncertainty of the gradients of the final energy,
      expressed in *energy_unit* / *dist_unit*.

      The *engine* argument should be the identifier of the file you
      wish to read. To access a file called "something.rkf" you need
      to call this function with "engine='something'". The *engine*
      argument can be omitted if there's only one engine results file
      in the job folder.

      Parameters:
         * **energy_unit** (*str*) --

         * **dist_unit** (*str*) --

         * **engine** (*str** | **None*) --

      Return type:
         *ndarray*

   get_gradients_magnitude_uncertainty(energy_unit='hartree', dist_unit='bohr', engine=None)

      Return the uncertainty of the magnitude of the gradients of the
      final energy, expressed in *energy_unit* / *dist_unit*.

      This is computed using error propagation. The *engine* argument
      should be the identifier of the file you wish to read. To access
      a file called "something.rkf" you need to call this function
      with "engine='something'". The *engine* argument can be omitted
      if there's only one engine results file in the job folder.

      Parameters:
         * **energy_unit** (*str*) --

         * **dist_unit** (*str*) --

         * **engine** (*str** | **None*) --

      Return type:
         *ndarray*

   get_stresstensor(engine=None)

      Return the final stress tensor, expressed in atomic units.

      The *engine* argument should be the identifier of the file you
      wish to read. To access a file called "something.rkf" you need
      to call this function with "engine='something'". The *engine*
      argument can be omitted if there's only one engine results file
      in the job folder.

      Parameters:
         **engine** (*str** | **None*) --

      Return type:
         *ndarray*

   get_hessian(engine=None)

      Return the Hessian matrix, i.e. the second derivative of the
      total energy with respect to the nuclear coordinates, expressed
      in atomic units.

      The *engine* argument should be the identifier of the file you
      wish to read. To access a file called "something.rkf" you need
      to call this function with "engine='something'". The *engine*
      argument can be omitted if there's only one engine results file
      in the job folder.

      Parameters:
         **engine** (*str** | **None*) --

      Return type:
         *ndarray*

   get_elastictensor(engine=None)

      Return the elastic tensor, expressed in atomic units.

      The *engine* argument should be the identifier of the file you
      wish to read. To access a file called "something.rkf" you need
      to call this function with "engine='something'". The *engine*
      argument can be omitted if there's only one engine results file
      in the job folder.

      Parameters:
         **engine** (*str** | **None*) --

      Return type:
         *ndarray*

   get_frequencies(unit='cm^-1', engine=None)

      Return a numpy array of vibrational frequencies, expressed in
      *unit*.

      The *engine* argument should be the identifier of the file you
      wish to read. To access a file called "something.rkf" you need
      to call this function with "engine='something'". The *engine*
      argument can be omitted if there's only one engine results file
      in the job folder.

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

         * **engine** (*str** | **None*) --

      Return type:
         *ndarray*

   get_frequency_spectrum(engine=None, broadening_type='gaussian', broadening_width=40, min_x=0, max_x=4000, x_spacing=0.5, post_process=None)

      Return the "frequency spectrum" (i.e. the frequencies broaden
      with intensity equal to 1). Units: frequencies are in cm-1, the
      intensities by the default are in mode counts units but
      post_process is equal to max_to_1 are in arbitrary units.

      The *engine* argument should be the identifier of the file you
      wish to read. To access a file called "something.rkf" you need
      to call this function with "engine='something'". The *engine*
      argument can be omitted if there's only one engine results file
      in the job folder.

      Parameters:
         * **engine** (*str** | **None*) --

         * **broadening_type** (*Literal**[**'gaussian'**,
           **'lorentzian'**]*) --

         * **broadening_width** (*int*) --

         * **min_x** (*int*) --

         * **max_x** (*int*) --

         * **x_spacing** (*float*) --

         * **post_process** (*Literal**[**'max_to_1'**] **| **None*)
           --

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

   get_force_constants(engine=None)

      Return a numpy array of force constants, expressed in atomic
      units (Hartree/bohr^2).

      The *engine* argument should be the identifier of the file you
      wish to read. To access a file called "something.rkf" you need
      to call this function with "engine='something'". The *engine*
      argument can be omitted if there's only one engine results file
      in the job folder.

      Parameters:
         **engine** (*str** | **None*) --

      Return type:
         *ndarray*

   get_pvdos(engine=None)

      Return a numpy array of Partial Vibrational Spectra (PVDOS) with
      shape: (nNormalModes, nAtoms), with values [0,1].

      The *engine* argument should be the identifier of the file you
      wish to read. To access a file called "something.rkf" you need
      to call this function with "engine='something'". The *engine*
      argument can be omitted if there's only one engine results file
      in the job folder.

      Parameters:
         **engine** (*str** | **None*) --

      Return type:
         *ndarray*

   get_reduced_masses(engine=None)

      Return a numpy array of reduced masses, expressed in amu units.
      If mass_weighted_hessian_eigenvectors=True it returns the
      mass_weighted_hessian_eigenvectors.

      The *engine* argument should be the identifier of the file you
      wish to read. To access a file called "something.rkf" you need
      to call this function with "engine='something'". The *engine*
      argument can be omitted if there's only one engine results file
      in the job folder.

      Parameters:
         **engine** (*str** | **None*) --

      Return type:
         *ndarray*

   get_normal_modes(engine=None, mass_weighted_hessian_eigenvectors=False)

      Return a numpy array of normal modes with shape:
      (num_normal_modes, num_atoms, 3), expressed in dimensionless
      units. If mass_weighted_hessian_eigenvectors=True it returns the
      mass_weighted_hessian_eigenvectors.

      The *engine* argument should be the identifier of the file you
      wish to read. To access a file called "something.rkf" you need
      to call this function with "engine='something'". The *engine*
      argument can be omitted if there's only one engine results file
      in the job folder.

      Parameters:
         * **engine** (*str** | **None*) --

         * **mass_weighted_hessian_eigenvectors** (*bool** | **None*)
           --

      Return type:
         *ndarray*

   get_charges(engine=None)

      Return the atomic charges, expressed in atomic units.

      The *engine* argument should be the identifier of the file you
      wish to read. To access a file called "something.rkf" you need
      to call this function with "engine='something'". The *engine*
      argument can be omitted if there's only one engine results file
      in the job folder.

      Parameters:
         **engine** (*str** | **None*) --

      Return type:
         *ndarray*

   get_atom_types(engine=None)

      Return the atomic types, for each atom in the system.

      The *engine* argument should be the identifier of the file you
      wish to read. To access a file called "something.rkf" you need
      to call this function with "engine='something'". The *engine*
      argument can be omitted if there's only one engine results file
      in the job folder.

      Parameters:
         **engine** (*str** | **None*) --

      Return type:
         *List*[str]

   get_dipolemoment(engine=None)

      Return the electric dipole moment, expressed in atomic units.

      The *engine* argument should be the identifier of the file you
      wish to read. To access a file called "something.rkf" you need
      to call this function with "engine='something'". The *engine*
      argument can be omitted if there's only one engine results file
      in the job folder.

      Parameters:
         **engine** (*str** | **None*) --

      Return type:
         *ndarray*

   get_dipolegradients(engine=None)

      Return the nuclear gradients of the electric dipole moment,
      expressed in atomic units. This is a (3*numAtoms x 3) matrix.

      The *engine* argument should be the identifier of the file you
      wish to read. To access a file called "something.rkf" you need
      to call this function with "engine='something'". The *engine*
      argument can be omitted if there's only one engine results file
      in the job folder.

      Parameters:
         **engine** (*str** | **None*) --

      Return type:
         *ndarray*

   get_polarizability(engine=None)

      Return the polarizability, expressed in atomic units
      [(e*bohr)^2/hartree]. This is a (3 x 3) matrix.

      The *engine* argument should be the identifier of the file you
      wish to read. To access a file called "something.rkf" you need
      to call this function with "engine='something'". The *engine*
      argument can be omitted if there's only one engine results file
      in the job folder.

      Parameters:
         **engine** (*str** | **None*) --

      Return type:
         *ndarray*

   get_zero_point_energy(unit='hartree', engine=None)

      Return zero point energy, expressed in *unit*. The zero point
      energy is found in Vibrations%ZeroPointEnergy of the engine rkf
      file.

      The *engine* argument should be the identifier of the file you
      wish to read. To access a file called "something.rkf" you need
      to call this function with "engine='something'". The *engine*
      argument can be omitted if there's only one engine results file
      in the job folder.

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

         * **engine** (*str** | **None*) --

      Return type:
         float

   get_ir_intensities(engine=None)

      Return the IR intensities in km/mol unit (kilometers/mol).

      The *engine* argument should be the identifier of the file you
      wish to read. To access a file called "something.rkf" you need
      to call this function with "engine='something'". The *engine*
      argument can be omitted if there's only one engine results file
      in the job folder.

      Parameters:
         **engine** (*str** | **None*) --

      Return type:
         *ndarray*

   get_raman_intensities(engine=None)

      Return the Raman intensities in Angstrom^4/amu unit.

      The *engine* argument should be the identifier of the file you
      wish to read. To access a file called "something.rkf" you need
      to call this function with "engine='something'". The *engine*
      argument can be omitted if there's only one engine results file
      in the job folder.

      Parameters:
         **engine** (*str** | **None*) --

      Return type:
         *ndarray*

   get_vcd_rotational_strength(engine=None)

      Return the vibrational rotational strengths in 10^(-44) esu^2
      cm^2

      The *engine* argument should be the identifier of the file you
      wish to read. To access a file called "something.rkf" you need
      to call this function with "engine='something'". The *engine*
      argument can be omitted if there's only one engine results file
      in the job folder.

      Parameters:
         **engine** (*str** | **None*) --

      Return type:
         *ndarray*

   get_ir_spectrum(engine=None, broadening_type='gaussian', broadening_width=40, min_x=0, max_x=4000, x_spacing=0.5, post_process=None)

      Return the IR spectrum. Units: frequencies are in cm-1, the
      intensities by the default are in km/mol units (kilometers/mol)
      but if post_process is all_intensities_to_1 the units are in
      modes counts otherwise if equal to max_to_1 are in arbitrary
      units.

      The *engine* argument should be the identifier of the file you
      wish to read. To access a file called "something.rkf" you need
      to call this function with "engine='something'". The *engine*
      argument can be omitted if there's only one engine results file
      in the job folder.

      Parameters:
         * **engine** (*str** | **None*) --

         * **broadening_type** (*Literal**[**'gaussian'**,
           **'lorentzian'**]*) --

         * **broadening_width** (*int*) --

         * **min_x** (*int*) --

         * **max_x** (*int*) --

         * **x_spacing** (*float*) --

         * **post_process** (*Literal**[**'all_intensities_to_1'**,
           **'max_to_1'**] **| **None*) --

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

   get_raman_spectrum(engine=None, broadening_type='gaussian', broadening_width=40, min_x=0, max_x=4000, x_spacing=0.5, post_process=None)

      Return the Raman spectrum. Units: frequencies are in cm-1, the
      intensities by the default are in Angstrom^4/amu units but if
      post_process is all_intensities_to_1 the units are in modes
      counts otherwise if equal to max_to_1 are in arbitrary units.

      The *engine* argument should be the identifier of the file you
      wish to read. To access a file called "something.rkf" you need
      to call this function with "engine='something'". The *engine*
      argument can be omitted if there's only one engine results file
      in the job folder.

      Parameters:
         * **engine** (*str** | **None*) --

         * **broadening_type** (*Literal**[**'gaussian'**,
           **'lorentzian'**]*) --

         * **broadening_width** (*int*) --

         * **min_x** (*int*) --

         * **max_x** (*int*) --

         * **x_spacing** (*float*) --

         * **post_process** (*Literal**[**'all_intensities_to_1'**,
           **'max_to_1'**] **| **None*) --

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

   get_vcd_spectrum(engine=None, broadening_type='gaussian', broadening_width=40, min_x=0, max_x=4000, x_spacing=0.5, post_process=None)

      Return the VCD spectrum in terms of rotatory strength. Units:
      frequencies are in cm-1, the intensities by the default are in
      10^(-44) esu^2 cm^2 but if post_process is all_intensities_to_1
      the units are in modes counts otherwise if equal to max_to_1 are
      in arbitrary units.

      The *engine* argument should be the identifier of the file you
      wish to read. To access a file called "something.rkf" you need
      to call this function with "engine='something'". The *engine*
      argument can be omitted if there's only one engine results file
      in the job folder.

      Parameters:
         * **engine** (*str** | **None*) --

         * **broadening_type** (*Literal**[**'gaussian'**,
           **'lorentzian'**]*) --

         * **broadening_width** (*int*) --

         * **min_x** (*int*) --

         * **max_x** (*int*) --

         * **x_spacing** (*float*) --

         * **post_process** (*Literal**[**'all_intensities_to_1'**,
           **'max_to_1'**] **| **None*) --

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

   get_n_spin(engine=None)

      n_spin is 1 in case of spin-restricted or spin-orbit coupling
      calculations, and 2 in case of spin-unrestricted calculations

      The *engine* argument should be the identifier of the file you
      wish to read. To access a file called "something.rkf" you need
      to call this function with "engine='something'". The *engine*
      argument can be omitted if there's only one engine results file
      in the job folder.

      Parameters:
         **engine** (*str** | **None*) --

      Return type:
         int

   get_orbital_energies(unit='Hartree', engine=None)

      Return the orbital energies in a numpy array of shape
      [nSpin,nOrbitals] (nSpin is 1 in case of spin-restricted or
      spin-orbit coupling and 2 in case of spin unrestricted)

      The *engine* argument should be the identifier of the file you
      wish to read. To access a file called "something.rkf" you need
      to call this function with "engine='something'". The *engine*
      argument can be omitted if there's only one engine results file
      in the job folder.

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

         * **engine** (*str** | **None*) --

      Return type:
         *ndarray*

   get_orbital_occupations(engine=None)

      Return the orbital occupations in a numpy array of shape
      [nSpin,nOrbitals]. For spin restricted calculations, the
      occupations will be between 0 and 2. For spin unrestricted or
      spin-orbit coupling the values will be between 0 and 1.

      The *engine* argument should be the identifier of the file you
      wish to read. To access a file called "something.rkf" you need
      to call this function with "engine='something'". The *engine*
      argument can be omitted if there's only one engine results file
      in the job folder.

      Parameters:
         **engine** (*str** | **None*) --

      Return type:
         *ndarray*

   get_homo_energies(unit='Hartree', engine=None)

      Return the homo energies per spin as a numpy array of size
      [nSpin]. nSpin is 1 in case of spin-restricted or spin-orbit
      coupling and 2 in case of spin unrestricted. See also
      "are_orbitals_fractionally_occupied()".

      The *engine* argument should be the identifier of the file you
      wish to read. To access a file called "something.rkf" you need
      to call this function with "engine='something'". The *engine*
      argument can be omitted if there's only one engine results file
      in the job folder.

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

         * **engine** (*str** | **None*) --

      Return type:
         *ndarray*

   get_lumo_energies(unit='Hartree', engine=None)

      Return the lumo energies per spin as a numpy array of size
      [nSpin]. nSpin is 1 in case of spin-restricted or spin-orbit
      coupling and 2 in case of spin unrestricted. See also
      "are_orbitals_fractionally_occupied()".

      The *engine* argument should be the identifier of the file you
      wish to read. To access a file called "something.rkf" you need
      to call this function with "engine='something'". The *engine*
      argument can be omitted if there's only one engine results file
      in the job folder.

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

         * **engine** (*str** | **None*) --

      Return type:
         *ndarray*

   get_smallest_homo_lumo_gap(unit='Hartree', engine=None)

      Returns a float containing the smallest HOMO-LUMO gap
      irrespective of spin (i.e. min(LUMO) - max(HOMO)). See also
      "are_orbitals_fractionally_occupied()".

      The *engine* argument should be the identifier of the file you
      wish to read. To access a file called "something.rkf" you need
      to call this function with "engine='something'". The *engine*
      argument can be omitted if there's only one engine results file
      in the job folder.

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

         * **engine** (*str** | **None*) --

      Return type:
         float

   are_orbitals_fractionally_occupied(engine=None)

      Returns a boolean indicating whether fractional occupations were
      detected. If that is the case, then the 'HOMO' and 'LUMO' labels
      are not well defined, since the demarcation between 'occupied'
      and 'empty' is somewhat arbitrary. See the AMSDriver
      documentation for more info.

      The *engine* argument should be the identifier of the file you
      wish to read. To access a file called "something.rkf" you need
      to call this function with "engine='something'". The *engine*
      argument can be omitted if there's only one engine results file
      in the job folder.

      Parameters:
         **engine** (*str** | **None*) --

      Return type:
         bool

   get_timings()

      Return a dictionary with timing statistics of the job execution.
      Returned dictionary contains keys cpu, system and elapsed. The
      values are corresponding timings, expressed in seconds.

      Return type:
         *Dict*[str, float]

   get_forcefield_params(engine=None)

      Read all force field data from a forcefield.rkf file into self

      * "filename" -- Name of the RKF file that contains ForceField
        data

      Parameters:
         **engine** (*str** | **None*) --

      Return type:
         *Tuple*[*List*[float], *List*[str], ForceFieldPatch | None]

   get_exit_condition_message()

      Tries to get the error message if the driver was stopped by an
      exit condition. Returns an empty string if no message was
      provided and the driver was not stopped by an exit condition

      Return type:
         str

   get_pesscan_results(molecules=True)

      For PESScan jobs, this functions extracts information about the
      scan coordinates and energies.

      molecules : bool
         Whether to return a Molecule at each PES point.

      Returns a dictionary, with the following keys:

      'RaveledScanCoords': a list of str with the names of the scan
      coordinates: ['sc_1_1','sc_1_2','sc_2_1','sc_2_2',...]

      'nRaveledScanCoords': the length of the previous list

      'RaveledUnits': a list of str with the units: ['bohr', 'bohr',
      'radian', 'bohr', ...]

      'RaveledPESCoords': a nested list with the values of the scan
      coordinates: [[val_1_1_1,val_1_1_2,...],[val_1_2_1,val_1_2_2,..
      .],[val_2_1_1,val_2_1_2,...],[val_2_2_1,val_2_2_2]]

      'ScanCoords': a nested list:
      [['sc_1_1','sc_1_2'],['sc_2_1','sc_2_2'],...]

      'nScanCoords': length of the previous list

      'Units': a nested list with the units: [['bohr',
      'bohr'],['radian', 'bohr'], ...]

      'OrigScanCoords': a list of str in the newline-separated format
      stored on the .rkf file: ['sc_1_1nsc_1_2','sc_2_1nsc_2_2',...]

      'nPESPoints': int, number of PES points

      'PES': list of float, the energies at each PES point

      'Converged': list of bool, whether the geometry optimization at
      each PES point converged

      'Molecules': list of Molecule, the structure at each PES point.
      Only if the property molecules == True

      'HistoryIndices': list of int, the indices (1-based) in the
      History section which correspond to the Molecules and PES.

      'ConstrainedAtoms': set of int, all atom indices (1-based) that
      were part of any PESScan scan coordinates (other constrained
      atoms are not included)

      'Properties': list of dict. The dictionary keys are what can be
      found on the AMSResults section of the engine .rkf file. These
      will only be populated if "CalcPropertiesAtPESPoints" is set to
      Yes when running the PES scan.

      Parameters:
         **molecules** (*bool*) --

      Return type:
         *Dict*[str, *Any*]

   get_neb_results(molecules=True, unit='au')

      Returns a dictionary with results from a NEB calculation.

      molecules : bool
         Whether to include the 'Molecules' key in the return result

      unit : str
         Energy unit for the Energies, LeftBarrier, RightBarrier, and
         ReactionEnergy

      Returns: dict
         'nImages': number of images (excluding end points)

         'nIterations': number of iterations

         'Energies': list of energies (including end points)

         'Climbing': bool, whether climbing image NEB was used

         'LeftBarrier': float, left reaction barrier

         'RightBarrier': float, right reaction barrier

         'ReactionEnergy': float, reaction energy

         'HistoryIndices': list of int, same length as 'Energies',
         contains indices in the History section

         'Molecules': list of Molecule (including end points)

      Parameters:
         * **molecules** (*bool*) --

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

      Return type:
         *Dict*[str, *Any*]

   get_irc_results(molecules=True, unit='au')

      Returns a dictionary with results from an IRC calculation.

      molecules : bool
         Whether to include the 'Molecules' key in the return result

      unit : str
         Energy unit for the Energies, LeftBarrier, RightBarrier

      Returns: dict
         'Energies': list of energies

         'RelativeEnergies': list of energies relative to the first
         point

         'LeftBarrier': float, left reaction barrier

         'RightBarrier': float, right reaction barrier

         'Molecules': list of Molecule (including end points)

         'IRCDirection': IRC direction

         'Energies': Energies (in "unit")

         'PathLength': Path length in angstrom

         'IRCIteration': list of int

         'IRCGradMax': list of float

         'IRCGradRms': list of float

         'ArcLength': list of float

      Parameters:
         * **molecules** (*bool*) --

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

      Return type:
         *Dict*[str, *Any*]

   get_time_step(history_section='MDHistory')

      Returns the time step between adjacent frames (NOT the TimeStep
      in the settings, but Timestep*SamplingFreq) in femtoseconds for
      MD simulation jobs

      Parameters:
         **history_section** (*Literal**[**'BinLog'**,
         **'MDHistory'**]*) --

      Return type:
         int

   get_velocity_acf(start_fs=0, end_fs=None, every_fs=None, max_dt_fs=None, atom_indices=None, x=True, y=True, z=True, normalize=False)

      Calculate the velocity autocorrelation function. Works only for
      trajectories with a constant number of atoms.

      start_fs : float
         Start time in femtoseconds. Defaults to 0 (first frame)

      end_fs : float
         End time in femtoseconds. Defaults to the end of the
         trajectory.

      max_dt_fs : float
         Maximum correlation time in femtoseconds.

      atom_indices: list of int
         Atom indices for which to calculate the velocity
         autocorrelation function. Defaults to all atoms. The atom
         indices start with 1.

      x : bool
         Whether to use the velocities along x

      y : bool
         Whether to use the velocities along y

      z : bool
         Whether to use the velocities along z

      normalize : bool
         Whether to normalize the velocity autocorrelation function so
         that it is 1 at time 0.

      Returns: 2-tuple (times, C)
         "times" is a 1D np array with times in femtoseconds. "C" is a
         1D numpy array with shape (max_dt,) containing the
         autocorrelation function. If not "normalize" then the unit is
         angstrom^2/fs^2.

      Parameters:
         * **start_fs** (*float*) --

         * **end_fs** (*float** | **None*) --

         * **every_fs** (*float** | **None*) --

         * **max_dt_fs** (*float** | **None*) --

         * **atom_indices** (*Sequence**[**int**] **| **None*) --

         * **x** (*bool*) --

         * **y** (*bool*) --

         * **z** (*bool*) --

         * **normalize** (*bool*) --

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

   get_dipole_derivatives_acf(start_fs=0, end_fs=None, every_fs=None, max_dt_fs=None, x=True, y=True, z=True, normalize=False)

      Calculate the velocity autocorrelation function. Works only for
      trajectories with a constant number of atoms.

      start_fs : float
         Start time in femtoseconds. Defaults to 0 (first frame)

      end_fs : float
         End time in femtoseconds. Defaults to the end of the
         trajectory.

      max_dt_fs : float
         Maximum correlation time in femtoseconds.

      x : bool
         Whether to use the velocities along x

      y : bool
         Whether to use the velocities along y

      z : bool
         Whether to use the velocities along z

      normalize : bool
         Whether to normalize the velocity autocorrelation function so
         that it is 1 at time 0.

      Returns: 2-tuple (times, C)
         "times" is a 1D np array with times in femtoseconds. "C" is a
         1D numpy array with shape (max_dt,) containing the
         autocorrelation function. If not "normalize" then the unit is
         angstrom^2/fs^2.

      Parameters:
         * **start_fs** (*float*) --

         * **end_fs** (*float** | **None*) --

         * **every_fs** (*float** | **None*) --

         * **max_dt_fs** (*float** | **None*) --

         * **x** (*bool*) --

         * **y** (*bool*) --

         * **z** (*bool*) --

         * **normalize** (*bool*) --

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

   get_diffusion_coefficient_from_velocity_acf(times=None, acf=None, n_dimensions=3)

      Diffusion coefficient by integration of the velocity
      autocorrelation function

      If "times" or "acf" is None, then a default velocity
      autocorrelation function will be calculated.

      times: 1D numpy array
         1D numpy array with the times

      acf: 1D numpy array
         1D numpy array with the velocity autocorrelation function in
         ang^2/fs^2

      n_dimensions: int
         Number of dimensions that were used to calculate the
         autocorrelation function.

      Returns: 2-tuple (times, diffusion_coefficient)
         "times" are the times in femtoseconds.
         "diffusion_coefficient" is a 1D numpy array with the
         diffusion coefficients in m^2/s.

      Parameters:
         * **times** (*ndarray** | **None*) --

         * **acf** (*ndarray** | **None*) --

         * **n_dimensions** (*int*) --

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

   get_power_spectrum(times=None, acf=None, max_dt_fs=None, max_freq=None, number_of_points=None)

      Calculates a power spectrum from the velocity autocorrelation
      function.

      If "times" or "acf" is None, then a default velocity
      autocorrelation function will be calculated.

      times: 1D numpy array of float
         The "times" returned by "AMSResults.get_velocity_acf()".

      acf: 1D numpy arra of float
         The "vacf" returned by "AMSResults.get_velocity_acf()"

      max_dt_fs : float
         Maximum correlation time in femtoseconds.

      max_freq: float
         Maximum frequency (in cm^-1) of the returned power spectrum.
         Defaults to 5000 cm^-1.

      number_of_points: int
         Number of points in the returned power spectrum. Defaults to
         a number so that the spacing between points is about 1 cm^-1.

      Returns: 2-tuple (frequencies, intensities)
         "frequencies" : Frequencies in cm^-1 (1D numpy array).
         "intensities": Intensities (1D numpy array).

      Parameters:
         * **times** (*ndarray** | **None*) --

         * **acf** (*ndarray** | **None*) --

         * **max_dt_fs** (*float** | **None*) --

         * **max_freq** (*float** | **None*) --

         * **number_of_points** (*int** | **None*) --

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

   get_ir_spectrum_md(times=None, acf=None, max_dt_fs=None, max_freq=5000, number_of_points=None)

      Calculates a ir spectrum from the dipole moment autocorrelation
      function.

      If "times" or "acf" is None, then a default velocity
      autocorrelation function will be calculated.

      times: 1D numpy array of float
         The "times" returned by
         "AMSResults.get_dipole_derivatives_acf()".

      acf: 1D numpy array of float
         The "acf" returned by
         "AMSResults.get_dipole_derivatives_acf()"

      max_dt_fs : float
         Maximum correlation time in femtoseconds.

      max_freq: float
         Maximum frequency (in cm^-1) of the returned power spectrum.
         Defaults to 5000 cm^-1.

      number_of_points: int
         Number of points in the returned power spectrum. Defaults to
         a number so that the spacing between points is about 1 cm^-1.

      Returns: 2-tuple (frequencies, intensities)
         "frequencies" : Frequencies in cm^-1 (1D numpy array).
         "intensities": Intensities (1D numpy array).

      Parameters:
         * **times** (*ndarray** | **None*) --

         * **acf** (*ndarray** | **None*) --

         * **max_dt_fs** (*float** | **None*) --

         * **max_freq** (*float** | **None*) --

         * **number_of_points** (*int** | **None*) --

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

   get_green_kubo_viscosity(start_fs=0, end_fs=None, every_fs=None, max_dt_fs=None, xy=True, yz=True, xz=True, pressuretensor=None)

      Calculates the viscosity using the Green-Kubo relation
      (integrating the off-diagonal pressure tensor autocorrelation
      function).

      start_fs : float
         Start time in femtoseconds. Defaults to 0 (first frame)

      end_fs : float
         End time in femtoseconds. Defaults to the end of the
         trajectory.

      max_dt_fs : float
         Maximum correlation time in femtoseconds.

      xy : bool
         Whether to use the xy off-diagonal elements

      yz : bool
         Whether to use the yz off-diagonal elements

      xz : bool
         Whether to use the xz off-diagonal elements

      pressuretensor : np.array shape (N,6)
         Pressure tensor in atomic units. If not specified, it will be
         read from the ams.rkf file.

      Returns: 2-tuple (times, C)
         "times" is a 1D np array with times in femtoseconds. "C" is a
         1D numpy array with shape (max_dt,) containing the viscosity
         (in mPa*s) integral. It should converge to the viscosity
         values as the time increases.

      Parameters:
         * **start_fs** (*float*) --

         * **end_fs** (*float** | **None*) --

         * **every_fs** (*float** | **None*) --

         * **max_dt_fs** (*float** | **None*) --

         * **xy** (*bool*) --

         * **yz** (*bool*) --

         * **xz** (*bool*) --

         * **pressuretensor** (*ndarray** | **None*) --

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

   get_density_along_axis(axis='z', density_type='mass', start_fs=0, end_fs=None, every_fs=None, bin_width=0.1, atom_indices=None)

      Calculates the density of atoms along a Cartesian coordinate
      axis.

      This only works if the axis is perpendicular to the other two
      axes. The system must be 3D-periodic and the number of atoms
      cannot change during the trajectory.

      axis : str
         'x', 'y', or 'z'

      density_type : str
         'mass' gives the density in g/cm^3. 'number' gives the number
         density in ang^-3, 'histogram' gives the number of atoms per
         frame (so the number depends on the bin size)

      start_fs : float
         Start time in fs

      end_fs : float
         End time in fs

      every_fs : float
         Use data every every_fs timesteps

      bin_width : float
         Bin width of the returned coordinates (in angstrom).

      atom_indices: list of int
         Indices (starting with 1) for the atoms to use for
         calculating the density.

      Returns: 2-tuple (coordinates, density)
         "coordinates" is a 1D array with the coordinates (in
         angstrom). "density" is a 1D array with the densities (in
         g/cm^3 or ang^-3 depending on "density_type").

      Parameters:
         * **axis** (*str*) --

         * **density_type** (*str*) --

         * **start_fs** (*float*) --

         * **end_fs** (*float** | **None*) --

         * **every_fs** (*float** | **None*) --

         * **bin_width** (*float*) --

         * **atom_indices** (*Sequence**[**int**] **| **None*) --

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

   get_work_function_results(energy_unit='hartree', dist_unit='bohr')

      Return a tuple with the results of the work function
      calculation.

      Returns: "coordinate", "planarAverage", "macroscopicAverage",
      "Efermi", "Vbulk", "Vvacuum", "WF".

      "coordinate": 1D array of float.
         The coordinate perpendicular to the surface. In units of
         "dist_unit"

      "planarAverage": 1D array of float.
         The planar average of the electrostatic potential along the
         coordinate. In units of "energy_unit"

      "macroscopicAverage": 1D array of float.
         The macroscopic average of the electrostatic potential along
         the coordinate. In units of "energy_unit"

      "Efermi": float.
         The Fermi energy. In units of "energy_unit".

      "Vbulk": float.
         The average electrostatic potential in the bulk region of the
         material. In units of "energy_unit".

      "Vvacuum": Tuple[float, float].
         The average electrostatic potential in the vacuum region far
         from the surface.

         This material is expected to have vacuum regions on *both*
         sides. Therefore, the potential is evaluated at the furthest
         point from the surface in each vacuum region. The first
         element corresponds to the potential at the furthest point
         from the surface on the left side, and the second element
         corresponds to the potential at the furthest point from the
         surface on the right side. In units of "energy_unit".

         Note: If the calculation uses a dipole correction with the QE
         engine (see *QuantumEspresso%Control%tefield* parameter in
         the QE engine), the potential is evaluated right before the
         x-coordinate where the dipole correction is applied.

      "WF": Tuple[float, float].
         The work function, calculated as WF = Vvacuum - Efermi.

         Like *Vvacuum*, it is evaluated at two points, far from the
         surface on both sides (left and right). The first and second
         elements correspond to the work function calculated using the
         first and second elements of *Vvacuum*, respectively. In
         units of "energy_unit".

      Arguments below:

      "energy_unit": str
         Unit of the returned energies

      "dist_unit": str
         Unit of the returned distances

      Parameters:
         * **energy_unit** (*str*) --

         * **dist_unit** (*str*) --

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

   recreate_molecule()

      Recreate the input molecule(s) for the corresponding job based
      on files present in the job folder.

      This method is used by "load_external()". If "ams.rkf" is
      present in the job folder, extract data from the "InputMolecule"
      and "InputMolecule(*)" sections.

      Return type:
         None | *Molecule* | *Dict*[str, *Molecule*]

   recreate_settings()

      Recreate the input "Settings" instance for the corresponding job
      based on files present in the job folder. This method is used by
      "load_external()".

      If "ams.rkf" is present in the job folder, extract user input
      and parse it back to a "Settings" instance using "scm.base"
      module. Remove the "system" branch from that instance.

      Return type:
         *Settings* | None

   ok()

      Check if the execution of the associated "job" was successful or
      not. See "Job.ok" for more information.

      Return type:
         bool

   get_errormsg()

      Tries to get an error message for a associated "job". This
      method returns "None" if the associated job was successful. See
      "Job.get_errormsg" for more information.

      Return type:
         str | None

   property name: str

      Return the "job.name" of the job associated with this results
      instance.

   get_energy_landscape()

      Returns the energy landscape obtained from a PESExploration job
      run by the AMS driver. The energy landscape is a set of
      stationary PES points (local minima and transition states).

      The returned object is of the type "AMSResults.EnergyLandscape"
      and offers convenient access to the states' energies, geometries
      as well as the information on which transition states connect
      which minima.

         el = results.get_energy_landscape()
         print(el)

         for state in el:
             print(f"Energy = {state.energy}")
             print(f"Is transition state = {state.isTS}")
             print("Geometry:", state.molecule)
             if state.isTS:
                 print(f"Forward  barrier: {state.energy - state.reactants.energy}")
                 print(f"Backward barrier: {state.energy - state.products.energy}")

      Return type:
         *EnergyLandscape*


Other functions
===============

hybrid_committee_engine_settings(settings_list)

   Creates the Settings for an AMS Hybrid engine that takes the
   average of all subengines as the prediction (for energies and
   forces).

   settings_list: list of Settings
      The Settings (at the top level) for each subengine.

   Returns: Settings (at the top level) for a Hybrid engine.

   Parameters:
      **settings_list** (*List**[**Settings**]*) --

   Return type:
      *Settings*
