diff --git a/.github/workflows/build_binder.yml b/.github/workflows/build_binder.yml deleted file mode 100644 index fb6b6b29..00000000 --- a/.github/workflows/build_binder.yml +++ /dev/null @@ -1,21 +0,0 @@ -name: Trigger Binder build -on: - push: - branches: - - main - -# Allow only one concurrent build, skipping runs queued between the run in-progress and latest queued. -# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. -concurrency: - group: "build_binder" - cancel-in-progress: false - -jobs: - trigger-binder-build: - runs-on: ubuntu-latest - steps: - - uses: s-weigand/trigger-mybinder-build@v1 - with: - target-repo: beamme-py/beamme/HEAD - service-name: gh - debug: true diff --git a/.github/workflows/check_code.yml b/.github/workflows/check_code.yml deleted file mode 100644 index 0d2ee751..00000000 --- a/.github/workflows/check_code.yml +++ /dev/null @@ -1,30 +0,0 @@ -name: Code quality - -on: - schedule: - - cron: '0 06 * * *' - push: - branches: - - main - pull_request: - types: - - opened - - reopened - - synchronize - workflow_dispatch: - type: choice - -jobs: - code-check: - name: Code check - runs-on: ubuntu-latest - defaults: - run: - shell: bash - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - name: Setup virtual python environment - uses: ./.github/actions/setup_virtual_python_environment - - name: Code quality checks - uses: ./.github/actions/code_check diff --git a/.github/workflows/check_meshpy_redirects.yml b/.github/workflows/check_meshpy_redirects.yml deleted file mode 100644 index 76040269..00000000 --- a/.github/workflows/check_meshpy_redirects.yml +++ /dev/null @@ -1,27 +0,0 @@ -name: Check MeshPy to BeamMe redirects - -on: - schedule: - - cron: "0 06 * * *" - -jobs: - check-redirects: - runs-on: ubuntu-latest - steps: - - name: Check GitHub repo redirect - run: | - final_url=$(curl -sL -o /dev/null -w "%{url_effective}" "https://github.com/imcs-compsim/meshpy/") - echo "Repo redirect: $final_url" - [ "$final_url" = "https://github.com/beamme-py/beamme" ] || { - echo "Repo redirect failed" - exit 1 - } - - - name: Check GitHub Pages meta redirect - run: | - redirect_url=$(curl -s "https://imcs-compsim.github.io/meshpy/" | grep -i 'http-equiv="refresh"' | sed -E 's/.*content="[0-9]+;\s*URL=([^"]*)".*/\1/I') - echo "Pages redirect: $redirect_url" - [ "$redirect_url" = "https://beamme-py.github.io/beamme/" ] || { - echo "Pages redirect failed" - exit 1 - } diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml deleted file mode 100644 index f0abf1ef..00000000 --- a/.github/workflows/documentation.yml +++ /dev/null @@ -1,37 +0,0 @@ -name: Build documentation - -on: - # Only build the documentation once the test suite has completed on the main branch - workflow_run: - workflows: [Protected test suite (can access cubit secrets)] - types: [completed] - branches: [main] - -jobs: - build_documentation: - name: Build documentation - # Only run if a push to main occurred, do not run after nightly cron job - if: ${{ github.event.workflow_run.event == 'push' }} - runs-on: ubuntu-latest - defaults: - run: - shell: bash - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - name: Setup virtual python environment - uses: ./.github/actions/setup_virtual_python_environment - - name: Install dependencies - run: | - cd ${GITHUB_WORKSPACE} - source python-workflow-venv/bin/activate - pip install -e .[dev,fourc] - - name: Build API documentation - run: | - source python-workflow-venv/bin/activate - pdoc --math --docformat google --output-dir api-documentation src/beamme/ - - name: Upload API documentation artifact - uses: actions/upload-artifact@v4 - with: - name: api-documentation - path: api-documentation/ diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml deleted file mode 100644 index b90926bd..00000000 --- a/.github/workflows/testing.yml +++ /dev/null @@ -1,49 +0,0 @@ -name: Test suite - -on: - schedule: - - cron: '0 06 * * *' - push: - branches: - - main - pull_request: - types: - - opened - - reopened - - synchronize - workflow_dispatch: - -jobs: - beamme-testing: - name: ${{ matrix.os-version }} python${{ matrix.python-version }} - strategy: - fail-fast: false - matrix: - os-version: [ubuntu-latest, macos-latest, windows-latest] - python-version: ["3.10", "3.13"] - runs-on: ${{ matrix.os-version }} - steps: - - name: Checkout PR code - uses: actions/checkout@v4 - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v5 - with: - python-version: ${{ matrix.python-version }} - - name: Run the test suite - uses: ./.github/actions/run_tests - with: - # Test coverage and editable install with Python 3.10, otherwise we use a - # non-editable installation and turn of coverage, because the coverage - # only works in editable mode. - install-command: >- - ${{ matrix.python-version == '3.10' && '-e .[dev,fourc]' || '.[dev,fourc]'}} - # The single space in the empty string is required, otherwise GitHub - # evaluates the if clause wrong. - additional-pytest-flags: >- - ${{ matrix.python-version == '3.10' && ' ' || '--no-cov' }} - - name: Upload test results on failure - if: failure() - uses: actions/upload-artifact@v4 - with: - name: ${{ github.job }}-${{ matrix.os-version }}-python${{ matrix.python-version }}-${{ github.run_number }} - path: ${{ env.PYTEST_TMPDIR }} diff --git a/.github/workflows/testing_protected.yml b/.github/workflows/testing_protected.yml deleted file mode 100644 index 1e5c775b..00000000 --- a/.github/workflows/testing_protected.yml +++ /dev/null @@ -1,120 +0,0 @@ -name: Protected test suite (can access cubit secrets) - -on: - schedule: - - cron: '0 06 * * *' - push: - branches: - - main - pull_request_target: - types: - - opened - - reopened - - synchronize - workflow_dispatch: - -env: - CUBIT_DOWNLOAD_URL: https://f002.backblazeb2.com/file/cubit-downloads/Coreform-Cubit/Releases/Linux/Coreform-Cubit-2025.8%2B61943-Lin64.tar.gz - -jobs: - beamme-testing-all-dependencies: - name: ubuntu-latest with all dependencies - runs-on: ubuntu-latest - environment: - # Use the trusted environment only for PRs authored by an COLLABORATOR (no approval needed); all others use the untrusted environment (someone has to approve the workflow run). - # Otherwise (scheduled or merge triggers) use the trusted environment (no approval needed). - name: ${{ github.event_name == 'pull_request_target' && (github.event.pull_request.author_association == 'COLLABORATOR') && 'cubit_secrets_trusted' || (github.event_name == 'pull_request_target' && 'cubit_secrets_untrusted' || 'cubit_secrets_trusted') }} - container: - image: ghcr.io/4c-multiphysics/4c:main - defaults: - run: - shell: bash - steps: - - name: Checkout PR code - uses: actions/checkout@v4 - with: - submodules: true - # For PR runs, checkout PR code; otherwise fallback to default branch or ref - ref: ${{ github.event.pull_request.head.ref || github.ref }} - repository: ${{ github.event.pull_request.head.repo.full_name || github.repository }} - - name: Setup cubit - id: cubit - uses: ./.github/actions/cubit_setup - with: - cubit_download_url: ${{ env.CUBIT_DOWNLOAD_URL }} - cubit_email: ${{ secrets.CUBIT_EMAIL }} - cubit_password: ${{ secrets.CUBIT_PASSWORD }} - - name: Setup virtual python environment - uses: ./.github/actions/setup_virtual_python_environment - - name: Build ArborX geometric search - uses: ./.github/actions/build_arborx_geometric_search - - name: Run the test suite - uses: ./.github/actions/run_tests - with: - source-command: "source python-workflow-venv/bin/activate" - install-command: "-e .[cubitpy,dev,fourc]" - additional-pytest-flags: "--4C --ArborX --CubitPy --cov-fail-under=93" - cubit-root: ${{ steps.cubit.outputs.cubit_root }} - - name: Free cubit license - if: ${{ always() }} - uses: ./.github/actions/cubit_finalize - with: - cubit-root: ${{ steps.cubit.outputs.cubit_root }} - - name: Upload test results on failure - if: failure() - uses: actions/upload-artifact@v4 - with: - name: ${{ github.job }}-${{ github.run_number }} - path: ${{ env.PYTEST_TMPDIR }} - - name: Coverage badge and report - uses: ./.github/actions/coverage - - beamme-performance-testing: - needs: beamme-testing-all-dependencies - name: performance tests - continue-on-error: true - runs-on: ubuntu-latest - environment: - # Use the trusted environment only for PRs authored by an COLLABORATOR (no approval needed); all others use the untrusted environment (someone has to approve the workflow run). - # Otherwise (scheduled or merge triggers) use the trusted environment (no approval needed). - name: ${{ github.event_name == 'pull_request_target' && (github.event.pull_request.author_association == 'COLLABORATOR') && 'cubit_secrets_trusted' || (github.event_name == 'pull_request_target' && 'cubit_secrets_untrusted' || 'cubit_secrets_trusted') }} - container: - image: ghcr.io/4c-multiphysics/4c:main - defaults: - run: - shell: bash - steps: - - name: Checkout repository - uses: actions/checkout@v4 - with: - submodules: true - # For PR runs, checkout PR code; otherwise fallback to default branch or ref - ref: ${{ github.event.pull_request.head.ref || github.ref }} - repository: ${{ github.event.pull_request.head.repo.full_name || github.repository }} - - name: Setup cubit - id: cubit - uses: ./.github/actions/cubit_setup - with: - cubit_download_url: ${{ env.CUBIT_DOWNLOAD_URL }} - cubit_email: ${{ secrets.CUBIT_EMAIL }} - cubit_password: ${{ secrets.CUBIT_PASSWORD }} - - name: Setup virtual python environment - uses: ./.github/actions/setup_virtual_python_environment - - name: Run the test suite - uses: ./.github/actions/run_tests - with: - install-command: ".[cubitpy,dev,fourc]" - source-command: "source python-workflow-venv/bin/activate" - additional-pytest-flags: "--performance-tests --exclude-standard-tests -s --no-cov" - cubit-root: ${{ steps.cubit.outputs.cubit_root }} - - name: Free cubit license - if: ${{ always() }} - uses: ./.github/actions/cubit_finalize - with: - cubit-root: ${{ steps.cubit.outputs.cubit_root }} - - name: Upload test results on failure - if: failure() - uses: actions/upload-artifact@v4 - with: - name: ${{ github.job }}-${{ github.run_number }} - path: ${{ env.PYTEST_TMPDIR }} diff --git a/.github/workflows/website.yml b/.github/workflows/website.yml index 7b566a33..0b47e1fc 100644 --- a/.github/workflows/website.yml +++ b/.github/workflows/website.yml @@ -1,22 +1,21 @@ name: Build and deploy website on: - # Only build and deploy the website once the documentation is built (and the test suite is completed => coverage report and badge are necessary) - workflow_run: - workflows: [Build documentation] - types: [completed] - branches: [main] - -# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. -# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. -concurrency: - group: "pages" - cancel-in-progress: false + schedule: + - cron: '0 06 * * *' + push: + branches: + - main + pull_request: + types: + - opened + - reopened + - synchronize + workflow_dispatch: jobs: build_website: # Only run if documentation was built successfully (if it is skipped, it is not successful) - if: ${{ github.event.workflow_run.conclusion == 'success' }} runs-on: ubuntu-latest steps: - name: Checkout repository @@ -27,62 +26,23 @@ jobs: python-version: '3.13' - name: Install dependencies run: | + sudo apt-get install -y libosmesa6-dev + sudo apt-get install -y pandoc + sudo apt-get install -y xvfb python -m pip install --upgrade pip pip install -r website/requirements.txt + pip install -e .[dev,fourc] - name: Prepare docs for the website run: | python website/docs/prepare_docs.py - name: Build Sphinx website run: | - sphinx-build -b html website/docs/source website/docs/build + export PYVISTA_OFF_SCREEN=true + export PYVISTA_USE_EGL=true + export VTK_DEFAULT_RENDER_WINDOW_OFFSCREEN=1 + xvfb-run -a sphinx-build -b html website/docs/source website/docs/build - name: Upload website artifact uses: actions/upload-artifact@v4 with: name: website path: website/docs/build/ - - deploy: - environment: - name: Website - # TODO Check if this url is really necessary or if it works without it - url: http://beamme-py.github.io/beamme - runs-on: ubuntu-latest - needs: build_website - permissions: - pages: write - id-token: write - contents: read - steps: - - name: Download website artifact - uses: actions/download-artifact@v4 - with: - name: website - path: ${{ github.workspace }} - - name: Download API documentation artifact - uses: dawidd6/action-download-artifact@v8 - with: - workflow: documentation.yml - name: api-documentation - path: ${{ github.workspace }}/api-documentation - branch: ${{ github.event.repository.default_branch }} - - name: Download coverage report artifact - uses: dawidd6/action-download-artifact@v8 - with: - workflow: testing_protected.yml - name: coverage-report - path: ${{ github.workspace }}/coverage-report - branch: ${{ github.event.repository.default_branch }} - - name: Download coverage badge artifact - uses: dawidd6/action-download-artifact@v8 - with: - workflow: testing_protected.yml - name: coverage-badge - path: ${{ github.workspace }}/coverage-badge - branch: ${{ github.event.repository.default_branch }} - - name: Upload all pages artifacts (website, coverage-report, coverage-badge, documentation) - uses: actions/upload-pages-artifact@v3 - id: deployment - with: - path: ${{ github.workspace }} - - name: Deploy to GitHub Pages - uses: actions/deploy-pages@v4 diff --git a/.gitignore b/.gitignore index 5099893d..92feec23 100644 --- a/.gitignore +++ b/.gitignore @@ -15,6 +15,7 @@ api-documentation/* # website folder which contains automatically copied files website/docs/source/md/* +website/docs/source/examples/* ### Automatically created ignores ### diff --git a/examples/example_1_finite_rotations.ipynb b/examples/example_1_finite_rotations.ipynb index 28aec940..e1d634f0 100644 --- a/examples/example_1_finite_rotations.ipynb +++ b/examples/example_1_finite_rotations.ipynb @@ -4,15 +4,22 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Example 1: Introduction to finite rotations in BeamMe\n", - "$\n", + "$$\n", "% Define TeX macros for this document\n", "\\def\\vv#1{\\boldsymbol{#1}}\n", "\\def\\mm#1{\\boldsymbol{#1}}\n", "\\def\\R#1{\\mathbb{R}^{#1}}\n", "\\def\\SO{SO(3)}\n", "\\def\\triad{\\mm{\\Lambda}}\n", - "$\n", + "$$" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Example 1: Introduction to finite rotations in BeamMe\n", + "\n", "When working with Cosserat continua in 3D, the mathematical treatment of finite rotations is required.\n", "This example gives an overview of the finite rotation functionality in BeamMe.\n", "For a more comprehensive and theoretical overview of finite rotations, the interested reader is referred to:\n", @@ -217,12 +224,6 @@ "metadata": {}, "outputs": [], "source": [ - "import vtk # We need to import vtk before pyvista for the TeX labels to work # noqa: F401, I001\n", - "import pyvista as pv\n", - "\n", - "pv.set_jupyter_backend(\"trame\")\n", - "\n", - "\n", "# Utility functionality for this example\n", "from utils.example_1_utils import (\n", " PyVistaPlotter,\n", diff --git a/examples/example_2_core_mesh_generation_functions.ipynb b/examples/example_2_core_mesh_generation_functions.ipynb index bc56e011..641d79b7 100644 --- a/examples/example_2_core_mesh_generation_functions.ipynb +++ b/examples/example_2_core_mesh_generation_functions.ipynb @@ -4,11 +4,18 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Example 2: Basic mesh generation functions\n", - "$\n", + "$$\n", "% Define TeX macros for this document\n", "\\def\\vv#1{\\boldsymbol{#1}}\n", - "$\n", + "$$" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Example 2: Basic mesh generation functions\n", + "\n", "This example shall showcase the core mesh generation functions provided in BeamMe." ] }, diff --git a/examples/utils/example_1_utils.py b/examples/utils/example_1_utils.py index 72bb1afc..fb8eec19 100644 --- a/examples/utils/example_1_utils.py +++ b/examples/utils/example_1_utils.py @@ -21,12 +21,15 @@ # THE SOFTWARE. """This file contains utility functions for the first example.""" +# We need to import vtk before pyvista for the TeX labels to work +import vtk # noqa: F401, I001 + import numpy as np import pyvista as pv +import sys from beamme.utils.environment import is_testing - -from .general_utils import reset_print_out +import beamme.utils.visualization as visualization def print_matrix(name, matrix): @@ -112,6 +115,10 @@ def __init__(self, *args, **kwargs): self.args = args self.kwargs = kwargs + # PyVista changes the printout, so store the original print out + # and restore it when done. + self.stdout = sys.stdout + def __enter__(self): """Return the plotter with the given arguments.""" @@ -125,5 +132,5 @@ def __exit__(self, exc_type, exc_value, traceback): console print out). """ if not is_testing(): - self.plotter.show() - reset_print_out() + visualization.show_plotter(self.plotter, nbsphinx_export_3d_view=False) + sys.stdout = self.stdout diff --git a/examples/utils/general_utils.py b/examples/utils/general_utils.py deleted file mode 100644 index 2a313393..00000000 --- a/examples/utils/general_utils.py +++ /dev/null @@ -1,40 +0,0 @@ -# The MIT License (MIT) -# -# Copyright (c) 2018-2025 BeamMe Authors -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -"""This file contains utility functions for the examples.""" - -import sys - -import pyvista as pv - -from beamme.utils.environment import is_mybinder - -# Store the default system out, so we can reset it after pyvista changes it -stdout = sys.stdout - -# If we are on mybinder, set to server side rendering -if is_mybinder(): - pv.start_xvfb() - - -def reset_print_out(): - """PyVista changes the printout, this resets it to the default.""" - sys.stdout = stdout diff --git a/src/beamme/core/mesh.py b/src/beamme/core/mesh.py index 553342e5..fdb909ad 100644 --- a/src/beamme/core/mesh.py +++ b/src/beamme/core/mesh.py @@ -68,6 +68,7 @@ from beamme.utils.nodes import get_nodal_coordinates as _get_nodal_coordinates from beamme.utils.nodes import get_nodal_quaternions as _get_nodal_quaternions from beamme.utils.nodes import get_nodes_by_function as _get_nodes_by_function +from beamme.utils.visualization import show_plotter as _show_plotter class Mesh: @@ -999,7 +1000,7 @@ def display_pyvista( plotter.add_mesh(solid_grid, color="white", show_edges=True, opacity=0.5) if not _is_testing(): - plotter.show() + _show_plotter(plotter) else: return plotter diff --git a/src/beamme/utils/environment.py b/src/beamme/utils/environment.py index 4dbd05de..eebb9cf7 100644 --- a/src/beamme/utils/environment.py +++ b/src/beamme/utils/environment.py @@ -48,6 +48,11 @@ def is_mybinder(): return "BINDER_LAUNCH_HOST" in _os.environ.keys() +def is_nbsphinx(): + """Check if the current environment is running in nbsphinx.""" + return "IS_NBSPHINX" in _os.environ + + def is_testing(): """Check if the current environment is a pytest testing run.""" return "PYTEST_CURRENT_TEST" in _os.environ diff --git a/src/beamme/utils/visualization.py b/src/beamme/utils/visualization.py new file mode 100644 index 00000000..c84614cf --- /dev/null +++ b/src/beamme/utils/visualization.py @@ -0,0 +1,92 @@ +# The MIT License (MIT) +# +# Copyright (c) 2018-2025 BeamMe Authors +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +"""Helper functions for visualizations.""" + +import os as _os +import uuid as _uuid +from pathlib import Path as _Path + +import ipywidgets as _widgets +import pyvista as _pv +from IPython.display import HTML as _HTML +from IPython.display import IFrame as _IFrame +from IPython.display import display as _display + +from beamme.utils.environment import is_mybinder as _is_mybinder +from beamme.utils.environment import is_nbsphinx as _is_nbsphinx + +# Start virtual framebuffer for MyBinder environments +if _is_mybinder(): + _pv.start_xvfb() + + +def show_plotter(plotter: _pv.Plotter, *, nbsphinx_export_3d_view: bool = True) -> None: + """Show a PyVista plotter. + + This wrapper either directly displays the plotter, default + development use for BeamMe. For the website representation of the + examples, we export static and interactive versions of the plotter + that will be embedded in the documentation. + + Args: + plotter: The PyVista plotter to show. + nbsphinx_export_3d_view: For nbsphinx documentation, whether to + export an interactive 3D view alongside the static image. + """ + + if not _is_nbsphinx(): + plotter.show() + else: + # Path where static documents are stored for the website. + static_doc_path = _Path(_os.environ["PYVISTA_DOCS_STATIC"]) + + # Get a unique identifier for the current plotter + plotter_uid = str(_uuid.uuid4().hex) + + # Export a screenshot of the plotter + plotter.screenshot(static_doc_path / f"{plotter_uid}.png") + static_frame_html = f""" + + """ + + if nbsphinx_export_3d_view: + # Export a html representation of the plotter + plotter.export_html(static_doc_path / f"{plotter_uid}.html") + + interactive_frame = _IFrame( + src=f"../_static/pyvista/{plotter_uid}.html", + width="100%", + height=600, + ) + tab = _widgets.Tab( + children=[ + _widgets.HTML(static_frame_html), + _widgets.HTML(interactive_frame._repr_html_()), + ] + ) + tab.set_title(0, "Static Scene") + tab.set_title(1, "Interactive Scene") + + _display(tab) + else: + _display(_HTML(static_frame_html)) diff --git a/website/README.md b/website/README.md index f4b8a956..875f784f 100644 --- a/website/README.md +++ b/website/README.md @@ -18,7 +18,7 @@ In the source directory of BeamMe simply execute python website/docs/prepare_docs.py ``` -to prepare the documents for the website build (currently only the readme gets copied into the website build directory). +to prepare the documents for the website build. Then build the website with diff --git a/website/docs/prepare_docs.py b/website/docs/prepare_docs.py index 51444d77..01b72213 100644 --- a/website/docs/prepare_docs.py +++ b/website/docs/prepare_docs.py @@ -29,17 +29,33 @@ def prepare_docs(): """Prepare documentation for the website. - Currently, this only copies the README.md file to the documentation - directory. - """ + This function copies all relevant files for the website. - markdown_dir = Path("website/docs/source/md") + Note: We remove the target directories before copying the files to ensure + that no stale files remain. + """ # create directory which contains all the markdown files + markdown_dir = Path("website/docs/source/md") + if markdown_dir.exists(): + shutil.rmtree(markdown_dir) os.makedirs(markdown_dir, exist_ok=True) # copy readme - shutil.copy("README.md", os.path.join(markdown_dir, "README.md")) + shutil.copy("README.md", markdown_dir / "README.md") + + # create directory which contains all the example files + examples_source_dir = Path("examples") + examples_target_dir = Path("website/docs/source/examples") + if examples_target_dir.exists(): + shutil.rmtree(examples_target_dir) + file_extensions_to_copy = {".ipynb", ".py"} + for file in examples_source_dir.rglob("*"): + if file.is_file() and file.suffix.lower() in file_extensions_to_copy: + rel_path = file.relative_to(examples_source_dir) + dest_path = examples_target_dir / rel_path + dest_path.parent.mkdir(parents=True, exist_ok=True) + shutil.copy(file, dest_path) if __name__ == "__main__": diff --git a/website/docs/source/conf.py b/website/docs/source/conf.py index 8140e4f8..ae68cc06 100644 --- a/website/docs/source/conf.py +++ b/website/docs/source/conf.py @@ -20,6 +20,9 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. +import os +from pathlib import Path + # general configuration project = "BeamMe" copyright = "2025, BeamMe Authors" @@ -36,6 +39,7 @@ # extensions extensions = [ "myst_parser", # to enable Markdown support + "nbsphinx", # to enable Jupyter Notebook support "sphinxcontrib.jquery", # to enable custom JavaScript (open links in new empty tab) ] @@ -49,3 +53,38 @@ # JavaScript configuration (to open links in new tabs) html_js_files = ["js/custom.js"] html_static_path = ["static"] + +# Always execute notebooks to ensure outputs are up-to-date +nbsphinx_execute = "always" + +# Prolog for nbsphinx to add a note with a link to the GitHub source and Binder +# at the top of each rendered html page +nbsphinx_prolog = r""" +{% set docname = env.doc2path(env.docname, base=False)|string %} +{% set binder_path = "/doc/tree/" ~ docname %} +{% set binder_urlpath = binder_path | urlencode %} + +.. raw:: html + +
+ This page was generated from + + {{ docname|e }} + . +
+ Interactive online version: + + Binder badge + +
+""" + +# Create the directory for static files created by pyvista plots +PYVISTA_DOCS_STATIC = Path(__file__).parent.parent / "build" / "_static" / "pyvista" +PYVISTA_DOCS_STATIC.mkdir(parents=True, exist_ok=True) +os.environ["PYVISTA_DOCS_STATIC"] = str(PYVISTA_DOCS_STATIC) + +# Set a flag that we are building the docs with nbsphinx +os.environ["IS_NBSPHINX"] = "1" diff --git a/website/docs/source/index.rst b/website/docs/source/index.rst index 4136b810..d80410b6 100644 --- a/website/docs/source/index.rst +++ b/website/docs/source/index.rst @@ -5,6 +5,7 @@ :maxdepth: 1 :hidden: + Examples API Documentation Coverage Report Github diff --git a/website/docs/source/index_examples.rst b/website/docs/source/index_examples.rst new file mode 100644 index 00000000..6cb3b5c3 --- /dev/null +++ b/website/docs/source/index_examples.rst @@ -0,0 +1,8 @@ +Examples +======== + +.. toctree:: + :maxdepth: 1 + :glob: + + examples/* diff --git a/website/requirements.txt b/website/requirements.txt index 8a10cd84..978d1bce 100644 --- a/website/requirements.txt +++ b/website/requirements.txt @@ -1,5 +1,7 @@ linkify-it-py myst-parser +nbsphinx +pandoc pydata-sphinx-theme sphinx sphinxcontrib-jquery