Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
5893f59
initial package outline, ignore some style checks (2h.43m)
kvnchv Mar 25, 2023
cbd2bf2
feat: parameter space definition (1h.11m)
kvnchv Mar 26, 2023
df21aa8
feat: solver supported parameters, subprocess run
kvnchv Mar 26, 2023
cb9d149
feat: xml results parsing
kvnchv Mar 26, 2023
f46e108
manager convergence loop
kvnchv Mar 27, 2023
df6c252
feat: solver parameterization for k-points
kvnchv Mar 28, 2023
d6a923c
docs: update README and notes to solver method, use regex match.
kvnchv Mar 28, 2023
1473f9e
fix: change tolerance check to relative
kvnchv Mar 29, 2023
7ff2880
test: small solver tests, docstrings
kvnchv Mar 29, 2023
fe2b949
fix: relative tolerance abs value, catch convergence failed
kvnchv Mar 30, 2023
e315c14
tests: add demo notebook
kvnchv Mar 30, 2023
4a78e65
add requirements
kvnchv Mar 30, 2023
1e91a95
ci: basic workflow
kvnchv Mar 30, 2023
5ca42b5
ci: exclude local solver required
kvnchv Mar 30, 2023
f993745
ci: fix workflow
kvnchv Mar 30, 2023
a441e2e
ci: lower python version
kvnchv Mar 30, 2023
0bf80ef
ci: python version typo
kvnchv Mar 30, 2023
fa259e4
test: specify test rootdir, update notebook
kvnchv Mar 30, 2023
1863e7b
test: add missing test resources
kvnchv Mar 30, 2023
fc01439
ci: try adding quantum-espresso install to workflow
kvnchv Mar 30, 2023
f6de367
ci: requires sudo
kvnchv Mar 30, 2023
9516e88
ci: build from source, fix tmpdir
kvnchv Mar 30, 2023
c5038c9
fix: workflow typo
kvnchv Mar 30, 2023
b8f741d
fix: qe-version
kvnchv Mar 30, 2023
fadeabd
ci: remove workflow
kvnchv Mar 30, 2023
0fba924
small style/naming fixes
kvnchv Mar 31, 2023
5c28dbb
re-add workflow.yml
kvnchv Mar 31, 2023
7da0ada
move to top level
kvnchv Mar 31, 2023
024016d
add explicit runner solver path
kvnchv Mar 31, 2023
6bf9d59
coverage
kvnchv Mar 31, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions .github/workflows/workflow.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
name: CodeCov
on: [push, pull_request]
jobs:
run:
runs-on: ubuntu-latest
env:
OS: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Python
uses: actions/setup-python@v4
with:
python-version: "3.10"
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r kvnchv/requirements.txt
pip install -e kvnchv/
pip install pytest pytest-cov pytest-assume
- name: Build qe from source
run: |
sudo apt-get install -y --no-install-recommends \
autoconf \
build-essential \
ca-certificates \
gfortran \
libblas3 \
libc6 \
libfftw3-dev \
libgcc-s1 \
liblapack-dev \
wget
cd $HOME
wget https://gitlab.com/QEF/q-e/-/archive/qe-7.1/q-e-qe-7.1.tar.gz
tar -xzf q-e-qe-7.1.tar.gz
cd q-e-qe-7.1
./configure
make pw
echo 'export PATH="$HOME/q-e-qe-7.1/bin:$PATH"' >> ~/.bashrc
source ~/.bashrc
- name: Run pytest
run: |
pytest --rootdir=/home/runner/work/rewotes/rewotes/kvnchv/ --cov=kvnchv/converger --cov-report=xml --verbose kvnchv/tests
- name: Build coverage report
run: coverage lcov
- name: Upload coverage reports to Codecov
uses: codecov/codecov-action@v3
with:
files: ./coverage.lcov
Binary file added kvnchv/.coverage
Binary file not shown.
13 changes: 13 additions & 0 deletions kvnchv/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# environment
venv
.vscode/

# packaging
*.egg-info/

# Byte-compiled / optimized / DLL files
__pycache__/

# testing files
tests/.pytest_cache
tests/resources/*/outdir/
13 changes: 13 additions & 0 deletions kvnchv/.pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
repos:
- repo: https://github.com/pycqa/flake8
rev: 5.0.4
hooks:
- id: flake8
additional_dependencies:
- flake8-import-order
args: ['--config=kvnchv/setup.cfg']
- repo: https://github.com/pycqa/pydocstyle
rev: 4.0.0 # pick a git hash / tag to point to
hooks:
- id: pydocstyle
args: ['--config=kvnchv/setup.cfg']
62 changes: 62 additions & 0 deletions kvnchv/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# K-point convergence tracker (Materials)

> Ideal candidate: scientists skilled in Density Functional Theory and proficient in python.

# Overview

The aim of this task is to create a python package that implements automatic convergence tracking mechanism for a materials simulations engine. The convergence is tracked with respect to the k-point sampling inside a reciprocal cell of a crystalline compound.

# Requirements

1. automatically find the dimensions of a k-point mesh that satisfy a certain criteria for total energy (eg. total energy is converged within dE = 0.01meV)
1. the code shall be written in a way that can facilitate easy addition of convergence wrt other characteristics extracted from simulations (forces, pressures, phonon frequencies etc)
1. the code shall support VASP or Quantum ESPRESSO

# Expectations

- correctly find k-point mesh that satisfies total energy convergence parameters for a set of 10 materials, starting from Si2, as simplest, to a 10-20-atom supercell of your choice
- modular and object-oriented implementation
- commit early and often - at least once per 24 hours

# Timeline

We leave exact timing to the candidate. Must fit Within 5 days total.

# User story

As a user of this software I can start it passing:

- path to input data (eg. pw.in / POSCAR, INCAR, KPOINTS) and
- kinetic energy cutoff

as parameters and get the k-point dimensions (eg. 5 5 5).

# Operating assumptions

The assumed directory structure is:
```
rundir/
pw.in
pseudo/
potentialfile.UPF
```

The input file should be named `pw.in` and is assumed to have the following:

&CONTROL section contains the following fixed parameters
- outdir = 'outdir'
- pseudo_dir = 'pseudo'
- prefix='__prefix__',
- tstress = .true.
- tprnfor = .true.

Automatically generated uniform of k-points, i.e. `K_POINTS automatic`
- K_POINTS is the last "card" in the input


# Notes

- *create an account at exabyte.io and use it for the calculation purposes*
- Could authenticate API but not submit jobs succesfully.
- Package tests are with a local quantum ESPRESSO install.
- suggested modeling engine: Quantum ESPRESSO
3 changes: 3 additions & 0 deletions kvnchv/converger/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from .manager import Manager
from .solvers import EspressoSolver
from .version import __version__
29 changes: 29 additions & 0 deletions kvnchv/converger/exceptions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
"""Custom exceptions."""


class InputFilesNotFoundError(Exception):
"""Error when the provided input path string is not a valid Path."""


class TargetOutputNotFoundError(Exception):
"""Error when requested target variable was not found in solver results."""


class SolverSubprocessFailedError(Exception):
"""Return processed subprocess error."""


class SolverNotImplementedError(Exception):
"""Error when the requested solver is not implemented."""


class SolverNotInstalledError(Exception):
"""Error when the requested solver is not installed."""


class UnsupportedParameterError(Exception):
"""Error when provided parameters are unsupported."""


class EspressoOutputNotFoundError(Exception):
"""Error when expected espresso output XML file was not found."""
130 changes: 130 additions & 0 deletions kvnchv/converger/manager.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
"""Convergence workflow manager."""
import itertools
from copy import deepcopy
from pathlib import Path

import numpy as np
import pandas as pd
from jsonschema import ValidationError, validate

from .exceptions import (InputFilesNotFoundError,
SolverNotImplementedError,
TargetOutputNotFoundError
)
from .schema import input_schema
from .solvers import EspressoSolver

solvers = {"espresso": EspressoSolver}


class Manager():
"""Main class for convergence workflow run."""

def __init__(self, input_dict: dict):
self.input_dict: dict = deepcopy(input_dict)
self.schema: dict = input_schema # schema to validate against

self.solver_input: dict # solver-configuration
self.input_path: Path
self.target: str # convergence target value name
self.tol: float # tolerance (absolute) to satisfy
self.parameter_space: list # list of input parameter specifications

self.data: pd.DataFrame # DataFrame for parameter sets and outputs

self.validate_schema()
self._parse_input()

def _parse_input(self):
"""Convert input dict to class attrs."""
for param, val in self.input_dict.items():
# special handling for input path
if param == "input_path":
if not Path(val).exists():
raise InputFilesNotFoundError
self.input_path = Path(val)
else:
setattr(self, param, val)

def validate_schema(self):
"""Validate against schema."""
try:
validate(self.input_dict, self.schema)
except ValidationError as err:
raise err
return True

def _get_solver(self, run_params: dict):
"""Initialize solver object."""
if self.solver_input["name"] not in solvers:
raise SolverNotImplementedError

return solvers[self.solver_input["name"]](
self.solver_input,
self.input_path,
run_params
)

def run(self):
"""Run convergence workflow."""
self.process_parameters()
return_target = np.zeros(len(self.data))
return_target.fill((np.nan))
return_reldelta = np.zeros(len(self.data))
return_reldelta.fill((np.nan))

# run first iteration
target_prev = 0.0
target_eval = self.run_solver(0)
return_target[0] = target_eval

rel_tol = (target_eval - target_prev) / target_eval
return_reldelta[0] = rel_tol
target_prev = target_eval

converged = False
for run_idx in range(1, len(self.data)):
if abs(rel_tol) < self.tol:
# assemble results dataframe
converged = True
break
target_eval = self.run_solver(run_idx)
return_target[run_idx] = target_eval

rel_tol = (target_eval - target_prev) / target_prev
return_reldelta[run_idx] = rel_tol
target_prev = target_eval

# update job results data
self.data[self.target] = return_target
self.data["relative_tol"] = return_reldelta
self.data = self.data.dropna()

if converged:
return 0
return 1

def run_solver(self, idx: int) -> float:
"""Run solver for parameter set 'idx' and extract target converence value."""
run_params = dict(self.data.iloc[idx])
solver = self._get_solver(run_params)
solver.run()
if self.target not in solver.results.keys():
err_string = (f"{self.target} not found in results."
f"Possible values are {list(solver.results.keys())}")
raise TargetOutputNotFoundError(err_string)
return solver.results[self.target]

def process_parameters(self):
"""Process parameter_space input values define simulation set."""
param_arrays = []
param_names = []
for param_d in deepcopy(self.parameter_space):
# if delta is not provided, assume unit step
del_p = 1.0 if "delta" not in param_d else param_d["delta"]
param_names.append(param_d["name"])
param_arrays.append(np.arange(param_d["start"], param_d["max"]+del_p, del_p))

self.data = pd.DataFrame(
list(itertools.product(*param_arrays)),
columns=param_names)
62 changes: 62 additions & 0 deletions kvnchv/converger/schema.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
"""Input schema for Manager convergence workflow."""

solver_schema = {
"description": "Solver parameters",
"type": "object",
"properties": {
"name": {
"description": "Name of solver type.",
"type": "string"
},
"solver_path": {
"description": "Explicit path to solver.",
"type": "string"
},
},
"required": ["name"]
}

input_schema = {
"type": "object",
"properties": {
"solver_input": solver_schema,
"input_path": {
"description": "Path containing required files for solver.",
"type": "string"
},
"tol": {
"description": "Minimum relative tolerance for target quantity.",
"type": "number"
},
"target": {
"description": "Output quantity to converge.",
"type": "string",
},
"parameter_space": {
"description": "Parametric space to evaluate convergence on.",
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {
"description": "Parameter name",
"type": "string"
},
"start": {
"description": "Initial parameter value.",
"type": "number"
},
"max": {
"description": "Final parameter value.",
"type": "number"
},
"delta": {
"description": "Step size between parameters"
}
},
"required": ["name", "start", "max"]
}
}
},
"required": ["solver_input", "input_path", "tol", "target", "parameter_space"]
}
1 change: 1 addition & 0 deletions kvnchv/converger/solvers/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from .espresso_solver import EspressoSolver
Loading