diff --git a/docs/_static/style.css b/docs/_static/style.css deleted file mode 100644 index 2580b006..00000000 --- a/docs/_static/style.css +++ /dev/null @@ -1,59 +0,0 @@ -@import 'theme.css'; - -.rst-content dl:not(.docutils) dt:first-child { - margin-top: 0; -} - -.rst-content dl:not(.docutils) dl dt { - margin-bottom: 4px; - border: none; - border-left: solid 3px #ccc; - background: #f0f0f0; - color: #555; -} - -.rst-content dl table, -.rst-content dl ul, -.rst-content dl ol, -.rst-content dl p { - margin-bottom: 8px !important; -} - -.rst-content dl:not(.docutils) dt { - display: table; - margin: 6px 0; - font-size: 90%; - line-height: normal; - background: #e7f2fa; - color: #2980B9; - border-top: solid 3px #6ab0de; - padding: 6px; - position: relative; -} - -html.writer-html5 .rst-content dl.field-list { - display: initial; -} - -html.writer-html5 .rst-content dl.field-list>dd, -html.writer-html5 .rst-content dl.field-list>dt { - margin-bottom: 4px; - padding-left: 6px; -} - -p { - line-height: 20px; - font-size: 14px; -} - -html.writer-html5 .rst-content dl.field-list>dt:after { - content: initial; -} - -dt.field-even { - text-transform: uppercase; -} - -dt.field-odd { - text-transform: uppercase; -} diff --git a/docs/source/conf.py b/docs/source/conf.py index a1afd589..5657f049 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -7,6 +7,31 @@ import os +def setup(app): + import inspect + from sphinx.util import inspect as sphinx_inspect + + # xref: https://github.com/wjakob/nanobind/discussions/707 + # Sphinx inspects all objects in the module and tries to resolve their type + # (attribute, function, class, module, etc.) by using its own functions in + # `sphinx.util.inspect`. These functions misidentify certain nanobind + # objects. We monkey patch those functions here. + def mpatch_ismethod(object): + if hasattr(object, "__name__") and type(object).__name__ == "nb_method": + return True + return inspect.ismethod(object) + + sphinx_inspect_isclassmethod = sphinx_inspect.isclassmethod + + def mpatch_isclassmethod(object, cls=None, name=None): + if hasattr(object, "__name__") and type(object).__name__ == "nb_method": + return False + return sphinx_inspect_isclassmethod(object, cls, name) + + sphinx_inspect.ismethod = mpatch_ismethod + sphinx_inspect.isclassmethod = mpatch_isclassmethod + + def get_latest_git_tag(repo_path="."): repo = git.Repo(repo_path) tags = sorted(repo.tags, key=lambda t: t.commit.committed_datetime) @@ -50,9 +75,6 @@ def get_latest_git_tag(repo_path="."): intersphinx_mapping = { "python": ("https://docs.python.org/3/", None), - "torch": ("https://pytorch.org/docs/stable/", None), - "sphinx": ("https://www.sphinx-doc.org/en/master/", None), - "pybind11": ("https://pybind11.readthedocs.io/en/stable/", None), } intersphinx_disabled_domains = ["std"] @@ -68,18 +90,14 @@ def get_latest_git_tag(repo_path="."): autoclass_content = "both" autodoc_typehints = "none" autodoc_inherit_docstrings = False -sphinx_autodoc_typehints = True html_show_sourcelink = True autodoc_default_options = { "members": True, "member-order": "bysource", - "exclude-members": "__weakref__", + "exclude-members": "__weakref__,precision", "undoc-members": False, "show-inheritance": True, "inherited-members": False, } -# Exclude all torchmdnet.datasets.*.rst files in source/generated/ + html_static_path = ["../_static"] -html_css_files = [ - "style.css", -] diff --git a/docs/source/extensions/customsig.py b/docs/source/extensions/customsig.py index ad8c77ec..9bd662cb 100644 --- a/docs/source/extensions/customsig.py +++ b/docs/source/extensions/customsig.py @@ -10,13 +10,40 @@ def setup(app: Sphinx): app.connect("autodoc-process-signature", process_signature) +def strip_type_hints(signature: str) -> str: + # This pattern matches parameters with type hints and optional default values + param_pattern = re.compile( + r""" + (\b\w+\b) # parameter name + \s*:\s* # colon and optional whitespace + (?:[^=,\[\]()]+(?:\[[^\[\]]*\])?) # base type with optional brackets + (?:\s*\|\s*[^=,()\[\]]+(?:\[[^\[\]]*\])?)* # optional union types + (\s*=\s*[^,()]+)? # optional default (capturing group) + """, + re.VERBOSE, + ) + + def replacer(match): + name = match.group(1) + default = match.group(2) if match.lastindex and match.lastindex >= 2 else None + if default and default.strip() != "= None": + return f"{name}{default}" + else: + return name + + # First replace all type-hinted parameters with clean ones + stripped = param_pattern.sub(replacer, signature) + # Normalize spacing around equals signs + return re.sub(r"\s*=\s*", " = ", stripped) + + def modify_signature(signature): # Remove the class method references (e.g., self: ...) modified = re.sub(r"\s*self:\s*[\w\.]+\s*,?\s*", "", signature) - # Remove the type hints from the parameters - modified = re.sub(r":\s*[\w\.\[\]]+", "", modified) + # modified = re.sub(r":\s*[\w\.\[\]]+", "", modified) + modified = strip_type_hints(modified) # Simplify numpy array default values to be just empty modified = re.sub(r"array\(\[\],\s*dtype=[\w]+\)", "", modified) @@ -28,7 +55,6 @@ def modify_signature(signature): modified = re.sub(r"\s*,\s*", ", ", modified) modified = re.sub(r"\s*\(", "(", modified) modified = re.sub(r"\s*\)", ")", modified) - return modified diff --git a/docs/source/solvers.rst b/docs/source/solvers.rst index be5fe459..785254ab 100644 --- a/docs/source/solvers.rst +++ b/docs/source/solvers.rst @@ -1,4 +1,5 @@ .. _solvers: + Available solvers ================= @@ -6,8 +7,6 @@ The following solvers are available in libMobility. Self Mobility ------------- -This module neglects hydrodynamic interactions and simply sets :math:`\boldsymbol{\mathcal{M} }= \frac{1}{6\pi\eta a} \mathbb{I}`. - .. autoclass:: libMobility.SelfMobility :members: :inherited-members: @@ -16,9 +15,6 @@ This module neglects hydrodynamic interactions and simply sets :math:`\boldsymbo Positively Split Ewald (PSE) ---------------------------- -This module computes the RPY mobility in triply periodic boundaries using Ewald splitting with the Positively Split Ewald method. - - .. autoclass:: libMobility.PSE :members: :inherited-members: @@ -30,8 +26,6 @@ This module computes the RPY mobility in triply periodic boundaries using Ewald NBody ----- -This module computes the RPY mobility in open boundaries using an :math:`O(N^2)` algorithm. - .. autoclass:: libMobility.NBody :members: :inherited-members: @@ -40,8 +34,6 @@ This module computes the RPY mobility in open boundaries using an :math:`O(N^2)` Doubly Periodic Stokes (DPStokes) --------------------------------- -This module computes hydrodynamic interactions in a doubly periodic environment. - .. autoclass:: libMobility.DPStokes :members: :inherited-members: diff --git a/docs/source/usage.rst b/docs/source/usage.rst index 91a85c99..e91e3378 100644 --- a/docs/source/usage.rst +++ b/docs/source/usage.rst @@ -1,3 +1,5 @@ +.. _usage: + Usage ----- diff --git a/environment.yml b/environment.yml index 5d8b1acf..34d15819 100644 --- a/environment.yml +++ b/environment.yml @@ -20,8 +20,8 @@ dependencies: - nanobind - nanobind-abi - pip: - - sphinx==7.2.6 - - sphinx-rtd-theme==1.3.0 - - sphinxcontrib-autoprogram==0.1.8 + - sphinx==8.1.3 + - sphinx-rtd-theme==3.0.2 + - sphinxcontrib-autoprogram==0.1.9 - sphinxcontrib-napoleon==0.7 - gitpython diff --git a/include/MobilityInterface/pythonify.h b/include/MobilityInterface/pythonify.h index 37878e00..0d893607 100644 --- a/include/MobilityInterface/pythonify.h +++ b/include/MobilityInterface/pythonify.h @@ -167,6 +167,8 @@ periodicityZ : str const char *initialize_docstring = R"pbdoc( Initialize the module with a given set of parameters. +.. warning:: :py:mod:`setParameters` must be called before this function. + Parameters ---------- temperature : float @@ -352,10 +354,12 @@ template auto call_thermalDrift(Solver &solver, real prefactor) { const char *thermaldrift_docstring = R"pbdoc( Computes the thermal drift, :math:`k_BT\boldsymbol{\partial}_\boldsymbol{x}\cdot \boldsymbol{\mathcal{M}}`. It is required that :py:mod:`setPositions` has been called before calling this function. + Parameters ---------- prefactor : float, optional Prefactor to multiply the result by. Default is 1.0. + Returns ------- array_like diff --git a/solvers/DPStokes/python_wrapper.cu b/solvers/DPStokes/python_wrapper.cu index ce0013aa..4cf4e598 100644 --- a/solvers/DPStokes/python_wrapper.cu +++ b/solvers/DPStokes/python_wrapper.cu @@ -3,10 +3,12 @@ #include "mobility.h" #include using DPStokesParameters = uammd_dpstokes::PyParameters; -static const char *docstring = R"pbdoc( -In the Doubly periodic Stokes geometry (DPStokes), an incompressible fluid exists in a domain which is periodic in the plane and open (or walled) in the third direction. +static const char *setparameters_docstring = R"pbdoc( When the periodicity is set to :code:`single_wall` a wall in the bottom of the domain is added. +When the periodicity is set to :code:`two_walls` a wall in the bottom and top of the domain is added. + +Even in open mode (Z periodicity set to `open`) the values of :code:`zmin` and :code:`zmax` are still required. The algorithm needs to define a grid in the z direction, and these values define the extents of that grid. The code will fail if a position outside of these extents is used. Parameters ---------- @@ -15,26 +17,36 @@ Lx : float Ly : float The box size in the y direction. zmin : float - The minimum value of the z coordinate. + The minimum value of the z coordinate. This is the position of the bottom wall if the Z periodicity is `single_wall` or `two_walls`. zmax : float - The maximum value of the z coordinate. + The maximum value of the z coordinate. This is the position of the top wall if the Z periodicity is `two_walls`. allowChangingBoxSize : bool Whether the periodic extents Lx & Ly can be modified during parameter selection. Default: false. )pbdoc"; +static const char *docstring = R"pbdoc( +In the Doubly periodic Stokes geometry (DPStokes), an incompressible fluid exists in a domain which is periodic in the plane and open (or walled) in the third direction. The algorithm is described in [1]. + +The periodicity must be set to `periodic` in the X and Y directions. The Z periodicity can be set to `open`, `single_wall`, or `two_walls`. The `open` option allows for an open boundary condition in the Z direction, while `single_wall` and `two_walls` add walls at the bottom and/or top of the simulation box. + +**References** + +[1] Aref Hashemi, Raúl P. Peláez, Sachin Natesh, Brennan Sprinkle, Ondrej Maxian, Zecheng Gan, Aleksandar Donev; Computing hydrodynamic interactions in confined doubly periodic geometries in linear time. J. Chem. Phys. 21 April 2023; 158 (15): 154101. https://doi.org/10.1063/5.0141371 +)pbdoc"; + MOBILITY_PYTHONIFY_WITH_EXTRA_CODE( - DPStokes, - solver.def( - "setParameters", - [](DPStokes &self, real Lx, real Ly, real zmin, real zmax, - bool allowChangingBoxSize) { - DPStokesParameters params; - params.Lx = Lx; - params.Ly = Ly; - params.zmin = zmin; - params.zmax = zmax; - params.allowChangingBoxSize = allowChangingBoxSize; - self.setParametersDPStokes(params); - }, - "Lx"_a, "Ly"_a, "zmin"_a, "zmax"_a, "allowChangingBoxSize"_a = false); + DPStokes, solver.def( + "setParameters", + [](DPStokes &self, real Lx, real Ly, real zmin, real zmax, + bool allowChangingBoxSize) { + DPStokesParameters params; + params.Lx = Lx; + params.Ly = Ly; + params.zmin = zmin; + params.zmax = zmax; + params.allowChangingBoxSize = allowChangingBoxSize; + self.setParametersDPStokes(params); + }, + "Lx"_a, "Ly"_a, "zmin"_a, "zmax"_a, + "allowChangingBoxSize"_a = false, setparameters_docstring); , docstring); diff --git a/solvers/NBody/python_wrapper.cu b/solvers/NBody/python_wrapper.cu index ae4b9af3..add597d5 100644 --- a/solvers/NBody/python_wrapper.cu +++ b/solvers/NBody/python_wrapper.cu @@ -19,6 +19,16 @@ static const char *docstringSetParameters = R"pbdoc( The height of the wall. Only valid if periodicityZ is single_wall. )pbdoc"; +static const char *docstring = R"pbdoc( +This module computes hydrodynamic interactions using an :math:`O(N^2)` algorithm. +Different hydrodynamic kernels can be chosen depending on the periodicity. + +This module only accepts open boundaries in the X and Y directions. The Z direction can be one of: + +- `open`: The Rotne-Prager-Yamakawa mobility is used. +- `single_wall`: The Rotne-Prager-Blake mobility is used, with a single wall at the bottom of the simulation box (see setParameters). + +)pbdoc"; namespace nbody_rpy { auto string2NBodyAlgorithm(std::string algo) { if (algo == "naive") @@ -46,5 +56,4 @@ MOBILITY_PYTHONIFY_WITH_EXTRA_CODE( }, docstringSetParameters, "algorithm"_a = "advise", "Nbatch"_a = -1, "NperBatch"_a = -1, "wallHeight"_a = std::nullopt); - , "This module computes the RPY mobility using an N^2 algorithm in the " - "GPU. Different hydrodynamic kernels can be chosen."); + , docstring); diff --git a/solvers/PSE/python_wrapper.cu b/solvers/PSE/python_wrapper.cu index 8b02a740..79223733 100644 --- a/solvers/PSE/python_wrapper.cu +++ b/solvers/PSE/python_wrapper.cu @@ -19,7 +19,16 @@ static const char *docstringSetParameters = R"pbdoc( )pbdoc"; static const char *docstring = R"pbdoc( -This module computes the RPY mobility in triply periodic boundaries using Ewald splitting with the Positively Split Ewald method.)pbdoc"; +This module computes the RPY mobility in triply periodic boundaries using Ewald splitting with the Positively Split Ewald method [1]. + + +This module will only accept periodic boundary conditions in the three directions. + +**References** + +[1] Andrew M. Fiore, Florencio Balboa Usabiaga, Aleksandar Donev, James W. Swan; Rapid sampling of stochastic displacements in Brownian dynamics simulations. J. Chem. Phys. 28 March 2017; 146 (12): 124116. https://doi.org/10.1063/1.4978242 + +)pbdoc"; MOBILITY_PYTHONIFY_WITH_EXTRA_CODE( PSE, diff --git a/solvers/SelfMobility/python_wrapper.cu b/solvers/SelfMobility/python_wrapper.cu index 6b992dce..d32d4afd 100644 --- a/solvers/SelfMobility/python_wrapper.cu +++ b/solvers/SelfMobility/python_wrapper.cu @@ -3,15 +3,29 @@ #include "mobility.h" #include -MOBILITY_PYTHONIFY_WITH_EXTRA_CODE( - SelfMobility, solver.def( - "setParameters", - [](SelfMobility &self, real parameter) { - self.setParametersSelfMobility(parameter); - }, - "parameter"_a, "Some example parameter"); - , "This module ignores hydrodynamic interactions, AKA the mobility matrix " - "is simply (1/(6*pi*eta*a))*I"); +static const char *setparameters_docstring = R"pbdoc( +Fixes the parameters of the SelfMobility module. For this module, this is just an example on how to set parameters and it is ignored. + +Parameters +---------- +parameter : float + An example parameter that is not used in this module. +)pbdoc"; + +static const char *docstring = R"pbdoc( +This module ignores hydrodynamic interactions, i.e. the mobility matrix is :math:`\frac{1}{6\pi\eta a}\mathbb{I}`. +This module will only accept open boundary conditions in the three directions. +)pbdoc"; + +MOBILITY_PYTHONIFY_WITH_EXTRA_CODE(SelfMobility, + solver.def( + "setParameters", + [](SelfMobility &self, real parameter) { + self.setParametersSelfMobility( + parameter); + }, + "parameter"_a, setparameters_docstring); + , docstring) // A module that does not use setParametersSolver can pythonify like this // instead MOBILITY_PYTHONIFY(SelfMobility, "This module ignores hydrodynamic