From ee99dd0cb091f346fd8538cf553b4606a31fe761 Mon Sep 17 00:00:00 2001 From: Toon Verstraelen Date: Fri, 12 Mar 2021 07:13:08 +0100 Subject: [PATCH] Fix minor issues and include input writer in doc --- .cardboardlint.yml | 2 +- .gitignore | 1 + doc/changelog.rst | 7 ++-- doc/gen_docs.sh | 4 ++- doc/gen_inputs.py | 71 +++++++++++++++++++++++++++++++++++++++++ doc/getting_started.rst | 36 +++++++++++++++++++++ doc/index.rst | 1 + iodata/api.py | 14 ++++---- 8 files changed, 126 insertions(+), 10 deletions(-) create mode 100755 doc/gen_inputs.py diff --git a/.cardboardlint.yml b/.cardboardlint.yml index d54cb1b5f..1dd42a944 100644 --- a/.cardboardlint.yml +++ b/.cardboardlint.yml @@ -3,7 +3,7 @@ linters: packages: ['iodata'] - namespace: filefilter: ['- */__init__.py', '- */test_*.py', '- *setup.py', '- tools/*', - '- doc/conf.py', '+ *.py', '+ *.pyx'] + '- doc/*.py', '+ *.py', '+ *.pyx'] - pylint: - pycodestyle: config: .pycodestylerc diff --git a/.gitignore b/.gitignore index 06fa58e17..5464cf493 100644 --- a/.gitignore +++ b/.gitignore @@ -20,6 +20,7 @@ iodata/overlap_accel.c doc/_build doc/pyapi doc/formats.rst +doc/inputs.rst doc/formats_tab.inc # Distribution / packaging diff --git a/doc/changelog.rst b/doc/changelog.rst index fcb4f978a..d16cc0d20 100644 --- a/doc/changelog.rst +++ b/doc/changelog.rst @@ -31,14 +31,17 @@ modernized and ported to Python 3. In this process, the API was seriously refactored, essentially designed from scratch. Compared to HORTON2, IOData 1.0.0 contains the following API-breaking changes: -* The user-facing API is now a set of four *free* functions: +* The user-facing API is now a set of five functions: :py:func:`iodata.api.load_one`, :py:func:`iodata.api.dump_one`, - :py:func:`iodata.api.load_many`, :py:func:`iodata.api.dump_many`. + :py:func:`iodata.api.load_many`, :py:func:`iodata.api.dump_many` and + :py:func:`iodata.api.write_input`. * The :py:class:`iodata.iodata.IOData` object is implemented with the `attrs `_ module, which facilites type hinting and checking. * The ``load_many`` and ``dump_many`` functions can handle trajectories and database formats. (At the time of writing, only XYZ and FCHK are supported.) +* The ``write_input`` function can be used to prepare inputs for quantum + chemistry software. This function supports user-provided templates. * IOData does not impose a specific ordering of the atomic orbital basis functions (within one shell). Practically all possible conventions are supported and one can easily convert from one to another. diff --git a/doc/gen_docs.sh b/doc/gen_docs.sh index 9d2d25669..6ae96ec72 100755 --- a/doc/gen_docs.sh +++ b/doc/gen_docs.sh @@ -1,6 +1,8 @@ #!/usr/bin/env bash -SPHINX_APIDOC_OPTIONS=members,undoc-members,show-inheritance,inherited-members sphinx-apidoc -o pyapi/ ../iodata/ ../iodata/test/test_*.py ../iodata/test/cached --separate +SPHINX_APIDOC_OPTIONS=members,undoc-members,show-inheritance,inherited-members sphinx-apidoc \ + -a -o pyapi/ ../iodata/ --separate ./gen_formats.py > formats.rst +./gen_inputs.py > inputs.rst ./gen_formats_tab.py > formats_tab.inc diff --git a/doc/gen_inputs.py b/doc/gen_inputs.py new file mode 100755 index 000000000..15eadac48 --- /dev/null +++ b/doc/gen_inputs.py @@ -0,0 +1,71 @@ +#!/usr/bin/env python3 +# IODATA is an input and output module for quantum chemistry. +# Copyright (C) 2011-2019 The IODATA Development Team +# +# This file is part of IODATA. +# +# IODATA is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 3 +# of the License, or (at your option) any later version. +# +# IODATA is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, see +# -- +# pylint: disable=unused-argument,redefined-builtin +"""Generate formats.rst.""" + +from gen_formats import _format_words, _print_section +from iodata.api import INPUT_MODULES + + +__all__ = [] + + +HEADER = """ + +.. _input_formats: + +Supported Input Formats +####################### + +""" + + +def main(): + """Write inputs.rst to stdout.""" + print(HEADER) + for modname, module in sorted(INPUT_MODULES.items()): + if not hasattr(module, "write_input"): + continue + lines = module.__doc__.split('\n') + # add labels for cross-referencing format (e.g. in formats table) + print(f".. _input_{modname}:") + print() + _print_section('{} (``{}``)'.format(lines[0][:-1], modname), "=") + print() + for line in lines[2:]: + print(line) + + _print_section(":py:func:`iodata.formats.{}.write_input`".format(modname), "-") + fn = getattr(module, "write_input", None) + print("- Requires", _format_words(fn.required)) + if fn.optional: + print("- May use", _format_words(fn.optional)) + if fn.kwdocs: + print("- Keyword arguments", _format_words(fn.kwdocs)) + if fn.notes: + print() + print(fn.notes) + print() + print() + print() + + +if __name__ == '__main__': + main() diff --git a/doc/getting_started.rst b/doc/getting_started.rst index b526ad7d2..9ec383c53 100644 --- a/doc/getting_started.rst +++ b/doc/getting_started.rst @@ -25,6 +25,7 @@ Getting Started IOData can be used to read and write different quantum chemistry file formats. + Script usage ------------ @@ -36,12 +37,14 @@ The simplest way to use IOData, without writing any code is to use the ``iodata- See the :code:`--help` option for more details on usage. + Code usage ---------- More complex use cases can be implemented in Python, using IOData as a library. IOData stores an object containing the data read from the file. + Reading ^^^^^^^ @@ -139,6 +142,38 @@ example. The following approach would be more memory efficient. dump_many(itermols(), "traj2.xyz") +Input files +^^^^^^^^^^^ + +IOData can be used to write input files for quantum-chemistry software. By +default minimal settings are used, which can be changed if needed. For example, +the following will prepare a Gaussian input for a HF/STO-3G calculation from +a PDB file: + +.. code-block:: python + + from iodata import load_one, write_input + + write_input(load_one("water.pdf"), "water.com", fmt="gaussian") + +The level of theory and other settings can be modified by setting corresponding +attributes in the IOData object: + +.. code-block:: python + + from iodata import load_one, write_input + + mol = load_one("water.pdf") + mol.lot = "B3LYP" + mol.obasis_name = "6-31g*" + mol.run_type = "opt" + write_input(mold, "water.com", fmt="gaussian") + +The run types can be any of the following: ``energy``, ``energy_force``, +``opt``, ``scan`` or ``freq``. These are translated into program-specific +keywords when the file is written. + + Data storage ^^^^^^^^^^^^ @@ -156,6 +191,7 @@ IOData can be used to store data in a consistent format for writing at a future .. _units: + Unit conversion ^^^^^^^^^^^^^^^ diff --git a/doc/index.rst b/doc/index.rst index 153b97861..f7886c0a8 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -70,6 +70,7 @@ User Documentation install getting_started formats + inputs basis changelog acknowledgments diff --git a/iodata/api.py b/iodata/api.py index 732313d43..abe65d036 100644 --- a/iodata/api.py +++ b/iodata/api.py @@ -83,8 +83,9 @@ def _find_input_modules(): result = {} for module_info in iter_modules(import_module('iodata.inputs').__path__): if not module_info.ispkg: - format_module = import_module('iodata.inputs.' + module_info.name) - result[module_info.name] = format_module + input_module = import_module('iodata.inputs.' + module_info.name) + if hasattr(input_module, "write_input"): + result[module_info.name] = input_module return result @@ -92,7 +93,7 @@ def _find_input_modules(): def _select_input_module(fmt: str) -> ModuleType: - """Find an input module with the requested attribute name. + """Find an input module. Parameters ---------- @@ -131,16 +132,17 @@ def load_one(filename: str, fmt: str = None, **kwargs) -> IOData: Returns ------- - out: IOData + out The instance of IOData with data loaded from the input files. """ format_module = _select_format_module(filename, 'load_one', fmt) lit = LineIterator(filename) try: - return IOData(**format_module.load_one(lit, **kwargs)) + iodata = IOData(**format_module.load_one(lit, **kwargs)) except StopIteration: lit.error("File ended before all data was read.") + return iodata def load_many(filename: str, fmt: str = None, **kwargs) -> Iterator[IOData]: @@ -162,7 +164,7 @@ def load_many(filename: str, fmt: str = None, **kwargs) -> Iterator[IOData]: Yields ------ - out: IOData + out An instance of IOData with data for one frame loaded for the file. """