From 1a9f7ebfc0eb5c5b850544804f7835ca35f6c244 Mon Sep 17 00:00:00 2001 From: Irfan Alibay Date: Wed, 1 Nov 2023 16:25:56 +0000 Subject: [PATCH] Add duecredit (#601) * Add basic skeleton for duecredit * Add initial duecredit tests * add duecredit to the environment * omit due.py from coverage * Update environment.yml * remove duecredit.p files * Update openfe/tests/utils/test_duecredit.py Co-authored-by: Irfan Alibay --------- Co-authored-by: Richard Gowers --- .github/workflows/ci.yaml | 1 + .gitignore | 1 + environment.yml | 1 + openfe/.duecredit.p | Bin 0 -> 4851 bytes openfe/due.py | 74 ++++++++++++++++++ .../openmm_afe/equil_solvation_afe_method.py | 25 +++++- .../protocols/openmm_rfe/equil_rfe_methods.py | 24 +++++- openfe/tests/utils/test_duecredit.py | 30 +++++++ pyproject.toml | 1 + 9 files changed, 155 insertions(+), 2 deletions(-) create mode 100644 openfe/.duecredit.p create mode 100644 openfe/due.py create mode 100644 openfe/tests/utils/test_duecredit.py diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 3a96c4b3d..a821704a1 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -79,6 +79,7 @@ jobs: env: # Set the OFE_SLOW_TESTS to True if running a Cron job OFE_SLOW_TESTS: ${{ fromJSON('{"false":"false","true":"true"}')[github.event_name != 'pull_request'] }} + DUECREDIT_ENABLE: 'yes' run: | pytest -n auto -v --cov=openfe --cov=openfecli --cov-report=xml --durations=10 diff --git a/.gitignore b/.gitignore index b8552aeeb..be594f514 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ # custom ignores +.duecredit.p .xxrun .idea/ .vscode/ diff --git a/environment.yml b/environment.yml index 5286eae95..fa29fb7b4 100644 --- a/environment.yml +++ b/environment.yml @@ -3,6 +3,7 @@ channels: - jaimergp/label/unsupported-cudatoolkit-shim - conda-forge dependencies: + - duecredit - numpy<1.24 - networkx - rdkit diff --git a/openfe/.duecredit.p b/openfe/.duecredit.p new file mode 100644 index 0000000000000000000000000000000000000000..ac4a3c2ea8ecf04a6cd2b3a159d9b86ae66e6f48 GIT binary patch literal 4851 zcmeHKTTdHD6o!OL0!iE?y(DTCjT)(<3SQPW#+V2NVw^O%fD43DCDaN$ znysbM`P|%mMg&)nEqC;T^D8zJv7w6<7az*AmIwx#8L1qQcbxmxVDc;9;>2A@>v~Sm z3&GfZR^2AMu^)PQ_k(joLrcppXNF1IUFy>l2rc`Z+Ae};5k+UyXjMZi+L1ioX51w5 zR35Kk?wB?=S#<#|qx1@@yV#-3@DQ<@%(blsq@!M2wSCvZX82mcZa6+?Rvj@5Z5fPM z20>51Kt-$0EJ9p3Q7`S-rr%J`z*k<0*8N76bfU{BQ5n~X*NG^vCzJWClH;n!U2O2a zND=c8?upgG#sSzszDH_kA963Zpahou2?=AzF`0om14)fEiD^4Bt7(WGstop7lOWG< znIkirM%bz$$FFwYNj~?n$#@Gav4-?gCl%wfZ|Jx}^|Lu>okfK;Rf+fhnTBCq9f z((uCSE5jZIeW=KzVzGz{8*Amq+r>?^wNX%M*KDSWq0$C0rc!!-X05PQ)FdFZY*?HY z!K#WIRPP>&P62pf}JTA7kDM5gZatmSC#jOQsF?8QIxbG6xQ?kF$ zB+@tnF90zEz-TBOs$Qtlg%+_IfW`I>w$KVx`de&5x3@Ui2Z8Jj>;S-^l%WE3C7eLG zgIyb1?Z22t4>2*j>^%#izu10X*oR>>75Eq1sSxF%5Ml?{i|urmeMjOSMCoKQqj$+= zi$M%^?GnZST5K;rDxq*7WYcNAA$p{1{FHzY>JRyWFb^i+2f zKb1{fW1i6t&pN#e>kRmVr(J}OpjF$7;J@wSCKPtVWDdp9Gg;v>94an|45lNM zOvY!vYl4T*Dd*0@%*^58p$0acmS(&4na?|^SvDu2)qb*JnXu9BB5c55%k={7$R_n% zGL_j2tEnm(S54LMqrNL7o1dFY_l(&fjYKfu1UmDX>}V#Jnu+kJIYyOfDP7ObW%J!L ze2QY?6ieXhgdno|Y`2d=8Yd2ta4e7EIet>w+# zM>{=QiFeuXtA23NU|g*Ob(hnjV6qSiN^t4E5kV66vb9e%$F;c)1i{m!pwXzfASBOy zX3Afc27wEt<_S9fvv?`M&V`Ph_!bVv6m5WYH+Xw?xC2elaVWs0N{vJpjctiwtPI!( z))9fP1^=iyth<(0te$wcpL6CNlci>;W~z7 zV%epyZQCO|kxI-^#+$HIVOfaa27qZ+)9>VTtv;KX)w8LrR%0%Qd^Y#QRB5Pmr8E=_ z-w~w;!Ejxy-oAT(C>RYj2;?$q*uYXmX~eJ6d%;8_CK7^PRW)0QE`f(#*TETW>84m` zv?Zb~^eUBmla_)H%fLnzu{>m)Mm$c?HCmzTztD|6LBGLtlWqlnCY)3Fbp7=)5T8X$ZnHF8`lK}aN>EDzIl@{ns;Gsa zw+BMvj9ZdQ|(3!=L(lX4yO^o1_bslJXJ?|>7SjUsOeI;B?Bq@<&3Yul22 zNS3njK4ndOx<2sGi(WDA*EDTRZ_Iqz+V{m>i z_|K802S#>6#S~ji(6kC^Q4{*4^ngC?rzOyUb23eE zl^YoEI~gX;shF=6JqMe10~aWE>*P3k`X|Kt*Rh|IxU$5{K<_71E5g^`%5y)y!lHG% I_5G^$FZoq0@&Et; literal 0 HcmV?d00001 diff --git a/openfe/due.py b/openfe/due.py new file mode 100644 index 000000000..f729f8438 --- /dev/null +++ b/openfe/due.py @@ -0,0 +1,74 @@ +# emacs: at the end of the file +# ex: set sts=4 ts=4 sw=4 et: +# ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### # +""" + +Stub file for a guaranteed safe import of duecredit constructs: if duecredit +is not available. + +To use it, place it into your project codebase to be imported, e.g. copy as + + cp stub.py /path/tomodule/module/due.py + +Note that it might be better to avoid naming it duecredit.py to avoid shadowing +installed duecredit. + +Then use in your code as + + from .due import due, Doi, BibTeX, Text + +See https://github.com/duecredit/duecredit/blob/master/README.md for examples. + +Origin: Originally a part of the duecredit +Copyright: 2015-2021 DueCredit developers +License: BSD-2 +""" + +__version__ = '0.0.9' + + +class InactiveDueCreditCollector(object): + """Just a stub at the Collector which would not do anything""" + def _donothing(self, *args, **kwargs): + """Perform no good and no bad""" + pass + + def dcite(self, *args, **kwargs): + """If I could cite I would""" + def nondecorating_decorator(func): + return func + return nondecorating_decorator + + active = False + activate = add = cite = dump = load = _donothing + + def __repr__(self): + return self.__class__.__name__ + '()' + + +def _donothing_func(*args, **kwargs): + """Perform no good and no bad""" + pass + + +try: + from duecredit import due, BibTeX, Doi, Url, Text # lgtm [py/unused-import] + if 'due' in locals() and not hasattr(due, 'cite'): + raise RuntimeError( + "Imported due lacks .cite. DueCredit is now disabled") +except Exception as e: + if not isinstance(e, ImportError): + import logging + logging.getLogger("duecredit").error( + "Failed to import duecredit due to %s" % str(e)) + # Initiate due stub + due = InactiveDueCreditCollector() + BibTeX = Doi = Url = Text = _donothing_func + +# Emacs mode definitions +# Local Variables: +# mode: python +# py-indent-offset: 4 +# tab-width: 4 +# indent-tabs-mode: nil +# End: diff --git a/openfe/protocols/openmm_afe/equil_solvation_afe_method.py b/openfe/protocols/openmm_afe/equil_solvation_afe_method.py index 5f35dca0e..7a716faf1 100644 --- a/openfe/protocols/openmm_afe/equil_solvation_afe_method.py +++ b/openfe/protocols/openmm_afe/equil_solvation_afe_method.py @@ -23,7 +23,7 @@ Acknowledgements ---------------- * Originally based on hydration.py in - `espaloma `_ + `espaloma_charge `_ """ from __future__ import annotations @@ -56,6 +56,29 @@ from ..openmm_utils import system_validation, settings_validation from .base import BaseAbsoluteUnit from openfe.utils import without_oechem_backend, log_system_probe +from openfe.due import due, Doi + + +due.cite(Doi("10.5281/zenodo.596504"), + description="Yank", + path="openfe.protocols.openmm_afe.equil_solvation_afe_method", + cite_module=True) + +due.cite(Doi("10.48550/ARXIV.2302.06758"), + description="EspalomaCharge", + path="openfe.protocols.openmm_afe.equil_solvation_afe_method", + cite_module=True) + +due.cite(Doi("10.5281/zenodo.596622"), + description="OpenMMTools", + path="openfe.protocols.openmm_afe.equil_solvation_afe_method", + cite_module=True) + +due.cite(Doi("10.1371/journal.pcbi.1005659"), + description="OpenMM", + path="openfe.protocols.openmm_afe.equil_solvation_afe_method", + cite_module=True) + logger = logging.getLogger(__name__) diff --git a/openfe/protocols/openmm_rfe/equil_rfe_methods.py b/openfe/protocols/openmm_rfe/equil_rfe_methods.py index 589a96177..e92081f69 100644 --- a/openfe/protocols/openmm_rfe/equil_rfe_methods.py +++ b/openfe/protocols/openmm_rfe/equil_rfe_methods.py @@ -1,6 +1,6 @@ # This code is part of OpenFE and is licensed under the MIT license. # For details, see https://github.com/OpenFreeEnergy/openfe -"""Equilibrium Relative Free Energy methods using OpenMM in a +"""Equilibrium Relative Free Energy methods using OpenMM and OpenMMTools in a Perses-like manner. This module implements the necessary methodology toolking to run calculate a @@ -14,6 +14,10 @@ ---- * Improve this docstring by adding an example use case. +Acknowledgements +---------------- +This Protocol is based on, and leverages components originating from +the Perses toolkit (https://github.com/choderalab/perses). """ from __future__ import annotations @@ -55,10 +59,28 @@ ) from . import _rfe_utils from ...utils import without_oechem_backend, log_system_probe +from openfe.due import due, Doi + logger = logging.getLogger(__name__) +due.cite(Doi("10.5281/zenodo.1297683"), + description="Perses", + path="openfe.protocols.openmm_rfe.equil_rfe_methods", + cite_module=True) + +due.cite(Doi("10.5281/zenodo.596622"), + description="OpenMMTools", + path="openfe.protocols.openmm_rfe.equil_rfe_methods", + cite_module=True) + +due.cite(Doi("10.1371/journal.pcbi.1005659"), + description="OpenMM", + path="openfe.protocols.openmm_rfe.equil_rfe_methods", + cite_module=True) + + def _get_resname(off_mol) -> str: # behaviour changed between 0.10 and 0.11 omm_top = off_mol.to_topology().to_openmm() diff --git a/openfe/tests/utils/test_duecredit.py b/openfe/tests/utils/test_duecredit.py new file mode 100644 index 000000000..dad7b9966 --- /dev/null +++ b/openfe/tests/utils/test_duecredit.py @@ -0,0 +1,30 @@ +import os +import importlib +import pytest + +import openfe + + +pytest.importorskip('duecredit') + + +@pytest.mark.skipif((os.environ.get('DUECREDIT_ENABLE', 'no').lower() + in ('no', '0', 'false')), + reason="duecredit is disabled") +class TestDuecredit: + + @pytest.mark.parametrize('module, dois', [ + ['openfe.protocols.openmm_afe.equil_solvation_afe_method', + ['10.5281/zenodo.596504', '10.48550/arxiv.2302.06758', + '10.5281/zenodo.596622', '10.1371/journal.pcbi.1005659']], + ['openfe.protocols.openmm_rfe.equil_rfe_methods', + ['10.5281/zenodo.1297683', '10.5281/zenodo.596622', + '10.1371/journal.pcbi.1005659']], + ]) + def test_duecredit_protocol_collection(self, module, dois): + importlib.import_module(module) + for doi in dois: + assert openfe.due.due.citations[(module, doi)].cites_module + + def test_duecredit_active(self): + assert openfe.due.due.active diff --git a/pyproject.toml b/pyproject.toml index 8a9cb5793..531619eea 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -57,6 +57,7 @@ ignore_missing_imports = true [tool.coverage.run] omit = [ + "openfe/due.py", "*/tests/dev/*py", "*/tests/protocols/test_openmm_rfe_slow.py" ]