title: libPSML API

The API for libPSML follows very closely the structure of the schema.

[TOC]

# Exported types

The library exports the opaque types:

* `ps_t`: Main handle for all the libPSML routines. It contains data
  structures resulting from parsing

* `ps_annotation_t`: A handle to deal with the content of
  `<annotation>` elements in the PSML file. More information on
  annotations can be found [here](./annotations.html).

* `ps_radfunc_t`: A handle for the data structures implementing the
  functionality of radial functions, in particular their evaluation.

More information about the associated implementations can be found in
the [developer notes](../../03_library_developer_notes/index.html).

In addition, the library exports the integer parameter `ps_real_kind`
that represents the kind of the real numbers accepted and returned by
the library.

For error handling, the library exports the subroutine
`ps_set_error_handler` that can be used to configure an error
handler. The handler must have as argument a string and should carry
out any needed cleaning up before stopping the program in the
appropriate manner. There is currently no provision for signaling
specific error conditions.

# Parsing

* The routine [[psml_reader(proc)]] (`filename, ps, debug, stat`) parses the PSML file
`filename` and populates the data structures in the handle
`ps`. An optional `debug` argument determines whether the
library issues debugging messages while parsing. An optional `stat` argument will
be set to 0 upon successful return, to -1 if the filename cannot be opened,
and (if using a recent version of xmlf90), to -2 if there is a XML parsing error.

* [[ps_destroy(proc)]] (`ps`) is a low-level routine provided for
completeness in cases where a pristine `ps` is needed for further
use.

# Library identification

* The function [[ps_GetLibPSMLVersion(proc)]] returns the version as
    an integer (for example: 1106) instead of the typical dot form
    (1.1.6).

# Data accessors

The API follows closely the element structure of the PSML format. Each
section in the high-level document structure of the
[schema](../../01_the_psml_format/schema.html) is mapped to a group of
routines in the API. Within each, there are routines to query any
internal structure (attributes, existence, number, or selection of
child elements) and routines to obtain specific data items
(attributes, content of child elements).

## Root attributes and namespace

The namespace and the attributes of the root element
```python
default namespace = "http://esl.cecam.org/PSML/ns/1.1"

Root.Attributes =  attribute energy_unit { "hartree" }
                 , attribute length_unit { "bohr" }
                 , attribute uuid { xsd:NMTOKEN }
                 , attribute version { xsd:decimal }
```

are read by the routine [[ps_RootAttributes_Get(proc)]]
(`ps,uuid,version,namespace`).  As in all the routines that follow, the
handle `ps` is mandatory. All other arguments are optional, with
`intent(out)`, and of type `character(len=*)`. The
argument version returns the PSML version of the file being
processed. A given version of the library is able to process files
with lower version numbers, up to a limit.

## Provenance data
```python
Provenance =  element provenance {
                attribute record-number { xsd:positiveInteger }?
              , attribute creator { xsd:string }
              , attribute date { xsd:string }
      
              , Annotation?
	      , InputFile*    # zero or more input files
              }
	      
InputFile =  element input-file {
               attribute name { xsd:NMTOKEN }, # No spaces or commas allowed
               text
             }
```
As there can be several `<provenance>` elements, the API provides a
function to enquire about their number (depth of provenance
information), and a routine to get the information from a given level:

* [[ps_Provenance_Depth(proc)]] (`ps`)  result(`depth`)

* [[ps_Provenance_Get(proc)]] (`ps,level,creator,date,annotation,number_of_input_files`)

  The integer argument `level` selects the provenance depth level (1
  is the deepest, or older, so to get the latest record the routine
  should be called with level=depth as returned from the previous
  routine). All other arguments are optional with
  intent(out). `creator` and `date` are strings. Here and in what
  follows, [annotation](./annotations.html) arguments are of the
  opaque type `ps_annotation_t`.  If there is no annotation, an empty
  structure is returned.
  
## Pseudo-atom specification attributes and annotation

```python
PseudoAtomSpec.Attributes = 
       attribute atomic-label { xsd:NMTOKEN },
       attribute atomic-number { xsd:double },
       attribute z-pseudo { xsd:double },
       attribute core-corrections { "yes" | "no" },
       attribute meta-gga { "yes" | "no" }?,
       attribute relativity { "no" | "scalar" | "dirac" },
       attribute spin-dft { "yes" | "no" }?,
       attribute flavor { xsd:string }?
```

* [[ps_PseudoAtomSpec_Get(proc)]] (`ps, atomic_symbol,
  atomic_label, atomic_number, z_pseudo,
  pseudo_flavor, relativity, spin_dft, core_corrections, meta_gga, annotation`)

  The arguments `spin_dft`, `core_corrections`, and `meta_gga` are boolean,
  and the routine returns an empty string in `flavor` if the
  attribute is not present (recall that `flavor` is a cascading
  attribute that can be set at multiple levels).
  

### Valence configuration
```python
ValenceConfiguration =  element valence-configuration {
                          attribute total-valence-charge { xsd:double },
	                  Annotation?,
                          ValenceShell+
                        }

ValenceShell =   Shell

Shell =  element shell {
           attribute_l,
           attribute_n,
           attribute occupation { xsd:double },
           attribute occupation-up { xsd:double }?,
           attribute occupation-down { xsd:double }?
         }

attribute_l = attribute l { "s" | "p" | "d" | "f" | "g" }
attribute_n = attribute n { "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" }
```

* [[ps_ValenceConfiguration_Get(proc)]] (`ps,nshells,charge,annotation`)

  This routine returns (as always, in optional arguments), the
  values of the top-level attributes, any annotation, and the number of
  `Shell` elements, which serves as upper limit for the index
  `i` in the following routine, which extracts shell information:
  
* [[ps_ValenceShell_Get(proc)]] (`ps,i,n,l,occupation,occ_up,occ_down`)

  The `n` and `l` quantum number arguments are integers
  (despite the use of spectroscopic symbols for the angular momentum
  in the format), and the occupations real.
  
### Exchange and correlation

```python
ExchangeCorrelation =  element exchange-correlation {
                         Annotation?
                         , element libxc-info {
                              attribute number-of-functionals { xsd:positiveInteger },
                              LibxcFunctional+
                           }
                        }

LibxcFunctional =   element functional {
                      attribute id { xsd:positiveInteger },
                      attribute name { xsd:string },
                      attribute weight { xsd:double }?,

                      # allow canonical names and libxc-style symbols
	      
                      attribute type { "exchange" | "correlation" | "exchange-correlation" |
	                               "XC_EXCHANGE" | "XC_CORRELATION" |
		  	                  "XC_EXCHANGE_CORRELATION" }?
                    }
```

The routines follow the same structure as those in the previous section.

* [[ps_ExchangeCorrelation_Get(proc)]] (`ps,annotation,n_libxc_functionals`)

* [[ps_LibxcFunctional_Get(proc)]] (`ps,i,name,code,type,weight`)

## Valence and Core Charges

```python
ValenceCharge =  element valence-charge {
                   attribute total-charge { xsd:double },
                   attribute is-unscreening-charge { "yes" | "no" }?,
                   attribute rescaled-to-z-pseudo { "yes" | "no" }?,
                   Annotation?,
                   Radfunc
                 }  
    
# =========
CoreCharge =  element pseudocore-charge {
                attribute matching-radius { xsd:double },
                attribute number-of-continuous-derivatives { xsd:nonNegativeInteger },
                Annotation?,
                Radfunc
              }
```

These are radial functions with some metadata in the form of
attributes, an optional annotation, and a `Radfunc` child. The accessors
have the extra optional argument `func` that returns a handle to
a `ps_radfunc_t` object, which can later be used to get extra
information.

* [[ps_ValenceCharge_Get(proc)]] (`ps,total_charge,
                      is_unscreening_charge, rescaled_to_z_pseudo,
                      annotation,func`)

  The routine returns an emtpy string in `is_unscreening_charge` and
  `rescaled_to_z_pseudo` if the attributes are not present in the
  PSML file. 
  
* [[ps_CoreCharge_Get(proc)]] (`ps,rc,nderivs,annotation,func`)

  `rc` corresponds to the matching radius and `nderivs` to the
  continuity information. Negative values are returned if the
  corresponding attributes are not present in the file.

The `func` object can be used to evaluate the radial functions at
a particular point `r`:

* function [[ps_GetValue(proc)]] (`func,r`) result(`val`)

but the API offers some convenience functions

* function [[ps_ValenceCharge_Value(proc)]] (`ps,r`) result(`val`)
* function [[ps_CoreCharge_Value(proc)]] (`ps,r`) result(`val`)

## Valence and core kinetic-energy density for MGGA

These are work in progress. The relevant routines are

```python
ValenceKineticDensity =  element valence-kinetic-energy-density {
                   attribute is-unscreening-tau { "yes" | "no" }?,
                   Annotation?,
                   Radfunc
                 }  
# =========
CoreKineticDensity =  element pseudocore-kinetic-energy-density {
                attribute matching-radius { xsd:double }?,
                attribute number-of-continuous-derivatives { xsd:nonNegativeInteger }?,
                Annotation?,
                Radfunc
              }

```


* [[ps_ValenceKineticDensity_Get(proc)]] (`is_unscreening_tau,
                      annotation,func`)

  The routine returns an emtpy string in `is_unscreening_tau` 
  if the attribute is not present in the PSML file. 
  
* [[ps_CoreKineticDensity_Get(proc)]] (`ps,rc,nderivs,annotation,func`)

  `rc` corresponds to the matching radius and `nderivs` to the
  continuity information. Negative values are returned if the
  corresponding attributes are not present in the file.

As above, the API offers some convenience functions to get the actual
values as a function of the radial coordinate:

* function [[ps_ValenceKineticDensity_Value(proc)]] (`ps,r`) result(`val`)
* function [[ps_CoreKineticDensity_Value(proc)]] (`ps,r`) result(`val`)

## Local Potential and Local Charge Density

```python
LocalPotential =  element local-potential {
                    attribute type { xsd:string },
                    Annotation?,
                    Grid?,
                    Radfunc,
		    LocalCharge?  # Optional local-charge element
                  }
    
LocalCharge =  element local-charge {
	         Radfunc
	       }
```

* [[ps_LocalPotential_Get(proc)]] (`ps,type,annotation,func,has_local_charge,func_local_charge`)

In this version of the API, the optional `<local-charge>` element is
not given a first-class status. To evaluate it (if the boolean
argument `has_local_charge` is `.true.`), the
`func_local_charge` argument has to be used in the
`ps_GetValue` routine above. The local potential can be
evaluated via the `func` object or with the convenience function 

* function [[ps_LocalPotential_Value(proc)]] (`ps,r`) result(`val`)

## Semilocal potentials

```python
SemiLocalPotentials =  element semilocal-potentials {
                         attribute_set,
                         attribute flavor  { xsd:string }?,
                         Annotation?,
			 Grid?,
                         Potential+
                        }

Potential =   element slps {
                attribute flavor { xsd:string }?,
                attribute_l,
                attribute_j ?,
                attribute_n,
                attribute rc { xsd:double },
                attribute eref { xsd:double }?,
                Radfunc
              }   
```

As explained [here](./sets.html) there can be several
`<semilocal-potentials>` elements corresponding to different
sets. Internally, the data is built up in linked lists during the
parsing stage and later all the data for the `<slps>` child elements
are re-arranged into flat tables, which can be queried like a simple
database. The table indexes for the potentials with
specific quantum numbers, or set membership, can be obtained with
the routine

* [[ps_SemilocalPotentials_Filter(proc)]] (`ps,indexes_in,l,j,n,set,indexes,number`)

    - `indexes_in`: (Optional, in) Initial set of indexes on which to
     perform the filtering operation. If not present, the full table is used.
    - `l,j,n,set`: (Optional, in) Values for filtering criteria.
    - `indexes`: (Optional, out) Set of indexes which satisfy the criteria.
    - `number`: (Optional, out) Number of items which satisfy the criteria.


The `set` argument has to be given using special integer symbols exported
by the API, as explained [here](./sets.html).

The appropriate indexes can then be fed into the following routines to
get specific information:

*  [[ps_Potential_Get(proc)]] (`ps,i,l,j,n,rc,eref,set,flavor,annotation,func`)

  All arguments except `ps` and `i` are optional. The value
  returned in `set` is an integer which can be converted to a
  mnemonic string through the [[str_of_set]] convenience function.
  The `annotation` returned corresponds to the optional `<annotation>`
  element of the parent block of the `<slps>` element.
  
  The routine returns a very large positive value in `eref` if the
  corresponding attribute is not present in the file.

* function [[ps_Potential_Value(proc)]] (`ps,i,r`) result(`val`)

## Nonlocal Projectors

```python
NonLocalProjectors =  element nonlocal-projectors {
                        attribute_set,
                        Annotation?,
                        Grid?,
	                Projector+
                      }

Projector =   element proj {
                attribute ekb { xsd:double },
                attribute eref { xsd:double }?,
                attribute_l,
                attribute_j ?,
                attribute seq { xsd:positiveInteger },
                attribute type { xsd:string },
                Radfunc
              }+
```

The ideas are exactly the same as for the semilocal potentials. The
relevant routines are:

* [[ps_NonlocalProjectors_Filter(proc)]] (`ps,indexes_in,l,j,seq,set,indexes,number`)

* [[ps_Projector_Get(proc)]] (`ps,i,l,j,seq,set,ekb,eref,type,annotation,func`)

  The routine returns a very large positive value in `eref` if the
  corresponding attribute is not present in the file.
  
* function [[ps_Projector_Value(proc)]] (`ps,i,r`) result(`val`)

## Pseudo Wavefunctions

```python
PseudoWaveFunctions =  element pseudo-wave-functions {
                         attribute_set,
                         Annotation?,
                         Grid?,
                         PseudoWf+
                       }

PseudoWf =  element pswf {
       	      attribute_l,
              attribute_j ?,
              attribute_n,
              attribute energy_level { xsd:double} ?,
              Radfunc
            }
```
Again, the same strategy:

* [[ps_PseudoWavefunctions_Filter(proc)]] (`ps,indexes_in,l,j,set,indexes,number`)
* [[ps_PseudoWf_Get(proc)]] (`ps,i,l,j,n,set,energy_level,annotation,func`)

  The routine returns a very large positive value in `energy_level` if the
  corresponding attribute is not present in the file.

* function [[ps_PseudoWf_Value(proc)]] (`ps,i,r`) result(`val`)


## Radial function and grid information

```python
Radfunc =  element radfunc {
             Grid?,              # Optional grid element
             element data {      
               list { xsd:double+ }       # One or more floating point numbers
             }
           }

Grid =  element grid {
          attribute npts { xsd:positiveInteger },
          Annotation?,
          element grid-data {
                 list { xsd:double+ }   # One or more floating point numbers
          }
        }
```

In keeping with the PSML philosophy of being grid-agnostic, the basic
API does not provide any direct means of accessing the data used in
the tabulation of the radial functions. The values of the functions at
a particular point `r` can be generally obtained through the
`ps_XXXX_Value` interfaces, or through the [[ps_GetValue]]
interface using `func` objects of type `ps_radfunc_t`.

It is nevertheless possible to get annotation data for the grid of a
particular radial function, or for the top-level grid, through the
function

* function [[ps_GridAnnotation(proc)]] (`ps,func`}) result(`annotation`)

  If a radial function handle `func` is given, the annotation for
  that radial function's grid is returned. Otherwise, the return value
  is the annotation for the top-level grid.  

# The evaluation engine

In the current version of the library the evaluation of tabulated
functions is performed by default with polynomial interpolation, using
a slightly modified version of an algorithm borrowed (with permission)
from the [oncvpsp](http://www.mat-simresearch.com) program by
D.R. Hamann.  By default seventh-order interpolation, as in `oncvpsp`,
is used.  If the library is compiled with the appropriate
pre-processor symbols, the interpolator and/or its order can be chosen
at runtime, but we note that this should be considered a debugging
feature. Reproducibility of results would be hampered if client codes
change the interpolation parameters at will. Generator codes should
instead strive to produce data tabulations that will guarantee a given
level of precision when interpolated with the default scheme, using
appropriate output grids on which to sample their internal data
sets. For example, our own work on enabling PSML output in `oncvpsp`
(see below) includes diagnostic tools to check the interpolation
accuracy.

Most codes use internally a non-uniform grid (e.g. logarithmic). We
have found that a good choice of output grid is a subset of the
producer's working grid points that leaves out most of the very close
points near the origin but maintains the rest. This can be achieved by
imposing a minimum inter-point separation $\delta$. This parameter
$\delta$ can be smaller than the typical linear-grid step used
currently by most codes, and still lead to smaller grids (in terms of
number of points) that preserve the accuracy of the output.

High-order interpolation can lead to *ringing* effects
(oscillations of the interpolating polynomial between points), notably
near edge regions when the shape of the function changes
abruptly. This is the case, for example, if the function drops to zero
within the interpolation range as a result of cutting off a tail. The
actual interpolated values will typically be very small, but might cause
undesirable effects in the client code. To avoid this problem, the
libPSML evaluator works internally with an effective end-of-range
that is determined by analyzing the data values after parsing.

If needed for debugging purposes, the evaluator engine can be
configured by the routine:

* [[ps_SetEvaluatorOptions(proc)]] (`quality_level,debug,
  use_effective_range,
  custom_interpolator`)

  All arguments are optional, and apply globally to the operation of
  the library. The `custom_interpolator` argument is not allowed
  if the underlying Fortran compiler does not support procedure
  pointers. `quality_level` (an integer) is by default and will
  typically be the interpolation order, but its meaning can change
  with the interpolator in use. The evaluator uses an effective range
  by default, as discussed above, but this feature can be turned off
  by setting `use_effective_range` to `.false.`. The
  `debug` argument will turn on any extra printing configured in
  the evaluator. By default, no extra printing is produced.
  
Finally, in case it is necessary to look at the raw tabular data for debugging 
purposes, the library also provides a low-level routine:

* [[ps_GetRawData(proc)]] (`func,rg,data`)

  If a radial function handle `func` is given, the grid points
  and the actual tabulated data are returned in `rg` and
  `data`, which must be passed as allocatable arrays.
  
# Predicate routines for contents

The following convenience functions return a logical value. For semi-local
potentials, non-local projectors, and pseudo-wave-functions, the test
is done on the union of all possible sets.

* [[ps_HasSemilocalPotentials]]
* [[ps_HasPsOperator]]
* [[ps_HasLocalPotential]]
* [[ps_HasProjectors]]

# Editing of ps structures

The PSML library has currently limited support for editing the content
of [[ps_t(type)]] objects from user programs. For example, such an
editing might be done by a KB-projector generator to insert a new
provenance record (and KB and local-potential data) in the
`ps` object, prior to dumping to a new PSML file.

* [[ps_RootAttributes_Set(proc)]] (`ps,version,uuid,namespace`)
* [[ps_Provenance_Add(proc)]] (`ps,creator,date,annotation`)

  Annotations can be created using routines exported by the PSML API
  (see below)
  
* [[ps_NonlocalProjectors_Delete(proc)]] (`ps`)
* [[ps_LocalPotential_Delete(proc)]] (`ps`)

# Dump of ps structures

The contents of a (possibly edited) [[ps_t(type)]] object can be dumped
to a PSML file using the routine

* [[ps_DumpToPSMLFile(proc)]] (`ps,fname,indent`)

  Here `fname` is the output file name, and `indent` is a
  logical variable that determines whether automatic indenting of
  elements is turned on (by default it is not).
  
# Annotation API

To support the annotation functionality, the library contains a module
implementing a basic instance of an association list (a data structure
holding key-value pairs), and exports the `ps_annotation_t`
type, the empty annotation object `EMPTY_ANNOTATION` and the following routines:

(The names of these routines are aliases of the originals in the [[assoc_list(module)]] module.)

* reset_annotation (`annotation`)

  Cleans the contents of the `ps_annotation_t` object
  `annotation` so that it can be reused.
  
* insert_annotation_pair (annotation,key,value,stat)

  Inserts the `key, value` pair of string variables in the
  `ps_annotation_t` object `annotation`. Internally, `annotation` can
  grow as much as needed.
  
* function nitems_annotation (`annotation`) result(`nitems`)

  Returns the number of key-value pairs in the annotation object

* get_annotation_value (`annotation,key,value,stat`)
* get_annotation_value (`annotation,i,value,stat`)

  This routine has two interfaces. The first gets the `value`
  associated to the `key`, and the second gets the `value`
  associated to the `i`'th entry in the annotation object.

* get_annotation_key (`annotation,i,key,stat`)
  
  Gets the `key` of the `i`'th entry in the annotation object.
  
  Together with the second form of `get_annotation_value`,
  this routine can be used to scan the complete annotation object. The
  first form of `get_annotation_value` is appropriate if the
  key(s) are known.

In all the above routines a non-zero `stat` signals an error condition.
