Schema for PSML

#
# Schema for PSML format
# RELAX-NG compact form. See: http://www.relaxng.org/compact-tutorial-20030326.htm
#
# This form can be processed by the jing/trang set of tools, available from
#   https://github.com/relaxng/jing-trang
#
# The 'jing' validator can work directly with this .rnc file (RELAX-NG compact form),
# and this is the preferred option.
#
# The 'trang' converter can be used to generate other schema forms, such as:
#
#     .rng (RELAX-NG xml form), .xsd (XML Schema)
#
# But note that some features of RELAX-NG cannot be completely represented by XML Schema
# schema files. In particular, in this file, the 'arbitrary attribute' idiom in the
# 'annotation' element is emulated.
#

default namespace = "http://esl.cecam.org/PSML/ns/1.1"

start = PSML

PSML =  element psml { 
            Root.Attributes         
          , Provenance+             # One or more provenance elements
          , PseudoAtomSpec          
          , Grid?                   # Optional top-level grid
          , ValenceCharge           
          , CoreCharge?             # Optional pseudo-core charge
        , (
             (SemiLocalPotentials+ , PSOperator?)
             |
             (SemiLocalPotentials* , PSOperator )
          )
          , PseudoWaveFunctions*          # Zero or more Pseudo Wavefunction groups
       }  

#
# Definitions of the above grammar symbols follow

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

# =========
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
             }

# =========
PseudoAtomSpec =  element pseudo-atom-spec {    PseudoAtomSpec.Attributes
                                              , Annotation?
                                              , ExchangeCorrelation
                                      , ValenceConfiguration
                                              , CoreConfiguration?
                                           }
# =========
PseudoAtomSpec.Attributes = 
       attribute atomic-label { xsd:NMTOKEN },
       attribute atomic-number { xsd:double },
       attribute z-pseudo { xsd:double },
       attribute core-corrections { "yes" | "no" },
       attribute relativity { "no" | "scalar" | "dirac" },
       attribute spin-dft { "yes" | "no" }?,
       attribute flavor { xsd:string }?

# =========
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" }?
                    }

# =========
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 }?
         }

# =========
CoreConfiguration =  element core-configuration {
                       attribute total-core-charge { xsd:double },
                   Annotation?,
               CoreShell+
                     }

CoreShell =   Shell

# =========
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
              }

# =========
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
              }   

# =========
PSOperator =   (   LocalPotential            # Local potential
                 , NonLocalProjectors* )     # Zero or more fully nonlocal groups


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

LocalCharge =  element local-charge {
             Radfunc
           }

# =========
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
              }+

# =========
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
            }

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

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

#
# This is the way to leave the annotations
# as collections of one or more arbitrary attributes
#
# This RELAX NG idiom cannot be directly translated to W3C schema (xsd form),
# but a similar result could be obtained with the <any> and <anyAttribute> xsd elements
#
any_attribute = attribute * { text }
Annotation =  element annotation { any_attribute+  }

#
# Convenience enumeration definitions
#
attribute_l = attribute l { "s" | "p" | "d" | "f" | "g" }
attribute_n = attribute n { "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" }
attribute_j = attribute j { "0.5" | "1.5" | "2.5" | "3.5" | "4.5" }

attribute_set = attribute set { "non_relativistic" |
                                "scalar_relativistic" | "spin_orbit" | "lj" |
                "up" | "down" | "spin_average" | "spin_difference" |
                "user_extension1" | "user_extension2" }