Skip to content

Commit eb1e0c6

Browse files
authored
Use new plotting API (#58)
* Update all notebooks to use new plotting * Update changelog * UPdate testing to check for build correctness * Update custom image
1 parent 421ec31 commit eb1e0c6

24 files changed

+621
-632
lines changed

.github/workflows/build-publish.yml

+2-3
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ jobs:
2020
build:
2121
# The type of runner that the job will run on
2222
runs-on: ubuntu-latest
23-
container: dokken92/dolfinx_custom:07012022
23+
container: dokken92/dolfinx_custom:21012022
2424

2525
env:
2626
HDF5_MPI: "ON"
@@ -84,8 +84,7 @@ jobs:
8484
- name: Build the book
8585
run: |
8686
PYVISTA_JUPYTER_BACKEND=static PYVISTA_OFF_SCREEN=false jupyter-book build -W .
87-
# Add in -W once matplotlib is gone
88-
# Pusb book to HTML to github pages
87+
# Push book to HTML to github pages
8988
- name: GitHub Pages action
9089
uses: peaceiris/actions-gh-pages@v3.5.9
9190
with:

.github/workflows/docker-image.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -37,4 +37,4 @@ jobs:
3737
run: echo ${{ secrets.DOCKERHUB_TOKEN }} | docker login -u ${{ secrets.DOCKERHUB_USERNAME }} --password-stdin
3838
- name: Push to the DockerHub registry
3939
run: |
40-
docker push dokken92/dolfinx_custom:07012022
40+
docker push dokken92/dolfinx_custom:21012022

.github/workflows/main-test.yml

+4-1
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,14 @@ jobs:
4242
apt-get clean
4343
rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
4444
pip3 install --no-cache-dir tqdm pandas seaborn
45-
pip3 install notebook nbconvert jupyter-book myst_parser pyvista jupyterlab
45+
pip3 install notebook nbconvert jupyter-book myst_parser pyvista jupyterlab jupyter
4646
pip3 install ipygany pythreejs
4747
pip3 install --no-cache-dir matplotlib setuptools --upgrade
4848
jupyter nbextension enable --py --sys-prefix ipygany
4949
rm -rf /usr/local/share/.cache/*
50+
- name: Test building the book
51+
run:
52+
PYVISTA_JUPYTER_BACKEND=static PYVISTA_OFF_SCREEN=false jupyter-book build -W .
5053
- name: Test notebooks in parallel
5154
run: |
5255
cd chapter1

Changelog.md

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
# Changelog
22

33
## Dev
4+
- All `pyvista` plotting has been rewritten to use `ipygany` and `pythreejs` as well as using a cleaner interface.
5+
- `dolfinx.plot.create_vtk_topology` has been renamed to `dolfinx.plot.create_vtk_mesh` and can now be directly used as input to `pyvista.UnstructuredGrid`.
6+
- `dolfinx.fem.Function.compute_point_values` has been deprecated. Interpolation into a CG-1 is now the way of getting vertex values.
47
- API updates wrt. DOLFINx. `Form`->`form`, `DirichletBC`->`dirichletbc`.
5-
- Switch plotting backend to `ipygany` and `pythreejs`
68
- Updates on error computations in [Error control: Computing convergence rates](chapter4/convergence).
79
- Added tutorial on interpolation of `ufl.Expression` in [Deflection of a membrane](chapter1/membrane_code).
810
- Added tutorial on how to apply constant-valued Dirichet conditions in [Deflection of a membrane](chapter1/membrane_code).

Dockerfile

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM dokken92/dolfinx_custom:07012022
1+
FROM dokken92/dolfinx_custom:21012022
22

33
# create user with a home directory
44
ARG NB_USER

chapter1/fundamentals_code.ipynb

+65-37
Original file line numberDiff line numberDiff line change
@@ -335,7 +335,7 @@
335335
"$ u = \\sum_{j=1}^N U_j\\phi_j.$\n",
336336
"By writing `problem.solve()` we compute all the coefficients $U_1,\\dots, U_N$. These values are known as the _degrees of freedom_ (dofs). We can access the degrees of freedom by accessing the underlying vector in `uh`.\n",
337337
"However, as a second order function space has more dofs than a linear function space, we cannot compare these arrays directly.\n",
338-
"Therefore we compute the values of both `uex` and `uD` at the mesh nodes (for a linear mesh this is the vertices)."
338+
"As we allready have interpolated the exact solution into the first order space when creating the boundary condition, we can compare the maximum values at any degree of freedom of the approximation space."
339339
]
340340
},
341341
{
@@ -353,9 +353,7 @@
353353
}
354354
],
355355
"source": [
356-
"u_vertex_values = uh.compute_point_values()\n",
357-
"u_ex_vertex_values = uex.compute_point_values()\n",
358-
"error_max = numpy.max(numpy.abs(u_vertex_values - u_ex_vertex_values))\n",
356+
"error_max = numpy.max(numpy.abs(uD.x.array-uh.x.array))\n",
359357
"# Only print the error on one process\n",
360358
"if mesh.comm.rank == 0:\n",
361359
" print(f\"Error_L2 : {error_L2:.2e}\")\n",
@@ -366,10 +364,10 @@
366364
"cell_type": "markdown",
367365
"metadata": {},
368366
"source": [
369-
"## Plotting the solution using pyvista\n",
370-
"Once the solution has been computed, we will visualize it using [pyvista](https://docs.pyvista.org/), an interface to the VTK toolkit.\n",
367+
"## Plotting the mesh using pyvista\n",
368+
"We will visualizing the mesh using [pyvista](https://docs.pyvista.org/), an interface to the VTK toolkit.\n",
371369
"We start by converting the mesh to a format that can be used with `pyvista`.\n",
372-
"To do this we use the function `dolfinx.plot.create_vtk_topology`. The first step is to create an unstructured grid that can be used by `pyvista`."
370+
"To do this we use the function `dolfinx.plot.create_vtk_mesh`. The first step is to create an unstructured grid that can be used by `pyvista`."
373371
]
374372
},
375373
{
@@ -378,8 +376,10 @@
378376
"metadata": {},
379377
"outputs": [],
380378
"source": [
381-
"from dolfinx.plot import create_vtk_topology\n",
382-
"topology, cell_types = create_vtk_topology(mesh, mesh.topology.dim)"
379+
"from dolfinx.plot import create_vtk_mesh\n",
380+
"import pyvista\n",
381+
"topology, cell_types, geometry = create_vtk_mesh(mesh, mesh.topology.dim)\n",
382+
"grid = pyvista.UnstructuredGrid(topology, cell_types, geometry)"
383383
]
384384
},
385385
{
@@ -395,46 +395,81 @@
395395
"metadata": {},
396396
"outputs": [],
397397
"source": [
398-
"import pyvista\n",
399398
"pyvista.set_jupyter_backend(\"pythreejs\")"
400399
]
401400
},
402401
{
403402
"cell_type": "markdown",
404403
"metadata": {},
405404
"source": [
406-
"We start by creating a pyvista grid the `vtk_topology` and the `mesh.geometry`.\n",
407-
"Next, we attach data from our solution `uh` by computing the values of the function at each vertex."
405+
"We can now use the `pyvista.Plotter` to visualize the mesh. We visualize it by showing it in 2D and warped in 3D.\n",
406+
"In the jupyter notebook environment, we use the default setting of `pyvista.OFF_SCREEN=False`, which will render plots directly in the notebook."
408407
]
409408
},
410409
{
411410
"cell_type": "code",
412411
"execution_count": 15,
413412
"metadata": {},
414-
"outputs": [],
413+
"outputs": [
414+
{
415+
"data": {
416+
"application/vnd.jupyter.widget-view+json": {
417+
"model_id": "8153fbfcef014a3899925003792a4ba1",
418+
"version_major": 2,
419+
"version_minor": 0
420+
},
421+
"text/plain": [
422+
"Renderer(camera=PerspectiveCamera(aspect=1.3333333333333333, children=(DirectionalLight(color='#fefefe', inten…"
423+
]
424+
},
425+
"metadata": {},
426+
"output_type": "display_data"
427+
}
428+
],
415429
"source": [
416-
"grid = pyvista.UnstructuredGrid(topology, cell_types, mesh.geometry.x)\n",
417-
"grid.point_data[\"u\"] = uh.compute_point_values().real\n",
418-
"grid.set_active_scalars(\"u\")"
430+
"plotter = pyvista.Plotter()\n",
431+
"plotter.add_mesh(grid, show_edges=True)\n",
432+
"plotter.view_xy()\n",
433+
"if not pyvista.OFF_SCREEN:\n",
434+
" plotter.show()\n",
435+
"else:\n",
436+
" pyvista.start_xvfb()\n",
437+
" figure = plotter.screenshot(\"fundamentals_mesh.png\")"
419438
]
420439
},
421440
{
422441
"cell_type": "markdown",
423442
"metadata": {},
424443
"source": [
425-
"We can now use the `pyvista.Plotter` to visualize the solution. We visualize it by showing it in 2D and warped in 3D.\n",
426-
"In the jupyter notebook environment, we use the default setting of `pyvista.OFF_SCREEN=False`, which will render plots directly in the notebook."
444+
"## Plotting a function using pyvista\n",
445+
"We want to plot the solution `uh`. As the function space used to defined the mesh is disconnected from the function space defining the mesh, we create a mesh based on the dof coordinates for the function space `V`. We use `dolfinx.plot.create_vtk_mesh` with the function space as input to create a mesh with mesh geometry based on the dof coordinates."
427446
]
428447
},
429448
{
430449
"cell_type": "code",
431450
"execution_count": 16,
432451
"metadata": {},
452+
"outputs": [],
453+
"source": [
454+
"u_topology, u_cell_types, u_geometry = create_vtk_mesh(V)"
455+
]
456+
},
457+
{
458+
"cell_type": "markdown",
459+
"metadata": {},
460+
"source": [
461+
"Next, we create the `pyvista.UnstructuredGrid` and add the dof-values to the mesh."
462+
]
463+
},
464+
{
465+
"cell_type": "code",
466+
"execution_count": 17,
467+
"metadata": {},
433468
"outputs": [
434469
{
435470
"data": {
436471
"application/vnd.jupyter.widget-view+json": {
437-
"model_id": "026dd4bdc8354af88ecd23be302ee96c",
472+
"model_id": "54e3bc7b8f764a31836e14906e188aff",
438473
"version_major": 2,
439474
"version_minor": 0
440475
},
@@ -447,14 +482,14 @@
447482
}
448483
],
449484
"source": [
450-
"plotter = pyvista.Plotter()\n",
451-
"plotter.add_mesh(grid, show_edges=True)\n",
452-
"plotter.view_xy()\n",
485+
"u_grid = pyvista.UnstructuredGrid(u_topology, u_cell_types, u_geometry)\n",
486+
"u_grid.point_data[\"u\"] = uh.x.array.real\n",
487+
"u_grid.set_active_scalars(\"u\")\n",
488+
"u_plotter = pyvista.Plotter()\n",
489+
"u_plotter.add_mesh(u_grid, show_edges=True)\n",
490+
"u_plotter.view_xy()\n",
453491
"if not pyvista.OFF_SCREEN:\n",
454-
" plotter.show()\n",
455-
"else:\n",
456-
" pyvista.start_xvfb()\n",
457-
" figure = plotter.screenshot(\"fundamentals.png\")"
492+
" u_plotter.show()"
458493
]
459494
},
460495
{
@@ -467,13 +502,13 @@
467502
},
468503
{
469504
"cell_type": "code",
470-
"execution_count": 17,
505+
"execution_count": 20,
471506
"metadata": {},
472507
"outputs": [
473508
{
474509
"data": {
475510
"application/vnd.jupyter.widget-view+json": {
476-
"model_id": "2b14ac59f2cf4d129b2843ce038c4b90",
511+
"model_id": "c03cdb732aa94579ab795f0188ce9572",
477512
"version_major": 2,
478513
"version_minor": 0
479514
},
@@ -487,7 +522,7 @@
487522
],
488523
"source": [
489524
"if not pyvista.OFF_SCREEN:\n",
490-
" warped = grid.warp_by_scalar()\n",
525+
" warped = u_grid.warp_by_scalar()\n",
491526
" plotter2 = pyvista.Plotter()\n",
492527
" plotter2.add_mesh(warped, show_edges=True, show_scalar_bar=True)\n",
493528
" plotter2.show(jupyter_backend=\"ipygany\")"
@@ -503,7 +538,7 @@
503538
},
504539
{
505540
"cell_type": "code",
506-
"execution_count": 18,
541+
"execution_count": 21,
507542
"metadata": {},
508543
"outputs": [],
509544
"source": [
@@ -523,13 +558,6 @@
523558
" :filter: cited and ({\"chapter1/fundamentals_code\"} >= docnames)\n",
524559
"```"
525560
]
526-
},
527-
{
528-
"cell_type": "code",
529-
"execution_count": null,
530-
"metadata": {},
531-
"outputs": [],
532-
"source": []
533561
}
534562
],
535563
"metadata": {

0 commit comments

Comments
 (0)