The tbcontrol package collects functions useful for the kinds of problems encountered in undergraduate process control
textbooks. It is the distributable part of this larger collection of Jupyter notebooks for CPN321 (Process Dynamics) and
CPB421 (Process Control) at the University of Pretoria.
There is also a Dynamics and Control YouTube channel with videos related to the notebooks.
The supported notebook environment is managed by uv and pinned to Python 3.13 through
.python-version. The published tbcontrol library is intentionally less restrictive: it supports Python 3.10 and newer,
has no upper bounds on its runtime dependencies, and is tested separately across Python 3.10–3.14.
On macOS, install uv with Homebrew:
brew install uvOther platforms can use the official uv installation instructions.
A plain sync installs the ordinary notebook environment:
uv sync
uv run jupyter labThe test tools are kept in focused groups. Install them when running the complete test suite:
uv sync --group test --group notebook-test
uv run --group test pytest tests
uv run --group test --group notebook-test pytest -n 2 --verbose test_all_notebooks.pyThe dependency groups are:
default: Jupyter and the constrained numerical, symbolic, control, spreadsheet, plotting, and notebook-diff packages used by this repository.test: the lightweighttbcontrolpackage-test dependency.notebook-test: notebook execution and parallel-test tooling.docs: Sphinx and the documentation extensions used by Read the Docs.
To build the documentation locally:
uv sync --group docs
uv run --group docs sphinx-build -M html . _buildThe repository's .envrc uses layout uv. Install direnv, add its shell hook, and install the
uv layout from the direnv wiki, then run:
direnv allowConfigure editors and notebook clients to use .venv/bin/python.
This repository commits notebook output on purpose — nbviewer and Read the Docs both serve the
stored results, and conf.py sets nbsphinx_execute = 'never'. Tools that strip output, such as a
default nbstripout setup, are therefore the wrong thing to install here.
What does get normalised is metadata. Editors and Jupyter clients write their own kernel labels and
ipywidgets state into notebooks, which otherwise shows up as noise in every diff. A git clean
filter handles this on the way into the index, so the working tree is never modified and editors are
left to do as they please. tools/nbclean.py drops anaconda-cloud and widgets, and settles
kernelspec.display_name on Python 3. Cell output is not touched.
Alongside it, nbdime provides readable notebook diffs and merges.
.gitattributes is committed, but the drivers it names are defined in .git/config and are not
cloned. Run this once per checkout, or the attributes silently do nothing:
make setup-gitTwo consequences worth knowing. Metadata that the filter removes lives only in your working copy, so
a git checkout or git stash round-trip drops it — that is the intent, but it means the file on
disk is not always what you last saved. And because git compares the filtered content, a notebook
whose only change is stripped metadata correctly shows up as unmodified.