# pyfplo.fploio

- INParser
- PObj
- FPLOInput
- OutGrep
- Basis
- BasDef
- BasDefSection
- MultiOrbital
- Data

This module contains the parser interface to read data from `=.in` type files. It can also be used to change data in the file. WARNING: it is better to use `pyfplo.fedit` for changing input. The `fedit` interface is kept reasonably clean, while the naming conventions in `=.in` which is accessed directly by the parser is at times chaotic. Furthermore, there are semantic dependencies in the file, as e.g. descriptors which are for readability only. If the user changes the underlying value but not the descriptor the idea of having descriptors is lost. Furthermore, there are data which depend on the symmetry. `fedit` takes care of all of that.

Please note, that the underlying data representation is in `str` format. This allows to use `'1/3'` insted of `0.33333...` for real values. Hence, the natural return value of the data is `str`. If however an explicite `int` or `float` representation is wanted and if the type of the specific data conforms to these types an `int` value is returned by `PObj.L` and a `float` by `PObj.D`.

Logicals are handled as `'f'` or `'t'` internally. Hence, `PObj.S` returns these values. However, if returned by `PObj.L`, `True` and `False` are represented by `1` or `0` respectively. When writing a logical via `PObj.S` `'t'`, `'T'` and `'+'` are considered to be `True` any other string as `False`. When writing a logical via `PObj.L` `0` and any nonzero `int` represent `False` and `True`, respectively.

Character variables defined as ‘char some_char=’x’; ‘ in the =. file (which are not used currently) are returned as `str` by `PObj.S` and as `int` by `PObj.L`. The latter interpretation is machine dependend and not recommended. Similarily, the variable can be written in both ways.

`fploExecutable`(suffix='')

  Parameters: | suffix (str) – for convenience, suffix is appended to the exec name

  Return the name of the fplo executable as `str`, which fits the pyfplo version. This is an educated guess. There is no garantee that it is correct. If you compiled with a non-default build branch, this function does not return the name including the build branch suffix but just the generic name. Use suffix to select the correct executable or just write it out explicitely anyway.

## INParser

class `INParser`

  This class is a low-level class for reading the `=.in` files. For manipulating `=.in` use the `pyfplo.fedit` class. This is safer, since it ensures data (semantics) consistency.

  To see how to use some of the low-level routines consult Reading =.in files, Write =.in with low level routines and =.files to json.

  Create an instance of the FPLO parser for files of type `=.in` in the following way:

      import pyfplo.fploio as fploio
      p=fploio.INParser()

  `parseFile`(filename)

    Parameters: | filename (str) – the filename of an existing file in FPLO =.-format

    This opens and reads the file given in filename. Safe use:

        import os
        import pyfplo.fploio as fploio
        p=fploio.INParser()
        if not os.path.exists('=.in'):
          raise RuntimeError('cannot find the file')
        try:
          p.parseFile("=.in")
        except RuntimeError,ex:
          print(ex)
          pass # do whatever to handle the error or quit the program

  `writeFile`(filename)

    Parameters: | filename (str) – the filename of the new file in FPLO =.-format

    Write the parser content to a file called filename.

  `__call__`()

    Return the root of the data tree. The returned object is an instance of class `PObj`, which can be used to access individual data.

  `varExists`(varname)

    Parameters: | varname (str) – the name of the queried variable
    Returns: | existence of variable definition
    Return type: | `bool`

    Check if a variable definition exists in the parse table. Return `True` if it does. This function tests the struct array definition not the actual elements. Example:

        d=p() # p is a INParser object and d is parser root
        d2=d('wyckoff_positions')# d2 is node wyckoff_positions (struct array)
        print d2[0].varExists('element') # -> True
        print d.varExists('wyckoff_positions.element') # -> False   but
        print p.varExists('wyckoff_positions.element') # -> True
        #also
        print d.varExists('wyckoff_positions[1].element') # -> True
        print d('wyckoff_positions')[1].varExists('element') # -> True

    Compare this to `PObj.varExists`.

## PObj

class `PObj`

  This class is a low-level class for reading the `=.in` files. For manipulating `=.in` use the `pyfplo.fedit` class. This is safer, since it ensures data (semantics) consistency.

  To see how to use some of the low-level routines consult Reading =.in files, Write =.in with low level routines and =.files to json.

  Usually `PObj` instances are returned by other methods. One does not create a `PObj` by itself.

  `fullName`()

    Return the full node name. This includes element access (e.g. `nkxyz[3]`).

  `varExists`(varname)

    Parameters: | varname (str) – the name of the queried variable
    Returns: | existence of variable definition
    Return type: | `bool`

    Check if a variable exists under the current node. Return `True` if it does. This function tests struct array elements in detail. Example:

        d=p() # p is a INParser object and d is parser root
        d2=d('wyckoff_positions')# d2 is node wyckoff_positions (struct array)
        print d2[0].varExists('element') # -> True
        print d.varExists('wyckoff_positions.element') # -> False   but
        print p.varExists('wyckoff_positions.element') # -> True
        #also
        print d.varExists('wyckoff_positions[1].element') # -> True
        print d('wyckoff_positions')[1].varExists('element') # -> True

    Compare this to `INParser.varExists`.

  `size`(dim=1)

    Parameters: | dim (int) – dimension
    Returns: | size of the dimension
    Return type: | `int`

    If `PObj` is array-like return its size`

    > For struct arrays and rank-one arrays `size()` returns the array size.

    > For `rank>1` arrays `size(dim)` returns the size of the dim-th dimension.

  `sizes`()

    Returns: | a `list` of all array sizes, such that `rank==len(n.sizes())`
    Return type: | `list` of `int`

  `resize`(size)

    Parameters: | size (`int` or `list`) – can be a scalar `int` or a list of `int` in case of multidimensional arrays.

    If `PObj` is an array or struct array, resize it. Note, that for multidimensional arrays only the last dimension can be resized in the moment. But `=.in` does not use multidimensional arrays. (`=.dens` does though, which however should not be touched.)

  `__call__`(name)

    Parameters: | name (str) – name of the node

    Return a new `PObj` which references the node called name under the current `PObs`parse tree node:

        d=p() # parse tree root
        ds=d('spin')
        print ds('mspin').L

    Compound names are possible:

        d=p() # parse tree root
        print d('spin.mspin').L

  `__getitem__`(*args)

    If the node pointed to by `PObj` represents a 1d array type the operator `[i]` returns the `PObj`, which represents the `i`-th element. For flag arrays the operator `[flagname]` returns the `PObj`, which represents this flag (if it exists). Use `S` to print the full flag name and use `L` to read or set the flags value. If `PObj` represents a multidimensional array (up to 5d) the operator `[i,j,...]` returns the corresponding `PObj` of the element.

    args can be:

    index1, index2,…: between one and five `int` indices:

        dw2=d('wyckoff_positions')[2]

    flagname: a single `str` which names a flag in a flag array:

        d('options')['FULLBZ'].L=True

  `name`()

    Return the node name. This excludes element access (e.g. `nkxyz[3]`).

  `first`()

    Returns: | first node on the current level. See =.files to json
    Return type: | `PObj`

  `next`()

    Returns: | the next node on the current level. See =.files to json
    Return type: | `PObj`

  `hasNext`()

    Returns: | `True` if next node exists. See =.files to json
    Return type: | `bool`

  `isScalar`()

    Returns: | `True` if node is a scalar. See =.files to json
    Return type: | `bool`

  `isArray`()

    Returns: | `True` if node is an array. See =.files to json
    Return type: | `bool`

  `isStruct`()

    Returns: | `True` if node is a srtuct. See =.files to json
    Return type: | `bool`

  `isStructArray`()

    Returns: | `True` if node is a srtuct array. See =.files to json
    Return type: | `bool`

  `isInt`()

    Returns: | `True` if node is a scalar int. See =.files to json
    Return type: | `bool`

  `isReal`()

    Returns: | `True` if node is a scalar int. See =.files to json
    Return type: | `bool`

  `isLogical`()

    Returns: | `True` if node is a scalar int. See =.files to json
    Return type: | `bool`

  `isString`()

    Returns: | `True` if node is a scalar int. See =.files to json
    Return type: | `bool`

  `isChar`()

    Returns: | `True` if node is a scalar int. See =.files to json
    Return type: | `bool`

  `isFlag`()

    Returns: | `True` if node is a scalar int. See =.files to json
    Return type: | `bool`

  `__str__`()

    return printable representation. You do not need to call this explicitly. An object obj with this function provides usefull info when printed:

        print(obj)

  `listS`

    If `PObj` refers to a 1d array, return it’s elements as a list of `str` or assign a list of `str` to the array elements. On assignment to a fixed size array the list must have the proper length On assignment to a variable size array, the array is resized accordingly. One can assign a list of `str` to integer and real arrays as long as the list’s elements represent the correct type:

        d('wyckoff_positions')[1]('tau').listD=[1./2,2./3,1./4]
        d('wyckoff_positions')[1]('tau').listS=['1/2','2/3','1/4']

    Note, the dot in `2./3`, which is needed to have `float` division while in the string version it is not allowed.

  `listD`

    If `PObj` refers to a real 1d array, return the elements as a list of `float` or assign the list (see `listS`).

  `listL`

    If `PObj` refers to an integer 1d array, return is elements as a list of `int` or assign the list (see `listS`).

  `L`

    If the current node refers to an integer type, return it as `int`. If it refers to a flag, `1` is returned if the flag is switched on, `0` otherwise. Assignment is possible:

        d('spin.mspin').L=2

  `D`

    If the current node refers to a real type, return it as `float`. Assignment is possible:

        d('wyckoff_positions')[0]('tau')[1].D=5.4

  `S`

    The current `PObj` instance sits at a certain node of the data tree, depending on the history of calls, which created it.

    If the current `PObj` instance refers to a node, which represents a single value (scalar) the value is returned as `str` representation.

    If `PObj` refers to a flag the flagname followed by `(+)` or `(-)` is returned.

    A new `str` value can be assigned to S. The user must ensure the correctness of the data, e.g. that a `str` representing an underlying `float` is actually a valid `float`.

    Let us assume there is a `real tolerance=1e-12;` in `=.in`. Then one can set this value as:

        # d is assumed to be the PObj under which the node tolerance sits.
        d('tolerance').D=1e-13
        # or
        d('tolerance').S='1e-13'

## FPLOInput

class `FPLOInput`(fname=None)

  `FPLOInput` is used to manage the manipulation of `=.in` files. It creates new files or reads existing files. The symmetry update functionality of fedit is hidden in this class. This is a low level interface. It is strongly recommended to use `pyfplo.fedit` for manipulating data instead.

  An example of proper usage is found in Write =.in with mid level routines.

  Parameters: | fname – the name of an existing `=.in` file or `None` or nothing

  Create a fresh `FPLOInput` object via:

      import pyfplo.fploio as fploio
      ...
      fio=fploio.FPLOInput()

  Create a fresh `FPLOInput` object and read a file and update the version, if needed, or create fresh input if the file does not exist via:

      import pyfplo.fploio as fploio
      ...
      fio=fploio.FPLOInput('=.in')

  which is equivalent to the following code:

      import os
      import pyfplo.fploio as fploio
      ...
      fio=fploio.FPLOInput()
      if os.path.exists('=.in'):
          fio.parseInFile(True,'=.in')
      else:
          fio.createNewFileContent() # make fresh content
          ... # or handle this case differently

  `parseInFile`(forceupdate, fname='=.in', newifnonexistent=False)

    Read the `=.` file called fname into an internal parse tree and optional update version or create fresh content. If something goes wrong an exception is raised.

    Parameters: | forceupdate (int) – If `True`, convert older version files into new ones fname (str) – `=.in`-type file name newifnonexistent (int) – If `True` and the file called fname does not exist, create fresh content in the parse tree (does not cretae a file).

  `symmetryUpdate`()

    After changing symmetry settings call this to update the rest of the data. A message is returned.

  `resetNonSymmetrySections`()

    Reset all data in the internal parse table except for the symmetry section. This is used to reset all non symmetry data to default values.

  `writeFile`(name)

    Parameters: | name (str) – filename of `=.in`-type file

    Write the parser content to the file called fname.

  `structureFromCIFFile`(ciffilename, wyckofftolerance=1e-06, symblockindex=0, symoptionindex=0, determinesymmetry=False, keeprotation=False)

    Read the cif-file called ciffilename and import the structure data into the parse tree.

    To see how to use this consult Reading cif-files.

    wyckofftolerance is used to convert approximations like 0.3333 into 1/3. If the round off error (as in 0.3333) is smaller than wyckofftolerance all numbers which are approximate fractionals n/d, d in [1,12] are replaced by the fractional. This is especially necessary for hexagonal structures where an approximation like 0.3333 for 1/3 leads to trouble (doubling of atoms, missing symmetry operations and such).

    Some cif files contain more than one data block. If several blocks are present and if several of these contain symmetry information symblockindex selects the corresponding symmetry block. By default symblockindex is 0, which selects the first such block. The available blocks are written to the output. An example could produce the following output:

        ------------------------------------------------------------------------
        Blocks contained in cif file 'cg049782osi20040706_123518.cif':
        ------------------------------------------------------------------------
        symmetry block No.   name
        ------------------------------------------------------------------------
                             global
             0               cu2as2o7
             1               cuasbeta
                             profile
        ------------------------------------------------------------------------

    There are four blocks of which two contain structure/symmetry information. Possible values for symblockindex are 0 or 1.

    In principle it is possible that a symmetry containing block has contradicting group information. Often the cif files contains a Hall-symbol, the xyz-operation symbol table and the space group number (or combinations of these). The space group number is not a good indicator, since it does not encode settings. Hence, we analyse the Hall- and xyz-symbols to determine the group. Only if both are missing we rely on the space group number. If, however, Hall- and xyz-symbols lead to different groups (rare but possible), we can select, which of the two to use. The possible symmetry options are written to the output and symoptionindex selects the corresponding option. By default symoptionindex is 0, which selects the first such option. The output of symmetry options could look like this:

        Symmetry information for datablock: data_1
        Space group number: 227
        Name-Hall         : F 4d 2 3 -1d
        xyz-symbol        :  gives hallsymbol -F 4vw 2vw 3

        Hall symbol: OK
        xyz symbols: OK but not equivalent

        The following symmetry options are available:
          0 'Hall symbol'
          1 'xyz-symbols'

    You can see that the xyz symbols give a different setting than the Hall symbol. Hence, we have to chose symoptionindex from 0 or 1. You also understand that 227 is not a complete description.

    If determinesymmetry is `True` the cif data are symmetry analysed before creating =.in.

    If determinesymmetry is `True` and keeprotation is `True` the orientation of the cell is retained during symmetry analysis.

    A possible way of importing a cif file works like this:

        import pyfplo.fploio as fploio
        import pyfplo.fedit as fedit

        fio=fploio.FPLOInput('=.in')
        fio.structureFromCIFFile('data.cif',wyckofftolerance=1e-4,
           symblockindex=0,symoptionindex=0)
        fio.writeFile('=.in') # now we have created or update =.in from the cif file

        fed=fedit.Fedit(recreate=True) # reset default input (except symmetry)
        fed.iteration(n=100) # ... and more settings
        fed.pipeFedit() # apply the settings

  `reset`()

    Reset the internal parse tree to nothing. The resulting `FPLOInput` object is like a newly created one.

  `createNewFileContent`()

    Create a completely new default file in the internal parse tree.

  `parser`()

    Returns: | the underlying `INParser`
    Return type: | `INParser`

## OutGrep

class `OutGrep`(outfilename='out', dir='.')

  OutGrep helps to grep results from fplo output files directly into python variables.

  Parameters: | outfilename (str) – the name of the fplo output file dir (str) – the directory in which it sits

  This reads the whole output file, so that using `grep` in a loop is more efficient.

  - Example: grep last total energy and gap:

        import pyfplo.fploio as fploio
        ...
        og=fploio.OutGrep('out')
        print('etot=',og.grep('EE')[-1],', gap=',og.grep('gap')[-1])

  - Example: show iteration progress:

        import os
        import pyfplo.fploio as fploio
        ...
        og=fploio.OutGrep('out')
        with open('res','w') as fh:
            res=og.grep('it')
            for i,r in enumerate(res):
                fh.write('{} {}\n'.format(i,r))
        os.system('xfbp res')

  - Example: grep all atom spins (as a function of iteration) and write them to file `res`:

        import os
        import pyfplo.fploio as fploio
        og=fploio.OutGrep('out')
        si=og.sites()

        with open('res','w') as fh:
            for i,s in enumerate(si):
                site=i+1
                res=og.grep('SSat',site)
                fh.write('# {}{}\n'.format(s.element,site))
                for it,r in enumerate(res):
                    fh.write('{} {}\n'.format(it,r))
                fh.write('\n')
        os.system('xfbp res')

  More exmples in Extract =.basdef from output file

  `modes`

    Type: | `dict` of mode:long-name

    class variable of all available `OutGrep` modes as in `grepfplo -h`

        for k in fploio.OutGrep.modes.keys():
            print('mode: {0:15s} : {1}'.format(k,fploio.OutGrep.modes[k]))

  `sites`()

    return a `list` of `pyfplo.common.Site`, which contains info for sites in output file.

  `grep`(mode, site=1, orbital=0)

    Parameters: | mode (str) – one of the modes defined as keys in `dict` `OutGrep.modes` site (int) – some modes need a site number (one-based, as in fplo output) orbital (int) – some modes (population analysis modes) need an orbital number. orbital is the one-based number of the wanted orbital in the order as printed in the population analysis. If orbital is out of range (e.g. 0) the total site population number is printed. For the mode `N_gros` there is one more number than for `N_net` and `S_...`, which is the number of excess electrons of the site.

    return a `list` of `str` of mode-dependend results for all iterations found in the output file. Some modes return a single result since it is not iteration dependend. If you need `float` results, convert like `float(og.grep('EE')[-1])` or `list(map(float,og.grep('EE')))`.

    See, examples under `OutGrep`.

## Basis

class `Basis`(version=None, elementsoratomicnumbers=None, basdeffile=None)

  `Basis` gives a low-level access to the basis definition. The basic operations are:

  > - Create a default basis for all sorts via input: basis-version + list of elements/atomic numbers.
  > - Optionally, read basis definitions from `=.basdef` (overwrites the default).
  > - Modify the basis thusly obtained.
  > - Write file `=.basdef`.

  Consequently, at minimum one needs to know the list of elements. See examples Extract default basis into =.basdef and User defined basis (in =.basdef). If a `=.in` already exists one can obtain this list via:

      import pyfplo.common as com
      import pyfplo.fploio as fploio
      p=fploio.INParser()
      p.parseFile('=.in')
      d=p()('wyckoff_positions')
      elements=[d[i]('element').S for i in range(d.size())]
      # optionally:
      atomicnumbers=list(map(lambda x: com.c_elements.index(x),elements))
      print(elements)
      print(atomicnumbers)

  To create an FPLO9 default basis in `=.basdef` do this:

      import pyfplo.fploio as fploio
      ...
      b=fploio.Basis('default FPLO9 basis',elements)
      # modify b if needed, then
      b.writeFile('=.basdef')

  To read and modify `=.basdef` do this:

      import pyfplo.fploio as fploio
      ...
      b=fploio.Basis('default FPLO9 basis',elements,basdeffile='=.basdef')
      # modify b if needed, then
      b.writeFile('=.basdef')

  Note

  Note, that you always need to provide the default version and the element/atomic number list, because some adjustments take place internally based on the elements.

  Parameters: | version (`int` or `str`) – ID (`int`) or unique search string (case sensitive) into version table. The version table is obtained via the class variable `pyfplo.fploio.Basis.versions`, which is a list of available basis versions, where each list member has two members: a unique ID (`int`) and a `str`, which can be used for searching. elementsoratomicnumbers (`list` of `str` and/or `int`) – list of element names or atomic numbers for each sort basdeffile (`str` or `None`) – read (and overwrite default) from file basdeffile if not `None`!

  `versions`

    class variable of all available Basis versions

    Type: | list of 2-lists of structure [ID,versionname]

  `writeFile`(filename='=.basdef')

    Parameters: | filename (str) – the basis definitions file name. For use with fplo this needs to be `=.basdef`.

    Write the basis definitions to file filename.

  `__getitem__`()

    `basis[i]` returns basis (`BasDef`) of sort `i`

## BasDef

class `BasDef`

  `BasDef` contains the basis of a particular atom. This class is return by `pyfplo.fploio.Basis.__getitem__` and cannot be instantiated otherwise.

  `__str__`()

    return printable representation. You do not need to call this explicitly. An object obj with this function provides usefull info when printed:

        print(obj)

  `core`
  `semicore`
  `valence`

## BasDefSection

class `BasDefSection`
  `append`(nl, Q=None, P=None, S=None)

    Parameters: | nl (str) – multiorbital name, e.g. `'3d'` Q (`float` or `list` of `float`-s) – P (`float` or `list` of `float`-s) – S (`float` or `list` of `float`-s) –

    append a double 4f-orbtial like `D4f Q=(q1,q2) P=(p1,p2)` via:

        basdef.valence.append('4f',Q=[q1,q2],P=[p1,p2])

    or a single 4f-orbtial like `s4f Q=(q1) P=(p1)` via:

        basdef.valence.append('4f',Q=q1,P=p1)

  `remove`(i)

    Parameters: | i (int) –

  `__len__`()

    `len(b)` returns the size of the Basis section (number of multi-orbitals).

  `__getitem__`()

    `BasDefSection[i]` returns the `i`-th MultiOrbital

  `__str__`()

    return printable representation. You do not need to call this explicitly. An object obj with this function provides usefull info when printed:

        print(obj)

## MultiOrbital

class `MultiOrbital`

  This class is return by `pyfplo.fploio.BasDefSection.__getitem__` and such

  `append`(Q=0.0, P=1.0, S=0.0)

    Parameters: | Q (float) – P (float) – S (float) –

  `removeLast`()
  `removeFirst`()
  `qns`(mult)

    Parameters: | mult (int) –
    Returns: |
    Return type: | `str`

  `Q`(mult)

    Parameters: | mult (int) –
    Returns: |
    Return type: | `float`

  `S`(mult)

    Parameters: | mult (int) –
    Returns: |
    Return type: | `float`

  `P`(mult)

    Parameters: | mult (int) –
    Returns: |
    Return type: | `float`

  `set`(mult, Q=None, P=None, S=None)

    Parameters: | mult (int) – Q (`float` or `None`) – P (`float` or `None`) – S (`float` or `None`) –

  `__str__`()

    return printable representation. You do not need to call this explicitly. An object obj with this function provides usefull info when printed:

        print(obj)

  `name`
  `multiplicity`

## Data

For convenience:

`version`

  copy of `pyfplo.common.version`

`Version`

  copy of `pyfplo.common.Version`

`c_elements`

  copy of `pyfplo.common.c_elements`