diff --git a/.github/workflows/book_stable.yml b/.github/workflows/book_stable.yml index 631a88b1..170755d2 100644 --- a/.github/workflows/book_stable.yml +++ b/.github/workflows/book_stable.yml @@ -16,7 +16,7 @@ env: jobs: build-book: runs-on: ubuntu-latest - container: ghcr.io/fenics/dolfinx/lab:v0.8.0 + container: ghcr.io/fenics/dolfinx/lab:stable env: PYVISTA_TRAME_SERVER_PROXY_PREFIX: "/proxy/" diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index cce3e7da..3e467708 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -38,7 +38,7 @@ jobs: uses: actions/checkout@v4 - name: Setup Pages - uses: actions/configure-pages@v4 + uses: actions/configure-pages@v5 - name: Download docs artifact uses: actions/download-artifact@v4 diff --git a/.github/workflows/publish_docker.yml b/.github/workflows/publish_docker.yml index 77ffc5f3..753bf033 100644 --- a/.github/workflows/publish_docker.yml +++ b/.github/workflows/publish_docker.yml @@ -45,7 +45,7 @@ jobs: images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} - name: Build Docker image - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . load: true @@ -56,7 +56,7 @@ jobs: labels: ${{ steps.meta.outputs.labels }} - name: Build (arm) and push (amd/arm) Docker image - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 if: github.event_name == 'push' with: context: . diff --git a/.github/workflows/test_nightly.yml b/.github/workflows/test_nightly.yml index e4f4736e..ed0f52c5 100644 --- a/.github/workflows/test_nightly.yml +++ b/.github/workflows/test_nightly.yml @@ -39,6 +39,9 @@ jobs: - name: Test building the book run: PYVISTA_OFF_SCREEN=false jupyter-book build -W . + - name: Test building the book + run: PYVISTA_OFF_SCREEN=false jupyter-book build -W . + - name: Test complex notebooks in parallel working-directory: chapter1 run: | @@ -67,7 +70,6 @@ jobs: mpirun -n 2 python3 nonlinpoisson_code.py mpirun -n 2 python3 ns_code1.py mpirun -n 2 python3 ns_code2.py - - name: Test chapter 3 working-directory: chapter3 run: | @@ -77,7 +79,6 @@ jobs: mpirun -n 2 python3 robin_neumann_dirichlet.py mpirun -n 2 python3 component_bc.py mpirun -n 2 python3 em.py - - name: Test chapter 4 working-directory: chapter4 run: | diff --git a/.github/workflows/test_stable.yml b/.github/workflows/test_stable.yml index ae7dd271..cb84bc96 100644 --- a/.github/workflows/test_stable.yml +++ b/.github/workflows/test_stable.yml @@ -14,7 +14,7 @@ env: jobs: test: runs-on: ubuntu-latest - container: ghcr.io/fenics/dolfinx/lab:v0.8.0 + container: ghcr.io/fenics/dolfinx/lab:stable env: PYVISTA_OFF_SCREEN: true @@ -33,7 +33,7 @@ jobs: run: | export PKG_CONFIG_PATH=/usr/local/dolfinx-complex/lib/pkgconfig:$PKG_CONFIG_PATH export PETSC_ARCH=linux-gnu-complex128-32 - export PYTHONPATH=/usr/local/dolfinx-complex/lib/python3.10/dist-packages:$PYTHONPATH + export PYTHONPATH=/usr/local/dolfinx-complex/lib/python3.12/dist-packages:$PYTHONPATH export LD_LIBRARY_PATH=/usr/local/dolfinx-complex/lib:$LD_LIBRARY_PATH python3 complex_mode.py mpirun -n 2 python3 complex_mode.py diff --git a/Changelog.md b/Changelog.md index 2817a0bb..7c79cc03 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,10 @@ # Changelog +## v0.9.0 + +- `scale` in `apply_lifting` has been renamed to `alpha` +- Use `dolfinx.fem.Function.x.petsc_vec` as opposed to `dolfinx.fem.Function.vector` + ## v0.8.0 - Replace all `ufl.FiniteElement` and `ufl.VectorElement` with the appropriate `basix.ufl.element` diff --git a/chapter2/linearelasticity_code.ipynb b/chapter2/linearelasticity_code.ipynb index 99e27b3f..19af6c18 100644 --- a/chapter2/linearelasticity_code.ipynb +++ b/chapter2/linearelasticity_code.ipynb @@ -5,16 +5,21 @@ "metadata": {}, "source": [ "# Implementation\n", + "\n", "Author: Jørgen S. Dokken\n", "\n", "In this tutorial, you will learn how to:\n", + "\n", "- Use a vector function space\n", "- Create a constant boundary condition on a vector space\n", - "- Visualize cell wise constant functions\n", + "- Visualize cell-wise constant functions\n", "- Compute Von Mises stresses\n", "\n", "## Test problem\n", - "As a test example, we will model a clamped beam deformed under its own weigth in 3D. This can be modeled, by setting the right-hand side body force per unit volume to $f=(0,0,-\\rho g)$ with $\\rho$ the density of the beam and $g$ the acceleration of gravity. The beam is box-shaped with length $L$ and has a square cross section of width $W$. We set $u=u_D=(0,0,0)$ at the clamped end, x=0. The rest of the boundary is traction free, that is, we set $T=0$. We start by defining the physical variables used in the program." + "\n", + "As a test example, we will model a clamped beam deformed under its own weight in 3D.\n", + "This can be modeled, by setting the right-hand side body force per unit volume to $f=(0,0,-\\rho g)$ with $\\rho$ the density of the beam and $g$ the acceleration of gravity. The beam is box-shaped with length $L$ and has a square cross section of width $W$. We set $u=u_D=(0,0,0)$ at the clamped end, x=0. The rest of the boundary is traction free, that is, we set $T=0$.\n", + "We start by defining the physical variables used in the program.\n" ] }, { @@ -32,6 +37,7 @@ "from mpi4py import MPI\n", "import ufl\n", "import numpy as np\n", + "\n", "L = 1\n", "W = 0.2\n", "mu = 1\n", @@ -49,7 +55,7 @@ "source": [ "We then create the mesh, which will consist of hexahedral elements, along with the function space.\n", "As we want a vector element with three compoenets, we add `(3, )` or `(domain.geometry.dim, )` to the element tuple to make it a triplet\n", - "However, we also could have used `basix.ufl`s functionality, creating a vector element `element = basix.ufl.element(\"Lagrange\", domain.topology.cell_name(), 1, shape=(domain.geometry.dim,))`, and intitializing the function space as `V = dolfinx.fem.functionspace(domain, element)`." + "However, we also could have used `basix.ufl`s functionality, creating a vector element `element = basix.ufl.element(\"Lagrange\", domain.topology.cell_name(), 1, shape=(domain.geometry.dim,))`, and intitializing the function space as `V = dolfinx.fem.functionspace(domain, element)`.\n" ] }, { @@ -60,9 +66,13 @@ }, "outputs": [], "source": [ - "domain = mesh.create_box(MPI.COMM_WORLD, [np.array([0, 0, 0]), np.array([L, W, W])],\n", - " [20, 6, 6], cell_type=mesh.CellType.hexahedron)\n", - "V = fem.functionspace(domain, (\"Lagrange\", 1, (domain.geometry.dim, )))" + "domain = mesh.create_box(\n", + " MPI.COMM_WORLD,\n", + " [np.array([0, 0, 0]), np.array([L, W, W])],\n", + " [20, 6, 6],\n", + " cell_type=mesh.CellType.hexahedron,\n", + ")\n", + "V = fem.functionspace(domain, (\"Lagrange\", 1, (domain.geometry.dim,)))" ] }, { @@ -70,7 +80,8 @@ "metadata": {}, "source": [ "## Boundary conditions\n", - "As we would like to clamp the boundary at $x=0$, we do this by using a marker function, which locate the facets where $x$ is close to zero by machine precision." + "\n", + "As we would like to clamp the boundary at $x=0$, we do this by using a marker function, which locate the facets where $x$ is close to zero by machine precision.\n" ] }, { @@ -94,7 +105,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "As we want the traction $T$ over the remaining boundary to be $0$, we create a `dolfinx.Constant`" + "As we want the traction $T$ over the remaining boundary to be $0$, we create a `dolfinx.Constant`\n" ] }, { @@ -110,7 +121,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "We also want to specify the integration measure $\\mathrm{d}s$, which should be the integral over the boundary of our domain. We do this by using `ufl`, and its built in integration measures" + "We also want to specify the integration measure $\\mathrm{d}s$, which should be the integral over the boundary of our domain. We do this by using `ufl`, and its built in integration measures\n" ] }, { @@ -127,7 +138,8 @@ "metadata": {}, "source": [ "## Variational formulation\n", - "We are now ready to create our variational formulation in close to mathematical syntax, as for the previous problems." + "\n", + "We are now ready to create our variational formulation in close to mathematical syntax, as for the previous problems.\n" ] }, { @@ -137,7 +149,8 @@ "outputs": [], "source": [ "def epsilon(u):\n", - " return ufl.sym(ufl.grad(u)) # Equivalent to 0.5*(ufl.nabla_grad(u) + ufl.nabla_grad(u).T)\n", + " \"\"\"Equivalent to 0.5*(ufl.nabla_grad(u) + ufl.nabla_grad(u).T)\"\"\"\n", + " return ufl.sym(ufl.grad(u))\n", "\n", "\n", "def sigma(u):\n", @@ -165,7 +178,8 @@ "```\n", "\n", "## Solve the linear variational problem\n", - "As in the previous demos, we assemble the matrix and right hand side vector and use PETSc to solve our variational problem" + "\n", + "As in the previous demos, we assemble the matrix and right hand side vector and use PETSc to solve our variational problem\n" ] }, { @@ -174,7 +188,9 @@ "metadata": {}, "outputs": [], "source": [ - "problem = LinearProblem(a, L, bcs=[bc], petsc_options={\"ksp_type\": \"preonly\", \"pc_type\": \"lu\"})\n", + "problem = LinearProblem(\n", + " a, L, bcs=[bc], petsc_options={\"ksp_type\": \"preonly\", \"pc_type\": \"lu\"}\n", + ")\n", "uh = problem.solve()" ] }, @@ -182,14 +198,14 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## Visualization" + "## Visualization\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "As in the previous demos, we can either use Pyvista or Paraview for visualization. We start by using Pyvista. Instead of adding scalar values to the grid, we add vectors." + "As in the previous demos, we can either use Pyvista or Paraview for visualization. We start by using Pyvista. Instead of adding scalar values to the grid, we add vectors.\n" ] }, { @@ -223,7 +239,7 @@ "source": [ "We could also use Paraview for visualizing this.\n", "As explained in previous sections, we save the solution with `XDMFFile`.\n", - "After opening the file `deformation.xdmf` in Paraview and pressing `Apply`, one can press the `Warp by vector button` ![Warp by vector](warp_by_vector.png) or go through the top menu (`Filters->Alphabetical->Warp by Vector`) and press `Apply`. We can also change the color of the deformed beam by changing the value in the color menu ![color](color.png) from `Solid Color` to `Deformation`." + "After opening the file `deformation.xdmf` in Paraview and pressing `Apply`, one can press the `Warp by vector button` ![Warp by vector](warp_by_vector.png) or go through the top menu (`Filters->Alphabetical->Warp by Vector`) and press `Apply`. We can also change the color of the deformed beam by changing the value in the color menu ![color](color.png) from `Solid Color` to `Deformation`.\n" ] }, { @@ -243,7 +259,8 @@ "metadata": {}, "source": [ "## Stress computation\n", - "As soon as the displacement is computed, we can compute various stress measures. We will compute the von Mises stress defined as $\\sigma_m=\\sqrt{\\frac{3}{2}s:s}$ where $s$ is the deviatoric stress tensor $s(u)=\\sigma(u)-\\frac{1}{3}\\mathrm{tr}(\\sigma(u))I$." + "\n", + "As soon as the displacement is computed, we can compute various stress measures. We will compute the von Mises stress defined as $\\sigma_m=\\sqrt{\\frac{3}{2}s:s}$ where $s$ is the deviatoric stress tensor $s(u)=\\sigma(u)-\\frac{1}{3}\\mathrm{tr}(\\sigma(u))I$.\n" ] }, { @@ -252,15 +269,15 @@ "metadata": {}, "outputs": [], "source": [ - "s = sigma(uh) - 1. / 3 * ufl.tr(sigma(uh)) * ufl.Identity(len(uh))\n", - "von_Mises = ufl.sqrt(3. / 2 * ufl.inner(s, s))" + "s = sigma(uh) - 1.0 / 3 * ufl.tr(sigma(uh)) * ufl.Identity(len(uh))\n", + "von_Mises = ufl.sqrt(3.0 / 2 * ufl.inner(s, s))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "The `von_Mises` variable is now an expression that must be projected into an appropriate function space so that we can visualize it. As `uh` is a linear combination of first order piecewise continuous functions, the von Mises stresses will be a cell-wise constant function." + "The `von_Mises` variable is now an expression that must be projected into an appropriate function space so that we can visualize it. As `uh` is a linear combination of first order piecewise continuous functions, the von Mises stresses will be a cell-wise constant function.\n" ] }, { @@ -279,7 +296,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "In the previous sections, we have only visualized first order Lagrangian functions. However, the Von Mises stresses are piecewise constant on each cell. Therefore, we modify our plotting routine slightly. The first thing we notice is that we now set values for each cell, which has a one to one correspondence with the degrees of freedom in the function space." + "In the previous sections, we have only visualized first order Lagrangian functions. However, the Von Mises stresses are piecewise constant on each cell. Therefore, we modify our plotting routine slightly. The first thing we notice is that we now set values for each cell, which has a one to one correspondence with the degrees of freedom in the function space.\n" ] }, { diff --git a/chapter2/linearelasticity_code.py b/chapter2/linearelasticity_code.py index 038e0cdc..0c7778c1 100644 --- a/chapter2/linearelasticity_code.py +++ b/chapter2/linearelasticity_code.py @@ -6,7 +6,7 @@ # extension: .py # format_name: light # format_version: '1.5' -# jupytext_version: 1.16.4 +# jupytext_version: 1.16.6 # kernelspec: # display_name: Python 3 (ipykernel) # language: python @@ -14,17 +14,24 @@ # --- # # Implementation +# # Author: Jørgen S. Dokken # # In this tutorial, you will learn how to: +# # - Use a vector function space # - Create a constant boundary condition on a vector space -# - Visualize cell wise constant functions +# - Visualize cell-wise constant functions # - Compute Von Mises stresses # # ## Test problem -# As a test example, we will model a clamped beam deformed under its own weigth in 3D. This can be modeled, by setting the right-hand side body force per unit volume to $f=(0,0,-\rho g)$ with $\rho$ the density of the beam and $g$ the acceleration of gravity. The beam is box-shaped with length $L$ and has a square cross section of width $W$. We set $u=u_D=(0,0,0)$ at the clamped end, x=0. The rest of the boundary is traction free, that is, we set $T=0$. We start by defining the physical variables used in the program. +# +# As a test example, we will model a clamped beam deformed under its own weight in 3D. +# This can be modeled, by setting the right-hand side body force per unit volume to $f=(0,0,-\rho g)$ with $\rho$ the density of the beam and $g$ the acceleration of gravity. The beam is box-shaped with length $L$ and has a square cross section of width $W$. We set $u=u_D=(0,0,0)$ at the clamped end, x=0. The rest of the boundary is traction free, that is, we set $T=0$. +# We start by defining the physical variables used in the program. +# +# + # Scaled variable import pyvista from dolfinx import mesh, fem, plot, io, default_scalar_type @@ -32,6 +39,7 @@ from mpi4py import MPI import ufl import numpy as np + L = 1 W = 0.2 mu = 1 @@ -41,18 +49,26 @@ beta = 1.25 lambda_ = beta g = gamma +# - # We then create the mesh, which will consist of hexahedral elements, along with the function space. # As we want a vector element with three compoenets, we add `(3, )` or `(domain.geometry.dim, )` to the element tuple to make it a triplet # However, we also could have used `basix.ufl`s functionality, creating a vector element `element = basix.ufl.element("Lagrange", domain.topology.cell_name(), 1, shape=(domain.geometry.dim,))`, and intitializing the function space as `V = dolfinx.fem.functionspace(domain, element)`. +# -domain = mesh.create_box(MPI.COMM_WORLD, [np.array([0, 0, 0]), np.array([L, W, W])], - [20, 6, 6], cell_type=mesh.CellType.hexahedron) -V = fem.functionspace(domain, ("Lagrange", 1, (domain.geometry.dim, ))) +domain = mesh.create_box( + MPI.COMM_WORLD, + [np.array([0, 0, 0]), np.array([L, W, W])], + [20, 6, 6], + cell_type=mesh.CellType.hexahedron, +) +V = fem.functionspace(domain, ("Lagrange", 1, (domain.geometry.dim,))) # ## Boundary conditions +# # As we would like to clamp the boundary at $x=0$, we do this by using a marker function, which locate the facets where $x$ is close to zero by machine precision. +# # + def clamped_boundary(x): @@ -67,20 +83,25 @@ def clamped_boundary(x): # - # As we want the traction $T$ over the remaining boundary to be $0$, we create a `dolfinx.Constant` +# T = fem.Constant(domain, default_scalar_type((0, 0, 0))) # We also want to specify the integration measure $\mathrm{d}s$, which should be the integral over the boundary of our domain. We do this by using `ufl`, and its built in integration measures +# ds = ufl.Measure("ds", domain=domain) # ## Variational formulation +# # We are now ready to create our variational formulation in close to mathematical syntax, as for the previous problems. +# # + def epsilon(u): - return ufl.sym(ufl.grad(u)) # Equivalent to 0.5*(ufl.nabla_grad(u) + ufl.nabla_grad(u).T) + """Equivalent to 0.5*(ufl.nabla_grad(u) + ufl.nabla_grad(u).T)""" + return ufl.sym(ufl.grad(u)) def sigma(u): @@ -104,14 +125,20 @@ def sigma(u): # ``` # # ## Solve the linear variational problem +# # As in the previous demos, we assemble the matrix and right hand side vector and use PETSc to solve our variational problem +# -problem = LinearProblem(a, L, bcs=[bc], petsc_options={"ksp_type": "preonly", "pc_type": "lu"}) +problem = LinearProblem( + a, L, bcs=[bc], petsc_options={"ksp_type": "preonly", "pc_type": "lu"} +) uh = problem.solve() # ## Visualization +# # As in the previous demos, we can either use Pyvista or Paraview for visualization. We start by using Pyvista. Instead of adding scalar values to the grid, we add vectors. +# # + pyvista.start_xvfb() @@ -136,6 +163,7 @@ def sigma(u): # We could also use Paraview for visualizing this. # As explained in previous sections, we save the solution with `XDMFFile`. # After opening the file `deformation.xdmf` in Paraview and pressing `Apply`, one can press the `Warp by vector button` ![Warp by vector](warp_by_vector.png) or go through the top menu (`Filters->Alphabetical->Warp by Vector`) and press `Apply`. We can also change the color of the deformed beam by changing the value in the color menu ![color](color.png) from `Solid Color` to `Deformation`. +# with io.XDMFFile(domain.comm, "deformation.xdmf", "w") as xdmf: xdmf.write_mesh(domain) @@ -143,19 +171,23 @@ def sigma(u): xdmf.write_function(uh) # ## Stress computation +# # As soon as the displacement is computed, we can compute various stress measures. We will compute the von Mises stress defined as $\sigma_m=\sqrt{\frac{3}{2}s:s}$ where $s$ is the deviatoric stress tensor $s(u)=\sigma(u)-\frac{1}{3}\mathrm{tr}(\sigma(u))I$. +# -s = sigma(uh) - 1. / 3 * ufl.tr(sigma(uh)) * ufl.Identity(len(uh)) -von_Mises = ufl.sqrt(3. / 2 * ufl.inner(s, s)) +s = sigma(uh) - 1.0 / 3 * ufl.tr(sigma(uh)) * ufl.Identity(len(uh)) +von_Mises = ufl.sqrt(3.0 / 2 * ufl.inner(s, s)) # The `von_Mises` variable is now an expression that must be projected into an appropriate function space so that we can visualize it. As `uh` is a linear combination of first order piecewise continuous functions, the von Mises stresses will be a cell-wise constant function. +# V_von_mises = fem.functionspace(domain, ("DG", 0)) stress_expr = fem.Expression(von_Mises, V_von_mises.element.interpolation_points()) stresses = fem.Function(V_von_mises) stresses.interpolate(stress_expr) -# In the previous sections, we have only visualized first order Lagrangian functions. However, the Von Mises stresses are piecewise constant on each cell. Therefore, we modify our plotting routine slightly. The first thing we notice is that we now set values for each cell, which has a one to one correspondence with the degrees of freedom in the function space. +# In the previous sections, we have only visualized first order Lagrangian functions. However, the Von Mises stresses are piecewise constant on each cell. Therefore, we modify our plotting routine slightly. The first thing we notice is that we now set values for each cell, which has a one to one correspondence with the degrees of freedom in the function space. +# warped.cell_data["VonMises"] = stresses.x.petsc_vec.array warped.set_active_scalars("VonMises") diff --git a/chapter2/ns_code2.ipynb b/chapter2/ns_code2.ipynb index d894cf5e..237dca40 100644 --- a/chapter2/ns_code2.ipynb +++ b/chapter2/ns_code2.ipynb @@ -651,6 +651,7 @@ " if pressure is not None:\n", " p_diff[i] -= pressure[0]\n", " break\n", + "progress.close()\n", "vtx_u.close()\n", "vtx_p.close()" ] diff --git a/chapter2/ns_code2.py b/chapter2/ns_code2.py index 12062c89..1c0be052 100644 --- a/chapter2/ns_code2.py +++ b/chapter2/ns_code2.py @@ -478,6 +478,7 @@ def __call__(self, x): if pressure is not None: p_diff[i] -= pressure[0] break +progress.close() vtx_u.close() vtx_p.close() diff --git a/chapter4/compiler_parameters.ipynb b/chapter4/compiler_parameters.ipynb index 77e42ab6..c067f1f7 100644 --- a/chapter4/compiler_parameters.ipynb +++ b/chapter4/compiler_parameters.ipynb @@ -50,7 +50,7 @@ "id": "2", "metadata": {}, "source": [ - "Next we generate a general function to assemble the mass matrix for a unit cube. Note that we use `dolfinx.fem.Form` to compile the variational form. For codes using `dolfinx.LinearProblem`, you can supply `jit_options` as a keyword argument." + "Next we generate a general function to assemble the mass matrix for a unit cube. Note that we use `dolfinx.fem.form` to compile the variational form. For codes using `dolfinx.fem.petsc.LinearProblem`, you can supply `jit_options` as a keyword argument." ] }, { diff --git a/chapter4/compiler_parameters.py b/chapter4/compiler_parameters.py index e5811c4d..25366121 100644 --- a/chapter4/compiler_parameters.py +++ b/chapter4/compiler_parameters.py @@ -46,7 +46,7 @@ print(f"Directory to put C files in: {cache_dir}") # - -# Next we generate a general function to assemble the mass matrix for a unit cube. Note that we use `dolfinx.fem.Form` to compile the variational form. For codes using `dolfinx.LinearProblem`, you can supply `jit_options` as a keyword argument. +# Next we generate a general function to assemble the mass matrix for a unit cube. Note that we use `dolfinx.fem.form` to compile the variational form. For codes using `dolfinx.fem.petsc.LinearProblem`, you can supply `jit_options` as a keyword argument. # + diff --git a/fem.md b/fem.md index f2f86710..14ad6afd 100644 --- a/fem.md +++ b/fem.md @@ -40,13 +40,13 @@ The tutorial uses several dependencies for meshing, plotting and timings. A comp To use the notebooks in this tutorial with DOLFINx on your own computer, you should use the docker image obtained using the following command: ```bash - docker run --init -p 8888:8888 -v "$(pwd)":/root/shared ghcr.io/jorgensd/dolfinx-tutorial:v0.7.2 + docker run --init -p 8888:8888 -v "$(pwd)":/root/shared ghcr.io/jorgensd/dolfinx-tutorial:release ``` This image can also be used as a normal docker container by adding: ```bash - docker run --ti -v "$(pwd)":/root/shared --entrypoint="/bin/bash" ghcr.io/jorgensd/dolfinx-tutorial:v0.7.2 + docker run --ti -v "$(pwd)":/root/shared --entrypoint="/bin/bash" ghcr.io/jorgensd/dolfinx-tutorial:release ``` The tutorials can also be exported as an IPython notebook or PDF by clicking the ![Download](save.png)-symbol in the top right corner of the relevant tutorial. The notebook can in turn be used with a Python kernel which has DOLFINx. @@ -58,7 +58,7 @@ The [Dockerfile](https://github.com/FEniCS/dolfinx/blob/main/docker/Dockerfile) provides a definitive build recipe. As the DOLFINx docker images are hosted at Docker-hub, one can directly access the image using: ```bash -docker run dolfinx/dolfinx:v0.8.0 +docker run dolfinx/dolfinx:stable ``` There are several ways of customizing a docker container, such as mounting volumes/sharing folder, setting a working directory, sharing graphical interfaces etc. See `docker run --help` for an extensive list.