Skip to content

Commit

Permalink
Add marda-extractors support for PW and CP input files (#73)
Browse files Browse the repository at this point in the history
  • Loading branch information
edan-bainglass committed Jan 12, 2024
1 parent c0e3c51 commit 8c50b4c
Show file tree
Hide file tree
Showing 9 changed files with 207 additions and 0 deletions.
6 changes: 6 additions & 0 deletions src/qe_tools/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,9 @@ class InputValidationError(Exception):
The input data for a calculation did not validate (e.g., missing
required input data, wrong data, ...)
"""


class PathIsNotAFile(OSError):
"""
The path is pointing to an object that is not a valid file.
"""
55 changes: 55 additions & 0 deletions src/qe_tools/extractors/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# -*- coding: utf-8 -*-
from pathlib import Path

from qe_tools.exceptions import PathIsNotAFile
from qe_tools.parsers import CpInputFile, PwInputFile

SUPPORTED_PARSERS = [
'PW',
'CP',
]


def extract(filepath: str, parser: str) -> dict:
"""Extract QE input file as a dictionary.
Parameters
----------
`filepath` : `str`
The path to the file.
`parser` : `str`
The QE parser type. Supported: ['PW', 'CP']
Returns
-------
`dict`
The parsed input as a dictionary.
Raises
------
`FileNotFoundError`
If the file does not exist.
`PathIsNotAFile`
If the path does not point to a file.
`ValueError`
If the parser type is not supported.
"""

path = Path(filepath)

if not path.exists():
raise FileNotFoundError(f"'{filepath}' does not exist")

if not path.is_file():
raise PathIsNotAFile(f"'{filepath}' is not a valid file.")

input_str = path.read_text(encoding='utf-8')
parser = parser.upper()

if parser == 'PW':
return PwInputFile(input_str).as_dict()

if parser == 'CP':
return CpInputFile(input_str).as_dict()

raise ValueError(f"Supported parsers: {SUPPORTED_PARSERS}; given: '{parser}'")
6 changes: 6 additions & 0 deletions src/qe_tools/parsers/_input_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,12 @@ def __init__(self, content, *, qe_version=None, validate_species_names=True):
qe_version=self._qe_version
)

def as_dict(self) -> dict:
"""Return parsed data as dictionary."""
return {
'structure': self.structure,
}


def _str2val(valstr):
"""
Expand Down
8 changes: 8 additions & 0 deletions src/qe_tools/parsers/_pw_input.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,14 @@ def __init__(self, content, *, qe_version=None, validate_species_names=True):
# Parse the K_POINTS card.
self.k_points = parse_k_points(self._input_txt)

def as_dict(self) -> dict:
"""Return parsed data as dictionary."""
dictionary = super().as_dict()
dictionary.update({
'k-points': self.k_points,
})
return dictionary


def parse_k_points(txt):
"""
Expand Down
49 changes: 49 additions & 0 deletions tests/data/cp.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
&control
title = ' Water Molecule ',
calculation = 'cp',
restart_mode = 'from_scratch',
ndr = 51,
ndw = 51,
nstep = 100,
iprint = 100,
isave = 100,
tstress = .TRUE.,
tprnfor = .TRUE.,
dt = 5.0d0,
etot_conv_thr = 1.d-9,
ekin_conv_thr = 1.d-4,
prefix = 'h2o'
verbosity = 'medium'
/
&system
ibrav = 14,
celldm(1) = 12.0,
celldm(2) = 1.0,
celldm(3) = 1.0,
celldm(4) = 0.0,
celldm(5) = 0.0,
celldm(6) = 0.0,
nat = 3,
ntyp = 2,
nbnd = 4,
ecutwfc = 80.0,
/
&electrons
emass = 400.d0,
emass_cutoff = 2.5d0,
orthogonalization = 'ortho',
electron_dynamics = 'damp',
electron_damping = 0.2
/
&ions
ion_dynamics = 'none',
ion_radius(1) = 0.8d0,
ion_radius(2) = 0.8d0,
/
ATOMIC_SPECIES
O 16.0d0 O.blyp-mt.UPF
H 1.00d0 H.blyp-vbc.UPF
ATOMIC_POSITIONS (bohr)
O 0.0099 0.0099 0.0000 0 0 0
H 1.8325 -0.2243 -0.0001 1 1 1
H -0.2243 1.8325 0.0002 1 1 1
20 changes: 20 additions & 0 deletions tests/data/extractor_ref/cp.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"structure": {
"positions": [
[0.005238854365041, 0.005238854365041, 0.0],
[0.9697172347411749, -0.11869444788673698, -5.2917720858999996e-5],
[-0.11869444788673698, 0.9697172347411749, 0.00010583544171799999]
],
"species": {
"names": ["O", "H"],
"masses": [16.0, 1.0],
"pseudo_file_names": ["O.blyp-mt.UPF", "H.blyp-vbc.UPF"]
},
"cell": [
[6.350126503079999, 0.0, 0.0],
[0.0, 6.350126503079999, 0.0],
[0.0, 0.0, 6.350126503079999]
],
"atom_names": ["O", "H", "H"]
}
}
21 changes: 21 additions & 0 deletions tests/data/extractor_ref/pw.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"structure": {
"positions": [
[0.005238854365041, 0.005238854365041, 0.0],
[0.9697172347411749, -0.11869444788673698, -5.2917720858999996e-5],
[-0.11869444788673698, 0.9697172347411749, 0.00010583544171799999]
],
"species": {
"names": ["O", "H"],
"masses": [16.0, 1.0],
"pseudo_file_names": ["O.blyp-mt.UPF", "H.blyp-vbc.UPF"]
},
"cell": [
[6.350126503079999, 0.0, 0.0],
[0.0, 6.350126503079999, 0.0],
[0.0, 0.0, 6.350126503079999]
],
"atom_names": ["O", "H", "H"]
},
"k-points": { "type": "gamma" }
}
27 changes: 27 additions & 0 deletions tests/data/pw.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
&control
calculation = 'relax',
/
&system
ibrav = 1,
celldm(1) = 12.0,
nat = 3,
ntyp = 2,
nbnd = 4,
ecutwfc = 80,
ecutfock=160,
input_dft = 'X3LYP'
exxdiv_treatment = 'gygi-baldereschi'
x_gamma_extrapolation = .TRUE.
/
&electrons
/
&ions
/
ATOMIC_SPECIES
O 16.0d0 O.blyp-mt.UPF
H 1.00d0 H.blyp-vbc.UPF
ATOMIC_POSITIONS (bohr)
O 0.0099 0.0099 0.0000
H 1.8325 -0.2243 -0.0001
H -0.2243 1.8325 0.0002
K_POINTS gamma
15 changes: 15 additions & 0 deletions tests/test_extractor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# -*- coding: utf-8 -*-
import json
from pathlib import Path

import pytest

from qe_tools.extractors import extract


@pytest.mark.parametrize('parser', ['pw', 'cp'])
def test_input_dict_extraction(parser: str):
datadir = Path(__file__).resolve().parent / 'data'
filepath = datadir / f'{parser}.in'
refpath = datadir / 'extractor_ref' / f'{parser}.json'
assert extract(filepath.as_posix(), parser) == json.loads(refpath.read_text())

0 comments on commit 8c50b4c

Please sign in to comment.