Skip to content

Commit

Permalink
Merge pull request #44 from GLVis/anywidget
Browse files Browse the repository at this point in the history
Rewrite to use anywidget
  • Loading branch information
justinlaughlin authored Aug 27, 2024
2 parents cd40a84 + f3d4b06 commit 549101f
Show file tree
Hide file tree
Showing 29 changed files with 310 additions and 3,764 deletions.
4 changes: 0 additions & 4 deletions .binder/postBuild
Original file line number Diff line number Diff line change
@@ -1,6 +1,2 @@
# install this
pip install .

# install jupyter-lab extension
jupyter labextension install @jupyter-widgets/jupyterlab-manager --no-build
jupyter labextension install glvis-jupyter
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
BSD 3-Clause License

Copyright (c) 2010-2021, Lawrence Livermore National Security, LLC
Copyright (c) 2010-2024, Lawrence Livermore National Security, LLC
All rights reserved.

Redistribution and use in source and binary forms, with or without
Expand Down
2 changes: 0 additions & 2 deletions MANIFEST.in

This file was deleted.

227 changes: 95 additions & 132 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,197 +1,152 @@
# Interactive GLVis Jupyter Widget
# PyGLVis

<!-- Badges generated at https://mybinder.readthedocs.io/en/latest/howto/badges.html -->
[![badge](examples/basic.svg "Basic GLVis + Jupyter Example")](https://mybinder.org/v2/gh/GLVis/pyglvis/HEAD?filepath=examples%2Fbasic.ipynb)
[![badge](examples/plot.svg "Plot grid functions")](https://mybinder.org/v2/gh/GLVis/pyglvis/HEAD?filepath=examples%2Fplot.ipynb)
[![badge](examples/ex1.svg "MFEM's Example 1")](https://mybinder.org/v2/gh/GLVis/pyglvis/HEAD?filepath=examples%2Fex1.ipynb)
[![badge](examples/ex9.svg "MFEM's Example 9")](https://mybinder.org/v2/gh/GLVis/pyglvis/HEAD?filepath=examples%2Fex9.ipynb)

This repository contains a [Jupyter](https://jupyter.org/) widget for the [GLVis](https://glvis.org/) finite element
visualization tool based on the [glvis-js](https://github.com/GLVis/glvis-js) JavaScript/WebAssembly library.
PyGLVis is an interactive [Jupyter](https://jupyter.org/) widget for visualizing finite element meshes and functions, built on-top of the [GLVis](https://glvis.org/) library.

## Usage
## 📦 Installation

```python
from glvis import glvis
The GLVis Jupyter widget is installed using `pip`. To install the latest version from the repository:

glvis(data[, width=640, height=480])
```bash
git clone https://github.com/GLVis/pyglvis.git
cd pyglvis
pip install .
```

# or assign if you want to update later
g = glvis(data)
# run a cell with `g` to show it
g
Or, install directly from PyPi,
```bash
pip install glvis
```

The `data` object and be one of:
PyGLVis requires the Python wrapper for MFEM, [PyMFEM](https://github.com/mfem/pymfem), which can be installed with
```bash
pip install mfem
```

- a `str`, in the format of `*.saved` files
- a `Mesh`, defined in [PyMFEM](https://github.com/mfem/pymfem)
- a `(Mesh, GridFunction)` tuple, defined in [PyMFEM](https://github.com/mfem/pymfem)

[PyMFEM](https://github.com/mfem/pymfem) can be installed with `pip install mfem --no-binary mfem`.

## 🚀 Usage

### Basic usage

Once you have a `glvis` object there are a few methods that can used to update the
visualization:
```python
# show a new Mesh/GridFunction, resets keys
g.plot(data)
# show an updated visualization with the same `Mesh` and `GridFunction`
# dimensions, preserves keys
g.update(data)
# change the image size
g.set_size(width, height)
# force the widget to render. if the widget isn't the last statement in a cell it
# will not be shown without this. see ex9.ipynb
g.render()
```
from glvis import glvis

See the [examples](examples/) directory for additional examples. To test those locally, start a Jupyter notebook server with
# Create a `glvis` object
g = glvis(data, width=640, height=480)

# Run a cell with `g` as the last statement to display the widget
g
```
jupyter notebook
```

## Installation

The GLVis Jupyter widget can be simply installed with `pip`:

```
pip install glvis
```
The `data` object can be one of:

It order for the installation to be useful you must enable the extension for one or both
of the [Classic Notebook](https://jupyter-notebook.readthedocs.io/en/stable/) and
[Jupyter Lab](https://jupyterlab.readthedocs.io/en/stable/), see the next two sections:
- `Mesh`, defined in [PyMFEM](https://github.com/mfem/pymfem)
- `(Mesh, GridFunction)` tuple, defined in [PyMFEM](https://github.com/mfem/pymfem)
- `str`, in the format of `*.saved` files [used by MFEM and GLVis](https://mfem.org/mesh-format-v1.0/). See [examples/basic.ipynb](examples/basic.ipynb) for an example.

### Jupyter Notebook
### Customization with key commands

To use the widget with the basic Notebook enable it with `jupyter nbextension enable`:
GLVis has many keyboard commands that can be used to customize the visualization.
A few of the most common are listed below. See the [GLVis README](https://github.com/GLVis/glvis?tab=readme-ov-file#key-commands) for a full list.
- `r` - reset the view
- `c` - toggle the colorbar
- `j` - toggle perspective
- `l` - toggle the light
- `g` - toggle the background color (white/black)
- `a` - cycle through bounding box axes states
- `m` - cycle through mesh states
- `p` - cycle through color palettes
- `t` - cycle through materials and lights
- `0` - begin rotating around z-axis
- `.` - pause rotation
- `*`/`/` - zoom in/out

These can be set using the `keys` argument when creating a `glvis` object.
```python
glvis(data, keys='rljgac//0')
```
jupyter nbextension enable --py glvis
```

After enabling the extension you can verify you're good to go with:
This combination of keys would: `r` reset the view, `l` toggle the light, `j` toggle perspective, `g` toggle the background color to black (default is white), `a` show the bounding box, `c` show the colorbar, `//` zoom out twice, and `0` begin rotating around the z-axis:

```
jupyter nbextension list
```
![pyglvis_preset_keys](https://github.com/GLVis/pyglvis/assets/27717785/de0e0a99-72ac-4a88-8369-708515600b09)

The output should be something like:
Alternatively, keys can be typed directly into the widget after it has been created:

```
Known nbextensions:
config dir: path/to/nbconfig
notebook section
glvis-jupyter/extension enabled
- Validating: OK
<possibly a different config dir>
jupyter-js-widgets/extension enabled
- Validating: OK
```
![pyglvis_using_keys](https://github.com/GLVis/pyglvis/assets/27717785/625f4f06-8f99-4390-94d7-4d317fd11e7f)

If `glvis-jupyter` and `jupyter-js-widgets` are not both listed, try the following:
### Other methods

Once you have a `glvis` object there are a few methods that can used to update the
visualization, besides using keys:
```python
# Show a new Mesh/GridFunction, resets keys
g.plot(data)
# Show an updated visualization with the same data, preserving keys
g.update(data)
# Change the image size
g.set_size(width, height)
# Force the widget to render. If the widget isn't the last statement in a cell it
# will not be shown without this. See ex9.ipynb
g.render()
```
jupyter nbextension install --user --py glvis
jupyter nbextension enable --user --py glvis
jupyter nbextension install --user --py widgetsnbextension
jupyter nbextension enable --user --py widgetsnbextension
```
You may also need to run these if you have upgraded to a newer version of the GLVis Jupyter widget.

### Jupyter Lab

[JupyterLab](https://jupyterlab.readthedocs.io) requires another set of install commands:
See the [examples](examples/) directory for additional examples. To test those locally, start a Jupyter lab server with

```
jupyter labextension install @jupyter-widgets/jupyterlab-manager --no-build
jupyter labextension install glvis-jupyter
jupyter lab
```

## Development

Development installation requires `npm`.
## 🐛 Troubleshooting

If you want to test a new version of `glvis`:
This widget was originally developed using the [jupyter widget cookiecutter](https://github.com/jupyter-widgets/widget-cookiecutter); however, [recent changes to the Jupyter ecosystem](https://jupyter-notebook.readthedocs.io/en/latest/migrate_to_notebook7.html#why-a-new-version) have broken a lot of functionality, leading to a rewrite using [anywidget](https://anywidget.dev/). If you encounter any problems, please consider supporting development by opening an [issue](https://github.com/GLVis/pyglvis/issues).

1. Bump the version in _pyglvis/js/package.json_ and _glvis-js/package.json_
2. `npm install path/to/glvis-js`


Each time you update stuff in _pyglvis/js/src_:
## 🤖 Development

1. `npm install`
2. `npx webpack`


Once:
### PyGLVis dependencies

```mermaid
graph TD;
A[mfem] --> B[pymfem];
A --> C[glvis];
C --> D[glvis-js];
Ext1[emscripten] --> D;
D-.-E["glvis-js\n(npm/esm mirror)"]
B & E --> G[pyglvis];
Ext2[jupyter] --> G;
```
git clone https://github.com/glvis/pyglvis.git
cd pyglvis
pip install -e .
```

### Developing in Jupyter Notebook
`pyglvis` is most directly depednent on `PyMFEM` and `glvis-js`. [PyMFEM](https://github.com/mfem/pymfem) is a Python wrapper of the finite element library, `MFEM`, while `glvis-js` is a JavaScript/WebAssembly port of `glvis`.

`glvis-js` is hosted on [github](https://github.com/glvis/glvis-js) and mirrored on [npm](https://www.npmjs.com/package/glvis). [esm.sh](https://esm.sh/glvis) allows `pyglvis` to pull the latest version of `glvis-js` directly from npm. This can be seen in the first line of [glvis/widget.js](glvis/widget.js):

```
jupyter nbextension install --py --symlink --sys-prefix glvis
jupyter nbextension enable --py --sys-prefix glvis
import glvis from "https://esm.sh/glvis";
```

### Developing in Jupyter Lab
You can specify a different version of `glvis-js` by adding `@x.y.z` to the end of this import statement, where `x.y.z` matches a version number available on `npm`, e.g.

```bash
jupyter labextension install @jupyter-widgets/jupyterlab-manager --no-build
# I believe you need node in the path Lab uses for this to work, I see an extension load error
# in a context where I don't have it:
# Failed to load resource: the server responded with a status of 500 (Internal Server Error)
# lab/api/extensions?1610138347763
# Which is just a python stacktrace, ending with:
# raise ValueError(msg)
# ValueError: Please install Node.js and npm before continuing installation.
jupyter labextension link ./js
```


### Troubleshooting

If you run into errors related to node/npm that aren't helpful try:

```bash
cd pyglvis
make clean
cd js
# fix errors in these steps, run `make -C .. clean` each time
npm install
npx webpack
import glvis from "https://esm.sh/glvis@0.6.3";
```

## Releasing

### Releasing a new version of glvis-jupyter on NPM:
### Releasing a new version of glvis on NPM:

- Update the required version of `glvis` in `js/package.json`
To publish a new version of `glvis-js`, follow the instructions on the [repo](https://github.com/GLVis/glvis-js/tree/master).

- Update the version in `js/package.json`

```bash
# clean out the `dist` and `node_modules` directories
git clean -fdx
npm install
npm publish
```

### Releasing a new version of glvis on PyPI:

- Update `glvis/_version.py`
- Set release version
- Update `extension_version` to match `js/package.json`
- Update `__version__` in `glvis/__about__.py`

- `git add` and `git commit` changes
- `glvis/_version.py`, `js/package.json`, and `js/package-lock.js`


You will need [twine](https://pypi.org/project/twine/) to publish to PyPI, install with `pip`.
Expand All @@ -202,3 +157,11 @@ twine upload dist/*
git tag -a X.X.X -m 'comment'
git push --tags
```


## 🌐 Links
- MFEM ([website](https://mfem.org/), [github](https://github.com/mfem/mfem))
- PyMFEM ([github](https://github.com/mfem/pymfem), [pypi](https://pypi.org/project/mfem/))
- GLVis ([website](https://glvis.org/), [github](https://github.com/glvis/glvis))
- glvis-js ([github](https://github.com/glvis/glvis-js), [npm](https://www.npmjs.com/package/glvis), [esm](https://esm.sh/glvis))
- pyglvis ([github](https://github.com/GLVis/pyglvis), [pypi]())
2 changes: 1 addition & 1 deletion examples/basic.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
"outputs": [],
"source": [
"# Another vsualization instance of the same stream\n",
"glvis(stream + 'rljg****tttac0', 300, 300)"
"glvis(stream, 300, 300, keys='rljg****tttac0')"
]
}
],
Expand Down
24 changes: 15 additions & 9 deletions examples/ex1.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"metadata": {
"tags": []
},
"source": [
"## MFEM Example 1\n",
"\n",
Expand Down Expand Up @@ -39,12 +41,14 @@
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"metadata": {
"tags": []
},
"outputs": [],
"source": [
"# Requires PyMFEM, see https://github.com/mfem/PyMFEM\n",
"import mfem.ser as mfem\n",
"from glvis import glvis, to_stream"
"from glvis import glvis"
]
},
{
Expand Down Expand Up @@ -107,11 +111,14 @@
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"metadata": {
"tags": []
},
"outputs": [],
"source": [
"# Plot the mesh + solution (all GLVis keys and mouse commands work)\n",
"glvis((mesh, x), 400, 400)"
"g=glvis((mesh, x))\n",
"g"
]
},
{
Expand All @@ -131,15 +138,14 @@
"outputs": [],
"source": [
"# Visualization with additional GLVis keys\n",
"g = glvis(to_stream(mesh,x) + 'keys ARjlmcbp*******')\n",
"g.set_size(600, 400)\n",
"g = glvis((mesh,x), keys='ARjlmcbp*******')\n",
"g"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
Expand All @@ -153,7 +159,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.9.1"
"version": "3.10.12"
}
},
"nbformat": 4,
Expand Down
Loading

0 comments on commit 549101f

Please sign in to comment.