diff --git a/.gitignore b/.gitignore index 5ed76bae..c950b957 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ -docs/src/* +docs/src/examples/ *build* *egg-info/ diff --git a/63c9c54.zip b/63c9c54.zip deleted file mode 100644 index 5c6010e6..00000000 Binary files a/63c9c54.zip and /dev/null differ diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index fb25ef17..689534a0 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -15,8 +15,8 @@ example in an external repository and link to it on the `Wiki page `_. If you feel unsure if a contribution is suitable, feel free to contact one of the `support`_ before. -Adding an sphinx-gallery examples ---------------------------------- +Adding a new examples +--------------------- To visualize examples on our readthedocs page we use `sphinx-gallery`. When building the doc the examples are run and compiled automatically into HTML files and moved to the @@ -27,16 +27,20 @@ If you do not know where to put your example, just put in the `examples/uncatego `_ folder and when doing a pull request, we will figure out where to put it. +After adding a file, you'll need to update ``tox.ini`` to build your example when +building the documentation. Look how it's done for the ``lode_linear`` example, and +do the same for yours! + Converting a Jupyter notebook to a sphinx-gallery compatible Python script -------------------------------------------------------------------------- Often it is more convenient to work in a Jupyter notebook and convert in later to sphinx-gallery example. To convert your Jupyter notebook you can just use the -`ipynb_to_gallery.py `_ file that is root folder of the repository +`ipynb-to-gallery.py `_ file that is root folder of the repository .. code-block:: bash - python ipynb_to_gallery.py + python ipynb-to-gallery.py Building the cookbook locally ----------------------------- @@ -59,13 +63,10 @@ formatting issues remaining, then the reviewer of your pull request can fix them To visualize the generated cookbook open in a browser the file ``docs/build/html/index.html``. -When you generate the examples locally all the notebook will be automatically generated -in the folder ``docs/src/examples/`` - Known issues ------------ -Sometimes the doc preview from readthedocs is not correcty rendered. If something works +Sometimes the doc preview from readthedocs is not rendered correctly. If something works in your local build but not in the readthedocs PR preview. It could that the issue is fixed once you merge with the main branch. diff --git a/docs/requirements-rascal.txt b/docs/requirements-rascal.txt deleted file mode 100644 index b69845b9..00000000 --- a/docs/requirements-rascal.txt +++ /dev/null @@ -1,6 +0,0 @@ -# rascal needs to be in a separate requirements file because we need to specify -# the packages index-url for it te use the prebuilt from -# from https://github.com/Luthaf/nightly-wheels -# extra-url does not work here because there is another rascal package on pypi ---index-url https://luthaf.fr/nightly-wheels/ -rascal diff --git a/docs/requirements.txt b/docs/requirements.txt index fe4ef1d2..e1efa3b0 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,25 +1,4 @@ -# Sphinx sphinx -sphinx_rtd_theme sphinx-gallery sphinx-toggleprompt furo - -# Jupyter widgets -ipywidgets -jupyter_sphinx - -# Scientific libraries -matplotlib -numpy -ase -scipy - -# COSMO stack and friends -scikit-learn -skmatter -chemiscope -equisolve @ https://github.com/lab-cosmo/equisolve/archive/63c9c54.zip - -metatensor -rascaline @ https://github.com/luthaf/rascaline/archive/581d0ca.zip diff --git a/docs/src/conf.py b/docs/src/conf.py index 958d5d9e..dd941401 100644 --- a/docs/src/conf.py +++ b/docs/src/conf.py @@ -1,31 +1,9 @@ -# Sphinx documentation build configuration file -import os - - # Add any Sphinx extension module names here, as strings. extensions = [ - "sphinx.ext.autodoc", # import the modules you are documenting - "sphinx.ext.intersphinx", # generate links to external projects - "sphinx.ext.viewcode", # add links to highlighted source code - "sphinx_gallery.gen_gallery", # provides a source parser for *.ipynb files + "sphinx.ext.viewcode", + "sphinx_gallery.load_style", ] -examples_root = os.path.join(os.getcwd(), "../../examples/") -examples_subdirs = [] -for path in os.listdir(examples_root): - # ignores files and hidden directories - if os.path.isdir(os.path.join(examples_root, path)) and path[0] != ".": - examples_subdirs.append(path) - -sphinx_gallery_conf = { - "filename_pattern": "/*", - "examples_dirs": [f"../../examples/{subdir}" for subdir in examples_subdirs], - "gallery_dirs": [f"{subdir}" for subdir in examples_subdirs], - "min_reported_time": 60, - # Make the code snippet for own functions clickable - "reference_url": {"cosmo-software-cookbook": None}, -} - templates_path = ["_templates"] exclude_patterns = ["_build"] @@ -34,47 +12,3 @@ htmlhelp_basename = "COSMO software-cookbook" html_theme = "furo" - -# We create the index.rst here because sphinx is not able to automatically -# include all subdirectories using regex expression -root_index_rst_content = r""" -COSMO Software Cookbook -======================= - -.. include:: ../../README.rst - :start-after: marker-intro-start - :end-before: marker-intro-end - -.. toctree:: - :caption: Table of Contents - -""" -root_index_rst_content += "".join( - [f" {subdir}/index\n" for subdir in examples_subdirs] -) -print("Creating index.rst including all examples") -print(root_index_rst_content) - -with open("index.rst", "w") as f: - f.write(root_index_rst_content) - -# Configuration for intersphinx: refer to the Python standard library -# and other packages used by the cookbook - -intersphinx_mapping = { - "ase": ("https://wiki.fysik.dtu.dk/ase/", None), - "chemiscope": ("https://chemiscope.org/docs/", None), - "metatensor": ("https://lab-cosmo.github.io/metatensor/latest/", None), - "equisolve": ("https://lab-cosmo.github.io/equisolve/latest/", None), - "matplotlib": ("https://matplotlib.org/stable/", None), - "numpy": ("https://numpy.org/doc/stable/", None), - "python": ("https://docs.python.org/3", None), - "rascaline": ("https://luthaf.fr/rascaline/latest/", None), - "rascal": ("https://lab-cosmo.github.io/librascal/", None), - "scipy": ("https://docs.scipy.org/doc/scipy/", None), - "sklearn": ( - "http://scikit-learn.org/stable", - (None, "./_intersphinx/sklearn-objects.inv"), - ), - "skmatter": ("https://scikit-matter.readthedocs.io/en/latest/", None), -} diff --git a/docs/src/index.rst b/docs/src/index.rst new file mode 100644 index 00000000..f8460251 --- /dev/null +++ b/docs/src/index.rst @@ -0,0 +1,14 @@ +COSMO Software Cookbook +======================= + +.. include:: ../../README.rst + :start-after: marker-intro-start + :end-before: marker-intro-end + +.. toctree:: + :caption: Table of Contents + :maxdepth: 1 + + examples/roy_gch/roy_gch + examples/lode_linear/lode_tutorial + examples/sample_selection/sample_selection_librascal diff --git a/examples/lode_linear/environement.yml b/examples/lode_linear/environement.yml new file mode 100644 index 00000000..6e07a10e --- /dev/null +++ b/examples/lode_linear/environement.yml @@ -0,0 +1,9 @@ +name: lode_linear +dependencies: + - python=3.11 + - pip + - pip: + - ase + - metatensor + - equisolve @ git+https://github.com/lab-cosmo/equisolve.git@63c9c54046507b27f115efb1d9b9fa8a1573c4da + - rascaline @ git+https://github.com/Luthaf/rascaline@581d0ca4dece424b4594c8c3cac40a7381e13ae5 diff --git a/examples/mlp_models/README.rst b/examples/mlp_models/README.rst deleted file mode 100644 index 572a37d4..00000000 --- a/examples/mlp_models/README.rst +++ /dev/null @@ -1,3 +0,0 @@ -Machine Learning Potentials -=========================== - diff --git a/examples/mlp_models/linear_model.py b/examples/mlp_models/linear_model.py deleted file mode 100644 index bbc8003c..00000000 --- a/examples/mlp_models/linear_model.py +++ /dev/null @@ -1,20 +0,0 @@ -#!/usr/bin/env python -# coding: utf-8 - -""" -MLP with Linear Model -===================== -""" -# %% -# - - -import numpy as np - - -# %% -# Explanation - -X = np.random.rand(10000) * 400 -Y = 0.1 * X + 10.1 -Y_p = Y + np.random.randn(10000) * 1.5 diff --git a/examples/roy_gch/environement.yml b/examples/roy_gch/environement.yml new file mode 100644 index 00000000..29ea918b --- /dev/null +++ b/examples/roy_gch/environement.yml @@ -0,0 +1,11 @@ +name: lode_linear +dependencies: + - python=3.11 + - pip + - pip: + - ase + - chemiscope + - skmatter + - metatensor + - equisolve @ git+https://github.com/lab-cosmo/equisolve.git@63c9c54046507b27f115efb1d9b9fa8a1573c4da + - rascaline @ git+https://github.com/Luthaf/rascaline@581d0ca4dece424b4594c8c3cac40a7381e13ae5 diff --git a/examples/roy_gch/roy_gch.py b/examples/roy_gch/roy_gch.py index 0b90a705..d4c4eeb2 100644 --- a/examples/roy_gch/roy_gch.py +++ b/examples/roy_gch/roy_gch.py @@ -6,7 +6,7 @@ `Beran et Al, Chemical Science (2022) `__, comparing the conventional density-energy convex hull with a Generalized Convex Hull -(GCH) analysis (see `Anelli et al., Phys. Rev. Materials +(GCH) analysis (see `Anelli et al., Phys. Rev. Materials (2018) `__). It uses features computed with `rascaline ` and uses the directional convex hull function from @@ -14,7 +14,7 @@ to make the figure. """ import chemiscope -import matplotlib.tri as mtri +import matplotlib.tri import numpy as np from matplotlib import pyplot as plt from metatensor import mean_over_samples @@ -40,7 +40,7 @@ # %% # Energy-density hull -# =================== +# ------------------- # # The Directional Convex Hull routines can be used to compute a # conventional density-energy hull @@ -59,7 +59,7 @@ # %% # # Hull energies -# ------------- +# ^^^^^^^^^^^^^ # # Structures on the hull are stable with respect to synthesis at constant # molar volume. Any other structure would lower the energy by decomposing @@ -85,7 +85,7 @@ # %% # Interactive visualization -# ------------------------- +# ^^^^^^^^^^^^^^^^^^^^^^^^^ # # You can also visualize the hull with ``chemiscope``. # This runs only in a notebook, and @@ -116,7 +116,7 @@ # %% # Generalized Convex Hull -# ======================= +# ----------------------- # # A GCH is a similar construction, in which generic structural descriptors # are used in lieu of composition, density or other thermodynamic @@ -131,7 +131,7 @@ # %% # Compute structural descriptors -# ------------------------------ +# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ # # A first step is to computes suitable ML descriptors. Here we have used # ``rascaline`` to evaluate average SOAP features for the structures. @@ -163,7 +163,7 @@ # %% # PCA projection -# -------------- +# ^^^^^^^^^^^^^^ # # Computes PCA projection to generate low-dimensional descriptors that # reflect structural diversity. Any other dimensionality reduction scheme @@ -182,7 +182,7 @@ # %% # Builds the Generalized Convex Hull -# ---------------------------------- +# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ # # Builds a convex hull on the first two PCA features @@ -196,7 +196,7 @@ # Generates a 3D Plot # -triang = mtri.Triangulation(pca_features[sel, 0], pca_features[sel, 1]) +triang = matplotlib.tri.Triangulation(pca_features[sel, 0], pca_features[sel, 1]) fig = plt.figure(figsize=(7, 5), tight_layout=True) ax = fig.add_subplot(projection="3d") ax.plot_trisurf(triang, energy[sel], color="gray") @@ -239,7 +239,6 @@ "y": {"property": "pca_2"}, "z": {"property": "energy"}, "symbol": "type", - "symbol": "type", "color": {"property": "hull_energy"}, "size": { "factor": 35, diff --git a/examples/sample_selection/environement.yml b/examples/sample_selection/environement.yml new file mode 100644 index 00000000..1dd5fdda --- /dev/null +++ b/examples/sample_selection/environement.yml @@ -0,0 +1,12 @@ +name: lode_linear +dependencies: + - python=3.10 + - pip + - pip: + # example dependencies + - chemiscope + - skmatter + - scikit-learn + - ase + # needed to install rascal + - setuptools diff --git a/generate-gallery.py b/generate-gallery.py new file mode 100644 index 00000000..1ccb0450 --- /dev/null +++ b/generate-gallery.py @@ -0,0 +1,68 @@ +import os +import shutil +import sys + +import sphinx_gallery.gen_gallery + + +HERE = os.path.realpath(os.path.dirname(__file__)) + + +class AttrDict(dict): + def __init__(self): + super().__init__() + self.__dict__ = self + + +class PseudoSphinxApp: + """ + Class pretending to be a sphinx App, used to configure and run sphinx-gallery + from the command line, without having an actual sphinx project. + """ + + def __init__(self, example): + gallery_dir = os.path.join( + HERE, "docs", "src", "examples", os.path.basename(example) + ) + if os.path.exists(gallery_dir): + shutil.rmtree(gallery_dir) + + # the options here are the minimal set of options to get sphinx-gallery to run + # feel free to add more if sphinx-gallery uses more options in the future + self.config = AttrDict() + self.config.html_static_path = [] + self.config.source_suffix = [".rst"] + self.config.default_role = "" + self.config.sphinx_gallery_conf = { + "filename_pattern": ".*", + "examples_dirs": os.path.join(HERE, example), + "gallery_dirs": gallery_dir, + "min_reported_time": 60, + } + + self.builder = AttrDict() + self.builder.srcdir = os.path.join(HERE, "docs", "src") + self.builder.outdir = "" + self.builder.name = os.path.basename(example) + + self.extensions = [] + + self.builder.config = AttrDict() + self.builder.config.plot_gallery = "True" + self.builder.config.abort_on_example_error = True + self.builder.config.highlight_language = None + + def add_css_file(self, path): + pass + + +if __name__ == "__main__": + if len(sys.argv) < 2: + print(f"usage: {sys.argv[0]} ") + sys.exit(1) + + app = PseudoSphinxApp(example=sys.argv[1]) + + sphinx_gallery.gen_gallery.fill_gallery_conf_defaults(app, app.config) + sphinx_gallery.gen_gallery.update_gallery_conf_builder_inited(app) + sphinx_gallery.gen_gallery.generate_gallery_rst(app) diff --git a/ipynb_to_gallery.py b/ipynb-to-gallery.py similarity index 100% rename from ipynb_to_gallery.py rename to ipynb-to-gallery.py diff --git a/tox.ini b/tox.ini index 1b4b1be2..cdd7509f 100644 --- a/tox.ini +++ b/tox.ini @@ -5,16 +5,65 @@ envlist = docs [testenv] +package = skip lint_folders = - "{toxinidir}/ipynb_to_gallery.py" \ + "{toxinidir}/ipynb-to-gallery.py" \ + "{toxinidir}/generate-gallery.py" \ "{toxinidir}/docs/src/conf.py" \ "{toxinidir}/examples" + +install_conda_env = + conda env update --file examples/{envname}/environement.yml --prefix {envdir}/conda --verbose + # install sphinx-gallery and its dependencies + {envdir}/conda/bin/python -m pip install sphinx-gallery sphinx pillow matplotlib + +generate_gallery = + {envdir}/conda/bin/python generate-gallery.py examples/{envname} + rm -rf docs/src/examples/{envname}/index.rst + +allowlist_externals = + rm + conda + {envdir}/conda/bin/python + commands = # error if the user gives a wrong testenv name in `tox -e` python -c "import sys; print('environement {env_name} does not exist'); sys.exit(1)" + +[testenv:docs] +deps = + -r docs/requirements.txt + +allowlist_externals = tox +commands = + # transform all examples to rst + tox -e lode_linear + tox -e roy_gch + tox -e sample_selection + + sphinx-build {posargs:-E} -W -b html docs/src docs/build/html + + +[testenv:lode_linear] +commands = + {[testenv]install_conda_env} + {[testenv]generate_gallery} + +[testenv:roy_gch] +commands = + {[testenv]install_conda_env} + {[testenv]generate_gallery} + +[testenv:sample_selection] +commands = + {[testenv]install_conda_env} + # manually install librascal from https://luthaf.fr/nightly-wheels/ + {envdir}/conda/bin/python -m pip install rascal --index-url https://luthaf.fr/nightly-wheels/ + {[testenv]generate_gallery} + + [testenv:lint] -skip_install = true deps = black blackdoc @@ -32,18 +81,10 @@ commands = -i "{toxinidir}/docs/src" \ {[testenv]lint_folders} "{toxinidir}/README.rst" "{toxinidir}/CONTRIBUTING.rst" -[testenv:docs] -deps = - -r docs/requirements.txt -commands = - # tox's can not handle our custom requirement files in the deps section. - pip install -r docs/requirements-rascal.txt - sphinx-build {posargs:-E} -W -b html docs/src docs/build/html [testenv:format] # Abuse tox to do actual formatting. Users can call `tox -e format` to run # formatting on all files -skip_install = true deps = black blackdoc