diff --git a/.clang-format b/.clang-format index 1c27bb2..a9b8259 100644 --- a/.clang-format +++ b/.clang-format @@ -34,7 +34,7 @@ AlwaysBreakBeforeMultilineStrings: false AlwaysBreakTemplateDeclarations: true BinPackArguments: true BinPackParameters: true -BraceWrapping: +BraceWrapping: AfterClass: false AfterControlStatement: false AfterEnum: false @@ -65,7 +65,7 @@ DisableFormat: false ExperimentalAutoDetectBinPacking: false FixNamespaceComments: true ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ] -IncludeCategories: +IncludeCategories: - Regex: '^"' Priority: 1 - Regex: '^<' diff --git a/.github/workflows/pull_request.yaml b/.github/workflows/pull_request.yaml index fcbd826..66c6fd7 100644 --- a/.github/workflows/pull_request.yaml +++ b/.github/workflows/pull_request.yaml @@ -24,7 +24,7 @@ jobs: check_formatting: uses: NWChemEx/.github/.github/workflows/check_formatting.yaml@master with: - license_config: ".github/.licenserc.yaml" + license_config: ".licenserc.yaml" cpp_source_dirs: "" test_nwx_docs: @@ -36,4 +36,3 @@ jobs: uses: NWChemEx/.github/.github/workflows/test_nwx_library.yaml@master with: compilers: '["gcc-11", "clang-14"]' - diff --git a/.github/.licenserc.yaml b/.licenserc.yaml similarity index 98% rename from .github/.licenserc.yaml rename to .licenserc.yaml index 5bab98d..3e2e6a5 100644 --- a/.github/.licenserc.yaml +++ b/.licenserc.yaml @@ -26,5 +26,6 @@ header: - docs/source/nitpick_exceptions - version.txt - cmake/friends.py.in + - build/ comment: never diff --git a/README.md b/README.md index 32a4dfa..41bf820 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ FriendZone [Documentation](https://nwchemex.github.io/FriendZone) -Provides SimDE compatible APIs so that NWChemEx can play nicely with its +Provides SimDE compatible APIs so that NWChemEx can play nicely with its friends, *i.e.*, this repo wraps existing electronic structure packages in modules so they can be called as submodules in NWChemEx. @@ -29,20 +29,20 @@ Packages supported through FriendZone include: - NWChem # Warnings -Existing packages come with a lot of different licenses. At the moment we only -consider interfaces to packages that have licenses compatible with -[SimDE](https://github.com/NWChemEx/SimDE) (and +Existing packages come with a lot of different licenses. At the moment we only +consider interfaces to packages that have licenses compatible with +[SimDE](https://github.com/NWChemEx/SimDE) (and [NWChemEx](https://github.com/NWChemEx/NWChemEx)). -Many of the other packages are by default configured/called suboptimally and -thus the modules in this repo should **NOT** be used for direct timing -comparisons unless otherwise noted. Performance contributions are greatly +Many of the other packages are by default configured/called suboptimally and +thus the modules in this repo should **NOT** be used for direct timing +comparisons unless otherwise noted. Performance contributions are greatly appreciated. # Installation -As with the majority of the NWChemEx stack, FriendZone uses CMake and the -[CMaize](https://github.com/CMakePP/CMaize) library for configuration and +As with the majority of the NWChemEx stack, FriendZone uses CMake and the +[CMaize](https://github.com/CMakePP/CMaize) library for configuration and building. This means that installation is usually achieved via a variation on: ```.sh @@ -63,6 +63,6 @@ More detailed install instructions can be found # Acknowledgments -This research was supported by the Exascale Computing Project (17-SC-20-SC), a -collaborative effort of the U.S. Department of Energy Office of Science and the -National Nuclear Security Administration. \ No newline at end of file +This research was supported by the Exascale Computing Project (17-SC-20-SC), a +collaborative effort of the U.S. Department of Energy Office of Science and the +National Nuclear Security Administration. diff --git a/cmake/ase.cmake b/cmake/ase.cmake index 8768d3f..baf480e 100644 --- a/cmake/ase.cmake +++ b/cmake/ase.cmake @@ -28,4 +28,4 @@ if("${ENABLE_ASE}") find_ase() endif() -add_library(ase INTERFACE) \ No newline at end of file +add_library(ase INTERFACE) diff --git a/docs/requirements.txt b/docs/requirements.txt index fb125f9..cb61418 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,3 +1,3 @@ sphinx -sphinx_rtd_theme sphinx-autoapi +sphinx_rtd_theme diff --git a/docs/source/conf.py b/docs/source/conf.py index b4576a4..34aa938 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -21,30 +21,33 @@ # http://www.sphinx-doc.org/en/master/config import os -import sys # -- Project information ----------------------------------------------------- -project = u'friendzone' -copyright = u'2020, NWChemEx Team' -author = u'NWChemEx Team' +project = "friendzone" +copyright = "2020, NWChemEx Team" +author = "NWChemEx Team" # Get the version from version.txt -version = '1.0.0' +version = "1.0.0" # The full version, including alpha/beta/rc tags release = version # -- General configuration --------------------------------------------------- # We use numref which is introduced in Sphinx 1.3 -needs_sphinx = '1.3' +needs_sphinx = "1.3" # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ - 'sphinx.ext.doctest', 'sphinx.ext.todo', 'sphinx.ext.coverage', - 'sphinx.ext.mathjax', 'sphinx.ext.githubpages', 'autoapi.extension' + "sphinx.ext.doctest", + "sphinx.ext.todo", + "sphinx.ext.coverage", + "sphinx.ext.mathjax", + "sphinx.ext.githubpages", + "autoapi.extension", ] dir_path = os.path.dirname(os.path.realpath(__file__)) doc_path = os.path.dirname(dir_path) @@ -57,17 +60,17 @@ # You can specify multiple suffix as a list of string: # # source_suffix = ['.rst', '.md'] -source_suffix = '.rst' +source_suffix = ".rst" # The master toctree document. -master_doc = 'index' +master_doc = "index" # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. # # This is also used if you do content translation via gettext catalogs. # Usually you set "language" from the command line for these cases. -language = 'en' +language = "en" # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. @@ -75,7 +78,7 @@ exclude_patterns = [] # The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' +pygments_style = "sphinx" # Should figures be numbered? numfig = True @@ -85,7 +88,7 @@ # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # -html_theme = 'sphinx_rtd_theme' +html_theme = "sphinx_rtd_theme" # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the @@ -111,7 +114,7 @@ # -- Options for HTMLHelp output --------------------------------------------- # Output file base name for HTML help builder. -htmlhelp_basename = project + 'doc' +htmlhelp_basename = project + "doc" # -- Options for LaTeX output ------------------------------------------------ @@ -119,15 +122,12 @@ # The paper size ('letterpaper' or 'a4paper'). # # 'papersize': 'letterpaper', - # The font size ('10pt', '11pt' or '12pt'). # # 'pointsize': '10pt', - # Additional stuff for the LaTeX preamble. # # 'preamble': '', - # Latex figure (float) alignment # # 'figure_align': 'htbp', @@ -137,16 +137,22 @@ # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ - (master_doc, project + '.tex', project + ' Documentation', author, - 'manual'), + ( + master_doc, + project + ".tex", + project + " Documentation", + author, + "manual", + ), ] # -- Options for manual page output ------------------------------------------ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). -man_pages = [(master_doc, project.lower(), project + ' Documentation', - [author], 1)] +man_pages = [ + (master_doc, project.lower(), project + " Documentation", [author], 1) +] # -- Options for Texinfo output ---------------------------------------------- @@ -154,23 +160,30 @@ # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ - (master_doc, project, project + ' Documentation', author, project, - 'One line description of project.', 'Miscellaneous'), + ( + master_doc, + project, + project + " Documentation", + author, + project, + "One line description of project.", + "Miscellaneous", + ), ] # -- Extension configuration ------------------------------------------------- autoapi_dirs = [ - '../../src', + "../../src", # '../../tests', ] autoapi_add_toctree_entry = False autoapi_options = [ - 'members', - 'undoc-members', - 'private-members', - 'show-inheritance', - 'show-module-summary', - 'special-members', + "members", + "undoc-members", + "private-members", + "show-inheritance", + "show-module-summary", + "special-members", # 'imported-members', ] @@ -178,8 +191,8 @@ # This skips classes that derived from ModuleBase, because those classes will # have Module API documentation producible by PluginPlay def skip_pluginplay_modules(app, what, name, obj, skip, options): - bases = obj.obj['bases'] if 'bases' in obj.obj.keys() else [] - if 'pluginplay.ModuleBase' in bases: + bases = obj.obj["bases"] if "bases" in obj.obj.keys() else [] + if "pluginplay.ModuleBase" in bases: skip = True return skip @@ -191,7 +204,7 @@ def setup(sphinx): # -- Options for intersphinx extension --------------------------------------- # Example configuration for intersphinx: refer to the Python standard library. -intersphinx_mapping = {'https://docs.python.org/': None} +intersphinx_mapping = {"https://docs.python.org/": None} # -- Options for todo extension ---------------------------------------------- @@ -203,7 +216,7 @@ def setup(sphinx): # Nitpick requires all references to be resolved # This will ignore those that references that can't be linked nitpick_ignore = [] -for line in open('nitpick_exceptions'): +for line in open("nitpick_exceptions"): if line.strip() == "" or line.startswith("#"): continue dtype, target = line.split(None, 1) diff --git a/docs/source/design.rst b/docs/source/design.rst index 9f07cff..14447d9 100644 --- a/docs/source/design.rst +++ b/docs/source/design.rst @@ -52,4 +52,4 @@ Notes on adding ASE: - ASE requires you to setup a calculator for the backend. The calculator's API is backend-specific. We have thus opted to treat each calculator as one - module since the setup of the calculator will be the guts of the module. \ No newline at end of file + module since the setup of the calculator will be the guts of the module. diff --git a/docs/source/module_api/index.rst b/docs/source/module_api/index.rst index adafd75..a98a8d9 100644 --- a/docs/source/module_api/index.rst +++ b/docs/source/module_api/index.rst @@ -18,4 +18,4 @@ Module API .. toctree:: :maxdepth: 1 - :caption: Packages \ No newline at end of file + :caption: Packages diff --git a/docs/source/nitpick_exceptions b/docs/source/nitpick_exceptions index accf631..39acdaa 100644 --- a/docs/source/nitpick_exceptions +++ b/docs/source/nitpick_exceptions @@ -6,4 +6,4 @@ py:class Varies depending on the requested property py:class qcelemental.models.Molecule -py:obj QCEngineEnergy \ No newline at end of file +py:obj QCEngineEnergy diff --git a/requirements.txt b/requirements.txt index 7fcec99..1fd6065 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ +ase +networkx pydantic>=1.10.13 qcengine -networkx -ase \ No newline at end of file diff --git a/src/python/friendzone/__init__.py b/src/python/friendzone/__init__.py index 72d316b..f19a2db 100644 --- a/src/python/friendzone/__init__.py +++ b/src/python/friendzone/__init__.py @@ -13,8 +13,8 @@ # limitations under the License. from .nwx2ase import load_ase_modules -from .nwx2qcengine import load_qcengine_modules from .nwx2qcelemental import load_qcelemental_modules +from .nwx2qcengine import load_qcengine_modules def load_modules(mm): diff --git a/src/python/friendzone/nwx2ase/__init__.py b/src/python/friendzone/nwx2ase/__init__.py index d91127c..e5032d7 100644 --- a/src/python/friendzone/nwx2ase/__init__.py +++ b/src/python/friendzone/nwx2ase/__init__.py @@ -17,14 +17,14 @@ def load_ase_modules(mm): - if not is_friend_enabled('ase'): + if not is_friend_enabled("ase"): return - if is_friend_enabled('nwchem'): - for method in ['SCF', 'MP2', 'CCSD', 'CCSD(T)']: - egy_key = 'ASE(NWChem) : ' + method - grad_key = egy_key + ' gradient' + if is_friend_enabled("nwchem"): + for method in ["SCF", "MP2", "CCSD", "CCSD(T)"]: + egy_key = "ASE(NWChem) : " + method + grad_key = egy_key + " gradient" mm.add_module(egy_key, NWChemEnergyViaASE()) mm.add_module(grad_key, NWChemGradientViaASE()) for key in [egy_key, grad_key]: - mm.change_input(key, 'method', method) + mm.change_input(key, "method", method) diff --git a/src/python/friendzone/nwx2ase/nwchem_via_ase.py b/src/python/friendzone/nwx2ase/nwchem_via_ase.py index 055041e..554624c 100644 --- a/src/python/friendzone/nwx2ase/nwchem_via_ase.py +++ b/src/python/friendzone/nwx2ase/nwchem_via_ase.py @@ -12,15 +12,17 @@ # See the License for the specific language governing permissions and # limitations under the License. -import pluginplay as pp -from simde import TotalEnergy, EnergyNuclearGradientStdVectorD -from ..utils.unwrap_inputs import unwrap_inputs -from .chemical_system_conversions import chemical_system2atoms -from ase.calculators.nwchem import NWChem -from ase import units import uuid + import numpy as np +import pluginplay as pp import tensorwrapper as tw +from ase import units +from ase.calculators.nwchem import NWChem +from simde import EnergyNuclearGradientStdVectorD, TotalEnergy + +from ..utils.unwrap_inputs import unwrap_inputs +from .chemical_system_conversions import chemical_system2atoms class NWChemEnergyViaASE(pp.ModuleBase): @@ -30,21 +32,21 @@ def __init__(self): pp.ModuleBase.__init__(self) self.description(NWChemEnergyViaASE.__doc__) self.satisfies_property_type(TotalEnergy()) - self.add_input('method').set_description('The level of theory to use.') - self.add_input('basis set').set_description( - 'The atomic basis set to use.') + self.add_input("method").set_description("The level of theory to use.") + self.add_input("basis set").set_description( + "The atomic basis set to use." + ) def run_(self, inputs, submods): pt = TotalEnergy() - method = inputs['method'].value() - basis = inputs['basis set'].value() + method = inputs["method"].value() + basis = inputs["basis set"].value() mol = unwrap_inputs(pt, inputs) atoms = chemical_system2atoms(mol) - atoms.calc = NWChem(theory=method, - basis=basis, - task='Energy', - label=str(uuid.uuid4())) + atoms.calc = NWChem( + theory=method, basis=basis, task="Energy", label=str(uuid.uuid4()) + ) egy = atoms.get_potential_energy() egy = tw.Tensor(np.array(egy / units.Hartree)) @@ -60,21 +62,24 @@ def __init__(self): self.description(NWChemGradientViaASE.__doc__) self.satisfies_property_type(TotalEnergy()) self.satisfies_property_type(EnergyNuclearGradientStdVectorD()) - self.add_input('method').set_description('The level of theory to use.') - self.add_input('basis set').set_description( - 'The atomic basis set to use.') + self.add_input("method").set_description("The level of theory to use.") + self.add_input("basis set").set_description( + "The atomic basis set to use." + ) def run_(self, inputs, submods): pt = EnergyNuclearGradientStdVectorD() - method = inputs['method'].value() - basis = inputs['basis set'].value() + method = inputs["method"].value() + basis = inputs["basis set"].value() mol = unwrap_inputs(pt, inputs) atoms = chemical_system2atoms(mol) - atoms.calc = NWChem(theory=method, - basis=basis, - task='Gradient', - label=str(uuid.uuid4())) + atoms.calc = NWChem( + theory=method, + basis=basis, + task="Gradient", + label=str(uuid.uuid4()), + ) egy = atoms.get_potential_energy() # units are eV grad = atoms.get_forces().flatten().tolist() # units are ev / Ang diff --git a/src/python/friendzone/nwx2qcelemental/__init__.py b/src/python/friendzone/nwx2qcelemental/__init__.py index 7169e0f..88a6d7e 100644 --- a/src/python/friendzone/nwx2qcelemental/__init__.py +++ b/src/python/friendzone/nwx2qcelemental/__init__.py @@ -13,14 +13,15 @@ # limitations under the License. import pluginplay as pp +import qcelemental from simde import MoleculeFromString + from ..nwx2qcelemental.chemical_system_conversions import qc_mol2molecule -import qcelemental class SystemViaMolSSI(pp.ModuleBase): - """Creates an NWChemEx ChemicalSystem by going through MolSSI's string - parser. + """Creates an NWChemEx ChemicalSystem by going through MolSSI's string + parser. """ def __init__(self): @@ -30,7 +31,7 @@ def __init__(self): def run_(self, inputs, submods): pt = MoleculeFromString() - mol_str, = pt.unwrap_inputs(inputs) + (mol_str,) = pt.unwrap_inputs(inputs) mol = qc_mol2molecule(qcelemental.models.Molecule.from_data(mol_str)) rv = self.results() @@ -44,4 +45,4 @@ def load_qcelemental_modules(mm): :type mm: pluginplay.ModuleManager """ - mm.add_module('ChemicalSystem via QCElemental', SystemViaMolSSI()) + mm.add_module("ChemicalSystem via QCElemental", SystemViaMolSSI()) diff --git a/src/python/friendzone/nwx2qcelemental/chemical_system_conversions.py b/src/python/friendzone/nwx2qcelemental/chemical_system_conversions.py index d73be76..fded269 100644 --- a/src/python/friendzone/nwx2qcelemental/chemical_system_conversions.py +++ b/src/python/friendzone/nwx2qcelemental/chemical_system_conversions.py @@ -17,7 +17,7 @@ def chemical_system2qc_mol(chem_sys): - """ Converts a chemist.ChemicalSystem object into a QCElemental.Molecule + """Converts a chemist.ChemicalSystem object into a QCElemental.Molecule At present there is far more information in the input chemical system then in the output object. In particular, our implementation only @@ -43,14 +43,13 @@ def chemical_system2qc_mol(chem_sys): y = str(atom_i.y * au2ang) z = str(atom_i.z * au2ang) out += symbol + " " + x + " " + y + " " + z + "\n" - return qcel.models.Molecule.from_data(out, - fix_com=True, - fix_orientation=True, - fix_symmetry="C1") + return qcel.models.Molecule.from_data( + out, fix_com=True, fix_orientation=True, fix_symmetry="C1" + ) def qc_mol2molecule(qc_mol): - """ Converts a QCElemental.Molecule object to a Chemist.Molecule object. + """Converts a QCElemental.Molecule object to a Chemist.Molecule object. .. note:: diff --git a/src/python/friendzone/nwx2qcengine/__init__.py b/src/python/friendzone/nwx2qcengine/__init__.py index 1c7e0d1..ed9e0d3 100644 --- a/src/python/friendzone/nwx2qcengine/__init__.py +++ b/src/python/friendzone/nwx2qcengine/__init__.py @@ -12,79 +12,77 @@ # See the License for the specific language governing permissions and # limitations under the License. -from ..friends import is_friend_enabled -import pluginplay as pp -from simde import TotalEnergy, EnergyNuclearGradientStdVectorD -from .call_qcengine import call_qcengine -from ..utils.unwrap_inputs import unwrap_inputs import numpy as np +import pluginplay as pp import tensorwrapper as tw +from simde import EnergyNuclearGradientStdVectorD, TotalEnergy + +from ..friends import is_friend_enabled +from ..utils.unwrap_inputs import unwrap_inputs +from .call_qcengine import call_qcengine def _run_impl(driver, inputs, rv, runtime): """ - Our strategy here is to use the fact that the inputs to the TotalEnergy + Our strategy here is to use the fact that the inputs to the TotalEnergy PT are a subset of those to other PTs """ # Step 0: Figure out the PT we're being run as pts = { - 'energy': TotalEnergy(), - 'gradient': EnergyNuclearGradientStdVectorD() + "energy": TotalEnergy(), + "gradient": EnergyNuclearGradientStdVectorD(), } # Step 1: Unwrap the inputs mol = unwrap_inputs(pts[driver], inputs) - program = inputs['program'].value() - method = inputs['method'].value() - basis = inputs['basis set'].value() + program = inputs["program"].value() + method = inputs["method"].value() + basis = inputs["basis set"].value() # Step 2: Call QCEngine - model = {'method': method, 'basis': basis} + model = {"method": method, "basis": basis} keywords = {} - outputs = call_qcengine(driver, - mol, - program, - runtime, - model=model, - keywords=keywords) + outputs = call_qcengine( + driver, mol, program, runtime, model=model, keywords=keywords + ) # Step 3: Prepare results - if driver == 'gradient': - grad = outputs['gradient'].flatten().tolist() + if driver == "gradient": + grad = outputs["gradient"].flatten().tolist() rv = pts[driver].wrap_results(rv, grad) - egy = tw.Tensor(np.array(outputs['energy'])) - return pts['energy'].wrap_results(rv, egy) + egy = tw.Tensor(np.array(outputs["energy"])) + return pts["energy"].wrap_results(rv, egy) class QCEngineEnergy(pp.ModuleBase): - """ Driver module for computing energies with QCEngine. - - This class relies on _run_impl to actually implement run_. + """Driver module for computing energies with QCEngine. + + This class relies on _run_impl to actually implement run_. """ def __init__(self): pp.ModuleBase.__init__(self) self.satisfies_property_type(TotalEnergy()) self.description(QCEngineEnergy.__doc__) - self.add_input('program').set_description('Friend to call') - self.add_input('method').set_description('Level of theory') - self.add_input('basis set').set_description('Name of AO basis set') + self.add_input("program").set_description("Friend to call") + self.add_input("method").set_description("Level of theory") + self.add_input("basis set").set_description("Name of AO basis set") def run_(self, inputs, submods): - return _run_impl('energy', inputs, self.results(), self.get_runtime()) + return _run_impl("energy", inputs, self.results(), self.get_runtime()) class QCEngineGradient(QCEngineEnergy): - """ Driver module for computing gradients with QCEngine. - - This class extends QCEngineEnergy (QCEngine always computes the energy - when computing the gradient thus this module will also compute the - energy). Relative to QCEngineEnergy the main differences are: - - - Addition of gradient property type - - Invocation of _run_impl with 'gradient' instead of 'energy' + """Driver module for computing gradients with QCEngine. + + This class extends QCEngineEnergy (QCEngine always computes the energy + when computing the gradient thus this module will also compute the + energy). Relative to QCEngineEnergy the main differences are: + + - Addition of gradient property type + - Invocation of _run_impl with 'gradient' instead of 'energy' """ def __init__(self): @@ -92,18 +90,19 @@ def __init__(self): self.satisfies_property_type(EnergyNuclearGradientStdVectorD()) def run_(self, inputs, submods): - return _run_impl('gradient', inputs, self.results(), - self.get_runtime()) + return _run_impl( + "gradient", inputs, self.results(), self.get_runtime() + ) def load_qcengine_modules(mm): """Loads the collection of modules that wrap QCElemental calls. Currently, the friends exported by this function are: - + #. NWChem - the levels of theory are: + the levels of theory are: #. SCF #. B3LYP @@ -112,21 +111,21 @@ def load_qcengine_modules(mm): #. CCSD(T) and we have 0-th and 1-st derivatives. - + The final set of modules is the Cartesian product of all of the above. :param mm: The ModuleManager that the NWChem Modules will be loaded into. :type mm: pluginplay.ModuleManager """ - for program in ['nwchem']: + for program in ["nwchem"]: if is_friend_enabled(program): - for method in ['SCF', 'B3LYP', 'MP2', 'CCSD', 'CCSD(T)']: - egy_key = program + ' : ' + method - grad_key = egy_key + ' Gradient' + for method in ["SCF", "B3LYP", "MP2", "CCSD", "CCSD(T)"]: + egy_key = program + " : " + method + grad_key = egy_key + " Gradient" mm.add_module(egy_key, QCEngineEnergy()) mm.add_module(grad_key, QCEngineGradient()) for key in [egy_key, grad_key]: - mm.change_input(key, 'program', program) - mm.change_input(key, 'method', method) + mm.change_input(key, "program", program) + mm.change_input(key, "method", method) diff --git a/src/python/friendzone/nwx2qcengine/call_qcengine.py b/src/python/friendzone/nwx2qcengine/call_qcengine.py index 463a370..67e8177 100644 --- a/src/python/friendzone/nwx2qcengine/call_qcengine.py +++ b/src/python/friendzone/nwx2qcengine/call_qcengine.py @@ -12,55 +12,57 @@ # See the License for the specific language governing permissions and # limitations under the License. -import qcengine as qcng import qcelemental as qcel -from ..nwx2qcelemental.chemical_system_conversions import chemical_system2qc_mol -from qcengine.config import TaskConfig +import qcengine as qcng + +from ..nwx2qcelemental.chemical_system_conversions import ( + chemical_system2qc_mol, +) def call_qcengine(driver, mol, program, runtime, **kwargs): - """ Wraps calling a program through the QCEngine API. - - .. note:: - - QCEngine only supports high-level modularity (modularity roughly at - the granularity of a call to an electronic structure package). We - thus have assumed that the molecular system will always be an input - to whatever QCEngine call we are running. - - This function is the main API for calling QCEngine from NWChemEx. The - idea is to more or less feed the inputs from a ``run_as`` call directly - into this function and then have this function convert the NWChemEx - objects to their QCElemental equivalents. Right now those mappings - include: - - - ChemicalSystem -> qcel.models.Molecule - - RuntimeView -> qcng.TaskConfig - - While not supported at the moment, similar conversions for the AO basis - set are possible. - - Because of the difference in philosophy between QCEngine and NWChemEx, - there are some inputs which can not easily be mapped automatically, for - example the electronic structure method (in NWChemEx methods correspond - to module instances, whereas QCEngine requires strings). It is the - responsibility of the module wrapping the call to ``call_qcengine`` to - pass these additional inputs in as kwargs that can be forwarded to a - QCElemental.models.AtomicInput object via the ``model`` keyword. - - :param pt: The property type we are computing. - :type pt: pluginplay.PropertyType - :param mol: The molecular system we are computing the properties of. - :type mol: chemist.ChemicalSystem - :param program: Which electronic structure package is being used as the - backend? - :type program: str - :param kwargs: Key-value pairs which will be forwarded to QCElemental's - ``AtomicInput`` class as kwargs. - - :return: A dictionary containing the requested property and any other - property of potential interest. - :rtype: Varies depending on the requested property + """Wraps calling a program through the QCEngine API. + + .. note:: + + QCEngine only supports high-level modularity (modularity roughly at + the granularity of a call to an electronic structure package). We + thus have assumed that the molecular system will always be an input + to whatever QCEngine call we are running. + + This function is the main API for calling QCEngine from NWChemEx. The + idea is to more or less feed the inputs from a ``run_as`` call directly + into this function and then have this function convert the NWChemEx + objects to their QCElemental equivalents. Right now those mappings + include: + + - ChemicalSystem -> qcel.models.Molecule + - RuntimeView -> qcng.TaskConfig + + While not supported at the moment, similar conversions for the AO basis + set are possible. + + Because of the difference in philosophy between QCEngine and NWChemEx, + there are some inputs which can not easily be mapped automatically, for + example the electronic structure method (in NWChemEx methods correspond + to module instances, whereas QCEngine requires strings). It is the + responsibility of the module wrapping the call to ``call_qcengine`` to + pass these additional inputs in as kwargs that can be forwarded to a + QCElemental.models.AtomicInput object via the ``model`` keyword. + + :param pt: The property type we are computing. + :type pt: pluginplay.PropertyType + :param mol: The molecular system we are computing the properties of. + :type mol: chemist.ChemicalSystem + :param program: Which electronic structure package is being used as the + backend? + :type program: str + :param kwargs: Key-value pairs which will be forwarded to QCElemental's + ``AtomicInput`` class as kwargs. + + :return: A dictionary containing the requested property and any other + property of potential interest. + :rtype: Varies depending on the requested property """ # Step 1: Prepare the chemistry-related input @@ -69,17 +71,17 @@ def call_qcengine(driver, mol, program, runtime, **kwargs): # Step 2: Prepare the runtime-related input # I *think* ncores is supposed to be the number of threads per MPI rank - task_config = {'nnodes': runtime.size(), 'ncores': 1, 'retries': 0} + task_config = {"nnodes": runtime.size(), "ncores": 1, "retries": 0} # Step 3: Run QCEngine results = qcng.compute(inp, program, task_config=task_config) # Step 4: Verify the computation ran correctly - if type(results) == qcel.models.common_models.FailedOperation: + if isinstance(results, qcel.models.common_models.FailedOperation): raise RuntimeError(results.error.error_message) # Step 5: Prepare the results rv = {driver: results.return_result} - if (driver == "gradient" and "qcvars" in results.extras): - rv['energy'] = float(results.extras["qcvars"]["CURRENT ENERGY"]) + if driver == "gradient" and "qcvars" in results.extras: + rv["energy"] = float(results.extras["qcvars"]["CURRENT ENERGY"]) return rv diff --git a/src/python/friendzone/utils/unwrap_inputs.py b/src/python/friendzone/utils/unwrap_inputs.py index f79278e..3a41a57 100644 --- a/src/python/friendzone/utils/unwrap_inputs.py +++ b/src/python/friendzone/utils/unwrap_inputs.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from simde import TotalEnergy, EnergyNuclearGradientStdVectorD +from simde import EnergyNuclearGradientStdVectorD, TotalEnergy def _compare_mol_and_point(mol, points): @@ -34,8 +34,8 @@ def _compare_mol_and_point(mol, points): def unwrap_inputs(pt, inputs): - """ Code factorization for unwrapping a module's inputs. - + """Code factorization for unwrapping a module's inputs. + Many of our friends expose interfaces which are analogous to high-level property types like TotalEnergy, AOEnergy, and EnergyNuclearGradient. Furthermore, most of our friends expose all of these calculations through @@ -47,15 +47,17 @@ def unwrap_inputs(pt, inputs): mol = None if pt.type() == TotalEnergy().type(): - mol, = pt.unwrap_inputs(inputs) + (mol,) = pt.unwrap_inputs(inputs) elif pt.type() == EnergyNuclearGradientStdVectorD().type(): mol, point = pt.unwrap_inputs(inputs) if not _compare_mol_and_point(mol.molecule, point): raise RuntimeError( - 'Derivative must be computed at molecular geometry') + "Derivative must be computed at molecular geometry" + ) else: - raise RuntimeError('Property type: ' + str(pt.type()) + - ' is not registered') + raise RuntimeError( + "Property type: " + str(pt.type()) + " is not registered" + ) return mol diff --git a/tests/python/unit_tests/molecules.py b/tests/python/unit_tests/molecules.py index c880800..138a47a 100644 --- a/tests/python/unit_tests/molecules.py +++ b/tests/python/unit_tests/molecules.py @@ -12,21 +12,21 @@ # See the License for the specific language governing permissions and # limitations under the License. -from chemist import Atom, Molecule, ChemicalSystem +from chemist import Atom, ChemicalSystem, Molecule def make_h2(): mol = Molecule() - mol.push_back(Atom('H', 1, 1837.15264648179, 0.0, 0.0, 0.0)) - mol.push_back(Atom('H', 1, 1837.15264648179, 0.0, 0.0, 1.68185)) + mol.push_back(Atom("H", 1, 1837.15264648179, 0.0, 0.0, 0.0)) + mol.push_back(Atom("H", 1, 1837.15264648179, 0.0, 0.0, 1.68185)) return ChemicalSystem(mol) def make_h2o(): mol = Molecule() - mol.push_back(Atom('H', 1, 1837.1526468179, 0.0, 1.419470, -0.879257)) - mol.push_back(Atom('H', 1, 1837.1526468179, 0.0, -1.419470, -0.879257)) - mol.push_back(Atom('O', 8, 29164.3928768326, 0.0, 0.0, 0.219814)) + mol.push_back(Atom("H", 1, 1837.1526468179, 0.0, 1.419470, -0.879257)) + mol.push_back(Atom("H", 1, 1837.1526468179, 0.0, -1.419470, -0.879257)) + mol.push_back(Atom("O", 8, 29164.3928768326, 0.0, 0.0, 0.219814)) return ChemicalSystem(mol) diff --git a/tests/python/unit_tests/nwx2ase/test_chemical_system_conversions.py b/tests/python/unit_tests/nwx2ase/test_chemical_system_conversions.py index 5556f46..5858666 100644 --- a/tests/python/unit_tests/nwx2ase/test_chemical_system_conversions.py +++ b/tests/python/unit_tests/nwx2ase/test_chemical_system_conversions.py @@ -12,15 +12,19 @@ # See the License for the specific language governing permissions and # limitations under the License. -from friendzone.nwx2ase.chemical_system_conversions import chemical_system2atoms -from molecules import make_h2, make_h2o -from ase import Atoms import unittest +from ase import Atoms +from friendzone.nwx2ase.chemical_system_conversions import ( + chemical_system2atoms, +) +from molecules import make_h2, make_h2o + def compare_ase(self, lhs, rhs): - for lhs_Z, rhs_Z in zip(lhs.get_atomic_numbers(), - rhs.get_atomic_numbers()): + for lhs_Z, rhs_Z in zip( + lhs.get_atomic_numbers(), rhs.get_atomic_numbers() + ): self.assertEqual(lhs_Z, rhs_Z) for lhs_xyz, rhs_xyz in zip(lhs.get_positions(), rhs.get_positions()): @@ -32,24 +36,29 @@ def compare_ase(self, lhs, rhs): class TestChemicalSystemConversions(unittest.TestCase): - def test_h2(self): corr_positions = [(0.0, 0.0, 0.0), (0.0, 0.0, 0.8899966917653396)] - corr = Atoms(numbers=(1, 1), - positions=corr_positions, - masses=[1.007825, 1.007825]) + corr = Atoms( + numbers=(1, 1), + positions=corr_positions, + masses=[1.007825, 1.007825], + ) h2_nwx = make_h2() ase_mol = chemical_system2atoms(h2_nwx) compare_ase(self, ase_mol, corr) def test_h2o(self): - corr_positions = [(0.0, 0.7511511750790556, -0.46528276662873125), - (0.0, -0.7511511750790556, -0.46528276662873125), - (0.0, 0.0, 0.11632055936288019)] - corr = Atoms(numbers=(1, 1, 8), - positions=corr_positions, - masses=[1.007825, 1.007825, 15.9990]) + corr_positions = [ + (0.0, 0.7511511750790556, -0.46528276662873125), + (0.0, -0.7511511750790556, -0.46528276662873125), + (0.0, 0.0, 0.11632055936288019), + ] + corr = Atoms( + numbers=(1, 1, 8), + positions=corr_positions, + masses=[1.007825, 1.007825, 15.9990], + ) h2o_nwx = make_h2o() ase_mol = chemical_system2atoms(h2o_nwx) diff --git a/tests/python/unit_tests/nwx2ase/test_nwchem_via_ase.py b/tests/python/unit_tests/nwx2ase/test_nwchem_via_ase.py index 2c97ece..353f105 100644 --- a/tests/python/unit_tests/nwx2ase/test_nwchem_via_ase.py +++ b/tests/python/unit_tests/nwx2ase/test_nwchem_via_ase.py @@ -12,31 +12,32 @@ # See the License for the specific language governing permissions and # limitations under the License. -from pluginplay import ModuleManager -from friendzone import friends, load_modules -from simde import TotalEnergy, EnergyNuclearGradientStdVectorD -from molecules import make_h2 import unittest + import numpy as np +from friendzone import friends, load_modules +from molecules import make_h2 +from pluginplay import ModuleManager +from simde import EnergyNuclearGradientStdVectorD, TotalEnergy class TestNWChemViaASE(unittest.TestCase): - def test_scf(self): mol = make_h2() - key = 'ASE(NWChem) : SCF' - self.mm.change_input(key, 'basis set', 'sto-3g') + key = "ASE(NWChem) : SCF" + self.mm.change_input(key, "basis set", "sto-3g") egy = self.mm.run_as(TotalEnergy(), key, mol) self.assertAlmostEqual(np.array(egy), -1.094184522864, places=5) def test_scf_gradient(self): mol = make_h2() - key = 'ASE(NWChem) : SCF Gradient' - self.mm.change_input(key, 'basis set', 'sto-3g') + key = "ASE(NWChem) : SCF Gradient" + self.mm.change_input(key, "basis set", "sto-3g") nuclei = mol.molecule.nuclei.as_nuclei() points = nuclei.charges.point_set - grad = self.mm.run_as(EnergyNuclearGradientStdVectorD(), key, mol, - points.as_point_set()) + grad = self.mm.run_as( + EnergyNuclearGradientStdVectorD(), key, mol, points.as_point_set() + ) corr = [0.0, 0.0, -0.11827177600466043, 0.0, 0.0, 0.11827177600466043] for g, c in zip(grad, corr): @@ -44,28 +45,28 @@ def test_scf_gradient(self): def test_mp2(self): mol = make_h2() - key = 'ASE(NWChem) : MP2' - self.mm.change_input(key, 'basis set', 'sto-3g') + key = "ASE(NWChem) : MP2" + self.mm.change_input(key, "basis set", "sto-3g") egy = self.mm.run_as(TotalEnergy(), key, mol) self.assertAlmostEqual(np.array(egy), -1.111247857166, places=5) def test_ccsd(self): mol = make_h2() - key = 'ASE(NWChem) : CCSD' - self.mm.change_input(key, 'basis set', 'sto-3g') + key = "ASE(NWChem) : CCSD" + self.mm.change_input(key, "basis set", "sto-3g") egy = self.mm.run_as(TotalEnergy(), key, mol) self.assertAlmostEqual(np.array(egy), -1.122251361965036, places=4) def test_ccsd_t(self): mol = make_h2() - key = 'ASE(NWChem) : CCSD(T)' - self.mm.change_input(key, 'basis set', 'sto-3g') + key = "ASE(NWChem) : CCSD(T)" + self.mm.change_input(key, "basis set", "sto-3g") egy = self.mm.run_as(TotalEnergy(), key, mol) self.assertAlmostEqual(np.array(egy), -1.122251361965036, places=4) def setUp(self): - if not friends.is_friend_enabled('nwchem'): - self.skipTest('NWChem backend is not enabled!') + if not friends.is_friend_enabled("nwchem"): + self.skipTest("NWChem backend is not enabled!") self.mm = ModuleManager() load_modules(self.mm) diff --git a/tests/python/unit_tests/nwx2qcelemental/test_chemical_system_conversions.py b/tests/python/unit_tests/nwx2qcelemental/test_chemical_system_conversions.py index c848324..e278979 100644 --- a/tests/python/unit_tests/nwx2qcelemental/test_chemical_system_conversions.py +++ b/tests/python/unit_tests/nwx2qcelemental/test_chemical_system_conversions.py @@ -12,15 +12,18 @@ # See the License for the specific language governing permissions and # limitations under the License. -from friendzone.nwx2qcelemental.chemical_system_conversions import * -from molecules import make_h2 -import qcelemental as qcel import unittest + +import qcelemental as qcel from compare_molecules import compare_molecules +from friendzone.nwx2qcelemental.chemical_system_conversions import ( + chemical_system2qc_mol, + qc_mol2molecule, +) +from molecules import make_h2 class TestChemicalSystem2QC(unittest.TestCase): - def test_h2(self): mol = make_h2() qcel_mol = chemical_system2qc_mol(mol) @@ -31,7 +34,6 @@ def test_h2(self): class TestQCMol2Molecule(unittest.TestCase): - def test_h2(self): corr = make_h2().molecule h2_as_str = "H 0.0 0.0 0.0\nH 0.0 0.0 0.8899966917653396" diff --git a/tests/python/unit_tests/nwx2qcelemental/test_system_via_molssi.py b/tests/python/unit_tests/nwx2qcelemental/test_system_via_molssi.py index 659c375..c9d16ae 100644 --- a/tests/python/unit_tests/nwx2qcelemental/test_system_via_molssi.py +++ b/tests/python/unit_tests/nwx2qcelemental/test_system_via_molssi.py @@ -12,21 +12,22 @@ # See the License for the specific language governing permissions and # limitations under the License. -from pluginplay import ModuleManager +import unittest + +from compare_molecules import compare_molecules from friendzone import load_modules -from simde import MoleculeFromString from molecules import make_h2 -from compare_molecules import compare_molecules -import unittest +from pluginplay import ModuleManager +from simde import MoleculeFromString class TestSystemViaMolSSI(unittest.TestCase): - def test_h2(self): corr = make_h2() - mol_str = 'units a.u.\nH 0.0 0.0 0.0\nH 0.0 0.0 1.68185' - mol = self.mm.run_as(self.pt, 'ChemicalSystem via QCElemental', - mol_str) + mol_str = "units a.u.\nH 0.0 0.0 0.0\nH 0.0 0.0 1.68185" + mol = self.mm.run_as( + self.pt, "ChemicalSystem via QCElemental", mol_str + ) compare_molecules(self, corr.molecule, mol) def setUp(self): diff --git a/tests/python/unit_tests/nwx2qcengine/test_nwchem.py b/tests/python/unit_tests/nwx2qcengine/test_nwchem.py index 1af52cf..7224f4d 100644 --- a/tests/python/unit_tests/nwx2qcengine/test_nwchem.py +++ b/tests/python/unit_tests/nwx2qcengine/test_nwchem.py @@ -12,31 +12,32 @@ # See the License for the specific language governing permissions and # limitations under the License. -from pluginplay import ModuleManager -from friendzone import friends, load_modules -from simde import TotalEnergy, EnergyNuclearGradientStdVectorD -from molecules import make_h2 import unittest + import numpy as np +from friendzone import friends, load_modules +from molecules import make_h2 +from pluginplay import ModuleManager +from simde import EnergyNuclearGradientStdVectorD, TotalEnergy class TestNWChem(unittest.TestCase): - def test_scf(self): mol = make_h2() - key = 'NWChem : SCF' - self.mm.change_input(key, 'basis set', 'sto-3g') + key = "NWChem : SCF" + self.mm.change_input(key, "basis set", "sto-3g") egy = self.mm.run_as(TotalEnergy(), key, mol) self.assertAlmostEqual(np.array(egy), -1.094184522864, places=5) def test_scf_gradient(self): mol = make_h2() - key = 'NWChem : SCF Gradient' - self.mm.change_input(key, 'basis set', 'sto-3g') + key = "NWChem : SCF Gradient" + self.mm.change_input(key, "basis set", "sto-3g") nuclei = mol.molecule.nuclei.as_nuclei() points = nuclei.charges.point_set - grad = self.mm.run_as(EnergyNuclearGradientStdVectorD(), key, mol, - points.as_point_set()) + grad = self.mm.run_as( + EnergyNuclearGradientStdVectorD(), key, mol, points.as_point_set() + ) corr = [0.0, 0.0, -0.11827177600466043, 0.0, 0.0, 0.11827177600466043] for g, c in zip(grad, corr): @@ -44,27 +45,27 @@ def test_scf_gradient(self): def test_mp2(self): mol = make_h2() - key = 'NWChem : MP2' - self.mm.change_input(key, 'basis set', 'sto-3g') + key = "NWChem : MP2" + self.mm.change_input(key, "basis set", "sto-3g") egy = self.mm.run_as(TotalEnergy(), key, mol) self.assertAlmostEqual(np.array(egy), -1.111247857166, places=5) def test_ccsd(self): mol = make_h2() - key = 'NWChem : CCSD' - self.mm.change_input(key, 'basis set', 'sto-3g') + key = "NWChem : CCSD" + self.mm.change_input(key, "basis set", "sto-3g") egy = self.mm.run_as(TotalEnergy(), key, mol) self.assertAlmostEqual(np.array(egy), -1.122251361965036, places=4) def test_ccsd_t(self): mol = make_h2() - key = 'NWChem : CCSD(T)' - self.mm.change_input(key, 'basis set', 'sto-3g') + key = "NWChem : CCSD(T)" + self.mm.change_input(key, "basis set", "sto-3g") egy = self.mm.run_as(TotalEnergy(), key, mol) self.assertAlmostEqual(np.array(egy), -1.122251361965036, places=4) def setUp(self): - if not friends.is_friend_enabled('nwchem'): + if not friends.is_friend_enabled("nwchem"): self.skipTest("NWChem backend is not enabled!") self.mm = ModuleManager() diff --git a/tests/python/unit_tests/test_friendzone.py b/tests/python/unit_tests/test_friendzone.py index 3d2b255..980b434 100644 --- a/tests/python/unit_tests/test_friendzone.py +++ b/tests/python/unit_tests/test_friendzone.py @@ -15,16 +15,17 @@ # import os -import parallelzone as pz import sys import unittest -if __name__ == '__main__': +import parallelzone as pz + +if __name__ == "__main__": rv = pz.runtime.RuntimeView() my_dir = os.path.dirname(os.path.realpath(__file__)) root_dir = os.path.dirname(os.path.dirname(os.path.dirname(my_dir))) - src_dir = os.path.join(root_dir, 'src', 'python') + src_dir = os.path.join(root_dir, "src", "python") sys.path.append(src_dir) loader = unittest.TestLoader() diff --git a/tests/python/unit_tests/utils/test_unwrap_inputs.py b/tests/python/unit_tests/utils/test_unwrap_inputs.py index 1b66992..900d632 100644 --- a/tests/python/unit_tests/utils/test_unwrap_inputs.py +++ b/tests/python/unit_tests/utils/test_unwrap_inputs.py @@ -12,15 +12,19 @@ # See the License for the specific language governing permissions and # limitations under the License. -from simde import TotalEnergy, EnergyNuclearGradientStdVectorD, MoleculeFromString +import unittest + from chemist import PointSetD from friendzone.utils.unwrap_inputs import unwrap_inputs from molecules import make_h2 -import unittest +from simde import ( + EnergyNuclearGradientStdVectorD, + MoleculeFromString, + TotalEnergy, +) class TestUnwrapInputs(unittest.TestCase): - def test_energy(self): inputs = self.egy_pt.wrap_inputs(self.egy_pt.inputs(), self.h2) mol = unwrap_inputs(self.egy_pt, inputs)