diff --git a/.gitignore b/.gitignore
index eb1c8849a..4788806e8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -35,4 +35,6 @@ config.log
coverage/
coverage.info
doc/sphinx/cython/examples
-doc/sphinx/matlab/examples
+doc/sphinx/matlab/examples/*.rst
+doc/sphinx/matlab/tutorials/*.rst
+doc/sphinx/matlab/code-docs/*.rst
diff --git a/SConstruct b/SConstruct
index 385cc473e..17d6a94a5 100644
--- a/SConstruct
+++ b/SConstruct
@@ -55,6 +55,8 @@ if 'clean' in COMMAND_LINE_TARGETS:
removeFile('include/cantera/base/config.h')
removeFile('ext/f2c_libs/arith.h')
removeDirectory('doc/sphinx/matlab/examples')
+ removeDirectory('doc/sphinx/matlab/tutorials')
+ removeDirectory('doc/sphinx/matlab/code-docs')
removeDirectory('doc/sphinx/cython/examples')
for name in os.listdir('.'):
if name.endswith('.msi'):
diff --git a/doc/SConscript b/doc/SConscript
index 3f80d9a94..a6fc29ed8 100644
--- a/doc/SConscript
+++ b/doc/SConscript
@@ -4,6 +4,70 @@ Import('env', 'build', 'install')
localenv = env.Clone()
+from collections import namedtuple
+Page = namedtuple('Page', ['name', 'title', 'objects'])
+
+# Set up functions to pseudo-autodoc the MATLAB toolbox
+def extract_matlab_docstring(mfile):
+ """
+ Return the docstring from mfile, assuming that it consists of the
+ first uninterrupted comment block.
+ """
+ docstring = ".. mat:function:: "
+ with open(mfile, 'r') as in_file:
+ # The function name is read from the first line
+ docstring += get_function_name(in_file.readline()) + '\n'
+
+ # By convention, the second line (called H1 in the MATLAB documentation)
+ # is read by various MATLAB functions, so it should be in the format
+ # MATLAB expects - FUNCTIONNAME Summary. We read in this line and
+ # add the summary to the docstring. If the line doesn't match the
+ # format, just write it to the docstring as is.
+ line = in_file.readline()
+ try:
+ docstring += ' ' + line.split(' ')[1] + '\n'
+ except IndexError:
+ docstring += line + '\n'
+
+ # Skip the next line, which is a duplicate of the first. It is here
+ # because MATLAB doesn't show the function definition in its help.
+ in_file.readline()
+
+ # For the rest of the lines in the file, get the line if it is
+ # in the first unbroken comment section and add it to the docstring.
+ for line in in_file.readlines():
+ try:
+ if line.lstrip().startswith('%'):
+ docstring += ' '*4 + line.lstrip()[2:-1] + '\n'
+ else:
+ break
+ except IndexError:
+ docstring += '\n'
+
+ return docstring + '\n'
+
+def get_function_name(str):
+ """
+ Return the function or classdef signature, assuming that
+ the string starts with either 'function ' or 'classdef '.
+ """
+ if str.startswith('function '):
+ sig = str[len('function '):]
+ elif str.startswith('classdef '):
+ sig = str[len('classdef '):]
+ else:
+ print "Unknown function declaration in MATLAB document", str
+
+ # Split the function signature on the equals sign, if it exists.
+ # We don't care about what comes before the equals sign, since
+ # if a function returns, the docs will tell us. If there is no
+ # =, return the whole signature.
+ if '=' in sig:
+ idx = sig.index('=')
+ return sig[idx+2:]
+ else:
+ return sig
+
if localenv['doxygen_docs']:
if localenv['graphvizdir']:
localenv.Append(PATH=localenv['graphvizdir'])
@@ -36,14 +100,112 @@ if localenv['sphinx_docs']:
build(b)
localenv.Depends(sphinxdocs, b)
+ # Create a list of MATLAB classes to document. This uses the NamedTuple
+ # structure defined at the top of the file. The @Data and @Utilities
+ # classes are fake classes for the purposes of documentation only. Each
+ # Page represents one html page of the documentation.
+ pages = [
+ Page('importing', 'Importing Phase Objects',
+ ['@Solution', '@Mixture',]
+ ),
+ Page('thermodynamics', 'Thermodynamic Properties',
+ ['@ThermoPhase']
+ ),
+ Page('kinetics', 'Chemical Kinetics', ['@Kinetics']),
+ Page('transport', 'Transport Properties', ['@Transport']),
+ Page('zero-dim', 'Zero-Dimensional Reactor Networks',
+ ['@Func', '@Reactor', '@ReactorNet', '@FlowDevice', '@Wall']
+ ),
+ Page('one-dim', 'One-Dimensional Reacting Flows',
+ ['1D/@Domain1D', '1D/@Stack']
+ ),
+ Page('data', 'Built-In Thermochemical Data',
+ ['@Data']
+ ),
+ Page('utilities', 'Utility Functions',
+ ['@Utilities', '@XML_Node']
+ ),
+ Page('interface', 'Interfaces', ['@Interface']),
+ ]
+
+ # Create a dictionary of extra files associated with each class. These
+ # files are listed relative to the top directory interfaces/matlab/cantera
+ extra = {
+ '@Solution': ['IdealGasMix.m', 'importPhase.m',],
+ '@Func': ['gaussian.m', 'polynom.m'],
+ '@Reactor': ['ConstPressureReactor.m',
+ 'FlowReactor.m', 'IdealGasConstPressureReactor.m',
+ 'IdealGasReactor.m', 'Reservoir.m'],
+ '@FlowDevice': ['MassFlowController.m', 'Valve.m'],
+ '1D/@Domain1D': ['1D/AxiStagnFlow.m', '1D/AxisymmetricFlow.m',
+ '1D/Inlet.m', '1D/Outlet.m', '1D/OutletRes.m',
+ '1D/Surface.m', '1D/SymmPlane.m'],
+ '1D/@Stack': ['1D/FreeFlame.m', '1D/npflame_init.m'],
+ '@Interface': ['importEdge.m', 'importInterface.m'],
+ '@Data': ['air.m', 'constants.m', 'gasconstant.m', 'GRI30.m',
+ 'Hydrogen.m', 'Methane.m', 'Nitrogen.m', 'oneatm.m',
+ 'Oxygen.m', 'Water.m'],
+ '@Utilities': ['adddir.m', 'ck2cti.m', 'cleanup.m', 'geterr.m',]
+ }
+
+ # These files do not need to be documented in the MATLAB classes because they
+ # are generics that are overloaded per-class. Since the loop checks for these
+ # strings in each file name, hndl.m is the same as *hndl.m* (to use globbing
+ # notation).
+ nodoc_matlab_files = ['set.m', 'clear.m', 'display.m', 'hndl.m', 'private', 'subsref.m']
+
+ # Loop through the pages list to document each class
+ for page in pages:
+ tempenv = env.Clone()
+
+ # Set the title header
+ title = page.title
+ tempenv['title'] = '='*len(title) + '\n' + title + '\n' + '='*len(title)
+ doc = ''
+
+ # The base directory of the MATLAB toolbox relative to the sphinx build directory
+ base = '../interfaces/matlab/toolbox'
+ for obj in page.objects:
+ all_files = []
+ # Set the subheader based on the class name
+ doc += obj.split('@')[1] + '\n' + '-'*len(obj.split('@')[1]) + '\n\n'
+ if os.path.isdir(pjoin(base,obj)):
+ class_files = os.listdir(pjoin(base,obj))
+ class_files = [name for name in class_files if not any(x in name for x in nodoc_matlab_files)]
+ all_files = [class_files.pop(class_files.index(obj.split('@')[1]+'.m'))]
+ extra_files = extra.get(obj,[])
+ all_files += sorted(class_files + extra_files)
+ class_files.insert(0,all_files[0])
+ else:
+ all_files = extra.get(obj,[])
+ for file in all_files:
+ if file in class_files:
+ doc += extract_matlab_docstring(os.path.relpath(pjoin(base,obj,file)))
+ else:
+ doc += extract_matlab_docstring(os.path.relpath(pjoin(base,file)))
+
+ tempenv['matlab_docstrings'] = doc
+ # Substitute the docstrings into the proper file. Since the docs change
+ # every time the source is changed, we don't want to have to commit the
+ # change in the rst file as well as the source - too much code churn. So
+ # we use a template and a SubstFile directive.
+ c = tempenv.SubstFile('#doc/sphinx/matlab/code-docs/%s.rst' % page.name,
+ '#doc/sphinx/matlab/matlab-template.rst.in')
+ build(c)
+ localenv.Depends(sphinxdocs, c)
+
# Matlab examples: create individual documentation pages with the source
# for each example
for f in mglob(env, '#samples/matlab', 'm'):
tmpenv = env.Clone()
tmpenv['script_name'] = f.name
tmpenv['script_path'] = '../../../../samples/matlab/%s' % f.name
- b = tmpenv.SubstFile('#doc/sphinx/matlab/examples/%s.rst' % f.name[:-2],
- '#doc/sphinx/matlab/example-script.rst.in')
+ if f.name.startswith('tut'):
+ b = tmpenv.SubstFile('#doc/sphinx/matlab/tutorials/%s.rst' % f.name[:-2],
+ '#doc/sphinx/matlab/example-script.rst.in')
+ else:
+ b = tmpenv.SubstFile('#doc/sphinx/matlab/examples/%s.rst' % f.name[:-2],
+ '#doc/sphinx/matlab/example-script.rst.in')
build(b)
localenv.Depends(sphinxdocs, b)
diff --git a/doc/sphinx/compiling.rst b/doc/sphinx/compiling.rst
index 876aac299..5ec0bddfc 100644
--- a/doc/sphinx/compiling.rst
+++ b/doc/sphinx/compiling.rst
@@ -522,6 +522,7 @@ Optional Programs
* `Pygments `_ (install with ``easy_install -U pygments``)
* `pyparsing `_ (install with ``easy_install -U pyparsing``)
* `doxylink `_ (install with ``easy_install sphinxcontrib-doxylink``)
+ * `matlabdomain `_ (install with ``easy_install sphinxcontrib-matlabdomain``)
* `Doxygen `_
diff --git a/doc/sphinx/conf.py b/doc/sphinx/conf.py
index 308d5fbac..9c43fc27d 100644
--- a/doc/sphinx/conf.py
+++ b/doc/sphinx/conf.py
@@ -31,11 +31,19 @@ sys.path.append(os.path.abspath('./exts'))
# Add any Sphinx extension module names here, as strings. They can be extensions
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
-extensions = ['sphinx.ext.autodoc',
+
+# sphinxcontrib.matlab has been added to add the MATLAB domain for the
+# documentation of the MATLAB functions. It is a new requirement to build the
+# documentation in 2.2. It should be loaded before sphinx.ext.autodoc because
+# loading it after gives errors when autodocumenting the Python interface.
+extensions = [
+ 'sphinxcontrib.matlab',
+ 'sphinx.ext.autodoc',
'sphinx.ext.todo',
'sphinx.ext.autosummary',
'mathjax',
- 'sphinxcontrib.doxylink']
+ 'sphinxcontrib.doxylink',
+ ]
# @todo: Sphinx version 1.1 adds support for MathJax, so we can remove the
# custom extension for that once that version becomes more standard
@@ -51,6 +59,10 @@ doxylink = {
'../../doxygen/html/')
}
+# Ensure that the primary domain is the Python domain, since we've added the
+# MATLAB domain with sphinxcontrib.matlab
+primary_domain = 'py'
+
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
diff --git a/doc/sphinx/matlab/examples.rst b/doc/sphinx/matlab/examples.rst
index 62d55ba1a..44a912c7f 100644
--- a/doc/sphinx/matlab/examples.rst
+++ b/doc/sphinx/matlab/examples.rst
@@ -1,6 +1,4 @@
-.. _sec-cython-examples:
-
-.. py:currentmodule:: cantera
+.. _sec-matlab-examples:
Index of Examples
=================
@@ -12,7 +10,7 @@ Tutorials
.. toctree::
:glob:
- examples/tut*
+ tutorials/*
Examples
--------
@@ -20,4 +18,4 @@ Examples
.. toctree::
:glob:
- examples/[!tut]*
+ examples/*
diff --git a/doc/sphinx/matlab/index.rst b/doc/sphinx/matlab/index.rst
index dea27c75c..2052a2139 100644
--- a/doc/sphinx/matlab/index.rst
+++ b/doc/sphinx/matlab/index.rst
@@ -7,4 +7,13 @@ Matlab Interface User's Guide
:maxdepth: 2
input-tutorial
+ code-docs/importing
+ code-docs/interface
+ code-docs/thermodynamics
+ code-docs/kinetics
+ code-docs/transport
+ code-docs/zero-dim
+ code-docs/one-dim
+ code-docs/data
+ code-docs/utilities
examples
diff --git a/doc/sphinx/matlab/matlab-template.rst.in b/doc/sphinx/matlab/matlab-template.rst.in
new file mode 100644
index 000000000..0c837a738
--- /dev/null
+++ b/doc/sphinx/matlab/matlab-template.rst.in
@@ -0,0 +1,4 @@
+
+@title@
+
+@matlab_docstrings@