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

Input Writer #188

Merged
merged 26 commits into from
Mar 12, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
3c30091
Add Gaussian input writer
wilhadams Aug 19, 2020
75e0b53
Add write_input to api.py module
FarnazH Aug 19, 2020
41aa3eb
Test gaussian input writer using default template
FarnazH Aug 20, 2020
0ea6726
Test gaussian input writer using user template
FarnazH Aug 20, 2020
9e5b2e6
Add __all__ to inputs/gaussian.py module
FarnazH Aug 20, 2020
17d5c88
Fix pylint no-else-return and wrong-import-order
FarnazH Aug 20, 2020
2709a3e
Fix docstring & update default values
FarnazH Aug 20, 2020
7cef0c9
Add write_input function for orca
lmacaya Aug 22, 2020
c4d733e
Add tests for orca write_input
lmacaya Aug 22, 2020
27a2a61
Add template for orca write_input testing
lmacaya Aug 22, 2020
eb988c3
Add __init__.py for relative import
lmacaya Aug 22, 2020
2a9961a
Add default docstring to gaussian write_input for future decorator
lmacaya Aug 22, 2020
e4eb9b2
Use attr.asdict in Gaussian input writer
FarnazH Aug 24, 2020
713e410
Add expected test inputs to test/data/input_*.txt
FarnazH Aug 24, 2020
8319cf0
Remove empty lines & unsed import in test_inputs
FarnazH Aug 24, 2020
a87c0f2
Add recurse=False in orca module & remove test fix
FarnazH Aug 24, 2020
23442ef
Remove attrname argument of _select_input_module
FarnazH Aug 24, 2020
328f38b
Fix pylint wrong-import-order
FarnazH Aug 24, 2020
0891a63
Add header to __init__
lmacaya Sep 2, 2020
6027980
Add write_input decorator and minor fixes
lmacaya Sep 4, 2020
22233a4
Update docstrings in api.py
lmacaya Sep 4, 2020
6a97b0d
Add populate_fields func to common
lmacaya Sep 4, 2020
28c167c
Reformat gaussian write_input
lmacaya Sep 4, 2020
0f628f8
Reformat orca write_input
lmacaya Sep 4, 2020
2d32f4e
Merge branch 'master' into write_input
tovrstra Mar 12, 2021
5c68134
Fix minor issues and include input writer in docs
tovrstra Mar 12, 2021
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
2 changes: 1 addition & 1 deletion .cardboardlint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ iodata/overlap_accel.c
doc/_build
doc/pyapi
doc/formats.rst
doc/inputs.rst
doc/formats_tab.inc

# Distribution / packaging
Expand Down
7 changes: 5 additions & 2 deletions doc/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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 <https://www.attrs.org>`_ 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.
Expand Down
4 changes: 3 additions & 1 deletion doc/gen_docs.sh
Original file line number Diff line number Diff line change
@@ -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
71 changes: 71 additions & 0 deletions doc/gen_inputs.py
Original file line number Diff line number Diff line change
@@ -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 <http://www.gnu.org/licenses/>
# --
# 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()
36 changes: 36 additions & 0 deletions doc/getting_started.rst
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ Getting Started

IOData can be used to read and write different quantum chemistry file formats.


Script usage
------------

Expand All @@ -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
^^^^^^^

Expand Down Expand Up @@ -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
^^^^^^^^^^^^

Expand All @@ -156,6 +191,7 @@ IOData can be used to store data in a consistent format for writing at a future

.. _units:


Unit conversion
^^^^^^^^^^^^^^^

Expand Down
1 change: 1 addition & 0 deletions doc/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ User Documentation
install
getting_started
formats
inputs
basis
changelog
acknowledgments
Expand Down
68 changes: 65 additions & 3 deletions iodata/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
from .utils import LineIterator


__all__ = ['load_one', 'load_many', 'dump_one', 'dump_many']
__all__ = ['load_one', 'load_many', 'dump_one', 'dump_many', 'write_input']


def _find_format_modules():
Expand Down Expand Up @@ -78,6 +78,41 @@ def _select_format_module(filename: str, attrname: str, fmt: str = None) -> Modu
attrname, filename))


def _find_input_modules():
"""Return all input modules found with importlib."""
result = {}
for module_info in iter_modules(import_module('iodata.inputs').__path__):
if not module_info.ispkg:
input_module = import_module('iodata.inputs.' + module_info.name)
if hasattr(input_module, "write_input"):
result[module_info.name] = input_module
return result


INPUT_MODULES = _find_input_modules()


def _select_input_module(fmt: str) -> ModuleType:
"""Find an input module.

Parameters
----------
fmt
The name of the input module to use.

Returns
-------
format_module
The module implementing the required input format.

"""
if fmt in INPUT_MODULES:
if not hasattr(INPUT_MODULES[fmt], 'write_input'):
raise ValueError(f'{fmt} input module does not have write_input!')
return INPUT_MODULES[fmt]
raise ValueError(f"Could not find input format {fmt}!")


def load_one(filename: str, fmt: str = None, **kwargs) -> IOData:
"""Load data from a file.

Expand All @@ -104,9 +139,10 @@ def load_one(filename: str, fmt: str = None, **kwargs) -> IOData:
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]:
Expand Down Expand Up @@ -177,7 +213,7 @@ def dump_many(iodatas: Iterator[IOData], filename: str, fmt: str = None, **kwarg
----------
iodatas
An iterator over IOData instances.
filename : str
filename
The file to write the data to.
fmt
The name of the file format module to use.
Expand All @@ -188,3 +224,29 @@ def dump_many(iodatas: Iterator[IOData], filename: str, fmt: str = None, **kwarg
format_module = _select_format_module(filename, 'dump_many', fmt)
with open(filename, 'w') as f:
format_module.dump_many(f, iodatas, **kwargs)


def write_input(iodata: IOData, filename: str, fmt: str, template: str = None, **kwargs):
"""Write input file using an instance of IOData for the specified software format.

Parameters
----------
iodata
An IOData instance containing the information needed to write input.
filename
The input file name.
fmt
The name of the software for which input file is generated.
template
The template input file.
**kwargs
Keyword arguments are passed on to the input-specific write_input function.

"""
input_module = _select_input_module(fmt)
# load template as a string
if template is not None:
with open(template, 'r') as t:
template = t.read()
with open(filename, 'w') as f:
input_module.write_input(f, iodata, template=template, **kwargs)
Loading