The API for libPSML follows very closely the structure of the schema.
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.
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.
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.
The routine psml_reader (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 (ps
) is a low-level routine provided for
completeness in cases where a pristine ps
is needed for further
use.
The API follows closely the element structure of the PSML format. Each section in the high-level document structure of the schema 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).
The namespace and the attributes of the root element
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
(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 = 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 (ps
) result(depth
)
ps_Provenance_Get (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 arguments are of the
opaque type ps_annotation_t
. If there is no annotation, an empty
structure is returned.
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, 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).
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,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,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.
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 (ps,annotation,n_libxc_functionals
)
ps_LibxcFunctional_Get (ps,i,name,code,type,weight
)
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,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,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
:
func,r
) result(val
)but the API offers some convenience functions
ps,r
) result(val
)ps,r
) result(val
)These are work in progress. The relevant routines are
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
}
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,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:
ps,r
) result(val
)ps,r
) result(val
)LocalPotential = element local-potential {
attribute type { xsd:string },
Annotation?,
Grid?,
Radfunc,
LocalCharge? # Optional local-charge element
}
LocalCharge = element local-charge {
Radfunc
}
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
ps,r
) result(val
)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 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 (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.
The appropriate indexes can then be fed into the following routines to get specific information:
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.
ps,i,r
) result(val
)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 (ps,indexes_in,l,j,seq,set,indexes,number
)
ps_Projector_Get (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.
ps,i,r
) result(val
)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,indexes_in,l,j,set,indexes,number
)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.
ps,i,r
) result(val
)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
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.
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 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:
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:
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.
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.
The PSML library has currently limited support for editing the content
of ps_t 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,version,uuid,namespace
)ps,creator,date,annotation
)Annotations can be created using routines exported by the PSML API (see below)
The contents of a (possibly edited) ps_t object can be dumped to a PSML file using the routine
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).
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.)
annotation
)Cleans the contents of the ps_annotation_t
object
annotation
so that it can be reused.
Inserts the key, value
pair of string variables in the
ps_annotation_t
object annotation
. Internally, annotation
can
grow as much as needed.
annotation
) result(nitems
)Returns the number of key-value pairs in the annotation object
annotation,key,value,stat
)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.
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.