From 9610b4d43bfbeae7b8bccdb2df941f66e96c5916 Mon Sep 17 00:00:00 2001 From: dieJaegerIn Date: Fri, 10 Jul 2026 13:56:59 +0200 Subject: [PATCH 1/2] Document path resolution for CLI list files --- docs/tutorials/CLI.ipynb | 160 ++++++++++++++++++++++++++++++++++----- src/dcTMD/__main__.py | 7 +- 2 files changed, 145 insertions(+), 22 deletions(-) diff --git a/docs/tutorials/CLI.ipynb b/docs/tutorials/CLI.ipynb index ba107ad..d06994d 100644 --- a/docs/tutorials/CLI.ipynb +++ b/docs/tutorials/CLI.ipynb @@ -18,7 +18,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": null, "metadata": {}, "outputs": [ { @@ -27,47 +27,45 @@ "text": [ "Usage: python -m dcTMD [OPTIONS]\n", "\n", - " ------------------------- | dcTMD |\n", - " ------------------------- Calculate free energy and friction for given\n", - " constraint force files.\n", + " Calculate free energy and friction for given constraint force files.\n", + "\n", + " -------------------------\n", + " | dcTMD |\n", + " -------------------------\n", "\n", " Analysis tools for dissipation-corrected targeted molecular dynamics, which\n", " is an enhanced sampling method to enforce rare events in biomolecular\n", " systems. When publishing results gained with this python package, please\n", " cite the following publications: (1) Tänzel, Victor and Jäger, Miriam and\n", " Wolf, Steffen in preparation. (2) Wolf, Steffen, and Gerhard Stock.\n", - " \"Targeted molecular dynamics calculations of free energy profiles using a\n", - " nonequilibrium friction correction.\" Journal of chemical theory and\n", + " 'Targeted molecular dynamics calculations of free energy profiles using a\n", + " nonequilibrium friction correction.' Journal of chemical theory and\n", " computation 14.12 (2018): 6175- 6182.\n", "\n", "Options:\n", " -m, --mode [work|force] Use either work or force autocovariance function to\n", - " calculatedcTMD quantities. [default: work;\n", + " calculate dcTMD quantities. [default: work;\n", " required]\n", " -f, --file TEXT Input: File containing list of all constraint force\n", - " file namesor glob pattern e.g.\"*.xvg\" to generate a\n", - " list of all constraint force files using\n", + " file names or glob pattern e.g.\"*.xvg\" to generate\n", + " a list of all constraint force files using\n", " glob.glob() [required]\n", " -o, --outname PATH Output: Path/prefix of output names.\n", " -T, --temperature FLOAT Simulation temperature in K. [required]\n", " -vel, --velocity FLOAT Pulling velocity in nm/ps. [required]\n", " --res INTEGER Striding to reduce size of returned free energy and\n", - " friction [default: 1]\n", + " friction. [default: 1]\n", " -s, --sigma FLOAT Standard deviation of gaussian filter in nm.\n", - " --resamples INTEGER Number of resamples used in optional bootstrapping.\n", - " This is only available in mode work\n", - " -v, --verbose Enable verbose mode. [default: False]\n", - " -p, --plot Plots free energy and smoothed friction. [default:\n", - " False]\n", - " -sd, --save_dataset Save the Work/ForceSet class to file. [default:\n", - " False]\n", + " -v, --verbose Enable verbose mode.\n", + " -p, --plot Plots free energy and smoothed friction.\n", + " -sd, --save_dataset Save the Work/ForceSet instance to file.\n", " --help Show this message and exit.\n" ] } ], "source": [ "%%bash\n", - "python -m dcTMD" + "python -m dcTMD --help" ] }, { @@ -79,11 +77,133 @@ "# Usage example:\n", "python -m dcTMD -f '*.xvg' -o path/to/output/name -T 290.5 -v 0.001 --verbose --plot --save_dataset" ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### A note on input file paths\n", + "\n", + "The `-f` option accepts either a glob pattern or a text file that lists one force trajectory per line. The two forms resolve paths as follows:\n", + "\n", + "A glob pattern is expanded relative to the current working directory, exactly as in the shell.\n", + "\n", + "A list file is read line by line and the listed paths are passed through unchanged. They are therefore opened relative to the current working directory, not relative to the location of the list file." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "/home/mj/projects/dcTMD/docs/tutorials\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Traceback (most recent call last):\n", + " File \"\", line 198, in _run_module_as_main\n", + " File \"\", line 88, in _run_code\n", + " File \"/home/mj/projects/dcTMD/src/dcTMD/__main__.py\", line 189, in \n", + " main() # pragma: no cover\n", + " ^^^^^^\n", + " File \"/home/mj/miniconda3/envs/dctmd/lib/python3.12/site-packages/click/core.py\", line 1569, in __call__\n", + " return self.main(*args, **kwargs)\n", + " ^^^^^^^^^^^^^^^^^^^^^^^^^^\n", + " File \"/home/mj/miniconda3/envs/dctmd/lib/python3.12/site-packages/click/core.py\", line 1490, in main\n", + " rv = self.invoke(ctx)\n", + " ^^^^^^^^^^^^^^^^\n", + " File \"/home/mj/miniconda3/envs/dctmd/lib/python3.12/site-packages/click/core.py\", line 1353, in invoke\n", + " return ctx.invoke(self.callback, **ctx.params)\n", + " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n", + " File \"/home/mj/miniconda3/envs/dctmd/lib/python3.12/site-packages/click/core.py\", line 907, in invoke\n", + " return callback(*args, **kwargs)\n", + " ^^^^^^^^^^^^^^^^^^^^^^^^^\n", + " File \"/home/mj/projects/dcTMD/src/dcTMD/__main__.py\", line 166, in main\n", + " dataset.fit(filenames)\n", + " File \"<@beartype(dcTMD.storing.WorkSet.fit) at 0x77c64457dd00>\", line 85, in fit\n", + " File \"/home/mj/projects/dcTMD/src/dcTMD/storing.py\", line 197, in fit\n", + " _get_time_from_testfile(self)\n", + " File \"/home/mj/projects/dcTMD/src/dcTMD/storing.py\", line 104, in _get_time_from_testfile\n", + " forceorworkset.time_ = np.loadtxt(\n", + " ^^^^^^^^^^^\n", + " File \"/home/mj/miniconda3/envs/dctmd/lib/python3.12/site-packages/numpy/lib/_npyio_impl.py\", line 1381, in loadtxt\n", + " arr = _read(fname, dtype=dtype, comment=comment, delimiter=delimiter,\n", + " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n", + " File \"/home/mj/miniconda3/envs/dctmd/lib/python3.12/site-packages/numpy/lib/_npyio_impl.py\", line 1011, in _read\n", + " fh = np.lib._datasource.open(fname, 'rt', encoding=encoding)\n", + " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n", + " File \"/home/mj/miniconda3/envs/dctmd/lib/python3.12/site-packages/numpy/lib/_datasource.py\", line 192, in open\n", + " return ds.open(path, mode, encoding=encoding, newline=newline)\n", + " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n", + " File \"/home/mj/miniconda3/envs/dctmd/lib/python3.12/site-packages/numpy/lib/_datasource.py\", line 529, in open\n", + " raise FileNotFoundError(f\"{path} not found.\")\n", + "FileNotFoundError: testdata/t_middle_01_pullf.xvg not found.\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "failed as expected\n" + ] + } + ], + "source": [ + "%%bash\n", + "# The list file exists, but the paths inside have the form \n", + "# 'testdata/t_middle_01_pullf.xvg'\n", + "# Thus, the following command fails when run from the project root.\n", + "pwd\n", + "python -m dcTMD -f ../../tests/testdata/pullf_filenames.dat -o /tmp/dcTMD_fails -T 290.15 -vel 0.001" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "When `dcTMD` is invoked from the `tests/` directory the run is successfull." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Loading & integrating force files: 100%|██████████| 18/18 [00:00<00:00, 123.53it/s]t/s]\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "save file /tmp/dcTMD_list_work_N18.dat\n", + "save file /tmp/dcTMD_list_work_N18.npz\n" + ] + } + ], + "source": [ + "%%bash\n", + "cd ../../tests\n", + "pwd\n", + "python -m dcTMD -f testdata/pullf_filenames.dat \\\n", + " -o /tmp/dcTMD_list -T 290.15 -vel 0.001" + ] } ], "metadata": { "kernelspec": { - "display_name": "base", + "display_name": "dctmd", "language": "python", "name": "python3" }, @@ -97,7 +217,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.7" + "version": "3.12.13" }, "orig_nbformat": 4 }, diff --git a/src/dcTMD/__main__.py b/src/dcTMD/__main__.py index d852865..52eceff 100755 --- a/src/dcTMD/__main__.py +++ b/src/dcTMD/__main__.py @@ -31,8 +31,11 @@ '--file', 'pullf_files', required=True, - help='Input: File containing list of all constraint force file names ' - + 'or glob pattern e.g."*.xvg" to generate a list of all constraint ' + help='Input: File containing a list of all constraint force file names ' + + 'listing one constraint force file per line. Paths inside a list file ' + + 'are resolved relative to the current working directory, not relative ' + + 'to the location of the list file. ' + + 'Or glob pattern e.g. "*.xvg" to generate a list of all constraint ' + 'force files using glob.glob()', ) @click.option( From 8b0989ee45245e65de423dbc91c77f6990c0d93e Mon Sep 17 00:00:00 2001 From: dieJaegerIn Date: Fri, 10 Jul 2026 18:30:04 +0200 Subject: [PATCH 2/2] clean up cell output --- docs/tutorials/CLI.ipynb | 86 +++++++++------------------------------- 1 file changed, 19 insertions(+), 67 deletions(-) diff --git a/docs/tutorials/CLI.ipynb b/docs/tutorials/CLI.ipynb index d06994d..d5c9c38 100644 --- a/docs/tutorials/CLI.ipynb +++ b/docs/tutorials/CLI.ipynb @@ -18,7 +18,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 1, "metadata": {}, "outputs": [ { @@ -46,10 +46,14 @@ " -m, --mode [work|force] Use either work or force autocovariance function to\n", " calculate dcTMD quantities. [default: work;\n", " required]\n", - " -f, --file TEXT Input: File containing list of all constraint force\n", - " file names or glob pattern e.g.\"*.xvg\" to generate\n", - " a list of all constraint force files using\n", - " glob.glob() [required]\n", + " -f, --file TEXT Input: File containing a list of all constraint\n", + " force file names listing one constraint force file\n", + " per line. Paths inside a list file are resolved\n", + " relative to the current working directory, not\n", + " relative to the location of the list file. Or glob\n", + " pattern e.g. \"*.xvg\" to generate a list of all\n", + " constraint force files using glob.glob()\n", + " [required]\n", " -o, --outname PATH Output: Path/prefix of output names.\n", " -T, --temperature FLOAT Simulation temperature in K. [required]\n", " -vel, --velocity FLOAT Pulling velocity in nm/ps. [required]\n", @@ -95,66 +99,7 @@ "cell_type": "code", "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "/home/mj/projects/dcTMD/docs/tutorials\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Traceback (most recent call last):\n", - " File \"\", line 198, in _run_module_as_main\n", - " File \"\", line 88, in _run_code\n", - " File \"/home/mj/projects/dcTMD/src/dcTMD/__main__.py\", line 189, in \n", - " main() # pragma: no cover\n", - " ^^^^^^\n", - " File \"/home/mj/miniconda3/envs/dctmd/lib/python3.12/site-packages/click/core.py\", line 1569, in __call__\n", - " return self.main(*args, **kwargs)\n", - " ^^^^^^^^^^^^^^^^^^^^^^^^^^\n", - " File \"/home/mj/miniconda3/envs/dctmd/lib/python3.12/site-packages/click/core.py\", line 1490, in main\n", - " rv = self.invoke(ctx)\n", - " ^^^^^^^^^^^^^^^^\n", - " File \"/home/mj/miniconda3/envs/dctmd/lib/python3.12/site-packages/click/core.py\", line 1353, in invoke\n", - " return ctx.invoke(self.callback, **ctx.params)\n", - " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n", - " File \"/home/mj/miniconda3/envs/dctmd/lib/python3.12/site-packages/click/core.py\", line 907, in invoke\n", - " return callback(*args, **kwargs)\n", - " ^^^^^^^^^^^^^^^^^^^^^^^^^\n", - " File \"/home/mj/projects/dcTMD/src/dcTMD/__main__.py\", line 166, in main\n", - " dataset.fit(filenames)\n", - " File \"<@beartype(dcTMD.storing.WorkSet.fit) at 0x77c64457dd00>\", line 85, in fit\n", - " File \"/home/mj/projects/dcTMD/src/dcTMD/storing.py\", line 197, in fit\n", - " _get_time_from_testfile(self)\n", - " File \"/home/mj/projects/dcTMD/src/dcTMD/storing.py\", line 104, in _get_time_from_testfile\n", - " forceorworkset.time_ = np.loadtxt(\n", - " ^^^^^^^^^^^\n", - " File \"/home/mj/miniconda3/envs/dctmd/lib/python3.12/site-packages/numpy/lib/_npyio_impl.py\", line 1381, in loadtxt\n", - " arr = _read(fname, dtype=dtype, comment=comment, delimiter=delimiter,\n", - " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n", - " File \"/home/mj/miniconda3/envs/dctmd/lib/python3.12/site-packages/numpy/lib/_npyio_impl.py\", line 1011, in _read\n", - " fh = np.lib._datasource.open(fname, 'rt', encoding=encoding)\n", - " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n", - " File \"/home/mj/miniconda3/envs/dctmd/lib/python3.12/site-packages/numpy/lib/_datasource.py\", line 192, in open\n", - " return ds.open(path, mode, encoding=encoding, newline=newline)\n", - " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n", - " File \"/home/mj/miniconda3/envs/dctmd/lib/python3.12/site-packages/numpy/lib/_datasource.py\", line 529, in open\n", - " raise FileNotFoundError(f\"{path} not found.\")\n", - "FileNotFoundError: testdata/t_middle_01_pullf.xvg not found.\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "failed as expected\n" - ] - } - ], + "outputs": [], "source": [ "%%bash\n", "# The list file exists, but the paths inside have the form \n", @@ -173,14 +118,21 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 4, "metadata": {}, "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "/home/mj/projects/dcTMD/tests\n" + ] + }, { "name": "stderr", "output_type": "stream", "text": [ - "Loading & integrating force files: 100%|██████████| 18/18 [00:00<00:00, 123.53it/s]t/s]\n" + "Loading & integrating force files: 100%|██████████| 18/18 [00:00<00:00, 152.05it/s]t/s]\n" ] }, {