diff --git a/.gitignore b/.gitignore
index 52b635e92ad15fd50b706c63232d10df9e005dab..1c5289f640ee61bb56cdcd74eec4be819a4bae5b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,3 +8,5 @@
 /teqp
 /dev/docker/valgrind/output
 /dev/docker/gcov/output
+/**/.ipynb_checkpoints
+/doc/build
\ No newline at end of file
diff --git a/doc/Makefile b/doc/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..d0c3cbf1020d5c292abdedf27627c6abe25e2293
--- /dev/null
+++ b/doc/Makefile
@@ -0,0 +1,20 @@
+# Minimal makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line, and also
+# from the environment for the first two.
+SPHINXOPTS    ?=
+SPHINXBUILD   ?= sphinx-build
+SOURCEDIR     = source
+BUILDDIR      = build
+
+# Put it first so that "make" without argument is like "make help".
+help:
+	@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
+
+.PHONY: help Makefile
+
+# Catch-all target: route all unknown targets to Sphinx using the new
+# "make mode" option.  $(O) is meant as a shortcut for $(SPHINXOPTS).
+%: Makefile
+	@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
diff --git a/doc/index.rst b/doc/index.rst
new file mode 100644
index 0000000000000000000000000000000000000000..336540f390c1b92359ee637380a838e7c779977a
--- /dev/null
+++ b/doc/index.rst
@@ -0,0 +1,10 @@
+
+***************************
+Converted IPython notebooks
+***************************
+
+.. note:: These static pages were auto-generated from the IPython notebooks residing `here on github <https://github.com/ibell/pdsim/tree/master/doc/notebooks>`_ They can be also be `viewed with nbviewer <http://nbviewer.ipython.org/github/ibell/pdsim/tree/master/doc/notebooks/>`_
+
+.. toctree::
+
+    MultiFluidTutorial
diff --git a/doc/make.bat b/doc/make.bat
new file mode 100644
index 0000000000000000000000000000000000000000..dc1312ab09ca6fb0267dee6b28a38e69c253631a
--- /dev/null
+++ b/doc/make.bat
@@ -0,0 +1,35 @@
+@ECHO OFF
+
+pushd %~dp0
+
+REM Command file for Sphinx documentation
+
+if "%SPHINXBUILD%" == "" (
+	set SPHINXBUILD=sphinx-build
+)
+set SOURCEDIR=source
+set BUILDDIR=build
+
+%SPHINXBUILD% >NUL 2>NUL
+if errorlevel 9009 (
+	echo.
+	echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
+	echo.installed, then set the SPHINXBUILD environment variable to point
+	echo.to the full path of the 'sphinx-build' executable. Alternatively you
+	echo.may add the Sphinx directory to PATH.
+	echo.
+	echo.If you don't have Sphinx installed, grab it from
+	echo.https://www.sphinx-doc.org/
+	exit /b 1
+)
+
+if "%1" == "" goto help
+
+%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
+goto end
+
+:help
+%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
+
+:end
+popd
diff --git a/doc/source/algorithms/VLE.ipynb b/doc/source/algorithms/VLE.ipynb
new file mode 100644
index 0000000000000000000000000000000000000000..7c9400a18cdfcd32106e1e79c2fff357f2e9b973
--- /dev/null
+++ b/doc/source/algorithms/VLE.ipynb
@@ -0,0 +1,38 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "id": "8218498b",
+   "metadata": {},
+   "source": [
+    "# Vapor-liquid equilibria\n",
+    "\n",
+    "Two basic approaches are implemented in teqp:\n",
+    "\n",
+    "* Iterative calculations given guess values\n",
+    "* Tracing along curves"
+   ]
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3 (ipykernel)",
+   "language": "python",
+   "name": "python3"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 3
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython3",
+   "version": "3.10.4"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
diff --git a/doc/source/algorithms/index.rst b/doc/source/algorithms/index.rst
new file mode 100644
index 0000000000000000000000000000000000000000..57169ed566c6e16605cea6f9d6dea353106dde89
--- /dev/null
+++ b/doc/source/algorithms/index.rst
@@ -0,0 +1,8 @@
+Algorithms
+==========
+
+.. toctree::
+   :maxdepth: 2
+   :caption: Contents:
+
+   VLE
\ No newline at end of file
diff --git a/doc/source/conf.py b/doc/source/conf.py
new file mode 100644
index 0000000000000000000000000000000000000000..5449eb132bd300c674dc79b743197507053894f2
--- /dev/null
+++ b/doc/source/conf.py
@@ -0,0 +1,56 @@
+# Configuration file for the Sphinx documentation builder.
+#
+# This file only contains a selection of the most common options. For a full
+# list see the documentation:
+# https://www.sphinx-doc.org/en/master/usage/configuration.html
+
+# -- Path setup --------------------------------------------------------------
+
+# If extensions (or modules to document with autodoc) are in another directory,
+# add these directories to sys.path here. If the directory is relative to the
+# documentation root, use os.path.abspath to make it absolute, like shown here.
+#
+# import os
+# import sys
+# sys.path.insert(0, os.path.abspath('.'))
+
+
+# -- Project information -----------------------------------------------------
+
+project = 'teqp'
+copyright = '2022, Ian Bell'
+author = 'Ian Bell'
+
+# The full version, including alpha/beta/rc tags
+import teqp
+release = teqp.__version__
+
+
+# -- General configuration ---------------------------------------------------
+
+# Add any Sphinx extension module names here, as strings. They can be
+# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
+# ones.
+extensions = [
+'nbsphinx'
+]
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ['_templates']
+
+# List of patterns, relative to source directory, that match files and
+# directories to ignore when looking for source files.
+# This pattern also affects html_static_path and html_extra_path.
+exclude_patterns = []
+
+# -- Options for HTML output -------------------------------------------------
+
+# The theme to use for HTML and HTML Help pages.  See the documentation for
+# a list of builtin themes.
+#
+html_theme = 'insipid'
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+html_static_path = ['_static']
\ No newline at end of file
diff --git a/doc/source/derivs/derivs.ipynb b/doc/source/derivs/derivs.ipynb
new file mode 100644
index 0000000000000000000000000000000000000000..7af50d86aa1f0ffc4e236a1302625a49d34749bb
--- /dev/null
+++ b/doc/source/derivs/derivs.ipynb
@@ -0,0 +1,79 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "id": "1afc4517",
+   "metadata": {},
+   "source": [
+    "# Thermodynamic Derivatives"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 6,
+   "id": "ca35cc29",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import teqp\n",
+    "import numpy as np"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 7,
+   "id": "8d254c40",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "Tc_K = [300]\n",
+    "pc_Pa = [4e6]\n",
+    "acentric = [0.01]\n",
+    "model = teqp.canonical_PR(Tc_K, pc_Pa, acentric)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 8,
+   "id": "49ee3453",
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "-0.06836660379313926"
+      ]
+     },
+     "execution_count": 8,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "z = np.array([1.0])\n",
+    "model.get_Ar01(300,300,z)"
+   ]
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3 (ipykernel)",
+   "language": "python",
+   "name": "python3"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 3
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython3",
+   "version": "3.10.4"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
diff --git a/doc/source/derivs/index.rst b/doc/source/derivs/index.rst
new file mode 100644
index 0000000000000000000000000000000000000000..8dccdb9bef2cb5289f2817e4eb7020b1b9d4fa0d
--- /dev/null
+++ b/doc/source/derivs/index.rst
@@ -0,0 +1,8 @@
+Derivatives
+===========
+
+.. toctree::
+   :maxdepth: 2
+   :caption: Contents:
+
+   derivs
\ No newline at end of file
diff --git a/doc/source/index.rst b/doc/source/index.rst
new file mode 100644
index 0000000000000000000000000000000000000000..d321eadc912705e22ba4ce5dee5673f3c10c71e5
--- /dev/null
+++ b/doc/source/index.rst
@@ -0,0 +1,17 @@
+Welcome to teqp's documentation!
+================================
+
+.. toctree::
+   :maxdepth: 2
+   :caption: Contents:
+
+   derivs/index
+   models/index
+   algorithms/index
+
+Indices and tables
+==================
+
+* :ref:`genindex`
+* :ref:`modindex`
+* :ref:`search`
diff --git a/doc/source/models/cubics.ipynb b/doc/source/models/cubics.ipynb
new file mode 100644
index 0000000000000000000000000000000000000000..2a597f271e2528c8ae0722657d3172c60345f268
--- /dev/null
+++ b/doc/source/models/cubics.ipynb
@@ -0,0 +1,43 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "id": "df447d16",
+   "metadata": {},
+   "source": [
+    "# Cubic EOS\n",
+    "\n",
+    "Something about cubic EOS"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "9a884dab",
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3 (ipykernel)",
+   "language": "python",
+   "name": "python3"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 3
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython3",
+   "version": "3.10.4"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
diff --git a/doc/source/models/index.rst b/doc/source/models/index.rst
new file mode 100644
index 0000000000000000000000000000000000000000..355677785073736c8116c9d9e04c2942ef887de9
--- /dev/null
+++ b/doc/source/models/index.rst
@@ -0,0 +1,9 @@
+Models
+======
+
+.. toctree::
+   :maxdepth: 2
+   :caption: Contents:
+
+   cubics
+   multifluid
diff --git a/doc/source/models/multifluid.ipynb b/doc/source/models/multifluid.ipynb
new file mode 100644
index 0000000000000000000000000000000000000000..82d80bca097c09fe06d8ad6f52e7ee59b7db5428
--- /dev/null
+++ b/doc/source/models/multifluid.ipynb
@@ -0,0 +1,274 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "# Multi-fluid EOS\n",
+    "\n",
+    "Peering into the innards of teqp"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 1,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "'0.8.1'"
+      ]
+     },
+     "execution_count": 1,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "import timeit, json\n",
+    "import pandas\n",
+    "import numpy as np\n",
+    "import teqp\n",
+    "teqp.__version__"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## Pure fluid loading"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 2,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "13.4 ms ± 158 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n"
+     ]
+    }
+   ],
+   "source": [
+    "# By default teqp looks for fluids relative to the set of fluids in ROOT/dev/fluids\n",
+    "# The name (case-sensitive) should match the .json file, without the json extension.\n",
+    "%timeit model = teqp.build_multifluid_model([\"Methane\", \"Ethane\"], teqp.get_datapath())"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 3,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "121 ms ± 3.07 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)\n"
+     ]
+    }
+   ],
+   "source": [
+    "# And if you provide valid aliases, alias lookup will be used to resolve the name\n",
+    "# But beware, this is rather a lot slower than the above because all fluid files need to be read\n",
+    "# in to build the alias map\n",
+    "%timeit model = teqp.build_multifluid_model([\"n-C1H4\", \"n-C3H8\"], teqp.get_datapath())"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "So, how to make it faster? Only do it once and cache"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 4,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "105 ms ± 968 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)\n"
+     ]
+    }
+   ],
+   "source": [
+    "# Here is the set of possible aliases to absolute paths of files\n",
+    "# Building this map takes a little while (somewhat faster in C++) due to all the file reads\n",
+    "# If you know your files will not change, good idea to build this alias map yourself.\n",
+    "%timeit aliasmap = teqp.build_alias_map(teqp.get_datapath())\n",
+    "aliasmap = teqp.build_alias_map(teqp.get_datapath())"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 5,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "14.1 ms ± 1.12 ms per loop (mean ± std. dev. of 7 runs, 100 loops each)\n"
+     ]
+    }
+   ],
+   "source": [
+    "# Then load the absolute paths from the alias map, \n",
+    "# which will guarantee that you hit exactly what you were looking for,\n",
+    "# resolving aliases as needed\n",
+    "identifiers = [aliasmap[n] for n in [\"Neon\", \"Hydrogen\"]]\n",
+    "%timeit model = teqp.build_multifluid_model(identifiers, teqp.get_datapath())"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "At some point soon teqp will support in-memory loading of JSON data for the pure components, without requiring reads from the operating system"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 6,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "# And you can also load the JSON that teqp is loading for the pure fluids\n",
+    "pureJSON = teqp.collect_component_json(['NEON','HYDROGEN'], teqp.get_datapath())"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## Mixture model loading"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 7,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "# Load the default JSON for the binary interaction parameters\n",
+    "BIP = json.load(open(teqp.get_datapath()+'/dev/mixtures/mixture_binary_pairs.json'))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 8,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "{'BibTeX': 'Kunz-JCED-2012',\n",
+       " 'CAS1': '74-82-8',\n",
+       " 'CAS2': '74-84-0',\n",
+       " 'F': 1.0,\n",
+       " 'Name1': 'Methane',\n",
+       " 'Name2': 'Ethane',\n",
+       " 'betaT': 0.996336508,\n",
+       " 'betaV': 0.997547866,\n",
+       " 'function': 'Methane-Ethane',\n",
+       " 'gammaT': 1.049707697,\n",
+       " 'gammaV': 1.006617867}"
+      ]
+     },
+     "execution_count": 8,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "# You can obtain interaction parameters either by pairs of names, where name is the name that teqp uses, the [\"INFO\"][\"NAME\"] field\n",
+    "params, swap_needed = teqp.get_BIPdep(BIP, ['Methane','Ethane'])\n",
+    "params"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 9,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "{'BibTeX': 'Kunz-JCED-2012',\n",
+       " 'CAS1': '74-82-8',\n",
+       " 'CAS2': '74-84-0',\n",
+       " 'F': 1.0,\n",
+       " 'Name1': 'Methane',\n",
+       " 'Name2': 'Ethane',\n",
+       " 'betaT': 0.996336508,\n",
+       " 'betaV': 0.997547866,\n",
+       " 'function': 'Methane-Ethane',\n",
+       " 'gammaT': 1.049707697,\n",
+       " 'gammaV': 1.006617867}"
+      ]
+     },
+     "execution_count": 9,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "# Or also by CAS#\n",
+    "params, swap_needed = teqp.get_BIPdep(BIP, ['74-82-8','74-84-0'])\n",
+    "params"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 10,
+   "metadata": {},
+   "outputs": [
+    {
+     "ename": "ValueError",
+     "evalue": "Can't match the binary pair for: 74-82-8/Ethane",
+     "output_type": "error",
+     "traceback": [
+      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
+      "\u001b[1;31mValueError\u001b[0m                                Traceback (most recent call last)",
+      "Input \u001b[1;32mIn [10]\u001b[0m, in \u001b[0;36m<cell line: 2>\u001b[1;34m()\u001b[0m\n\u001b[0;32m      1\u001b[0m \u001b[38;5;66;03m# But mixing is not allowed\u001b[39;00m\n\u001b[1;32m----> 2\u001b[0m params, swap_needed \u001b[38;5;241m=\u001b[39m \u001b[43mteqp\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mget_BIPdep\u001b[49m\u001b[43m(\u001b[49m\u001b[43mBIP\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m[\u001b[49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43m74-82-8\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[43m,\u001b[49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43mEthane\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[43m]\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m      3\u001b[0m params\n",
+      "\u001b[1;31mValueError\u001b[0m: Can't match the binary pair for: 74-82-8/Ethane"
+     ]
+    }
+   ],
+   "source": [
+    "# But mixing is not allowed\n",
+    "params, swap_needed = teqp.get_BIPdep(BIP, ['74-82-8','Ethane'])\n",
+    "params"
+   ]
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3 (ipykernel)",
+   "language": "python",
+   "name": "python3"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 3
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython3",
+   "version": "3.10.4"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
+}