Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
9 changes: 9 additions & 0 deletions .github/workflows/merge.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,15 @@ jobs:
permissions:
id-token: write
steps:
# build_pypi_dist imports the built wheel to check it, and importing
# friendzone imports pluginplay, whose prebuilt wheel links against the
# system libmpi. build_pypi_dist only installs MPI for compiled
# (cibuildwheel) packages, and bare GitHub runners have none, so install
# the same runtime MPI it uses there.
- name: Install Runtime MPI
run: |
sudo apt-get update
sudo apt-get install -y libopenmpi-dev openmpi-bin
- name: Build PyPI Distribution
uses: NWChemEx/.github/.github/actions/build_pypi_dist@master

Expand Down
9 changes: 9 additions & 0 deletions .github/workflows/test_python_build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,15 @@ jobs:
test_pypi_deploy:
runs-on: ubuntu-latest
steps:
# build_pypi_dist imports the built wheel to check it, and importing
# friendzone imports pluginplay, whose prebuilt wheel links against the
# system libmpi. build_pypi_dist only installs MPI for compiled
# (cibuildwheel) packages, and bare GitHub runners have none, so install
# the same runtime MPI it uses there.
- name: Install Runtime MPI
run: |
sudo apt-get update
sudo apt-get install -y libopenmpi-dev openmpi-bin
- name: Build PyPI Distribution
uses: NWChemEx/.github/.github/actions/build_pypi_dist@master

Expand Down
2 changes: 2 additions & 0 deletions docs/source/nitpick_exceptions
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
py:class pluginplay.ModuleManager
py:class pluginplay.Plugin
py:obj pluginplay.Plugin
py:class pluginplay.PropertyType
py:class chemist.ChemicalSystem

Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ classifiers = [
"Programming Language :: Python :: 3.14",
]
dynamic = ["version"]
dependencies = ["nwchemex-simde>=0.0.77"]
dependencies = ["nwchemex-simde>=0.0.85"]

[project.optional-dependencies]
ase = ["ase"]
Expand Down
23 changes: 14 additions & 9 deletions python/friendzone/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,19 @@
# See the License for the specific language governing permissions and
# limitations under the License.

import pluginplay

from .nwx2ase import load_ase_modules
from .nwx2molssi import load_molssi_modules


def load_modules(mm):
"""Loads the collection of all modules provided by Friendzone.
class FriendZone(pluginplay.Plugin):
"""The FriendZone plugin.

This function calls the various friend specific module loading functions,
including:
Load it into a ModuleManager with
``mm.load_plugin(friendzone.FriendZone())``. The plugin's name is
"friendzone". Its modules are added by the various friend specific module
loading functions, including:

* ``load_ase_modules``
* ``load_molssi_modules``
Expand All @@ -29,9 +33,10 @@ def load_modules(mm):

Some and/or all of these may be no-ops depending on what friends were
enabled.

:param mm: The ModuleManager that the all Modules will be loaded into.
:type mm: pluginplay.ModuleManager
"""
load_ase_modules(mm)
load_molssi_modules(mm)

def __init__(self):
pluginplay.Plugin.__init__(self)
self.set_name("friendzone")
load_ase_modules(self)
load_molssi_modules(self)
10 changes: 5 additions & 5 deletions python/friendzone/nwx2ase/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@
from .nwchem_via_ase import load_nwchem_via_ase_modules


def load_ase_modules(mm):
"""Loads the collection of all ASE modules.
def load_ase_modules(plugin):
"""Adds the collection of all ASE modules to a plugin.

This function calls the various submodule specific loading functions,
including:
Expand All @@ -31,8 +31,8 @@ def load_ase_modules(mm):
Some and/or all of these may be no-ops depending on what friends were
enabled. This function is a no-op if ASE is not installed.

:param mm: The ModuleManager that the all Modules will be loaded into.
:type mm: pluginplay.ModuleManager
:param plugin: The plugin the modules will be added to.
:type plugin: pluginplay.Plugin
"""
if is_ase_enabled():
load_nwchem_via_ase_modules(mm)
load_nwchem_via_ase_modules(plugin)
14 changes: 7 additions & 7 deletions python/friendzone/nwx2ase/nwchem_via_ase.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,24 +94,24 @@ def run_(self, inputs, submods):
return pt.wrap_results(rv, augrad)


def load_nwchem_via_ase_modules(mm):
"""Loads the collection of all ASE(NWChem) modules.
def load_nwchem_via_ase_modules(plugin):
"""Adds the collection of all ASE(NWChem) modules to a plugin.

.. note::

This function is a no-op if NWChem is not installed.

:param mm: The ModuleManager that the all Modules will be loaded into.
:type mm: pluginplay.ModuleManager
:param plugin: The plugin the modules will be added to.
:type plugin: pluginplay.Plugin
"""
if is_nwchem_enabled():
# Loop over methods and add energy and gradient modules for each
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())
plugin.add_module(egy_key, NWChemEnergyViaASE())
plugin.add_module(grad_key, NWChemGradientViaASE())

for key in [egy_key, grad_key]:
mm.change_input(key, "method", method)
plugin.modules().change_input(key, "method", method)
14 changes: 7 additions & 7 deletions python/friendzone/nwx2molssi/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@
from .system_via_molssi import load_system_via_molssi_modules


def load_molssi_modules(mm):
"""Loads the collection of all MolSSI modules.
def load_molssi_modules(plugin):
"""Adds the collection of all MolSSI modules to a plugin.

This function calls the various submodule specific loading functions,
including:
Expand All @@ -40,10 +40,10 @@ def load_molssi_modules(mm):
* ``qcengine``
* ``networkx``

:param mm: The ModuleManager that the all Modules will be loaded into.
:type mm: pluginplay.ModuleManager
:param plugin: The plugin the modules will be added to.
:type plugin: pluginplay.Plugin
"""
if is_molssi_enabled():
load_system_via_molssi_modules(mm)
load_nwchem_via_molssi_modules(mm)
load_qcschema_driver_modules(mm)
load_system_via_molssi_modules(plugin)
load_nwchem_via_molssi_modules(plugin)
load_qcschema_driver_modules(plugin)
16 changes: 8 additions & 8 deletions python/friendzone/nwx2molssi/nwchem_via_molssi.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,8 @@ def run_(self, inputs, submods):
)


def load_nwchem_via_molssi_modules(mm):
"""Loads the collection of modules that wrap QCElemental calls.
def load_nwchem_via_molssi_modules(plugin):
"""Adds the collection of modules that wrap QCEngine calls to a plugin.

Currently, the friends exported by this function are:

Expand All @@ -119,16 +119,16 @@ def load_nwchem_via_molssi_modules(mm):

This function is a no-op if NWChem is not installed.

:param mm: The ModuleManager that the NWChem Modules will be loaded into.
:type mm: pluginplay.ModuleManager
:param plugin: The plugin the NWChem modules will be added to.
:type plugin: pluginplay.Plugin
"""
if is_nwchem_enabled():
for method in ["SCF", "B3LYP", "MP2", "CCSD", "CCSD(T)"]:
egy_key = "NWChem" + " : " + method
grad_key = egy_key + " Gradient"
mm.add_module(egy_key, QCEngineEnergy())
mm.add_module(grad_key, QCEngineGradient())
plugin.add_module(egy_key, QCEngineEnergy())
plugin.add_module(grad_key, QCEngineGradient())

for key in [egy_key, grad_key]:
mm.change_input(key, "program", "nwchem")
mm.change_input(key, "method", method)
plugin.modules().change_input(key, "program", "nwchem")
plugin.modules().change_input(key, "method", method)
11 changes: 5 additions & 6 deletions python/friendzone/nwx2molssi/qcschema_driver.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,11 +108,10 @@ def run_(self, inputs, submods):
return pt.wrap_results(rv, result)


def load_qcschema_driver_modules(mm):
"""Loads the QCSchemaDriver module.
def load_qcschema_driver_modules(plugin):
"""Adds the QCSchemaDriver module to a plugin.

:param mm: The ModuleManager that the QCSchemaDriver module will be
loaded into.
:type mm: pluginplay.ModuleManager
:param plugin: The plugin the QCSchemaDriver module will be added to.
:type plugin: pluginplay.Plugin
"""
mm.add_module("QCSchema Driver", QCSchemaDriver())
plugin.add_module("QCSchema Driver", QCSchemaDriver())
10 changes: 5 additions & 5 deletions python/friendzone/nwx2molssi/system_via_molssi.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,10 @@ def run_(self, inputs, submods):
return pt.wrap_results(rv, mol)


def load_system_via_molssi_modules(mm):
"""Loads the collection of modules that wrap QCElemental calls.
def load_system_via_molssi_modules(plugin):
"""Adds the collection of modules that wrap QCElemental calls to a plugin.

:param mm: The ModuleManager that the NWChem Modules will be loaded into.
:type mm: pluginplay.ModuleManager
:param plugin: The plugin the modules will be added to.
:type plugin: pluginplay.Plugin
"""
mm.add_module("ChemicalSystem via QCElemental", SystemViaMolSSI())
plugin.add_module("ChemicalSystem via QCElemental", SystemViaMolSSI())
4 changes: 2 additions & 2 deletions tests/python/unit_tests/nwx2ase/test_nwchem_via_ase.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
import unittest

import numpy as np
from friendzone import load_modules
from friendzone import FriendZone
from friendzone.friends import is_ase_enabled, is_nwchem_enabled
from molecules import make_h2
from pluginplay import ModuleManager
Expand Down Expand Up @@ -72,4 +72,4 @@ def setUp(self):
self.skipTest("NWChem backend is not enabled!")

self.mm = ModuleManager()
load_modules(self.mm)
self.mm.load_plugin(FriendZone())
4 changes: 2 additions & 2 deletions tests/python/unit_tests/nwx2molssi/test_nwchem_via_molssi.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
import unittest

import numpy as np
from friendzone import load_modules
from friendzone import FriendZone
from friendzone.friends import is_molssi_enabled, is_nwchem_enabled
from molecules import make_h2
from pluginplay import ModuleManager
Expand Down Expand Up @@ -72,4 +72,4 @@ def setUp(self):
self.skipTest("NWChem backend is not enabled!")

self.mm = ModuleManager()
load_modules(self.mm)
self.mm.load_plugin(FriendZone())
4 changes: 2 additions & 2 deletions tests/python/unit_tests/nwx2molssi/test_qcschema_driver.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

import unittest

from friendzone import load_modules
from friendzone import FriendZone
from friendzone.friends import is_molssi_enabled

if is_molssi_enabled():
Expand Down Expand Up @@ -101,7 +101,7 @@ def setUp(self):
self.skipTest("MolSSI friend is not enabled!")

self.mm = ModuleManager()
load_modules(self.mm)
self.mm.load_plugin(FriendZone())
self.mm.add_module("Dummy AOEnergy", DummyAOEnergyModule())
self.mm.change_submod("QCSchema Driver", "AOEnergy", "Dummy AOEnergy")

Expand Down
4 changes: 2 additions & 2 deletions tests/python/unit_tests/nwx2molssi/test_system_via_molssi.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
import unittest

from compare_molecules import compare_molecules
from friendzone import load_modules
from friendzone import FriendZone
from friendzone.friends import is_molssi_enabled
from molecules import make_h2
from pluginplay import ModuleManager
Expand All @@ -36,5 +36,5 @@ def setUp(self):
self.skipTest("MolSSI friend is not enabled!")

self.mm = ModuleManager()
load_modules(self.mm)
self.mm.load_plugin(FriendZone())
self.pt = MoleculeFromString()
41 changes: 41 additions & 0 deletions tests/python/unit_tests/test_plugin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Copyright 2026 NWChemEx-Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import unittest

from friendzone import FriendZone, friends
from pluginplay import ModuleManager


class TestFriendZonePlugin(unittest.TestCase):
def test_identity(self):
self.assertEqual(self.plugin.name(), "friendzone")
self.assertEqual(self.plugin.preferred_providers(), [])

def test_modules(self):
modules = self.plugin.get_modules()
has_molssi = friends.is_molssi_enabled()
self.assertEqual(modules.count("QCSchema Driver"), int(has_molssi))
self.assertEqual(
modules.count("ChemicalSystem via QCElemental"), int(has_molssi)
)

def test_load_plugin(self):
mm = ModuleManager()
mm.load_plugin(FriendZone())
self.assertTrue(mm.has_plugin("friendzone"))
self.assertEqual(mm.size(), self.plugin.get_modules().size())

def setUp(self):
self.plugin = FriendZone()
Loading