Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add command-line option --allow-changes to iodata-convert #374

Merged
merged 3 commits into from
Jul 14, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
19 changes: 15 additions & 4 deletions iodata/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,14 @@ def parse_args():
parser.add_argument(
"-o", "--outfmt", help="Select the output format, overrides automatic detection."
)
parser.add_argument(
"-c",
"--allow-changes",
default=False,
action="store_true",
help="Allow reorganizing the input data to make it comatible with the output format. "
tovrstra marked this conversation as resolved.
Show resolved Hide resolved
"Warnings will be emitted for all changes made.",
)
parser.add_argument(
"-m",
"--many",
Expand All @@ -95,7 +103,7 @@ def parse_args():
return parser.parse_args()


def convert(infn, outfn, many, infmt, outfmt):
def convert(infn: str, outfn: str, many: bool, infmt: str, outfmt: str, allow_changes: bool):
tovrstra marked this conversation as resolved.
Show resolved Hide resolved
"""Convert file from one format to another.

Parameters
Expand All @@ -110,12 +118,15 @@ def convert(infn, outfn, many, infmt, outfmt):
The input format.
outfmt
The output format.
allow_changes
Allow prepare_dump functions to modify the data
to make it compatible with the output format.

"""
if many:
dump_many(load_many(infn, fmt=infmt), outfn, fmt=outfmt)
dump_many(load_many(infn, fmt=infmt), outfn, allow_changes=allow_changes, fmt=outfmt)
tovrstra marked this conversation as resolved.
Show resolved Hide resolved
else:
dump_one(load_one(infn, fmt=infmt), outfn, fmt=outfmt)
dump_one(load_one(infn, fmt=infmt), outfn, allow_changes=allow_changes, fmt=outfmt)


def main():
Expand All @@ -124,7 +135,7 @@ def main():
np.seterr(divide="raise", over="raise", invalid="raise")

args = parse_args()
convert(args.input, args.output, args.many, args.infmt, args.outfmt)
convert(args.input, args.output, args.many, args.infmt, args.outfmt, args.allow_changes)


if __name__ == "__main__":
Expand Down
99 changes: 58 additions & 41 deletions iodata/test/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,59 +18,91 @@
# --
"""Unit tests for iodata.__main__."""

import functools
import os
import subprocess
import sys
from functools import partial
from importlib.resources import as_file, files
from warnings import warn

import pytest
from numpy.testing import assert_allclose, assert_equal

from ..__main__ import convert
from ..__main__ import convert as convfn
from ..api import load_many, load_one
from ..utils import PrepareDumpError, PrepareDumpWarning


def _convscript(infn: str, outfn: str, many: bool, infmt: str, outfmt: str, allow_changes: bool):
tovrstra marked this conversation as resolved.
Show resolved Hide resolved
args = [sys.executable, "-m", "iodata.__main__", infn, outfn]
if many:
args.append("-m")
if infmt is not None:
args.append(f"--infmt={infmt}")
if outfmt is not None:
args.append(f"--outfmt={outfmt}")
if allow_changes:
args.append("-c")
try:
subprocess.run(args, check=True)
except subprocess.CalledProcessError as exc:
raise PrepareDumpError("Translated error") from exc
if allow_changes:
warn(PrepareDumpWarning("Some message"), stacklevel=2)


def _check_convert_one(myconvert, tmpdir):
outfn = os.path.join(tmpdir, "tmp.xyz")
with as_file(files("iodata.test.data").joinpath("hf_sto3g.fchk")) as infn:
myconvert(infn, outfn)
myconvert(infn, outfn, allow_changes=False)
iodata = load_one(outfn)
assert iodata.natom == 2
assert_equal(iodata.atnums, [9, 1])
assert_allclose(iodata.atcoords, [[0.0, 0.0, 0.190484394], [0.0, 0.0, -1.71435955]])


def test_convert_one_autofmt(tmpdir):
myconvert = functools.partial(convert, many=False, infmt=None, outfmt=None)
_check_convert_one(myconvert, tmpdir)
def _check_convert_one_changes(myconvert, tmpdir):
tovrstra marked this conversation as resolved.
Show resolved Hide resolved
tovrstra marked this conversation as resolved.
Show resolved Hide resolved
outfn = os.path.join(tmpdir, "tmp.mkl")
with as_file(files("iodata.test.data").joinpath("hf_sto3g.fchk")) as infn:
with pytest.raises(PrepareDumpError):
myconvert(infn, outfn, allow_changes=False)
assert not os.path.isfile(outfn)
with pytest.warns(PrepareDumpWarning):
myconvert(infn, outfn, allow_changes=True)
iodata = load_one(outfn)
assert iodata.natom == 2
assert_equal(iodata.atnums, [9, 1])
assert_allclose(iodata.atcoords, [[0.0, 0.0, 0.190484394], [0.0, 0.0, -1.71435955]])


def test_convert_one_manfmt(tmpdir):
myconvert = functools.partial(convert, many=False, infmt="fchk", outfmt="xyz")
@pytest.mark.parametrize("convert", [convfn, _convscript])
tovrstra marked this conversation as resolved.
Show resolved Hide resolved
tovrstra marked this conversation as resolved.
Show resolved Hide resolved
def test_convert_one_autofmt(tmpdir, convert):
myconvert = partial(convfn, many=False, infmt=None, outfmt=None)
_check_convert_one(myconvert, tmpdir)


def test_script_one_autofmt(tmpdir):
def myconvert(infn, outfn):
subprocess.run([sys.executable, "-m", "iodata.__main__", infn, outfn], check=True)
@pytest.mark.parametrize("convert", [convfn, _convscript])
def test_convert_one_autofmt_changes(tmpdir, convert):
myconvert = partial(convert, many=False, infmt=None, outfmt=None)
_check_convert_one_changes(myconvert, tmpdir)

_check_convert_one(myconvert, tmpdir)

@pytest.mark.parametrize("convert", [convfn, _convscript])
def test_convert_one_manfmt(tmpdir, convert):
myconvert = partial(convert, many=False, infmt="fchk", outfmt="xyz")
_check_convert_one(myconvert, tmpdir)

def test_script_one_manfmt(tmpdir):
def myconvert(infn, outfn):
subprocess.run(
[sys.executable, "-m", "iodata.__main__", infn, outfn, "-i", "fchk", "-o", "xyz"],
check=True,
)

_check_convert_one(myconvert, tmpdir)
@pytest.mark.parametrize("convert", [convfn, _convscript])
def test_convert_one_manfmt_changes(tmpdir, convert):
myconvert = partial(convert, many=False, infmt="fchk", outfmt="molekel")
_check_convert_one_changes(myconvert, tmpdir)


def _check_convert_many(myconvert, tmpdir):
outfn = os.path.join(tmpdir, "tmp.xyz")
with as_file(files("iodata.test.data").joinpath("peroxide_relaxed_scan.fchk")) as infn:
myconvert(infn, outfn)
myconvert(infn, outfn, allow_changes=False)
trj = list(load_many(outfn))
assert len(trj) == 13
for iodata in trj:
Expand All @@ -80,28 +112,13 @@ def _check_convert_many(myconvert, tmpdir):
assert_allclose(trj[5].atcoords[0], [0.0, 1.32466211, 0.0], atol=1e-5)


def test_convert_many_autofmt(tmpdir):
myconvert = functools.partial(convert, many=True, infmt=None, outfmt=None)
@pytest.mark.parametrize("convert", [convfn, _convscript])
def test_convert_many_autofmt(tmpdir, convert):
myconvert = partial(convert, many=True, infmt=None, outfmt=None)
_check_convert_many(myconvert, tmpdir)


def test_convert_many_manfmt(tmpdir):
myconvert = functools.partial(convert, many=True, infmt="fchk", outfmt="xyz")
_check_convert_many(myconvert, tmpdir)


def test_script_many_autofmt(tmpdir):
def myconvert(infn, outfn):
subprocess.run([sys.executable, "-m", "iodata.__main__", infn, outfn, "-m"], check=True)

_check_convert_many(myconvert, tmpdir)


def test_script_many_manfmt(tmpdir):
def myconvert(infn, outfn):
subprocess.run(
[sys.executable, "-m", "iodata.__main__", infn, outfn, "-m", "-i", "fchk", "-o", "xyz"],
check=True,
)

@pytest.mark.parametrize("convert", [convfn, _convscript])
def test_convert_many_manfmt(tmpdir, convert):
myconvert = partial(convert, many=True, infmt="fchk", outfmt="xyz")
_check_convert_many(myconvert, tmpdir)