From dddbfa073e929290ecfb0a063cf7c8ed027fca13 Mon Sep 17 00:00:00 2001 From: Noam Bernstein Date: Mon, 12 Dec 2022 16:01:34 -0500 Subject: [PATCH 1/3] Update test_doc_examples.py to correctly handle (at least specially) crafted `wfl` command line tests. Note that this patch does not include the files needed for the example test. --- docs/source/examples.cli.ipynb | 91 ++++++++++++++++++++++++++++++++++ tests/test_doc_examples.py | 48 +++++++++++++++--- 2 files changed, 132 insertions(+), 7 deletions(-) create mode 100644 docs/source/examples.cli.ipynb diff --git a/docs/source/examples.cli.ipynb b/docs/source/examples.cli.ipynb new file mode 100644 index 00000000..478a974c --- /dev/null +++ b/docs/source/examples.cli.ipynb @@ -0,0 +1,91 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "72125898", + "metadata": {}, + "source": [ + "# command line examples" + ] + }, + { + "cell_type": "markdown", + "id": "ad34d6f2", + "metadata": {}, + "source": [ + "## evaluating configurations" + ] + }, + { + "cell_type": "markdown", + "id": "699abc83", + "metadata": {}, + "source": [ + "A very simple example of evaluating energies/forces of a configuration with a machine learning potential.\n", + "\n", + "Potential and input files are stored in `docs/example_files/cli`, and example is written assuming that jupyter server is using the directory containing the example notebook as the current working directory." + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "id": "28d02f61", + "metadata": {}, + "outputs": [], + "source": [ + "# make sure for the purpose of this example that the output file doesn't exist yet\n", + "from pathlib import Path\n", + "Path(\"GAP_evaluated_wfl_eval_configs.xyz\").unlink()" + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "id": "6dec15c0", + "metadata": {}, + "outputs": [], + "source": [ + "!wfl eval gap -pf ../example_files/cli/GAP.xml \\\n", + " -i ../example_files/cli/wfl_eval_configs.xyz \\\n", + " -o GAP_evaluated_wfl_eval_configs.xyz" + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "id": "f91acc7b", + "metadata": { + "nbsphinx": "hidden" + }, + "outputs": [], + "source": [ + "import numpy as np\n", + "import ase.io\n", + "\n", + "for atoms in ase.io.read(\"GAP_evaluated_wfl_eval_configs.xyz\", \":\"):\n", + " assert np.allclose(atoms.arrays[\"ref_error_calc_forces\"], atoms.arrays[\"gap_forces\"], atol=1.0e-6)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.13" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/tests/test_doc_examples.py b/tests/test_doc_examples.py index 3500c277..3599cda6 100644 --- a/tests/test_doc_examples.py +++ b/tests/test_doc_examples.py @@ -1,5 +1,7 @@ import os import shutil +import re +from pathlib import Path import json import pytest @@ -10,20 +12,52 @@ def _get_coding_blocks(nb_file): nb = json.load(fo) return [''.join(cell['source']) for cell in nb['cells'] if cell['cell_type'] == 'code'] +def _fix_wfl(code_block): + if re.match(r"!\s*wfl\s+", code_block): + # join lines + code_block = re.sub(r"\\\n", " ", code_block) + # find args for cli runner + args_str = re.sub(r"^!\s*wfl\s+", "", code_block) + # run cli comand + code_block = ("from click.testing import CliRunner\n" + + "from wfl.cli.cli import cli\n" + + "import shlex\n" + + "runner = CliRunner()\n" + + f"runner.invoke(cli, shlex.split(\"\"\"{args_str}\"\"\"))") + + return code_block + @pytest.mark.parametrize( - ('nb_file', 'idx_execute'), + ('nb_file', 'idx_execute', 'wfl_cli'), ( - pytest.param('examples.buildcell.ipynb', 'all', id='buildcell', + pytest.param('examples.buildcell.ipynb', 'all', False, id='buildcell', marks=pytest.mark.skipif(not shutil.which("buildcell"), reason="buildcell not in PATH")), - pytest.param('examples.dimers.ipynb', 'all', id='dimer structures') + pytest.param('examples.dimers.ipynb', 'all', False, id='dimer structures'), + pytest.param('examples.cli.ipynb', 'all', True, id='command line interface') ) ) -def test_example(tmp_path, nb_file, idx_execute): + +def test_example(tmp_path, monkeypatch, nb_file, idx_execute, wfl_cli): basepath = os.path.join(f'{os.path.dirname(__file__)}/../docs/source') coding_blocks = _get_coding_blocks(f'{basepath}/{nb_file}') - code = '\n'.join([cb_i for idx_i, cb_i in enumerate(coding_blocks) if idx_execute == 'all' or idx_i in idx_execute]) + if wfl_cli: + coding_blocks_exec = [_fix_wfl(cb_i) for idx_i, cb_i in enumerate(coding_blocks) if idx_execute == 'all' or idx_i in idx_execute] + + example_name = nb_file.replace("examples.", "").replace(".ipynb", "") + + source_example_files_dir = (Path(__file__).parent.parent / "docs" / "example_files" / example_name) + if source_example_files_dir.is_dir(): + pytest_example_files_dir = tmp_path / "example_files" / example_name + shutil.copytree(source_example_files_dir, pytest_example_files_dir) + + # examples look for their files under "../example_files//", so move + # down one to allow the ".." to work + (tmp_path / "run").mkdir() + monkeypatch.chdir(tmp_path / "run") + else: + coding_blocks_exec = [cb_i for idx_i, cb_i in enumerate(coding_blocks) if idx_execute == 'all' or idx_i in idx_execute] + + code = '\n'.join(coding_blocks_exec) assert code is not '' - os.chdir(tmp_path) exec(code) - os.chdir('..') From 41099029077b54c80e21b4ebd9899c2ef97a9482 Mon Sep 17 00:00:00 2001 From: Noam Bernstein Date: Tue, 13 Dec 2022 09:31:02 -0500 Subject: [PATCH 2/3] More explanatory text for examples.cli.ipynb --- docs/source/examples.cli.ipynb | 46 ++++++++++++++++++++++++++++------ 1 file changed, 39 insertions(+), 7 deletions(-) diff --git a/docs/source/examples.cli.ipynb b/docs/source/examples.cli.ipynb index 478a974c..ed8c8212 100644 --- a/docs/source/examples.cli.ipynb +++ b/docs/source/examples.cli.ipynb @@ -23,24 +23,55 @@ "source": [ "A very simple example of evaluating energies/forces of a configuration with a machine learning potential.\n", "\n", - "Potential and input files are stored in `docs/example_files/cli`, and example is written assuming that jupyter server is using the directory containing the example notebook as the current working directory." + "Potential and input files are stored in `docs/example_files/cli`, and example is written assuming that jupyter server has the current working directory set to the directory containing this example notebook." + ] + }, + { + "cell_type": "markdown", + "id": "54564961", + "metadata": {}, + "source": [ + "### clean up files from previous runs" + ] + }, + { + "cell_type": "markdown", + "id": "fa31f4c8", + "metadata": {}, + "source": [ + "Make sure that old output files are not there, otherwise autoparallelization of workflow will skip the evaluation." ] }, { "cell_type": "code", - "execution_count": 36, - "id": "28d02f61", + "execution_count": 1, + "id": "197b7226", "metadata": {}, "outputs": [], "source": [ - "# make sure for the purpose of this example that the output file doesn't exist yet\n", "from pathlib import Path\n", - "Path(\"GAP_evaluated_wfl_eval_configs.xyz\").unlink()" + "Path(\"GAP_evaluated_wfl_eval_configs.xyz\").unlink(missing_ok=True)" + ] + }, + { + "cell_type": "markdown", + "id": "39ed76f2", + "metadata": {}, + "source": [ + "### evaluate using the command line" + ] + }, + { + "cell_type": "markdown", + "id": "b7147883", + "metadata": {}, + "source": [ + "Run a `wfl` command line one liner with standardized arguments for input and output files (all commands), and potential filename (standardized for all machine learning potentials subcommands: `ace`, `gap`, and `mace`)." ] }, { "cell_type": "code", - "execution_count": 37, + "execution_count": 2, "id": "6dec15c0", "metadata": {}, "outputs": [], @@ -52,13 +83,14 @@ }, { "cell_type": "code", - "execution_count": 38, + "execution_count": 3, "id": "f91acc7b", "metadata": { "nbsphinx": "hidden" }, "outputs": [], "source": [ + "# test that output is correct (hidden from nbsphinx autogenerated documentation)\n", "import numpy as np\n", "import ase.io\n", "\n", From 4bd61b46d249e92cd595ef4d79aa03357e04b02c Mon Sep 17 00:00:00 2001 From: gelzinyte Date: Tue, 31 Jan 2023 13:24:25 +0000 Subject: [PATCH 3/3] finish resolving merge conflict --- tests/test_doc_examples.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/tests/test_doc_examples.py b/tests/test_doc_examples.py index 33b0df38..3599cda6 100644 --- a/tests/test_doc_examples.py +++ b/tests/test_doc_examples.py @@ -32,13 +32,8 @@ def _fix_wfl(code_block): ( pytest.param('examples.buildcell.ipynb', 'all', False, id='buildcell', marks=pytest.mark.skipif(not shutil.which("buildcell"), reason="buildcell not in PATH")), -<<<<<<< HEAD pytest.param('examples.dimers.ipynb', 'all', False, id='dimer structures'), pytest.param('examples.cli.ipynb', 'all', True, id='command line interface') -======= - pytest.param('examples.dimers.ipynb', 'all', id='dimer structures'), - pytest.param('examples.select_fps.ipynb', 'all', id='select fps') ->>>>>>> main ) )