Skip to content

Commit

Permalink
Merge pull request #47 from QEF/develop
Browse files Browse the repository at this point in the history
Update dependencies and fix XPath selection on document schema
  • Loading branch information
brunato authored May 28, 2024
2 parents 5d0e87c + 19aaef8 commit 1994864
Show file tree
Hide file tree
Showing 11 changed files with 1,467 additions and 36 deletions.
7 changes: 3 additions & 4 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@ jobs:
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
python-version: ['3.7', '3.8', '3.9', '3.10', '3.11']
python-version: ['3.8', '3.9', '3.10', '3.11', '3.12']
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v2
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Display Python version
Expand All @@ -29,7 +29,6 @@ jobs:
python -m pip install --upgrade pip
pip install -r requirements-dev.txt
- name: Lint with flake8
if: ${{ matrix.python-version != '3.7' }}
run: |
flake8 qeschema --max-line-length=100 --statistics
- name: Run tests
Expand Down
4 changes: 2 additions & 2 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@
# -- Project information -----------------------------------------------------

project = 'qeschema'
copyright = '2015-2023, Quantum Espresso Foundation and SISSA'
copyright = '2015-2024, Quantum Espresso Foundation and SISSA'
author = 'Davide Brunato, Pietro Delugas'

# The full version, including alpha/beta/rc tags
release = '1.5.1'
release = '1.5.2'


# -- General configuration ---------------------------------------------------
Expand Down
2 changes: 1 addition & 1 deletion qeschema/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
from .exceptions import QESchemaError, XmlDocumentError
from .utils import set_logger

__version__ = '1.5.1'
__version__ = '1.5.2'

__all__ = [
'XmlDocument', 'QeDocument', 'PwDocument', 'PhononDocument', 'NebDocument',
Expand Down
2 changes: 1 addition & 1 deletion qeschema/cards.py
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,7 @@ def labnl(nnum, lnum):

label = labnl(value['n2_number'], value['l2_number'])
if 'two' in background:
label = f"{label}-{labnl(value['n3_number'],value['l3_number'])}"
label = f"{label}-{labnl(value['n3_number'], value['l3_number'])}"
lines.append(f"U {specie}-{label} {value['$']:8.3f}")
elif tag == 'V':
speclab1 = f"{value['@specie1']}-{value['@label1']}"
Expand Down
53 changes: 30 additions & 23 deletions qeschema/documents.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
import xmlschema
from xmlschema import etree_tostring


try:
import yaml
except ImportError:
Expand All @@ -27,7 +26,7 @@
NebInputConverter, TdInputConverter, TdSpectrumInputConverter, \
XSpectraInputConverter, EPWInputConverter
from .exceptions import XmlDocumentError
from .utils import etree_iter_path
from .utils import etree_iter_path, get_target_prefix

logger = logging.getLogger('qeschema')

Expand All @@ -48,7 +47,7 @@ def removeprefix(s, prefix):
return s[len(prefix):] if s.startswith(prefix) else s


class XmlDocument(object):
class XmlDocument:
"""
Base class for a generic XML document based on an XSD schema. The schema
associated is used for checking types, validation of the XML data and for
Expand Down Expand Up @@ -118,11 +117,15 @@ def __init__(self, source=None, schema=None):
def namespaces(self):
"""
XML data namespaces map, a dictionary that maps prefixes to URI. An empty
dictionary if the XML data file is not loaded or it doesn't contain any
dictionary if the XML data file is not loaded or if it doesn't contain any
namespace declaration.
"""
return {k: v for k, v in self._namespaces.items()}

@property
def default_namespace(self):
return self._namespaces.get('', '')

@classmethod
def fetch_schema(cls, filename):
filename = filename.strip()
Expand Down Expand Up @@ -442,20 +445,25 @@ def __init__(self, source=None, schema=None, input_builder=None):
self.input_builder = self.DEFAULT_INPUT_BUILDER
elif not isinstance(input_builder, type) or \
not issubclass(input_builder, RawInputConverter):
msg = "3rd argument must be a {!r} subclass"
raise XmlDocumentError(msg.format(RawInputConverter))
raise XmlDocumentError(f"3rd argument must be a {RawInputConverter!r} subclass")
else:
self.input_builder = input_builder

self.default_namespace = self.schema.target_namespace
qe_prefixes = ['qes', 'neb', 'qes_ph', 'qes_lr', 'qes_spectrum',
'qes_xspectra', 'epw']
qe_nslist = list(map(self.schema.namespaces.get, qe_prefixes))
if self.default_namespace not in qe_nslist:
self._xpath_namespaces = {k: v for k, v in self.schema.namespaces.items() if k}

prefix = get_target_prefix(self.schema.namespaces, self.schema.target_namespace)
if prefix not in ('qes', 'neb', 'qes_ph', 'qes_lr',
'qes_spectrum', 'qes_xspectra', 'epw'):
raise NotImplementedError(
"Converter not implemented for this schema {}".format(self.default_namespace)
f"Converter not implemented for namespace {self.schema.target_namespace!r}"
)

def _xsd_find(self, path, xsd_element=None):
if xsd_element is None:
return self.schema.find(path, self._xpath_namespaces)
else:
return xsd_element.find(path, self._xpath_namespaces)

@property
def input_path(self):
"""The path to XML input section."""
Expand Down Expand Up @@ -491,15 +499,15 @@ def get_fortran_input(self, use_defaults=True):
raise XmlDocumentError("Missing input {!r} in XML data!".format(input_path))

for schema_root in self.schema.elements.values():
if schema_root.find(input_path):
if self._xsd_find(input_path, schema_root):
break
else:
raise XmlDocumentError("Missing input element in XSD schema!")

# Extract values from input's subtree of the XML document
for elem, path in etree_iter_path(input_root, path=input_path):
rel_path = path.replace(input_path, '.')
xsd_element = schema_root.find(path)
xsd_element = self._xsd_find(path, schema_root)
if xsd_element is None:
logger.error("%r doesn't match any element!", path)
continue
Expand Down Expand Up @@ -546,7 +554,7 @@ def get_atomic_positions(self):
path = './/output//atomic_positions'
elem = self.find(path)
if elem is not None:
atomic_positions = self.schema.find(path).decode(elem)
atomic_positions = self._xsd_find(path).decode(elem)
atoms = atomic_positions.get('atom')
if not isinstance(atoms, list):
atoms = [atoms]
Expand All @@ -564,7 +572,7 @@ def get_cell_parameters(self):
path = './/output//cell'
elem = self.find(path)
if elem is not None:
cell = self.schema.find(path).decode(elem)
cell = self._xsd_find(path).decode(elem)
return [cell['a1'], cell['a2'], cell['a3']]

@requires_xml_data
Expand All @@ -577,7 +585,7 @@ def get_stress(self):
path = './/output//stress'
elem = self.find(path)
if elem is not None:
stress = self.schema.find(path).decode(elem)
stress = self._xsd_find(path).decode(elem)
try:
stress = stress['$']
except TypeError:
Expand All @@ -595,10 +603,9 @@ def get_forces(self):
path = './/output/forces'
elem = self.find(path)
if elem is not None:
forces = self.schema.find(path).decode(elem)
forces = self._xsd_find(path).decode(elem)
path = './/output//atomic_positions'
breakpoint()
atomic_positions = self.schema.find(path).decode(self.find(path))
atomic_positions = self._xsd_find(path).decode(self.find(path))
atoms = atomic_positions.get('atom', [])
if not isinstance(atoms, list):
atoms = [atoms]
Expand All @@ -616,7 +623,7 @@ def get_k_points(self):
:return: nested list with k_points
"""
path = './/output//ks_energies/k_point'
return [self.schema.find(path).decode(e)['$'] for e in self.findall(path)]
return [self._xsd_find(path).decode(e)['$'] for e in self.findall(path)]

@requires_xml_data
def get_ks_eigenvalues(self):
Expand All @@ -628,7 +635,7 @@ def get_ks_eigenvalues(self):
path = './/output//ks_energies/eigenvalues'
eigenvalues = []
for e in self.findall(path):
obj = self.schema.find(path).decode(e)
obj = self._xsd_find(path).decode(e)
if isinstance(obj, dict):
eigenvalues.append(obj['$']) # pragma: no cover
else:
Expand All @@ -644,7 +651,7 @@ def get_total_energy(self):
:return: total energy in Hartree Units
"""
path = './/output//etot'
return self.schema.find(path).decode(self.find(path))
return self._xsd_find(path).decode(self.find(path))


class PhononDocument(QeDocument):
Expand Down
Loading

0 comments on commit 1994864

Please sign in to comment.