From ce0be1b3fd8199aaa990fe79b7d35846999b4b86 Mon Sep 17 00:00:00 2001 From: jorgensd Date: Fri, 3 Oct 2025 21:57:27 +0200 Subject: [PATCH 1/9] Try removing xvfb for most tutorials --- chapter1/fundamentals_code.ipynb | 4 +--- chapter1/fundamentals_code.py | 2 -- chapter1/nitsche.ipynb | 2 -- chapter1/nitsche.py | 2 -- chapter2/amr.ipynb | 1 - chapter2/amr.py | 1 - chapter2/diffusion_code.ipynb | 2 -- chapter2/diffusion_code.py | 2 -- chapter2/hyperelasticity.ipynb | 1 - chapter2/hyperelasticity.py | 1 - chapter2/linearelasticity_code.ipynb | 2 -- chapter2/linearelasticity_code.py | 2 -- chapter2/ns_code1.ipynb | 2 -- chapter2/ns_code1.py | 2 -- chapter3/component_bc.ipynb | 2 -- chapter3/component_bc.py | 2 -- chapter3/multiple_dirichlet.ipynb | 1 - chapter3/multiple_dirichlet.py | 1 - chapter3/neumann_dirichlet_code.ipynb | 10 ---------- chapter3/neumann_dirichlet_code.py | 2 -- chapter3/robin_neumann_dirichlet.ipynb | 1 - chapter3/robin_neumann_dirichlet.py | 1 - chapter3/subdomains.ipynb | 2 -- chapter3/subdomains.py | 2 -- chapter4/newton-solver.ipynb | 1 - chapter4/newton-solver.py | 1 - 26 files changed, 1 insertion(+), 51 deletions(-) diff --git a/chapter1/fundamentals_code.ipynb b/chapter1/fundamentals_code.ipynb index 63b72acd..3689572c 100644 --- a/chapter1/fundamentals_code.ipynb +++ b/chapter1/fundamentals_code.ipynb @@ -473,7 +473,6 @@ "We start by converting the mesh to a format that can be used with {py:mod}`pyvista`.\n", "To do this we use the function {py:func}`dolfinx.plot.vtk_mesh`.\n", "It creates the data required to create a {py:class}`pyvista.UnstructuredGrid`.\n", - "On Linux (in docker) we need to start a virtual framebuffer for plotting through docker containers.\n", "You can print the current backend and change it with {py:func}`pyvista.set_jupyter_backend`." ] }, @@ -486,8 +485,7 @@ "source": [ "import pyvista\n", "\n", - "print(pyvista.global_theme.jupyter_backend)\n", - "pyvista.start_xvfb(0.1)" + "print(pyvista.global_theme.jupyter_backend)" ] }, { diff --git a/chapter1/fundamentals_code.py b/chapter1/fundamentals_code.py index 94a29c56..2d01c369 100644 --- a/chapter1/fundamentals_code.py +++ b/chapter1/fundamentals_code.py @@ -309,14 +309,12 @@ # We start by converting the mesh to a format that can be used with {py:mod}`pyvista`. # To do this we use the function {py:func}`dolfinx.plot.vtk_mesh`. # It creates the data required to create a {py:class}`pyvista.UnstructuredGrid`. -# On Linux (in docker) we need to start a virtual framebuffer for plotting through docker containers. # You can print the current backend and change it with {py:func}`pyvista.set_jupyter_backend`. # + import pyvista print(pyvista.global_theme.jupyter_backend) -pyvista.start_xvfb(0.1) # + from dolfinx import plot diff --git a/chapter1/nitsche.ipynb b/chapter1/nitsche.ipynb index 5ae919ae..97615de9 100644 --- a/chapter1/nitsche.ipynb +++ b/chapter1/nitsche.ipynb @@ -188,8 +188,6 @@ "source": [ "import pyvista\n", "\n", - "pyvista.start_xvfb(1.0)\n", - "\n", "grid = pyvista.UnstructuredGrid(*plot.vtk_mesh(V))\n", "grid.point_data[\"u\"] = uh.x.array.real\n", "grid.set_active_scalars(\"u\")\n", diff --git a/chapter1/nitsche.py b/chapter1/nitsche.py index e757a7ab..0d2bf6ff 100644 --- a/chapter1/nitsche.py +++ b/chapter1/nitsche.py @@ -107,8 +107,6 @@ # + import pyvista -pyvista.start_xvfb(1.0) - grid = pyvista.UnstructuredGrid(*plot.vtk_mesh(V)) grid.point_data["u"] = uh.x.array.real grid.set_active_scalars("u") diff --git a/chapter2/amr.ipynb b/chapter2/amr.ipynb index e235df98..cb0c05af 100644 --- a/chapter2/amr.ipynb +++ b/chapter2/amr.ipynb @@ -138,7 +138,6 @@ }, "outputs": [], "source": [ - "pyvista.start_xvfb(1.0)\n", "\n", "grid = pyvista.UnstructuredGrid(*dolfinx.plot.vtk_mesh(mesh))\n", "grid.cell_data[\"ct\"] = ct.values\n", diff --git a/chapter2/amr.py b/chapter2/amr.py index 96c5a733..005b66e0 100644 --- a/chapter2/amr.py +++ b/chapter2/amr.py @@ -72,7 +72,6 @@ # We use pyvista to visualize the mesh. # + tags=["hide-input"] -pyvista.start_xvfb(1.0) grid = pyvista.UnstructuredGrid(*dolfinx.plot.vtk_mesh(mesh)) grid.cell_data["ct"] = ct.values diff --git a/chapter2/diffusion_code.ipynb b/chapter2/diffusion_code.ipynb index f89975ca..2eed8667 100644 --- a/chapter2/diffusion_code.ipynb +++ b/chapter2/diffusion_code.ipynb @@ -263,8 +263,6 @@ "metadata": {}, "outputs": [], "source": [ - "pyvista.start_xvfb(1.0)\n", - "\n", "grid = pyvista.UnstructuredGrid(*plot.vtk_mesh(V))\n", "\n", "plotter = pyvista.Plotter()\n", diff --git a/chapter2/diffusion_code.py b/chapter2/diffusion_code.py index 229b16b6..888b43bf 100644 --- a/chapter2/diffusion_code.py +++ b/chapter2/diffusion_code.py @@ -136,8 +136,6 @@ def initial_condition(x, a=5): # We use the DOLFINx plotting functionality, which is based on pyvista to plot the solution at every $15$th time step. We would also like to visualize a colorbar reflecting the minimal and maximum value of $u$ at each time step. We use the following convenience function `plot_function` for this: # + -pyvista.start_xvfb(1.0) - grid = pyvista.UnstructuredGrid(*plot.vtk_mesh(V)) plotter = pyvista.Plotter() diff --git a/chapter2/hyperelasticity.ipynb b/chapter2/hyperelasticity.ipynb index c1a5b1da..1271a4bb 100644 --- a/chapter2/hyperelasticity.ipynb +++ b/chapter2/hyperelasticity.ipynb @@ -389,7 +389,6 @@ "metadata": {}, "outputs": [], "source": [ - "pyvista.start_xvfb(1.0)\n", "plotter = pyvista.Plotter()\n", "plotter.open_gif(\"deformation.gif\", fps=3)\n", "\n", diff --git a/chapter2/hyperelasticity.py b/chapter2/hyperelasticity.py index 24c6146e..8462f8de 100644 --- a/chapter2/hyperelasticity.py +++ b/chapter2/hyperelasticity.py @@ -174,7 +174,6 @@ def right(x): # We create a function to plot the solution at each time step. # + -pyvista.start_xvfb(1.0) plotter = pyvista.Plotter() plotter.open_gif("deformation.gif", fps=3) diff --git a/chapter2/linearelasticity_code.ipynb b/chapter2/linearelasticity_code.ipynb index fb6e2ce7..82a10761 100644 --- a/chapter2/linearelasticity_code.ipynb +++ b/chapter2/linearelasticity_code.ipynb @@ -230,8 +230,6 @@ "metadata": {}, "outputs": [], "source": [ - "pyvista.start_xvfb(1.0)\n", - "\n", "# Create plotter and pyvista grid\n", "p = pyvista.Plotter()\n", "topology, cell_types, geometry = plot.vtk_mesh(V)\n", diff --git a/chapter2/linearelasticity_code.py b/chapter2/linearelasticity_code.py index 696b7be1..37c37d0c 100644 --- a/chapter2/linearelasticity_code.py +++ b/chapter2/linearelasticity_code.py @@ -144,8 +144,6 @@ def sigma(u): # In previous tutorials, we have considered scalar values, while the following section considers vectors. # + -pyvista.start_xvfb(1.0) - # Create plotter and pyvista grid p = pyvista.Plotter() topology, cell_types, geometry = plot.vtk_mesh(V) diff --git a/chapter2/ns_code1.ipynb b/chapter2/ns_code1.ipynb index 6fb798a9..3bff8e41 100644 --- a/chapter2/ns_code1.ipynb +++ b/chapter2/ns_code1.ipynb @@ -639,8 +639,6 @@ "metadata": {}, "outputs": [], "source": [ - "pyvista.start_xvfb(1.0)\n", - "\n", "topology, cell_types, geometry = vtk_mesh(V)\n", "values = np.zeros((geometry.shape[0], 3), dtype=np.float64)\n", "values[:, : len(u_n)] = u_n.x.array.real.reshape((geometry.shape[0], len(u_n)))\n", diff --git a/chapter2/ns_code1.py b/chapter2/ns_code1.py index f18504a1..0b71f369 100644 --- a/chapter2/ns_code1.py +++ b/chapter2/ns_code1.py @@ -456,8 +456,6 @@ def u_exact(x): # In this section we will look at how to visualize vector functions with glyphs, instead of warping the mesh. # + -pyvista.start_xvfb(1.0) - topology, cell_types, geometry = vtk_mesh(V) values = np.zeros((geometry.shape[0], 3), dtype=np.float64) values[:, : len(u_n)] = u_n.x.array.real.reshape((geometry.shape[0], len(u_n))) diff --git a/chapter3/component_bc.ipynb b/chapter3/component_bc.ipynb index 9f61febd..bc2c5de2 100644 --- a/chapter3/component_bc.ipynb +++ b/chapter3/component_bc.ipynb @@ -301,8 +301,6 @@ }, "outputs": [], "source": [ - "pyvista.start_xvfb(1.0)\n", - "\n", "# Create plotter and pyvista grid\n", "p = pyvista.Plotter()\n", "topology, cell_types, x = vtk_mesh(V)\n", diff --git a/chapter3/component_bc.py b/chapter3/component_bc.py index 95486f6a..8b6bd726 100644 --- a/chapter3/component_bc.py +++ b/chapter3/component_bc.py @@ -176,8 +176,6 @@ def sigma(u): # ## Visualization # + -pyvista.start_xvfb(1.0) - # Create plotter and pyvista grid p = pyvista.Plotter() topology, cell_types, x = vtk_mesh(V) diff --git a/chapter3/multiple_dirichlet.ipynb b/chapter3/multiple_dirichlet.ipynb index 6e3cec95..ac9108b7 100644 --- a/chapter3/multiple_dirichlet.ipynb +++ b/chapter3/multiple_dirichlet.ipynb @@ -162,7 +162,6 @@ "metadata": {}, "outputs": [], "source": [ - "pyvista.start_xvfb(1.0)\n", "pyvista_cells, cell_types, geometry = vtk_mesh(V)\n", "grid = pyvista.UnstructuredGrid(pyvista_cells, cell_types, geometry)\n", "grid.point_data[\"u\"] = uh.x.array\n", diff --git a/chapter3/multiple_dirichlet.py b/chapter3/multiple_dirichlet.py index 05937996..f0266d23 100644 --- a/chapter3/multiple_dirichlet.py +++ b/chapter3/multiple_dirichlet.py @@ -121,7 +121,6 @@ def u_exact(x): # To visualize the solution, run the script with in a Jupyter notebook with `off_screen=False` or as a python script with `off_screen=True`. # + -pyvista.start_xvfb(1.0) pyvista_cells, cell_types, geometry = vtk_mesh(V) grid = pyvista.UnstructuredGrid(pyvista_cells, cell_types, geometry) grid.point_data["u"] = uh.x.array diff --git a/chapter3/neumann_dirichlet_code.ipynb b/chapter3/neumann_dirichlet_code.ipynb index 99b71d17..d628474b 100644 --- a/chapter3/neumann_dirichlet_code.ipynb +++ b/chapter3/neumann_dirichlet_code.ipynb @@ -222,16 +222,6 @@ "To look at the actual solution, run the script as a python script with `off_screen=True` or as a Jupyter notebook with `off_screen=False`" ] }, - { - "cell_type": "code", - "execution_count": null, - "id": "9", - "metadata": {}, - "outputs": [], - "source": [ - "pyvista.start_xvfb(1.0)" - ] - }, { "cell_type": "code", "execution_count": null, diff --git a/chapter3/neumann_dirichlet_code.py b/chapter3/neumann_dirichlet_code.py index e646d2aa..b00d9a44 100644 --- a/chapter3/neumann_dirichlet_code.py +++ b/chapter3/neumann_dirichlet_code.py @@ -185,8 +185,6 @@ def boundary_D(x): # ## Visualization # To look at the actual solution, run the script as a python script with `off_screen=True` or as a Jupyter notebook with `off_screen=False` -pyvista.start_xvfb(1.0) - # + pyvista_cells, cell_types, geometry = vtk_mesh(V) grid = pyvista.UnstructuredGrid(pyvista_cells, cell_types, geometry) diff --git a/chapter3/robin_neumann_dirichlet.ipynb b/chapter3/robin_neumann_dirichlet.ipynb index 7a473945..4d2d8189 100644 --- a/chapter3/robin_neumann_dirichlet.ipynb +++ b/chapter3/robin_neumann_dirichlet.ipynb @@ -362,7 +362,6 @@ "uh = problem.solve()\n", "\n", "# Visualize solution\n", - "pyvista.start_xvfb(1.0)\n", "pyvista_cells, cell_types, geometry = vtk_mesh(V)\n", "grid = pyvista.UnstructuredGrid(pyvista_cells, cell_types, geometry)\n", "grid.point_data[\"u\"] = uh.x.array\n", diff --git a/chapter3/robin_neumann_dirichlet.py b/chapter3/robin_neumann_dirichlet.py index 5292b4b3..113fcc01 100644 --- a/chapter3/robin_neumann_dirichlet.py +++ b/chapter3/robin_neumann_dirichlet.py @@ -268,7 +268,6 @@ def type(self): uh = problem.solve() # Visualize solution -pyvista.start_xvfb(1.0) pyvista_cells, cell_types, geometry = vtk_mesh(V) grid = pyvista.UnstructuredGrid(pyvista_cells, cell_types, geometry) grid.point_data["u"] = uh.x.array diff --git a/chapter3/subdomains.ipynb b/chapter3/subdomains.ipynb index 0a12b4ed..c43de222 100644 --- a/chapter3/subdomains.ipynb +++ b/chapter3/subdomains.ipynb @@ -45,8 +45,6 @@ "import numpy as np\n", "import pyvista\n", "\n", - "pyvista.start_xvfb(1.0)\n", - "\n", "mesh = create_unit_square(MPI.COMM_WORLD, 10, 10)\n", "Q = functionspace(mesh, (\"DG\", 0))" ] diff --git a/chapter3/subdomains.py b/chapter3/subdomains.py index 2acb2d95..07724a36 100644 --- a/chapter3/subdomains.py +++ b/chapter3/subdomains.py @@ -46,8 +46,6 @@ import numpy as np import pyvista -pyvista.start_xvfb(1.0) - mesh = create_unit_square(MPI.COMM_WORLD, 10, 10) Q = functionspace(mesh, ("DG", 0)) diff --git a/chapter4/newton-solver.ipynb b/chapter4/newton-solver.ipynb index 2cefd9a9..f180907a 100644 --- a/chapter4/newton-solver.ipynb +++ b/chapter4/newton-solver.ipynb @@ -553,7 +553,6 @@ "metadata": {}, "outputs": [], "source": [ - "pyvista.start_xvfb(1.0)\n", "u_topology, u_cell_types, u_geometry = dolfinx.plot.vtk_mesh(V)\n", "u_grid = pyvista.UnstructuredGrid(u_topology, u_cell_types, u_geometry)\n", "u_grid.point_data[\"u\"] = uh.x.array.real\n", diff --git a/chapter4/newton-solver.py b/chapter4/newton-solver.py index 89b5e7eb..ecdc37f6 100644 --- a/chapter4/newton-solver.py +++ b/chapter4/newton-solver.py @@ -319,7 +319,6 @@ def u_exact(x): if domain.comm.rank == 0: print(f"Error_max: {error_max:.2e}") -pyvista.start_xvfb(1.0) u_topology, u_cell_types, u_geometry = dolfinx.plot.vtk_mesh(V) u_grid = pyvista.UnstructuredGrid(u_topology, u_cell_types, u_geometry) u_grid.point_data["u"] = uh.x.array.real From e098c8eee410c622182f40cecebb6acbdf65bc4a Mon Sep 17 00:00:00 2001 From: jorgensd Date: Fri, 3 Oct 2025 23:05:08 +0200 Subject: [PATCH 2/9] Remove xvfb --- chapter1/membrane_code.ipynb | 4 +--- chapter1/membrane_code.py | 3 --- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/chapter1/membrane_code.ipynb b/chapter1/membrane_code.ipynb index 59c3b905..f5f7d4cf 100644 --- a/chapter1/membrane_code.ipynb +++ b/chapter1/membrane_code.ipynb @@ -306,9 +306,7 @@ "outputs": [], "source": [ "from dolfinx.plot import vtk_mesh\n", - "import pyvista\n", - "\n", - "pyvista.start_xvfb(0.1)" + "import pyvista\n" ] }, { diff --git a/chapter1/membrane_code.py b/chapter1/membrane_code.py index 310522a7..0c4a5b3d 100644 --- a/chapter1/membrane_code.py +++ b/chapter1/membrane_code.py @@ -151,12 +151,9 @@ def on_boundary(x): # ## Plotting the solution over a line # We first plot the deflection $u_h$ over the domain $\Omega$. -# + from dolfinx.plot import vtk_mesh import pyvista -pyvista.start_xvfb(0.1) -# - # Extract topology from mesh and create pyvista mesh From a0d6cb950b5d91bd7855e88b25c175d8c1cfc89b Mon Sep 17 00:00:00 2001 From: jorgensd Date: Fri, 3 Oct 2025 23:06:39 +0200 Subject: [PATCH 3/9] Update dockerfile and pyproject.toml --- docker/Dockerfile | 16 +++++++++------- pyproject.toml | 4 ++-- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index 5d039524..82f502d8 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,4 +1,4 @@ -# Execute from root of repo as: docker buildx build --platform=linux/arm64,linux/amd64 -f docker/Dockerfile ./docker/ --progress=plain +# Execute from root of repo as: docker buildx build --platform=linux/arm64,linux/amd64 -f docker/Dockerfile . --progress=plain FROM ghcr.io/fenics/dolfinx/lab:nightly ARG TARGETPLATFORM @@ -7,11 +7,10 @@ ARG TARGETPLATFORM ENV DEB_PYTHON_INSTALL_LAYOUT=deb_system ENV HDF5_MPI="ON" ENV HDF5_DIR="/usr/local" -ENV PYVISTA_JUPYTER_BACKEND="static" WORKDIR /tmp/ # Requirements for pyvista (gl1 and render1) and jupyterlab (nodejs and curl) -RUN apt-get update && apt-get install -y libgl1-mesa-dev libxrender1 xvfb curl +RUN apt-get update && apt-get install -y libgl1-mesa-dev mesa-utils curl RUN curl -sL https://deb.nodesource.com/setup_18.x -o nodesource_setup.sh && \ bash nodesource_setup.sh && \ apt -y install nodejs @@ -38,10 +37,13 @@ RUN python3 -m pip install vtk ADD pyproject.toml /tmp/pyproject.toml WORKDIR /tmp -RUN if [ "$TARGETPLATFORM" = "linux/arm64" ]; then python3 -m pip install --no-cache-dir --no-binary=h5py -v .; fi -RUN if [ "$TARGETPLATFORM" = "linux/amd64" ]; then python3 -m pip install --no-cache-dir --no-binary=h5py -v .[netgen]; fi - -RUN python3 -m pip install -v ngsPETSc@git+https://github.com/NGSolve/ngsPETSc.git scipy --no-deps +RUN python3 -m pip install --no-cache-dir --no-binary=h5py -v .[netgen] RUN python3 -m pip cache purge +ENV PYVISTA_TRAME_SERVER_PROXY_PREFIX="/proxy/" +ENV PYVISTA_TRAME_SERVER_PROXY_ENABLED="True" +ENV PYVISTA_OFF_SCREEN="false" +ENV PYVISTA_JUPYTER_BACKEND="static" +ENV LIBGL_ALWAYS_SOFTWARE=1 + ENTRYPOINT ["jupyter", "lab", "--ip", "0.0.0.0", "--no-browser", "--allow-root"] diff --git a/pyproject.toml b/pyproject.toml index ef80ddcd..efb85931 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -12,7 +12,7 @@ dependencies = [ "seaborn", "pandas", "tqdm", - "pyvista[all]>=0.43.0", + "pyvista[all]>=0.45.0", "fenics-dolfinx>0.9.0", "sphinx-codeautolink", ] @@ -20,7 +20,7 @@ dependencies = [ [project.optional-dependencies] dev = ["pdbpp", "ipython", "jupytext", "ruff", "pre-commit"] netgen = [ - "ngsPETSc@git+https://github.com/NGSolve/ngsPETSc.git", + "ngsPETSc>=0.1.1", ] # Has to be optional as we cant get netgen on linux/arm64 [tool.setuptools] From c51adea4606d1a1c733929fff3a0e5fb6c954192 Mon Sep 17 00:00:00 2001 From: jorgensd Date: Sun, 5 Oct 2025 11:18:30 +0200 Subject: [PATCH 4/9] Try removing xvfb completely for osmesa backend instead --- .../actions/install-dependencies/action.yml | 2 +- .github/workflows/book_stable.yml | 2 -- .github/workflows/test_nightly.yml | 1 - .github/workflows/test_stable.yml | 1 - README.md | 29 ++++++++++++++++--- chapter1/complex_mode.ipynb | 1 - chapter1/complex_mode.py | 1 - chapter3/em.ipynb | 1 - chapter3/em.py | 1 - docker/Dockerfile | 4 ++- pyproject.toml | 4 ++- 11 files changed, 32 insertions(+), 15 deletions(-) diff --git a/.github/actions/install-dependencies/action.yml b/.github/actions/install-dependencies/action.yml index 59a62112..c49f3d98 100644 --- a/.github/actions/install-dependencies/action.yml +++ b/.github/actions/install-dependencies/action.yml @@ -6,4 +6,4 @@ runs: - name: Install apt dependencies and upgrade pip shell: bash -el {0} run: | - apt-get update && apt-get install -y libxrender1 xvfb + apt-get update && apt-get install -y libxrender1 diff --git a/.github/workflows/book_stable.yml b/.github/workflows/book_stable.yml index 3b9d44a4..a691d34f 100644 --- a/.github/workflows/book_stable.yml +++ b/.github/workflows/book_stable.yml @@ -20,8 +20,6 @@ jobs: container: ghcr.io/fenics/dolfinx/lab:stable env: - PYVISTA_TRAME_SERVER_PROXY_PREFIX: "/proxy/" - PYVISTA_TRAME_SERVER_PROXY_ENABLED: "True" PYVISTA_OFF_SCREEN: false PYVISTA_JUPYTER_BACKEND: "html" diff --git a/.github/workflows/test_nightly.yml b/.github/workflows/test_nightly.yml index 4837f51f..fc533577 100644 --- a/.github/workflows/test_nightly.yml +++ b/.github/workflows/test_nightly.yml @@ -53,7 +53,6 @@ jobs: - name: Test chapter 1 working-directory: chapter1 run: | - python3 -c "from pyvista import start_xvfb; start_xvfb(0.1)" mpirun -n 2 python3 fundamentals_code.py mpirun -n 2 python3 nitsche.py mpirun -n 2 python3 membrane_code.py diff --git a/.github/workflows/test_stable.yml b/.github/workflows/test_stable.yml index eacbe4c9..b0be68bb 100644 --- a/.github/workflows/test_stable.yml +++ b/.github/workflows/test_stable.yml @@ -45,7 +45,6 @@ jobs: - name: Test chapter 1 working-directory: chapter1 run: | - python3 -c "from pyvista import start_xvfb; start_xvfb(0.1)" mpirun -n 2 python3 fundamentals_code.py mpirun -n 2 python3 nitsche.py mpirun -n 2 python3 membrane_code.py diff --git a/README.md b/README.md index a62629a3..02b292a8 100644 --- a/README.md +++ b/README.md @@ -10,15 +10,36 @@ If you have any comments, corrections or questions, please submit an issue in th ## Contributing -If you want to contribute to this tutorial, please make a fork of the repository, make your changes, and test that the CI passes. You can do this locally by downloading [act](https://github.com/nektos/act) and call +If you want to contribute to this tutorial, please make a fork of the repository, make your changes, and test that the CI passes. +Alternatively, if you want to add a separate chapter, a Jupyter notebook can be added to a pull request, without integrating it into the tutorial. If so, the notebook will be reviewed and modified to be included in the tutorial. + +Any code added to the tutorial should work in parallel. If any changes are made to `ipynb` files, please ensure that these changes are reflected in the corresponding `py` files by using [`jupytext`](https://jupytext.readthedocs.io/en/latest/faq.html#can-i-use-jupytext-with-jupyterhub-binder-nteract-colab-saturn-or-azure): + + +## Building the book and running code +The book is built using [jupyterbook](https://jupyterbook.org/). The following environment variables should be set if you want to build the book ```bash -act -j test-nightly +PYVISTA_OFF_SCREEN=false +PYVISTA_JUPYTER_BACKEND="html" +JUPYTER_EXTENSION_ENABLED=true +LIBGL_ALWAYS_SOFTWARE=1 ``` -Alternatively, if you want to add a separate chapter, a Jupyter notebook can be added to a pull request, without integrating it into the tutorial. If so, the notebook will be reviewed and modified to be included in the tutorial. +If you run the tutorial using `jupyter-lab`, for instance through `conda`, one should set the following environment variables +```bash +PYVISTA_OFF_SCREEN=false +PYVISTA_JUPYTER_BACKEND="trame" +JUPYTER_EXTENSION_ENABLED=true +LIBGL_ALWAYS_SOFTWARE=1 +``` +If you use docker to run your code, you should set the following variables: +```bash +docker run -ti -e DISPLAY=$DISPLAY -e LIBGL_ALWAYS_SOFTWARE=1 -e PYVISTA_OFF_SCREEN=false -e PYVISTA_JUPYTER_BACKEND="trame" -e JUPYTER_EXTENSION_ENABLED=true --network=host -v $(pwd):/root/shared -w /root/shared .... +``` + +To run python scripts, either choose `PYVISTA_OFF_SCREEN=True` to get screenshots, or render interactive plots with `PYVISTA_OFF_SCREEN=False` -Any code added to the tutorial should work in parallel. If any changes are made to `ipynb` files, please ensure that these changes are reflected in the corresponding `py` files by using [`jupytext`](https://jupytext.readthedocs.io/en/latest/faq.html#can-i-use-jupytext-with-jupyterhub-binder-nteract-colab-saturn-or-azure): ```bash python3 -m jupytext --sync */*.ipynb --set-formats ipynb,py:light diff --git a/chapter1/complex_mode.ipynb b/chapter1/complex_mode.ipynb index 29dc7d72..3f7c8310 100644 --- a/chapter1/complex_mode.ipynb +++ b/chapter1/complex_mode.ipynb @@ -270,7 +270,6 @@ "source": [ "import pyvista\n", "\n", - "pyvista.start_xvfb(0.1)\n", "mesh.topology.create_connectivity(mesh.topology.dim, mesh.topology.dim)\n", "p_mesh = pyvista.UnstructuredGrid(*dolfinx.plot.vtk_mesh(mesh, mesh.topology.dim))\n", "pyvista_cells, cell_types, geometry = dolfinx.plot.vtk_mesh(V)\n", diff --git a/chapter1/complex_mode.py b/chapter1/complex_mode.py index 63e2a81a..6bafdcda 100644 --- a/chapter1/complex_mode.py +++ b/chapter1/complex_mode.py @@ -181,7 +181,6 @@ # + import pyvista -pyvista.start_xvfb(0.1) mesh.topology.create_connectivity(mesh.topology.dim, mesh.topology.dim) p_mesh = pyvista.UnstructuredGrid(*dolfinx.plot.vtk_mesh(mesh, mesh.topology.dim)) pyvista_cells, cell_types, geometry = dolfinx.plot.vtk_mesh(V) diff --git a/chapter3/em.ipynb b/chapter3/em.ipynb index a73bb474..d286266a 100644 --- a/chapter3/em.ipynb +++ b/chapter3/em.ipynb @@ -272,7 +272,6 @@ }, "outputs": [], "source": [ - "pyvista.start_xvfb(1.0)\n", "plotter = pyvista.Plotter()\n", "tdim = mesh.topology.dim\n", "mesh.topology.create_connectivity(tdim, tdim)\n", diff --git a/chapter3/em.py b/chapter3/em.py index 163fa2ba..e04c41a8 100644 --- a/chapter3/em.py +++ b/chapter3/em.py @@ -226,7 +226,6 @@ # We can also visualize the subdommains using pyvista -pyvista.start_xvfb(1.0) plotter = pyvista.Plotter() tdim = mesh.topology.dim mesh.topology.create_connectivity(tdim, tdim) diff --git a/docker/Dockerfile b/docker/Dockerfile index 82f502d8..603d7b25 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,6 +1,7 @@ # Execute from root of repo as: docker buildx build --platform=linux/arm64,linux/amd64 -f docker/Dockerfile . --progress=plain FROM ghcr.io/fenics/dolfinx/lab:nightly + ARG TARGETPLATFORM @@ -37,7 +38,8 @@ RUN python3 -m pip install vtk ADD pyproject.toml /tmp/pyproject.toml WORKDIR /tmp -RUN python3 -m pip install --no-cache-dir --no-binary=h5py -v .[netgen] +# As we install netgen from source we don't need the netgen deps here +RUN python3 -m pip install --no-cache-dir --no-binary=h5py -v . RUN python3 -m pip cache purge ENV PYVISTA_TRAME_SERVER_PROXY_PREFIX="/proxy/" diff --git a/pyproject.toml b/pyproject.toml index efb85931..777eee26 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -15,10 +15,12 @@ dependencies = [ "pyvista[all]>=0.45.0", "fenics-dolfinx>0.9.0", "sphinx-codeautolink", + "trame_jupyter_extension", + "jupytext", ] [project.optional-dependencies] -dev = ["pdbpp", "ipython", "jupytext", "ruff", "pre-commit"] +dev = ["pdbpp", "ipython", "ruff", "pre-commit"] netgen = [ "ngsPETSc>=0.1.1", ] # Has to be optional as we cant get netgen on linux/arm64 From 0a993525e9d78ce7aac905e2bf82f0bdc0374a68 Mon Sep 17 00:00:00 2001 From: jorgensd Date: Sun, 5 Oct 2025 11:20:44 +0200 Subject: [PATCH 5/9] Publish env on dispatch --- .github/workflows/publish_docker.yml | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/.github/workflows/publish_docker.yml b/.github/workflows/publish_docker.yml index a6b6773b..c2b05196 100644 --- a/.github/workflows/publish_docker.yml +++ b/.github/workflows/publish_docker.yml @@ -67,7 +67,7 @@ jobs: run: | mkdir -p ${{ runner.temp }}/digests digest="${{ steps.build.outputs.digest }}" - touch "${{ runner.temp }}/digests/${digest#sha256:}" + touch "${{ runner.temp }}/digests/${digest#sha256:}" - name: Upload digest if: github.event_name == 'push' @@ -77,9 +77,9 @@ jobs: path: ${{ runner.temp }}/digests/* if-no-files-found: error retention-days: 1 - + merge-and-publish: - if: github.event_name == 'push' + if: github.event_name == 'push' || github.event_name == 'workflow_dispatch' runs-on: ubuntu-latest needs: - build @@ -103,15 +103,13 @@ jobs: uses: docker/metadata-action@v5 with: images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} - - name: Create manifest list and push working-directory: ${{ runner.temp }}/digests run: | docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \ - $(printf '${{ env.REGISTRY }}/${{ env.IMAGE_NAME}}@sha256:%s ' *) + $(printf '${{ env.REGISTRY }}/${{ env.IMAGE_NAME}}@sha256:%s ' *) - name: Inspect image run: | - docker buildx imagetools inspect ${{ env.REGISTRY }}/${{ env.IMAGE_NAME}}:${{ steps.meta.outputs.version }} - + docker buildx imagetools inspect ${{ env.REGISTRY }}/${{ env.IMAGE_NAME}}:${{ steps.meta.outputs.version }} From c016e89f845c3f988054622b9c9ce32d94adfa8a Mon Sep 17 00:00:00 2001 From: jorgensd Date: Sun, 5 Oct 2025 11:21:58 +0200 Subject: [PATCH 6/9] rename workflow --- .github/workflows/test_nightly.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test_nightly.yml b/.github/workflows/test_nightly.yml index fc533577..ef8b4f13 100644 --- a/.github/workflows/test_nightly.yml +++ b/.github/workflows/test_nightly.yml @@ -1,4 +1,4 @@ -name: Test release branch against DOLFINx nightly build +name: Test against DOLFINx nightly build # Controls when the action will run. on: From 3349a0f8044c4a43a6058c8b28066fc8b2cf7cb7 Mon Sep 17 00:00:00 2001 From: jorgensd Date: Sun, 5 Oct 2025 11:23:38 +0200 Subject: [PATCH 7/9] Try removing display --- .github/workflows/book_stable.yml | 1 - .github/workflows/test_nightly.yml | 1 - 2 files changed, 2 deletions(-) diff --git a/.github/workflows/book_stable.yml b/.github/workflows/book_stable.yml index a691d34f..576d30a1 100644 --- a/.github/workflows/book_stable.yml +++ b/.github/workflows/book_stable.yml @@ -10,7 +10,6 @@ on: env: HDF5_MPI: "ON" HDF5_DIR: "/usr/local/" - DISPLAY: ":99.0" DEB_PYTHON_INSTALL_LAYOUT: deb_system LIBGL_ALWAYS_SOFTWARE: 1 diff --git a/.github/workflows/test_nightly.yml b/.github/workflows/test_nightly.yml index ef8b4f13..15cc37ea 100644 --- a/.github/workflows/test_nightly.yml +++ b/.github/workflows/test_nightly.yml @@ -22,7 +22,6 @@ jobs: env: HDF5_MPI: "ON" PYVISTA_OFF_SCREEN: true - DISPLAY: ":99.0" PYVISTA_JUPYTER_BACKEND: html LIBGL_ALWAYS_SOFTWARE: 1 From 622840098cc789e864d9d55b943d06e0e87a538b Mon Sep 17 00:00:00 2001 From: jorgensd Date: Sun, 5 Oct 2025 11:25:41 +0200 Subject: [PATCH 8/9] Remvoe variables from docker env --- docker/Dockerfile | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index 603d7b25..7de9a055 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -42,10 +42,8 @@ WORKDIR /tmp RUN python3 -m pip install --no-cache-dir --no-binary=h5py -v . RUN python3 -m pip cache purge -ENV PYVISTA_TRAME_SERVER_PROXY_PREFIX="/proxy/" -ENV PYVISTA_TRAME_SERVER_PROXY_ENABLED="True" ENV PYVISTA_OFF_SCREEN="false" -ENV PYVISTA_JUPYTER_BACKEND="static" +ENV PYVISTA_JUPYTER_BACKEND="trame" ENV LIBGL_ALWAYS_SOFTWARE=1 ENTRYPOINT ["jupyter", "lab", "--ip", "0.0.0.0", "--no-browser", "--allow-root"] From 1a6b6c498aaa6ba146ef59bdc1ac6c828892abc9 Mon Sep 17 00:00:00 2001 From: jorgensd Date: Sun, 5 Oct 2025 11:56:26 +0200 Subject: [PATCH 9/9] Revert docker publish --- .github/workflows/publish_docker.yml | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/.github/workflows/publish_docker.yml b/.github/workflows/publish_docker.yml index c2b05196..a6b6773b 100644 --- a/.github/workflows/publish_docker.yml +++ b/.github/workflows/publish_docker.yml @@ -67,7 +67,7 @@ jobs: run: | mkdir -p ${{ runner.temp }}/digests digest="${{ steps.build.outputs.digest }}" - touch "${{ runner.temp }}/digests/${digest#sha256:}" + touch "${{ runner.temp }}/digests/${digest#sha256:}" - name: Upload digest if: github.event_name == 'push' @@ -77,9 +77,9 @@ jobs: path: ${{ runner.temp }}/digests/* if-no-files-found: error retention-days: 1 - + merge-and-publish: - if: github.event_name == 'push' || github.event_name == 'workflow_dispatch' + if: github.event_name == 'push' runs-on: ubuntu-latest needs: - build @@ -103,13 +103,15 @@ jobs: uses: docker/metadata-action@v5 with: images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + - name: Create manifest list and push working-directory: ${{ runner.temp }}/digests run: | docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \ - $(printf '${{ env.REGISTRY }}/${{ env.IMAGE_NAME}}@sha256:%s ' *) + $(printf '${{ env.REGISTRY }}/${{ env.IMAGE_NAME}}@sha256:%s ' *) - name: Inspect image run: | - docker buildx imagetools inspect ${{ env.REGISTRY }}/${{ env.IMAGE_NAME}}:${{ steps.meta.outputs.version }} + docker buildx imagetools inspect ${{ env.REGISTRY }}/${{ env.IMAGE_NAME}}:${{ steps.meta.outputs.version }} +