Developer Notes¶
Name: “super_hydro”¶
As of 9 September 2018, there are only 69 matches on Google for the search term “super_hydro” (with quotes) and most are squirreled away (email addresses, etc.) This seems like a safe choice for a project name. For comparison “SuperHydro” returned about 21k matches.
Environment variables will be prefixed with
SUPER_HYDRO_to minimize conflicts.The main module is
super_hydro.Config files will use
super_hydro.
Developer Install¶
We use Poetry to manage environments and dependencies, however, Conda can be useful, especially when trying to install GPU dependencies. For primary development work you should try to use Poetry, falling back to Conda if needed for speed or to get binary dependencies.
Install a virtual environment. This can either be pure pure python or with Conda:
Pure Python:
poetry env use python3.9
Conda: If you want to use Conda, first create an environment and activate it. We have an
anaconda-project.yamlfile which can be used to create appropriate Conda environments:anaconda-project prepare --refresh conda activate envs/super_hydro
or
anaconda-project prepare --env-spec super_hydro_gpu # If you have an NVIDIA GPU conda activate envs/super_hydro_gpu
Use Poetry to install
super_hydrowith thetestsanddocsextras and optionally with thefftworgpuextras if you have the FFTW libraries and/or NVIDIA Cuda libraries installed:poetry install -E tests -E docs poetry install -E tests -E docs -E fftw # If you have the FFTW libraries poetry install -E tests -E docs -E gpu # If you have an NVIDIA GPU
Note: If you are using Conda, be sure to first
conda activatethe appropriate environment. I do not know a way to getpoetry env useto do this by default.Run a shell and use
super_hydro:poetry shell # If using pure poetry conda activate envs/super_hydro # If using Conda
Jupyter Kernel
If you run Jupyter outside of the virtual environment, then you should install the super_hydro
kernel so it is available with your version of Jupyter. For example, the following
will register a kernel named super_hydro in your personal Jupyter configuration folder.
jupyter kernelspec install $(poetry env info -p) --user --name=super_hydro
poetry run python3 -m ipykernel install --sys-prefix
If you need to add dependencies, use poetry add and then correct in pyproject.toml:
poetry add uncertainties
poetry add -D sphinx-autobuild
poetry update # Update lock file
poetry install
You can then activate this with poetry shell for development work.
The development install process is specified in the Makefile, but needs a few tools
installed:
Conda: We use this for installing binary packages (makes working with the GPU easier for example). I install a fairly minimal Miniconda base environment.
Poetry: The recommended approach for installing this is actually outside of the environment:
curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py | python -poetry2conda: This can then be installed with pip (optionally in your user folder with--userso it is available with multiple environments:python3 -m pip --user install poetry2conda
. Currently this uses Poetry to
specify dependencies etc. in pyproject.toml, then uses poetry2conda to generate an
environment-*.yml file which can be used to create a Conda super_hydro
environment:
make conda-env
make conda-env-gpu # Add cupy
Conda¶
Some issues/questions related to the Conda super_hydro environment:
Where should we create this? Probably not in the user’s base installation (which might be write-only), so we should probably allow the
--prefix ${CONDA_PREFIX}option to be set. (Maybe provide aconfigurescript which allows this to be set?). To be able to activate this environment by name we need to run something like:conda config --append envs_dirs ${CONDA_PREFIX}
Unfortunately, this update the global env_dirs path, which is the hard to remove (uninstall). We should probably just rely on setting
CONDA_ENVS_PATHas part of the environment.
Environment Activation
In the remainder of the documentation, we shall assume that the appropriate environment is first activated with one of the following:
poetry shell # If using poetry
conda activate super_hydro # If using conda
Running the Server¶
There are several options for running the computation server.
Notebook Client¶
With the notebook interface, you can launch a server and view the results from a Jupyter notebook. Start a jupyter notebook server and visit Docs/Demonstrations/Contents%20(Start%20Here).ipynb:
conda activate super_hydro
jupyter notebook "Docs/Demonstrations/Contents (Start Here).ipynb"
Standalone Server¶
The computation server can also be started independently by running:
conda activate super_hydro
python bin/server
This will start a server listening on the port specified in super_hydro.conf (by
default 27372). You can then connect to this port with any of the clients. By
default, for security, the server only listens for connection from the same computer
(localhost). To run a server on another machine, first setup an ssh tunnel to the
server forwarding the port. For example:
<local > $ ssh <user@remote.machine.edu> -L 27372:27372
<remote> $ cd super_hydro
<remote> $ conda activate super_hydro # or poetry shell as needed
<remote> $ python bin/server
Flask Client¶
The simplest approach is to just run the Flask client:
poetry run python bin/client # If using pure python
anaconda-project run client # If using conda
This will start a Flask webserver listening on the specified port. Connecting a browser to this will allow you to select the various demonstrations from a menu, which will then launch a server, allowing you to run the simulation.
Documentation¶
The main API documentation is in Docs/sphinx-source and can be made by running:
anaconda-project run shell
# Now build the documentation.
cd Docs
make html
# Open the docs:
open _build/html/index.html
# Or run the following, which will continuously monitor your docs and
# update your page:
make auto
If you are working on documentation, you can have it auto-build when you save changes by running:
make dev-server
then visiting http://127.0.0.1:8000.
Sphinx/Jupyter Book¶
The documentation uses Sphinx, following the approach of Jupyter Book without explicitly using the Jupyter Book tools as discussed in the section Jupyter Book is a distribution of Sphinx.
Read the Docs¶
The documentation nd should be maintained so that it can be published at Read the Docs. See Getting Started with Sphinx for details.
Structure¶
The documentation is defined in the Docs/sphinx-source folder
with the following files:
conf.py: Configuration of Sphinx and the documentation in general.index.md: Master file. This defines the landing page and the overall structure through{toctree}statements. The file is written in the MyST format which is an extension of [Markdown] with special syntax to support ReStructure text features used by Sphinx.README.md: Symlink to the top-level README file. This is currently directly included inindex.mdand so should be maintained so that it displays properly on GitHub and GitLab repos. (See this SO question for some suggestions on how to validate your page.)
We use the nbsphinx extension so that we can include Jupyter
notebooks in the documentation. A couple of points about this:
Don’t include a Table of Contents in your notebooks: this does not look good.
Look closely at the headers: the structure of the documentation is dictated by these. Notebooks should all have a
# Titlefollowed by everything in appropriate subsection.By default, empty notebooks will be executed so that the output gets rendered. This is good for example, but not so good for interactive sessions! Some options:
Store some output in the notebook: this will disable execution.
Added metadata
{"nbsphinx": "hidden"}to the cell. (Use theView->Cell Toolbar->Edit Metadataoption to get theEdit Metadatabutton.)Hack in to
nbconvertto control output. I have included code to add theTagRemovePreprocessor, but keep in mind that this runs after execution. This allows you to addremove_cellas a tag for example. (See this comment for details.)
Citations¶
Citations to academic references can be included in
local.bib and included as described in the Jupyter{book}
documentation:
{cite}`Saint-Jalm:2019` : results in [].
References¶
numpydocformat: We use thenumpydocformat for documenting functions, classes, etc. so that they can be automatically included in documentation.
Architecture¶
Asynchronous Code¶
To achieve good performance and a responsive user interface, we must use some sort of asynchronous execution. For example:
Computation Server: This should run as fast as possible, without being slowed down by network communications or the user interface. Ideally, the computation should be run in a separate process (even on a separate machine), but for debugging purposes, it can be useful to run it in the same process space as the main code. We structure our code so that the computation engine can be run either as a separate thread or a separate process.
User Interface: The user interface should be run in a separate thread or process so that it remains responsive.
Threading¶
One option for asynchronous execution is to use multiple threads through the
threading standard library module.
This allows the separate threads to execute asynchronously, but because of the global
interpreter lock
(GIL)
this does not allow you to take advantage of multiple cores. This is a good option if
all but one of the threads spend most of their time listening for events, and is the
approach taken in the Server.
Server¶
The server process runs the following two threads:
super_hydro.server.Computation: Main computation thread. This does the actual computations, and should be run on a computer with high performance characteristics such as a GPU. For example, this might be run onswan.physics.wsu.edu.super_hydro.server.Server(IServer): This thread manages communication between theComputationthread and clients using thequeue.Queueclass. This implementation is designed to be used directly from the same process as the client (mainly for debugging purposes).super_hydro.server.NetworkServer(IServer): Subclass ofsuper_hydro.server.Serverthat listens for network connections and sends communications across the network.
Publication Goals¶
Installation:
Currently we have a bunch of environment files for various configurations such as client, server, and testing. Is there a point? Maybe these should be hidden in a directory and a single file provided.
[ ] Test all installation paths with nox. [ ] Make sure this is downloadable from anaconda cloud from my channel. [ ] Test install on various platforms. [ ] Integrate CI.
Testing:
Documentation:
[ ] Full docs should be published on Read the Docs. [ ] Document installation. [ ] Document running. [ ] Document making new models.
Models and Examples:
Clients:
[ ] Jupyter Notebook Client. * Does not have mouse support. * Still has issues with interrupts. [ ] Flask Client.
Other clients might be useful as demonstration about how to interact with server. [ ] Kivi Client. (Do we still need?) [ ] Node Client. (Do we still need?)
Configuration:
Current configuration is not so flexible. We are using a combination of
Deployment¶
Update version number in:
pyproject.toml
To Do¶
Remove unnecessary dependencies:
Definitely scipy (used for sinc…)
Possibly matplotlib.
Add tests and make them pass, the complete coverage.
Clean up repo - remove old clients and organize everything in proper folders.
Complete documentation from start to finish and helper scripts.
Upload to PyPI and Anaconda Cloud.
Clean up environment file.
Flask Client
Read list of models from a config file.
Preserve aspect ratio.
Click and drag finger.
“Go”, “Pause”, “Reset”
Don’t cache pause.
Sane cooling range and interpretation (low priority).
Running and shutdown issues.
Issues¶
Default config file in server directory.
Load this first, then read user’s file.
Don’t fail if no config file exists, make one.
Simple strategy for locating the config file: use the
__file__variable.os.path.dirname(__file__)Choose better port - with some sort of resolution if it is used.
# Set the NBPORT variable for the user from /etc/nbports export NBPORT=$(sed -n -e "s/${USER}: //p" /etc/nbports) if [ -z "$NBPORT" ]; then # https://unix.stackexchange.com/a/132524/37813 export NBPORT=$(python - <<-END import socket s = socket.socket() s.bind(("", 0)) print(s.getsockname()[1]) s.close() END ) >&2 echo "Warning: ${USER} not in /etc/nbports. Using random port ${NBPORT}" fi
Put a default port (not 8888) in config file, then search forward incrementally until a free port is found.
MMF: Make simple logger.
Refactor socket communication making sure that messages are properly sent and buffered.
Dependencies¶
Consider two different clients - a lite client that has minimal imports (but requires the server to compute everything) and a more full-bodied client that can perform some computations such as the tracer particles to reduce server load. The idea is to keep the lite client for mobile devices which cannot import numpy/scipy etc.
To Do¶
When running the network_server from the notebook client, Quit does not release zmq sockets, so one gets ZMQError: Address already in use when trying to restart.
Is there any way to rename the
ipykerneltosuper_hydro? This could be done by running the following after install, but such behavior is not permitted by the new python packaging system.python3 -m ipykernel install --sys-prefix --name super_hydro --display-name super_hydro
Lobby Display¶
Iteration 0: Run everything on penguin (or swan).
Install SSH.
Generate a key, and save in a non-privileged account on penguin.
Configure to forward appropriate port.
SSH and run server + flask.
Open http://localhost:
in fullscreen browser. Implement a timeout on the server.
Single command to start both computation and flask server.