From ae58f7dc1a515a59b6fa7cc9b5965e44139e9ef8 Mon Sep 17 00:00:00 2001 From: ElliottKasoar Date: Thu, 28 Mar 2024 12:02:18 +0000 Subject: [PATCH] Add tests for CLI yaml summary --- tests/test_geomopt_cli.py | 82 ++++++++++++++++++++++++++++++++--- tests/test_md_cli.py | 3 +- tests/test_singlepoint_cli.py | 73 ++++++++++++++++++++++++++++--- 3 files changed, 143 insertions(+), 15 deletions(-) diff --git a/tests/test_geomopt_cli.py b/tests/test_geomopt_cli.py index 4ccbe0ae..730b1b9b 100644 --- a/tests/test_geomopt_cli.py +++ b/tests/test_geomopt_cli.py @@ -5,6 +5,7 @@ from ase.io import read import pytest from typer.testing import CliRunner +import yaml from janus_core.cli import app from tests.utils import read_atoms @@ -14,7 +15,7 @@ runner = CliRunner() -def test_geomopt_help(): +def test_help(): """Test calling `janus geomopt --help`.""" result = runner.invoke(app, ["geomopt", "--help"]) assert result.exit_code == 0 @@ -22,9 +23,11 @@ def test_geomopt_help(): assert "Usage: root geomopt [OPTIONS]" in result.stdout -def test_geomopt(): +def test_geomopt(tmp_path): """Test geomopt calculation.""" results_path = Path("./NaCl-opt.xyz").absolute() + summary_path = tmp_path / "summary.yml" + assert not results_path.exists() result = runner.invoke( @@ -35,6 +38,8 @@ def test_geomopt(): DATA_PATH / "NaCl.cif", "--max-force", "0.2", + "--summary", + summary_path, ], ) @@ -42,9 +47,11 @@ def test_geomopt(): assert result.exit_code == 0 -def test_geomopt_log(tmp_path, caplog): +def test_log(tmp_path, caplog): """Test log correctly written for geomopt.""" results_path = tmp_path / "NaCl-opt.xyz" + log_path = tmp_path / "test.log" + summary_path = tmp_path / "summary.yml" with caplog.at_level("INFO", logger="janus_core.geom_opt"): result = runner.invoke( @@ -56,7 +63,9 @@ def test_geomopt_log(tmp_path, caplog): "--out", results_path, "--log", - f"{tmp_path}/test.log", + log_path, + "--summary", + summary_path, ], ) assert result.exit_code == 0 @@ -64,10 +73,11 @@ def test_geomopt_log(tmp_path, caplog): assert "Using filter" not in caplog.text -def test_geomopt_traj(tmp_path): +def test_traj(tmp_path): """Test trajectory correctly written for geomopt.""" results_path = tmp_path / "NaCl-opt.xyz" traj_path = f"{tmp_path}/test.xyz" + summary_path = tmp_path / "summary.yml" result = runner.invoke( app, @@ -79,6 +89,8 @@ def test_geomopt_traj(tmp_path): results_path, "--traj", traj_path, + "--summary", + summary_path, ], ) assert result.exit_code == 0 @@ -89,6 +101,7 @@ def test_geomopt_traj(tmp_path): def test_fully_opt(tmp_path, caplog): """Test passing --fully-opt without --vectors-only""" results_path = tmp_path / "NaCl-opt.xyz" + summary_path = tmp_path / "summary.yml" with caplog.at_level("INFO", logger="janus_core.geom_opt"): result = runner.invoke( @@ -100,6 +113,8 @@ def test_fully_opt(tmp_path, caplog): "--out", results_path, "--fully-opt", + "--summary", + summary_path, ], ) assert result.exit_code == 0 @@ -114,6 +129,9 @@ def test_fully_opt(tmp_path, caplog): def test_fully_opt_and_vectors(tmp_path, caplog): """Test passing --fully-opt with --vectors-only.""" results_path = tmp_path / "NaCl-opt.xyz" + log_path = tmp_path / "test.log" + summary_path = tmp_path / "summary.yml" + with caplog.at_level("INFO", logger="janus_core.geom_opt"): result = runner.invoke( app, @@ -126,7 +144,9 @@ def test_fully_opt_and_vectors(tmp_path, caplog): "--out", results_path, "--log", - f"{tmp_path}/test.log", + log_path, + "--summary", + summary_path, ], ) assert result.exit_code == 0 @@ -141,6 +161,8 @@ def test_fully_opt_and_vectors(tmp_path, caplog): def test_vectors_not_fully_opt(tmp_path, caplog): """Test passing --vectors-only without --fully-opt.""" results_path = tmp_path / "NaCl-opt.xyz" + summary_path = tmp_path / "summary.yml" + with caplog.at_level("INFO", logger="janus_core.geom_opt"): result = runner.invoke( app, @@ -151,15 +173,19 @@ def test_vectors_not_fully_opt(tmp_path, caplog): "--out", results_path, "--vectors-only", + "--summary", + summary_path, ], ) assert result.exit_code == 0 assert "hydrostatic_strain: True" in caplog.text -def duplicate_traj(tmp_path): +def test_duplicate_traj(tmp_path): """Test trajectory file cannot be not passed via traj_kwargs.""" traj_path = tmp_path / "NaCl-traj.xyz" + summary_path = tmp_path / "summary.yml" + result = runner.invoke( app, [ @@ -168,6 +194,8 @@ def duplicate_traj(tmp_path): DATA_PATH / "NaCl.cif", "--opt-kwargs", f"{{'trajectory': '{str(traj_path)}'}}", + "--summary", + summary_path, ], ) assert result.exit_code == 1 @@ -179,6 +207,7 @@ def test_restart(tmp_path): data_path = DATA_PATH / "NaCl-deformed.cif" restart_path = tmp_path / "NaCl-res.pkl" results_path = tmp_path / "NaCl-opt.xyz" + summary_path = tmp_path / "summary.yml" result = runner.invoke( app, @@ -192,6 +221,8 @@ def test_restart(tmp_path): f"{{'restart': '{str(restart_path)}'}}", "--steps", 2, + "--summary", + summary_path, ], ) assert result.exit_code == 0 @@ -210,9 +241,46 @@ def test_restart(tmp_path): f"{{'restart': '{str(restart_path)}'}}", "--steps", 2, + "--summary", + summary_path, ], ) assert result.exit_code == 0 atoms = read(results_path) final_energy = atoms.get_potential_energy() assert final_energy < intermediate_energy + + +def test_summary(tmp_path): + """Test summary file can be read correctly.""" + results_path = tmp_path / "NaCl-results.xyz" + summary_path = tmp_path / "summary.yml" + + result = runner.invoke( + app, + [ + "geomopt", + "--struct", + DATA_PATH / "NaCl.cif", + "--out", + results_path, + "--summary", + summary_path, + ], + ) + + assert result.exit_code == 0 + + # Read geomopt summary file + with open(summary_path, encoding="utf8") as file: + md_summary = yaml.safe_load(file) + + assert "command" in md_summary[0] + assert "janus geomopt" in md_summary[0]["command"] + assert "start_time" in md_summary[1] + assert "end_time" in md_summary[3] + + assert "inputs" in md_summary[2] + assert "opt_kwargs" in md_summary[2]["inputs"] + assert "struct" in md_summary[2]["inputs"] + assert "n_atoms" in md_summary[2]["inputs"]["struct"] diff --git a/tests/test_md_cli.py b/tests/test_md_cli.py index 50cec36d..66920450 100644 --- a/tests/test_md_cli.py +++ b/tests/test_md_cli.py @@ -67,7 +67,7 @@ def test_md(ensemble, tmp_path): assert isinstance(atoms, Atoms) -def test_md_log(tmp_path, caplog): +def test_log(tmp_path, caplog): """Test log correctly written for MD.""" file_prefix = tmp_path / "nvt-T300" stats_path = tmp_path / "nvt-T300-stats.dat" @@ -225,6 +225,7 @@ def test_summary(tmp_path): summary = yaml.safe_load(file) assert "command" in summary[0] + assert "janus md" in summary[0]["command"] assert "start_time" in summary[1] assert "inputs" in summary[2] assert "end_time" in summary[3] diff --git a/tests/test_singlepoint_cli.py b/tests/test_singlepoint_cli.py index be25aa05..98a783fb 100644 --- a/tests/test_singlepoint_cli.py +++ b/tests/test_singlepoint_cli.py @@ -4,6 +4,7 @@ from ase.io import read from typer.testing import CliRunner +import yaml from janus_core.cli import app from tests.utils import read_atoms @@ -29,9 +30,10 @@ def test_singlepoint_help(): assert "Usage: root singlepoint [OPTIONS]" in result.stdout -def test_singlepoint(): +def test_singlepoint(tmp_path): """Test singlepoint calculation.""" results_path = Path("./NaCl-results.xyz").absolute() + summary_path = tmp_path / "summary.yml" result = runner.invoke( app, @@ -39,19 +41,23 @@ def test_singlepoint(): "singlepoint", "--struct", DATA_PATH / "NaCl.cif", + "--summary", + summary_path, ], ) + # Check atoms can read read, then delete file atoms = read_atoms(results_path) assert result.exit_code == 0 assert atoms.get_potential_energy() is not None assert "forces" in atoms.arrays -def test_singlepoint_properties(tmp_path): +def test_properties(tmp_path): """Test properties for singlepoint calculation.""" results_path_1 = tmp_path / "H2O-energy-results.xyz" results_path_2 = tmp_path / "H2O-stress-results.xyz" + summary_path = tmp_path / "summary.yml" # Check energy is can be calculated successfully result = runner.invoke( @@ -64,6 +70,8 @@ def test_singlepoint_properties(tmp_path): "energy", "--out", results_path_1, + "--summary", + summary_path, ], ) assert result.exit_code == 0 @@ -81,6 +89,8 @@ def test_singlepoint_properties(tmp_path): "stress", "--out", results_path_2, + "--summary", + summary_path, ], ) assert result.exit_code == 1 @@ -88,9 +98,10 @@ def test_singlepoint_properties(tmp_path): assert isinstance(result.exception, ValueError) -def test_singlepoint_read_kwargs(tmp_path): +def test_read_kwargs(tmp_path): """Test setting read_kwargs for singlepoint calculation.""" results_path = tmp_path / "benzene-traj-results.xyz" + summary_path = tmp_path / "summary.yml" result = runner.invoke( app, @@ -104,6 +115,8 @@ def test_singlepoint_read_kwargs(tmp_path): results_path, "--property", "energy", + "--summary", + summary_path, ], ) assert result.exit_code == 0 @@ -115,6 +128,7 @@ def test_singlepoint_read_kwargs(tmp_path): def test_singlepoint_calc_kwargs(tmp_path): """Test setting calc_kwargs for singlepoint calculation.""" results_path = tmp_path / "NaCl-results.xyz" + summary_path = tmp_path / "summary.yml" result = runner.invoke( app, @@ -128,15 +142,20 @@ def test_singlepoint_calc_kwargs(tmp_path): results_path, "--property", "energy", + "--summary", + summary_path, ], ) assert result.exit_code == 0 assert "Using float32 for MACECalculator" in result.stdout -def test_singlepoint_log(tmp_path, caplog): +def test_log(tmp_path, caplog): """Test log correctly written for singlepoint.""" results_path = tmp_path / "NaCl-results.xyz" + log_path = tmp_path / "test.log" + summary_path = tmp_path / "summary.yml" + with caplog.at_level("INFO", logger="janus_core.single_point"): result = runner.invoke( app, @@ -144,13 +163,53 @@ def test_singlepoint_log(tmp_path, caplog): "singlepoint", "--struct", DATA_PATH / "NaCl.cif", - "--property", - "energy", "--out", results_path, + "--property", + "energy", "--log", - f"{tmp_path}/test.log", + log_path, + "--summary", + summary_path, ], ) assert "Starting single point calculation" in caplog.text assert result.exit_code == 0 + + +def test_summary(tmp_path): + """Test summary file can be read correctly.""" + results_path = tmp_path / "benzene-traj-results.xyz" + summary_path = tmp_path / "summary.yml" + + result = runner.invoke( + app, + [ + "singlepoint", + "--struct", + DATA_PATH / "benzene-traj.xyz", + "--read-kwargs", + "{'index': ':'}", + "--out", + results_path, + "--summary", + summary_path, + ], + ) + + assert result.exit_code == 0 + + # Read singlepoint summary file + with open(summary_path, encoding="utf8") as file: + sp_summary = yaml.safe_load(file) + + assert "command" in sp_summary[0] + assert "janus singlepoint" in sp_summary[0]["command"] + assert "start_time" in sp_summary[1] + assert "inputs" in sp_summary[2] + assert "end_time" in sp_summary[3] + + assert "traj" in sp_summary[2]["inputs"] + assert "length" in sp_summary[2]["inputs"]["traj"] + assert "struct" in sp_summary[2]["inputs"]["traj"] + assert "n_atoms" in sp_summary[2]["inputs"]["traj"]["struct"]