cantera/doc/SConscript
KyleLinevitchJr 43bd96b5ba [Matlab] Make counterflow diffusion flame simulation more general
The CounterFlowDiffusionFlame (CFDF) code is able to perform more general cases
of npflame_init for multiple species fuel and oxidizer streams. The
stoichiometric mixture fraction in the CFDF code uses the Bilger definition of
mixture fraction, using the conservation of elements C, H, and O. This method is
used in the python module, but not the MATLAB npflame_init function.

Also, the CFDF code uses the fuel stream density to calculate the fuel stream
velocity and the oxidizer stream density to calculate the oxidizer stream
velocity, where as the npflame_init code uses the fuel density for both velocity
calculations.

The elementMassFraction code is a MATLAB version of the python function:
elemental_mass_fraction, which is needed to run the CFDF code.

Update the diffflame.m example to use the more general CFDF function since the
input parameters are different than the npflame_init function. This example is
the same as the diffusion_flame.py sample in the Python module.
2017-06-10 16:44:50 -04:00

299 lines
12 KiB
Python

from buildutils import *
import ast
Import('env', 'build', 'install')
localenv = env.Clone()
from collections import namedtuple
Page = namedtuple('Page', ['name', 'title', 'objects'])
def extract_python_docstring(pyfile, summary_only=True):
""" Returns the docstring from a Python script """
with open(pyfile) as f:
mod = ast.parse(f.read())
doc = ''
for node in mod.body:
if isinstance(node, ast.Expr) and isinstance(node.value, ast.Str):
doc = node.value.s
doc = doc.strip()
if summary_only:
doc = doc.split('\n\n')[0]
return doc
def extract_matlab_summary(mfile):
""" Returns a one-line summary comment from a .m file """
doc = ''
with open(mfile) as f:
for line in f:
line = line.strip()
if line.startswith('%'):
doc = line.strip('%').strip()
if doc:
break
name = os.path.basename(mfile)[:-2].replace('_', ' ')
if doc.lower().replace('_', ' ').startswith(name):
doc = doc[len(name):].strip()
return doc
# Set up functions to pseudo-autodoc the MATLAB toolbox
def extract_matlab_docstring(mfile, level):
"""
Return the docstring from mfile, assuming that it consists of the
first uninterrupted comment block.
:param mfile:
File name of the matlab file from which the documentation will be
read
:param level:
Level of documentation. Class = 0, Function = 1
"""
# Set the start of the docstring based on the level passed in. This is only
# necessary for the old-style MATLAB classes, where each method is its own
# file.
if level == 0:
docstring = ".. mat:class:: "
elif level == 1:
docstring = " .. mat:function:: "
else:
print "Unknown level for MATLAB documentation."
sys.exit(1)
# The leader is the number of spaces at the beginning of a regular line
# of documentation.
leader = ' '*4*(level + 1)
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 += leader + 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 += leader + 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']:
docs = build(localenv.Command('#build/docs/doxygen/html/index.html',
'doxygen/Doxyfile', 'doxygen $SOURCE'))
env.Depends(docs, env.Glob('#doc/doxygen/*') +
mglob(env, '#include/cantera', 'h') +
mglob(env, '#include/cantera/*', 'h') +
mglob(env, '#src/cantera/*', 'h', 'cpp'))
env.Alias('doxygen', docs)
install('$inst_docdir/doxygen/html',
mglob(localenv, '#/build/docs/doxygen/html', 'html', 'svg', 'css', 'png'))
if localenv['sphinx_docs']:
localenv['SPHINXBUILD'] = Dir('#build/docs/sphinx')
localenv['SPHINXSRC'] = Dir('sphinx')
sphinxdocs = build(localenv.Command('${SPHINXBUILD}/html/index.html',
'sphinx/conf.py',
'${sphinx_cmd} -b html -d ${SPHINXBUILD}/doctrees ${SPHINXSRC} ${SPHINXBUILD}/html'))
env.Alias('sphinx', sphinxdocs)
# Python examples: Create individual documentation pages with the source
# for each example
example_root = Dir('#interfaces/cython/cantera/examples').abspath
indexenv = env.Clone()
for subdir in subdirs(example_root):
summaries = []
for f in mglob(env, pjoin(example_root, subdir), 'py'):
docname = 'examples/{0}_{1}'.format(subdir, f.name[:-3])
summaries.append(':doc:`{0} <{1}>`:'.format(f.name, docname))
summaries.append(extract_python_docstring(f.abspath))
summaries.append('')
tmpenv = env.Clone()
tmpenv['script_name'] = f.name
tmpenv['script_path'] = '../../../../interfaces/cython/cantera/examples/%s/%s' % (subdir, f.name)
b = tmpenv.SubstFile('#doc/sphinx/cython/{0}.rst'.format(docname),
'#doc/sphinx/cython/example-script.rst.in')
build(b)
localenv.Depends(sphinxdocs, b)
indexenv['python_{0}_examples'.format(subdir)] = '\n'.join(summaries)
b = indexenv.SubstFile('#doc/sphinx/cython/examples.rst',
'#doc/sphinx/cython/examples.rst.in')
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'],
'@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',
'1D/CounterFlowDiffusionFlame.m'],
'@Interface': ['importEdge.m', 'importInterface.m'],
'@Data': ['Air.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',
'getDataDirectories.m', 'canteraVersion.m',
'canteraGitCommit.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 = ['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)):
# Get a list of the functions in this class as long as its a file we care about
functions = [name for name in os.listdir(pjoin(base,obj)) if not any(x in name for x in nodoc_matlab_files)]
# Add the docstring for the class name at level 0
class_file = functions.pop(functions.index(obj.split('@')[1]+'.m'))
doc += extract_matlab_docstring(os.path.relpath(pjoin(base,obj,class_file)), 0)
# Get the extra files from the extra dictionary and sort them with
# the regular functions.
extra_files = extra.get(obj,[])
all_files += sorted(functions + extra_files)
else:
all_files = extra.get(obj,[])
for file in all_files:
if file in functions:
doc += extract_matlab_docstring(os.path.relpath(pjoin(base,obj,file)), 1)
else:
doc += extract_matlab_docstring(os.path.relpath(pjoin(base,file)), 1)
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
examples = []
tutorials = []
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')
build(b)
localenv.Depends(sphinxdocs, b)
summary = [':doc:`{0} <examples/{1}>`:'.format(f.name, f.name[:-2]),
extract_matlab_summary(f.abspath),
'']
if f.name.startswith('tut'):
tutorials.extend(summary)
else:
examples.extend(summary)
localenv['matlab_tutorials'] = '\n'.join(tutorials)
localenv['matlab_examples'] = '\n'.join(examples)
b = localenv.SubstFile('#doc/sphinx/matlab/examples.rst',
'#doc/sphinx/matlab/examples.rst.in')
build(b)
localenv.Depends(sphinxdocs, b)
localenv.AlwaysBuild(sphinxdocs)