Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
52 changes: 0 additions & 52 deletions .github/workflows/dev_ci.yml

This file was deleted.

11 changes: 4 additions & 7 deletions .github/workflows/master_ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
strategy:
fail-fast: false
matrix:
python-version: ["3.12"]
python-version: ["3.14"]

steps:
- uses: actions/checkout@v3
Expand All @@ -27,12 +27,9 @@ jobs:
- name: Install dependencies
run: |
python -m pip install --upgrade pip
python -m pip install flake8 pytest
if [ -f requirements.txt ]; then pip install -r requirements.txt
else pip install -e .; fi
- name: Lint with flake8
run: |
flake8
python -m pip install . --group dev
- name: Lint with ruff
uses: pre-commit/action@v3.0.1
- name: Test with pytest
run: |
pytest
9 changes: 9 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# update the pinned versions by running:
# pre-commit autoupdate
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.16.3
hooks:
- id: ruff-check
args: [--fix]
- id: ruff-format
21 changes: 11 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ default name hint.
import json
from molbench import ExternalParser


def my_parser(filepath):
raw = json.load(open(filepath))
name = raw["name"]
Expand All @@ -121,18 +122,17 @@ def my_parser(filepath):
"gs": {
"basis": raw["basis"],
"method": raw["method"],
"data": {
"energy": {"value": raw["energy"], "unit": "au"}
},
"data": {"energy": {"value": raw["energy"], "unit": "au"}},
}
}
return name, system_data, state_data


computed = ExternalParser().load(
"/path/to/output/dir",
parser=my_parser,
out_suffix=".out", # scan for files with this extension
assignment_suffix=".ass", # companion assignment files (optional)
out_suffix=".out", # scan for files with this extension
assignment_suffix=".ass", # companion assignment files (optional)
)
```

Expand All @@ -144,7 +144,7 @@ For excitation spectra benchmarks you will typically also need
```python
from molbench import Comparison

c = Comparison() # default separators: ("basis", "method")
c = Comparison() # default separators: ("basis", "method")
c.add(bench)
c.add(computed)

Expand Down Expand Up @@ -179,8 +179,8 @@ errors_rel = stats.compare(
interest={"method": "my_method"},
reference={"method": "TBE"},
relative=True,
relative_damping=0.01, # adds 0.01 to |ref| in denominator
error_thresh=0.5, # warn if |error| > threshold
relative_damping=0.01, # adds 0.01 to |ref| in denominator
error_thresh=0.5, # warn if |error| > threshold
)
```

Expand Down Expand Up @@ -306,8 +306,7 @@ for post-processing.
from molbench import CompressedTemplateConstructor

tc = CompressedTemplateConstructor("pyscf_ordmp2")
tc.create_inputs(bench, "/path/to/inputs", calc,
reference_path="references.json")
tc.create_inputs(bench, "/path/to/inputs", calc, reference_path="references.json")
```

---
Expand Down Expand Up @@ -416,6 +415,7 @@ from molbench import Statistics, register_as_error_measure
from molbench.statistics import _collect_errors
import numpy as np


@register_as_error_measure
def winsorized_mae(signed_errors, assign):
errors = _collect_errors(signed_errors, assign)
Expand All @@ -424,6 +424,7 @@ def winsorized_mae(signed_errors, assign):
arr = np.clip(np.abs(errors), None, np.percentile(np.abs(errors), 90))
return float(arr.mean()), len(errors)


result = stats.evaluate(errors, "winsorized_mae", proptype="energy")
```

Expand Down
39 changes: 26 additions & 13 deletions molbench/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,37 @@
TemplateConstructor, and LatexExporter for the package's API.
"""

from .configuration import config
from .benchmark_parser import JSONBenchmarkParser
from .input_constructor import (InputConstructor, TemplateConstructor,
CompressedTemplateConstructor)
from .bash_wrapper import create_bash_files, make_send_script
from .benchmark_parser import JSONBenchmarkParser
from .comparison import Comparison
from .statistics import Statistics, register_as_error_measure
from .configuration import config
from .export import Exporter, LatexExporter
from .external_parser import ExternalParser
from .molecule import Molecule
from .input_constructor import (
CompressedTemplateConstructor,
InputConstructor,
TemplateConstructor,
)
from .json_encoder import MolbenchJSONEncoder
from .molecule import Molecule
from .statistics import Statistics, register_as_error_measure

__all__ = ["config", "Molecule", "InputConstructor",
"TemplateConstructor", "CompressedTemplateConstructor",
"create_bash_files", "make_send_script",
"JSONBenchmarkParser",
"Comparison", "Statistics", "register_as_error_measure",
"Exporter", "LatexExporter",
"ExternalParser", "MolbenchJSONEncoder"]
__all__ = [
"Comparison",
"CompressedTemplateConstructor",
"Exporter",
"ExternalParser",
"InputConstructor",
"JSONBenchmarkParser",
"LatexExporter",
"MolbenchJSONEncoder",
"Molecule",
"Statistics",
"TemplateConstructor",
"config",
"create_bash_files",
"make_send_script",
"register_as_error_measure",
]
__version__ = "0.0.1"
__authors__ = ["Linus Bjarne Dittmer", "Jonas Leitner"]
46 changes: 28 additions & 18 deletions molbench/assignment.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
from collections.abc import Callable

from . import logger as log
from typing import Callable


def new_assignment_file(state_ids: list, comment_token: str = "#",
id_separator: str = "==>",
null_token: str = "null") -> str:
def new_assignment_file(
state_ids: list,
comment_token: str = "#",
id_separator: str = "==>",
null_token: str = "null",
) -> str:
"""
Creates the content of an assignment file.

Expand All @@ -26,12 +30,14 @@ def new_assignment_file(state_ids: list, comment_token: str = "#",
return content


def parse_assignment_file(assignmentfile: str,
comment_token: str = "#",
id_separator: str = "==>",
null_token: str = "null",
import_external: Callable | None = None,
import_ref: Callable | None = None) -> dict:
def parse_assignment_file(
assignmentfile: str,
comment_token: str = "#",
id_separator: str = "==>",
null_token: str = "null",
import_external: Callable | None = None,
import_ref: Callable | None = None,
) -> dict:
"""
Parses an assignment file.

Expand Down Expand Up @@ -71,15 +77,16 @@ def parse_assignment_file(assignmentfile: str,
# - read both state id's and import them if necessary
assignment = assignment.split(id_separator)
if len(assignment) != 2:
log.critical(f"Invalid use of state id separator {id_separator} in"
f" line {line} of assignment file {assignmentfile}.",
"Assignment: parse_assignment_file")
log.critical(
f"Invalid use of state id separator {id_separator} in"
f" line {line} of assignment file {assignmentfile}.",
"Assignment: parse_assignment_file",
)

ref = assignment[0].strip()
external = assignment[1].strip()
if ref == null_token or external == null_token: # skip not assigned
log.warning(f"Unassigned state in file {assignmentfile}.",
"Assigment")
log.warning(f"Unassigned state in file {assignmentfile}.", "Assigment")
continue

if import_external is not None:
Expand All @@ -88,8 +95,11 @@ def parse_assignment_file(assignmentfile: str,
ref = import_ref(ref)

if external in state_assignments:
log.warning(f"The external state {external} in assignment file "
f"{assignmentfile} is assigned twice. Overwriting the "
"first assignment.", "Assigment")
log.warning(
f"The external state {external} in assignment file "
f"{assignmentfile} is assigned twice. Overwriting the "
"first assignment.",
"Assigment",
)
state_assignments[external] = ref
return state_assignments
35 changes: 18 additions & 17 deletions molbench/bash_wrapper.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import os
import glob
import os
import subprocess
import typing

from . import logger as log
from .configuration import config
from .functions import substitute_template
from . import config


def create_bash_files(files: list, command: str) -> list:
Expand Down Expand Up @@ -34,28 +35,28 @@ def create_bash_files(files: list, command: str) -> list:
try:
infilename = os.path.basename(f)
cmd = command.strip() + " " + infilename
log.info(f"Now building script for {infilename}: {f}",
"Bash Wrapper")
result = subprocess.run(cmd, shell=True)
log.info(f"Now building script for {infilename}: {f}", "Bash Wrapper")
result = subprocess.run(cmd, shell=True, check=False)
if getattr(result, "returncode", 0) != 0:
log.error(f"Command failed for {infilename} (exit code "
f"{result.returncode}): {cmd}", "Bash Wrapper")
log.error(
f"Command failed for {infilename} (exit code "
f"{result.returncode}): {cmd}",
"Bash Wrapper",
)
log.debug(f"Executing command : {cmd}", "Bash Wrapper")

fname_no_ext = os.path.splitext(infilename)[0]
all_shs = glob.glob("*.sh")
all_shs.extend(glob.glob("*.sbatch"))

local_execs = [os.path.abspath(sh) for sh in all_shs
if fname_no_ext in sh]
local_execs = [os.path.abspath(sh) for sh in all_shs if fname_no_ext in sh]
bash_files.extend(local_execs)
finally:
os.chdir(basepath)
return bash_files


def make_send_script(bashfiles: list, send_command: str,
sendscript: typing.IO):
def make_send_script(bashfiles: list, send_command: str, sendscript: typing.IO):
"""
Generate a script for sending all jobscripts to a cluster.

Expand All @@ -72,19 +73,19 @@ def make_send_script(bashfiles: list, send_command: str,
sendscript_content = (
"#!/bin/bash\n"
"function cd_and_sbatch() {\n"
" local script_file=\"$1\"\n"
" local folder=\"$2\"\n"
" echo \"Sending $script_file\"\n"
" cd \"$folder\"\n"
f" {send_command.strip()} \"$script_file\"\n"
' local script_file="$1"\n'
' local folder="$2"\n'
' echo "Sending $script_file"\n'
' cd "$folder"\n'
f' {send_command.strip()} "$script_file"\n'
"}\n\n"
)

for f in bashfiles:
fpath = os.path.abspath(os.path.dirname(f))
infilename = os.path.basename(f)

addendum = f"cd_and_sbatch \"{infilename}\" \"{fpath}\"\n"
addendum = f'cd_and_sbatch "{infilename}" "{fpath}"\n'
sendscript_content += addendum

sendscript.write(sendscript_content)
Loading
Loading