[ufl] 02/03: New upstream version 2017.2.0

Drew Parsons dparsons at moszumanska.debian.org
Sat Oct 7 07:25:19 UTC 2017


This is an automated email from the git hooks/post-receive script.

dparsons pushed a commit to branch experimental
in repository ufl.

commit 896e94a9b7232ca6913ea6e49a9b7e90b9c8b71c
Author: Drew Parsons <dparsons at debian.org>
Date:   Sat Oct 7 15:16:45 2017 +0800

    New upstream version 2017.2.0
---
 .circleci/config.yml                           |  22 +
 .mailmap                                       | 132 +++++
 .travis.yml                                    |  18 +
 ChangeLog.rst                                  |  10 +
 bitbucket-pipelines.yml                        |  12 +
 demo/Poisson.ufl                               |   2 +-
 doc/sphinx/source/conf.py                      |   2 +-
 doc/sphinx/source/releases.rst                 |   1 +
 doc/sphinx/source/releases/v2017.1.0.post1.rst |  10 +
 doc/sphinx/source/releases/v2017.2.0.rst       |   8 +
 release.conf                                   |   8 +
 scripts/ufl-convert                            |  11 +-
 setup.cfg                                      |   2 +-
 setup.py                                       |   6 +-
 shippable.yml                                  |  12 +
 test/conftest.py                               |  61 +++
 test/test_algorithms.py                        |  16 +-
 test/test_unicode_convert.py                   |  36 ++
 ufl/__init__.py                                |   7 +-
 ufl/algorithms/__init__.py                     |  20 +-
 ufl/algorithms/apply_geometry_lowering.py      |   4 +-
 ufl/algorithms/compute_form_data.py            |   3 +-
 ufl/algorithms/domain_analysis.py              |  22 +-
 ufl/algorithms/estimate_degrees.py             |  31 +-
 ufl/algorithms/formdata.py                     |   7 +-
 ufl/argument.py                                |   5 -
 ufl/cell.py                                    |  11 +-
 ufl/coefficient.py                             |   5 -
 ufl/constantvalue.py                           |   7 +-
 ufl/core/expr.py                               |  62 ---
 ufl/core/multiindex.py                         |  12 +-
 ufl/core/operator.py                           |   2 +-
 ufl/domain.py                                  |  33 +-
 ufl/finiteelement/elementlist.py               |   6 +-
 ufl/finiteelement/enrichedelement.py           |   2 +-
 ufl/finiteelement/finiteelement.py             |  18 +-
 ufl/finiteelement/finiteelementbase.py         |  18 +-
 ufl/finiteelement/mixedelement.py              |  17 +-
 ufl/finiteelement/restrictedelement.py         |  18 +-
 ufl/form.py                                    |  59 +--
 ufl/formatting/ufl2unicode.py                  | 702 +++++++++++++++++++++++++
 ufl/integral.py                                |  30 +-
 ufl/measure.py                                 |  63 ++-
 ufl/sobolevspace.py                            |   3 +-
 ufl/tensoralgebra.py                           |  29 +-
 ufl/utils/sequences.py                         |  18 +-
 ufl/utils/sorting.py                           |   9 -
 47 files changed, 1267 insertions(+), 325 deletions(-)

diff --git a/.circleci/config.yml b/.circleci/config.yml
new file mode 100644
index 0000000..bd57d63
--- /dev/null
+++ b/.circleci/config.yml
@@ -0,0 +1,22 @@
+version: 2
+jobs:
+  build:
+    docker:
+      - image: circleci/python:3.6
+    working_directory: ~/ufl-test
+    steps:
+      - checkout
+      - run:
+          name: Install dependencies  # Install with sudo as tests not run as superuser in circleci/python
+          command: sudo pip install flake8 numpy pytest six --upgrade
+      - run:
+          name: Install UFL
+          command: pip install --user .
+      - run:
+          name: Run flake8 tests
+          command: python -m flake8 .
+      - run:
+          name: Run unit tests
+          command: python -m pytest --junitxml=test-output test/
+      - store_test_results:
+          path: test-output/
\ No newline at end of file
diff --git a/.mailmap b/.mailmap
new file mode 100644
index 0000000..e267362
--- /dev/null
+++ b/.mailmap
@@ -0,0 +1,132 @@
+Anders Logg <logg at chalmers.se>
+Anders Logg <logg at chalmers.se> <ffc at bamse>
+Anders Logg <logg at chalmers.se> <logg at yavanna>
+Anders Logg <logg at chalmers.se> <logg at aule>
+Anders Logg <logg at chalmers.se> logg <logg at localhost.localdomain>
+Anders Logg <logg at chalmers.se> fenics <devnull at localhost>
+Anders Logg <logg at chalmers.se> hg <hg at numenor>
+Anders Logg <logg at chalmers.se> root <root at leja>
+Anders Logg <logg at chalmers.se> <logg at glaurung>
+Anders Logg <logg at chalmers.se> <logg at simula.no>
+Anders Logg <logg at chalmers.se> <anders.logg+bitbucket at gmail.com>
+Anders Logg <logg at chalmers.se> <logg at olorin>
+Anders Logg <logg at chalmers.se> <logg at tti-c.org>
+Anders Logg <logg at chalmers.se> anders <anders at localhost.localdomain>
+Anders Logg <logg at chalmers.se> logg <devnull at localhost>
+Anders Logg <logg at chalmers.se> logg <logg at bigblue.simula.no>
+Anders Logg <logg at chalmers.se> logg <logg at bunjil.simula.no>
+Anders Logg <logg at chalmers.se> logg <logg at olorin>
+Andy R. Terrel <andy.terrel at gmail.com>
+Andy R. Terrel <andy.terrel at gmail.com> <aterrel at uchicago.edu>
+Andy R. Terrel <andy.terrel at gmail.com> aterrel <devnull at localhost>
+Andy R. Terrel <andy.terrel at gmail.com> <aterrel at dhcp-11-20.cs.uchicago.edu>
+Benjamin Kehlet <benjamik at simula.no>
+Benjamin Kehlet <benjamik at simula.no> <benjamik at benjamik-hp.simula.no>
+Benjamin Kehlet <benjamik at simula.no> <benjamik at ifi.uio.no>
+Benjamin Kehlet <benjamik at simula.no> <benjamik at login.ifi.uio.no>
+Benjamin Kehlet <benjamik at simula.no> <bkehlet at bkehlet-laptop>
+Chris Richardson <chris at bpi.cam.ac.uk>
+Chris Richardson <chris at bpi.cam.ac.uk> chris <chris at Lenovo-Edge.(none)>
+Chris Richardson <chris at bpi.cam.ac.uk> Chris Richardson <bpi.cam.ac.uk>
+Chris Richardson <chris at bpi.cam.ac.uk> root <root at bpi.cam.ac.uk>
+Corrado Maurini <corrado.maurini at upmc.fr>
+Corrado Maurini <corrado.maurini at upmc.fr> <cmaurini at gmail.com>
+Dag Lindbo <dag at csc.kth.se>
+Dag Lindbo <dag at csc.kth.se> dag <dag at dag-laptop>
+Dag Lindbo <dag at csc.kth.se> dag <dag at na55.nada.kth.se>
+David Ham <david.ham at imperial.ac.uk>
+David Ham <david.ham at imperial.ac.uk> david.ham at imperial.ac.uk <>
+Evan Lezar <evanlezar at gmail.com>
+Evan Lezar <evanlezar at gmail.com> <mail at evanlezar.com>
+Evan Lezar <evanlezar at gmail.com> elezar <elezar at labby>
+Evan Lezar <evanlezar at gmail.com> elezar <elezar at lefauve>
+Fredrik Valdmanis <fredrik at valdmanis.com>
+Fredrik Valdmanis <fredrik at valdmanis.com> Fredrik Valdmanis <fredva at ifi.uio.no>
+Garth N. Wells <gnw20 at cam.ac.uk>
+Garth N. Wells <gnw20 at cam.ac.uk> root <root at fornax.esc.cam.ac.uk>
+Garth N. Wells <gnw20 at cam.ac.uk> garth <devnull at localhost>
+Garth N. Wells <gnw20 at cam.ac.uk> <garth at debian.eng.cam.ac.uk>
+Garth N. Wells <gnw20 at cam.ac.uk> <garth at fedora32-virtual>
+Garth N. Wells <gnw20 at cam.ac.uk> <garth at gnw20pc>
+Garth N. Wells <gnw20 at cam.ac.uk> <garth at home-laptop>
+Garth N. Wells <gnw20 at cam.ac.uk> <garth at localhost.localdomain>
+Garth N. Wells <gnw20 at cam.ac.uk> <garth at ubuntu32-virtual>
+Garth N. Wells <gnw20 at cam.ac.uk> <garth at ubuntu.eng.cam.ac.uk>
+Garth N. Wells <gnw20 at cam.ac.uk> <garth at garth-laptop>
+Garth N. Wells <gnw20 at cam.ac.uk> <garth at gnw20pc>
+Garth N. Wells <gnw20 at cam.ac.uk> <g.n.wells at tudelft.nl>
+Garth N. Wells <gnw20 at cam.ac.uk> gnw20 at cam.ac.uk <>
+gideonsimpson <gideonsimpson at dyn-128-59-151-14.dyn.columbia.edu>
+Gustav Magnus Vikström <gustavv at simula.no>
+Gustav Magnus Vikström <gustavv at simula.no> <gustavv at ifi.uio.no>
+Gustav Magnus Vikström <gustavv at simula.no> <gustavv at ifi.uio.no>
+Gustav Magnus Vikström <gustavv at simula.no> <gustavv at utlaan-laptop-1>
+Harish Narayanan <hnarayanan at gmail.com>
+Harish Narayanan <hnarayanan at gmail.com> Harish Narayanan <harish at simula.no>
+Johan Hoffman <jhoffman at csc.kth.se>
+Johan Hoffman <jhoffman at csc.kth.se> hoffman <devnull at localhost>
+Johan Hoffman <jhoffman at csc.kth.se> <jhoffman at na41.nada.kth.se>
+Johan Hoffman <jhoffman at csc.kth.se> <jhoffman at na42.nada.kth.se>
+Ilmar Wilbers <ilmarw at simula.no>
+Ilmar Wilbers <ilmarw at simula.no> <ilmarw at gogmagog.simula.no>
+Ilmar Wilbers <ilmarw at simula.no> <ilmarw at multiboot.local>
+Jack S. Hale <jack.hale at uni.lu>
+Jack S. Hale <jack.hale at uni.lu> <j.hale09 at imperial.ac.uk>
+Johan Hake <hake.dev at gmail.com>
+Johan Hake <hake.dev at gmail.com> <johan.hake at gmail.com>
+Johan Hake <hake.dev at gmail.com> <hake at simula.no>
+Johan Jansson <jjan at csc.kth.se>
+Johan Jansson <jjan at csc.kth.se> johan <johan at localhost.localdomain>
+Johan Jansson <jjan at csc.kth.se> johan <johan at nova>
+Johan Jansson <jjan at csc.kth.se> johanjan <devnull at localhost>
+Johan Jansson <jjan at csc.kth.se> johanjan <johanjan at localhost.localdomain>
+Johan Jansson <jjan at csc.kth.se> Johan Jansson <johanjan at math.chalmers.se>
+Johannes Ring <johannr at simula.no>
+Johannes Ring <johannr at simula.no> <johannr at communalis.simula.no>
+Kent-Andre Mardal <kent-and at simula.no>
+Kent-Andre Mardal <kent-and at simula.no> <kent-and at localhost>
+Kristian B. Ølgaard <k.b.oelgaard at gmail.com>
+Kristian B. Ølgaard <k.b.oelgaard at gmail.com> <k.b.oelgaard at tudelft.nl>
+Kristian B. Ølgaard <k.b.oelgaard at gmail.com> <oelgaard at localhost.localdomain>
+Magnus Vikstrøm <gustavv at ifi.uio.no>
+Marco Morandini <marco.morandini at polimi.it>
+Marco Morandini <marco.morandini at polimi.it> <morandini at aero.polimi.it>
+Marie E. Rognes <meg at simula.no>
+Marie E. Rognes <meg at simula.no> <meg at math.uio.no>
+Marie E. Rognes <meg at simula.no> <meg at meg-laptop>
+Marie E. Rognes <meg at simula.no> Marie E. Rognes (meg at simula.no) <Marie E. Rognes (meg at simula.no)>
+Marie E. Rognes <meg at simula.no> meg at simula.no <>
+Martin Sandve Alnæs <martinal at simula.no>
+Martin Sandve Alnæs <martinal at simula.no> <martinal at localhost>
+Martin Sandve Alnæs <martinal at simula.no> <martinal at martinal-desktop>
+Michele Zaffalon <michele.zaffalon at gmail.com>
+Mikael Mortensen <mikaem at math.uio.no>
+Mikael Mortensen <mikaem at math.uio.no> <mikael.mortensen at gmail.com>
+Nate Sime <njcs4 at cam.ac.uk>
+Nate Sime <njcs4 at cam.ac.uk> <njcs4 at galah.bpi.cam.ac.uk>
+Nuno Lopes <ndl at ptmat.fc.ul.pt>
+Nuno Lopes <ndl at ptmat.fc.ul.pt> N.Lopes <devnull at localhost>
+Patrick Farrell <patrick.farrell at maths.ox.ac.uk>
+Patrick Farrell <patrick.farrell at maths.ox.ac.uk> <patrick.farrell06 at imperial.ac.uk>
+Patrick Farrell <patrick.farrell at maths.ox.ac.uk> <patrick.farrell at imperial.ac.uk>
+Quang Ha <qth20 at cam.ac.uk>
+Kristoffer Selim <selim at simula.no>
+Kristoffer Selim <selim at simula.no> <selim at selim-laptop>
+Simon Funke <simon at simula.no>
+Simon Funke <simon at simula.no> <simon.funke at gmail.com>
+Simon Funke <simon at simula.no> <s.funke09 at imperial.ac.uk>
+Solveig Bruvoll <solveio at ifi.uio.no>
+Solveig Masvie <smasvie at gmail.com>
+Steven Vandekerckhove <steven.vandekerckhove at kuleuven-kulak.be>
+Steven Vandekerckhove <steven.vandekerckhove at kuleuven-kulak.be> <Steven.Vandekerckhove at kuleuven-kulak.be>
+stockli <stockli at carleman.nada.kth.se>
+stockli <stockli at carleman.nada.kth.se> <stockli at localhost>
+Tianyi Li <tianyikillua at gmail.com>
+Steffen Müthing <steffen.muething at ipvs.uni-stuttgart.de>
+Steffen Müthing <steffen.muething at ipvs.uni-stuttgart.de> Steffen Müthing steffen.muething at ipvs.uni-stuttgart.de <>
+Miklós Homolya <m.homolya14 at imperial.ac.uk>
+Åsmund Ødegård <aasmund at simula.no>
+Åsmund Ødegård <aasmund at simula.no> <aasmundo at manpower.local>
+Ola Skavhaug <skavhaug at simula.no>
+Andre Massing <massing at simula.no>
+Andrew McRae <a.mcrae12 at imperial.ac.uk>
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..9caaa01
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,18 @@
+notifications:
+  slack:
+    secure: r9t4/J1Y85Tv/D/bJBVPGaW153mSU+I5r+EZrq7E6HnYqFRQ0X0AJw0pUX1ap/MXGtSrHFmQRYmIN1TTUMLPAHlzF4YA4rFA1LxJxxlPQ1TuhmkLbu5DEf5qkSsVeOjIe8+ptx8E8rSUSVfzv1g/RpdgO6mOD6qge94gQhQ4XekrWkZE1OudkqPvHfznCvxtFs3T5dxZWX3suQsGA+l1Gn3gp2YFITTGEfCXFek+rRWL3aqn6Oq+nvI6jfrooei7NSmNQOpGawAW868uvOlWQ2NiH1nsUkOH9U9c4YpTPD7RLLc2r3pAisSzdhZ5LsT+9o+lycqJleGizcVqtF7BP0tBzc1G4uJQwOCCcypLG62VwuwF6HD3zx4bDXD2MTqEneUaLVNlT2JiAsXmg2RKn/z8+5OKrPNe23pDvD3h+NdtHzJxPSnSRynpKD1FBXh1tfiGSVvgY+Mm7Bdu1unuK2tbpb6OLhcq [...]
+language: python
+python:
+  - "2.7"
+  - "3.4"
+
+before_install:
+  - pip install flake8
+  - pip install pytest
+
+install:
+  - pip install .
+
+script:
+  - flake8 ufl/
+  - py.test test/
diff --git a/ChangeLog.rst b/ChangeLog.rst
index caa2be5..bbfa1a9 100644
--- a/ChangeLog.rst
+++ b/ChangeLog.rst
@@ -1,6 +1,16 @@
 Changelog
 =========
 
+2017.2.0 (2017-09-15)
+---------------------
+
+- Testing release method
+
+2017.1.0.post1 (2017-09-12)
+---------------------------
+
+- Change PyPI package name to fenics-ufl.
+
 2017.1.0 (2017-05-09)
 ---------------------
 
diff --git a/bitbucket-pipelines.yml b/bitbucket-pipelines.yml
new file mode 100644
index 0000000..c47e4d5
--- /dev/null
+++ b/bitbucket-pipelines.yml
@@ -0,0 +1,12 @@
+image: quay.io/fenicsproject/pipelines
+
+pipelines:
+  default:
+    - step:
+        script:
+          - pip2 install .
+          - pip3 install .
+          - python2 -m flake8 ufl/
+          - python3 -m flake8 ufl/
+          - python2 -m pytest -v test/
+          - python3 -m pytest -v test/
diff --git a/demo/Poisson.ufl b/demo/Poisson.ufl
index 0a09bc1..23f81ed 100644
--- a/demo/Poisson.ufl
+++ b/demo/Poisson.ufl
@@ -28,5 +28,5 @@ u = TrialFunction(element)
 v = TestFunction(element)
 f = Coefficient(element)
 
-a = dot(grad(v), grad(u))*dx(degree=1)
+a = inner(grad(v), grad(u))*dx(degree=1)
 L = v*f*dx(degree=2)
diff --git a/doc/sphinx/source/conf.py b/doc/sphinx/source/conf.py
index d8a68e5..f89d46c 100644
--- a/doc/sphinx/source/conf.py
+++ b/doc/sphinx/source/conf.py
@@ -58,7 +58,7 @@ project = u'Unified Form Language (UFL)'
 this_year = datetime.date.today().year
 copyright = u'%s, FEniCS Project' % this_year
 author = u'FEniCS Project'
-version = pkg_resources.get_distribution("ufl").version
+version = pkg_resources.get_distribution("fenics-ufl").version
 release = version
 
 # The language for content autogenerated by Sphinx. Refer to documentation
diff --git a/doc/sphinx/source/releases.rst b/doc/sphinx/source/releases.rst
index ee8b507..4758874 100644
--- a/doc/sphinx/source/releases.rst
+++ b/doc/sphinx/source/releases.rst
@@ -9,6 +9,7 @@ Release notes
    :maxdepth: 2
 
    releases/next
+   releases/v2017.1.0.post1
    releases/v2017.1.0
    releases/v2016.2.0
    releases/v2016.1.0
diff --git a/doc/sphinx/source/releases/v2017.1.0.post1.rst b/doc/sphinx/source/releases/v2017.1.0.post1.rst
new file mode 100644
index 0000000..8bbdad8
--- /dev/null
+++ b/doc/sphinx/source/releases/v2017.1.0.post1.rst
@@ -0,0 +1,10 @@
+=================================
+Changes in version 2017.1.0.post1
+=================================
+
+UFL 2017.1.0.post1 was released on 2017-09-12.
+
+Summary of changes
+==================
+
+- Change PyPI package name to fenics-ufl.
diff --git a/doc/sphinx/source/releases/v2017.2.0.rst b/doc/sphinx/source/releases/v2017.2.0.rst
new file mode 100644
index 0000000..ea1fb3f
--- /dev/null
+++ b/doc/sphinx/source/releases/v2017.2.0.rst
@@ -0,0 +1,8 @@
+===========================
+Changes in version 2017.2.0
+===========================
+
+UFL 2017.2.0 was released on 2017-09-15.
+
+Summary of changes
+==================
diff --git a/release.conf b/release.conf
new file mode 100644
index 0000000..cfe8c7c
--- /dev/null
+++ b/release.conf
@@ -0,0 +1,8 @@
+# Configuration file for fenics-release
+
+PACKAGE="ufl"
+BRANCH="master"
+FILES="ChangeLog.rst \
+       setup.py \
+       doc/sphinx/source/releases/next.rst \
+       doc/sphinx/source/releases.rst"
diff --git a/scripts/ufl-convert b/scripts/ufl-convert
index 8da9b9d..567bd56 100755
--- a/scripts/ufl-convert
+++ b/scripts/ufl-convert
@@ -11,6 +11,7 @@ from pprint import pprint
 from ufl.algorithms import tree_format, compute_form_data
 from ufl.formatting.ufl2dot import ufl2dot
 from ufl.formatting.ufl2latex import forms2latexdocument
+from ufl.formatting.ufl2unicode import form2unicode
 from ufl.algorithms.formfiles import load_ufl_file
 
 # --- Utilities
@@ -62,7 +63,7 @@ option_list = [ \
     opt("labeling",  "l", "str", "repr", "Set to 'repr' or 'compact' for different naming of graph nodes."),
     opt("compile",   "c", "int", 0, "'Compile' forms: apply expression transformations like in a quadrature based form compilation. Only used for latex formatting."),
     # Output formats:
-    opt("format",    "f", "str", "", "Rendering format (str, repr, tree, dot, latex)."),
+    opt("format",    "f", "str", "", "Rendering format (str, repr, tree, dot, latex, unicode)."),
     opt("filetype",  "t", "str", "", "Output file type (txt, py, dot, tex, ps, pdf, png)."),
     ]
 
@@ -105,6 +106,12 @@ for arg in args:
         format = "tex"
     if format == "tex":
         rendered = forms2latexdocument(forms, uflfilename, compile=options.compile)
+    elif format == "unicode":
+        data = []
+        for form, form_data in zip(forms, form_datas):
+            tmp = form2unicode(form, form_data)
+            data.append(tmp)
+        rendered = "\n\n".join(data)
     elif format in ("str", "repr", "tree"):
         data = []
         for i, fd in enumerate(form_datas):
@@ -154,6 +161,8 @@ for arg in args:
             filetype = "dot"
         elif format == "tex":
             filetype = "tex"
+        elif format == "unicode":
+            filetype = "txt"
 
     # Guess that the filetype is the ext, usually the case
     ext = filetype
diff --git a/setup.cfg b/setup.cfg
index f597cbd..2d79134 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,3 +1,3 @@
 [flake8]
-ignore = E305,E501,E226,W503,E127,E123,E128,E265
+ignore = E501,E226
 exclude = .git,__pycache__,doc/sphinx/source/conf.py,build,dist,test
diff --git a/setup.py b/setup.py
index dd79757..4d65742 100755
--- a/setup.py
+++ b/setup.py
@@ -12,12 +12,12 @@ if sys.version_info < (2, 7):
     print("Python 2.7 or higher required, please upgrade.")
     sys.exit(1)
 
-version = "2017.1.0"
+version = "2017.2.0"
 
 url = "https://bitbucket.org/fenics-project/%s/" % module_name
 tarball = None
 if 'dev' not in version:
-    tarball = url + "downloads/%s-%s.tar.gz" % (module_name, version)
+    tarball = url + "downloads/fenics-%s-%s.tar.gz" % (module_name, version)
 
 script_names = ("ufl-analyse", "ufl-convert", "ufl-version", "ufl2py")
 
@@ -57,7 +57,7 @@ Topic :: Scientific/Engineering :: Mathematics
 Topic :: Software Development :: Libraries :: Python Modules
 """
 
-setup(name="UFL",
+setup(name="fenics-ufl",
       version=version,
       description="Unified Form Language",
       author="Martin Sandve Alnæs, Anders Logg",
diff --git a/shippable.yml b/shippable.yml
new file mode 100644
index 0000000..ab12e61
--- /dev/null
+++ b/shippable.yml
@@ -0,0 +1,12 @@
+language: python
+python:
+  - 2.7
+  - 3.6
+
+build:
+  ci:
+  - pip install --upgrade pip
+  - pip install flake8 pytest
+  - pip install .
+  - python -m flake8 ufl/
+  - python -m pytest -v test/
diff --git a/test/conftest.py b/test/conftest.py
index aeb0dd3..489ffd4 100644
--- a/test/conftest.py
+++ b/test/conftest.py
@@ -2,9 +2,14 @@
 
 import pytest
 
+import os
+import glob
+
 import ufl
 from ufl import as_ufl, inner, dx
 from ufl.algorithms import compute_form_data
+from ufl.algorithms.formfiles import load_ufl_file
+
 
 
 class Tester:
@@ -56,9 +61,65 @@ class Tester:
 def self():
     return Tester()
 
+
+def testspath():
+    return os.path.abspath(os.path.dirname(__file__))
+
+
+def filespath():
+    return os.path.abspath(os.path.join(testspath(), "../demo"))
+
+
+class UFLTestDataBase(object):
+    def __init__(self):
+        self.filespath = filespath()
+        self.cache = {}
+        self.names = glob.glob(os.path.join(self.filespath, "*.ufl"))
+        # Filter out files startign with udnerscore
+        self.names = [n for n in self.names
+                      if not os.path.basename(n).startswith('_')]
+
+    def __len__(self):
+        return len(self.names)
+
+    def __iter__(self):
+        return self.keys()
+
+    def __hasitem__(self, name):
+        return names in self.names
+
+    def keys(self):
+        return iter(self.names)
+
+    def values(self):
+        return (self[name] for name in self.names)
+
+    def items(self):
+        return ((name, self[name]) for name in self.names)
+
+    def __getitem__(self, name):
+        if isinstance(name, int):
+            name = self.names[name]
+        # Cache file reads
+        content = self.cache.get(name)
+        if content is None:
+            content = load_ufl_file(name)
+            self.cache[name] = content
+        return content
+
+_db = UFLTestDataBase()
+
+
+def _example_paths():
+    return _db.values()
+
 _all_cells = [ufl.interval, ufl.triangle, ufl.tetrahedron]
 
 
 @pytest.fixture(params=_all_cells)
 def cell(request):
     return request.param
+
+ at pytest.fixture(params=_example_paths())
+def example_files(request):
+    return request.param
diff --git a/test/test_algorithms.py b/test/test_algorithms.py
index 315be4d..b5484de 100755
--- a/test/test_algorithms.py
+++ b/test/test_algorithms.py
@@ -9,11 +9,14 @@ __date__ = "2008-03-12 -- 2009-01-28"
 import pytest
 from pprint import *
 
-from ufl import *
-from ufl.algorithms import *
-from ufl.classes import Sum, Product
-
-from ufl.corealg.traversal import *
+from ufl import (FiniteElement, TestFunction, TrialFunction, triangle,
+                 div, grad, Argument, dx, adjoint, Coefficient,
+                 FacetNormal, inner, dot, ds)
+from ufl.algorithms import (extract_arguments, expand_derivatives,
+                            expand_indices, extract_elements,
+                            extract_unique_elements, extract_coefficients)
+from ufl.corealg.traversal import (pre_traversal, post_traversal,
+                                   unique_pre_traversal, unique_post_traversal)
 
 # TODO: add more tests, covering all utility algorithms
 
@@ -84,7 +87,8 @@ def test_pre_and_post_traversal():
     s = p1 + p2
 
     # NB! These traversal algorithms are intended to guarantee only
-    # parent before child and vice versa, not this particular ordering:
+    # parent before child and vice versa, not this particular
+    # ordering:
     assert list(pre_traversal(s)) == [s, p2, g, v, p1, f, v]
     assert list(post_traversal(s)) == [g, v, p2, f, v, p1, s]
     assert list(unique_pre_traversal(s)) == [s, p2, g, v, p1, f]
diff --git a/test/test_unicode_convert.py b/test/test_unicode_convert.py
new file mode 100644
index 0000000..c9eccbd
--- /dev/null
+++ b/test/test_unicode_convert.py
@@ -0,0 +1,36 @@
+
+from six import text_type
+
+from ufl.algorithms import compute_form_data
+from ufl.formatting.ufl2unicode import form2unicode
+
+
+def valid_forms(forms_list):
+    forms = []
+    form_datas = []
+    for f in forms_list:
+        fd = None
+        try:
+            fd = compute_form_data(f)
+        except:
+            fd = None
+        if fd is not None:
+            forms.append(f)
+            form_datas.append(fd)
+    return forms, form_datas
+
+
+def test_convert_examples(example_files):
+    # Get example forms that can be analysed 
+    forms, form_datas = valid_forms(example_files.forms)
+    if not forms:
+        return
+
+    # Mainly tests for execution without errors
+    data = []
+    for form, form_data in zip(forms, form_datas):
+        tmp = form2unicode(form, form_data)
+        data.append(tmp)
+    rendered = u"\n\n".join(data)
+    assert isinstance(rendered, text_type)
+    assert len(rendered)
diff --git a/ufl/__init__.py b/ufl/__init__.py
index 674c14c..b043db5 100644
--- a/ufl/__init__.py
+++ b/ufl/__init__.py
@@ -9,8 +9,7 @@ notation close to the mathematical one.
 This Python module contains the language as well as algorithms to work
 with it.
 
-* To import the language, type
-::
+* To import the language, type::
 
     from ufl import *
 
@@ -244,7 +243,7 @@ A very brief overview of the language contents follows:
 
 import pkg_resources
 
-__version__ = pkg_resources.get_distribution("ufl").version
+__version__ = pkg_resources.get_distribution("fenics-ufl").version
 
 ########## README
 # Imports here should be what the user sees when doing "from ufl import *",
@@ -419,4 +418,4 @@ __all__ = as_native_strings([
     'quadrilateral', 'hexahedron', 'facet',
     'i', 'j', 'k', 'l', 'p', 'q', 'r', 's',
     'e', 'pi',
-    ])
+])
diff --git a/ufl/algorithms/__init__.py b/ufl/algorithms/__init__.py
index fab1ff4..fb751c0 100644
--- a/ufl/algorithms/__init__.py
+++ b/ufl/algorithms/__init__.py
@@ -64,17 +64,17 @@ __all__ = as_native_strings([
     "compute_form_functional",
     "compute_form_signature",
     "tree_format",
-    ])
+])
 
 # Utilities for traversing over expression trees in different ways
-#from ufl.algorithms.traversal import iter_expressions
+# from ufl.algorithms.traversal import iter_expressions
 
 # Keeping these imports here for backwards compatibility, doesn't cost
 # anything.  Prefer importing from ufl.corealg.traversal in future
 # code.
-#from ufl.corealg.traversal import pre_traversal
+# from ufl.corealg.traversal import pre_traversal
 from ufl.corealg.traversal import post_traversal
-#from ufl.corealg.traversal import traverse_terminals, traverse_unique_terminals
+# from ufl.corealg.traversal import traverse_terminals, traverse_unique_terminals
 
 
 # Utilities for extracting information from forms and expressions
@@ -87,11 +87,11 @@ from ufl.algorithms.analysis import (
     extract_unique_elements,
     extract_sub_elements,
     sort_elements,
-    )
+)
 
 
 # Preprocessing a form to extract various meta data
-#from ufl.algorithms.formdata import FormData
+# from ufl.algorithms.formdata import FormData
 from ufl.algorithms.compute_form_data import compute_form_data
 
 # Utilities for checking properties of forms
@@ -103,14 +103,14 @@ from ufl.algorithms.checks import validate_form
 # Utilites for modifying expressions and forms
 from ufl.corealg.multifunction import MultiFunction
 from ufl.algorithms.transformer import Transformer, ReuseTransformer
-#from ufl.algorithms.transformer import is_post_handler
+# from ufl.algorithms.transformer import is_post_handler
 from ufl.algorithms.transformer import apply_transformer
 from ufl.algorithms.transformer import strip_variables
-#from ufl.algorithms.replace import Replacer
+# from ufl.algorithms.replace import Replacer
 from ufl.algorithms.replace import replace
 from ufl.algorithms.change_to_reference import change_to_reference_grad
 from ufl.algorithms.expand_compounds import expand_compounds
-#from ufl.algorithms.estimate_degrees import SumDegreeEstimator
+# from ufl.algorithms.estimate_degrees import SumDegreeEstimator
 from ufl.algorithms.estimate_degrees import estimate_total_polynomial_degree
 from ufl.algorithms.expand_indices import expand_indices, purge_list_tensors
 
@@ -134,6 +134,6 @@ from ufl.algorithms.formfiles import load_ufl_file
 from ufl.algorithms.formfiles import load_forms
 
 # Utilities for UFL object printing
-#from ufl.formatting.printing import integral_info, form_info
+# from ufl.formatting.printing import integral_info, form_info
 from ufl.formatting.printing import tree_format
 from ufl.formatting.ufl2latex import ufl2latex
diff --git a/ufl/algorithms/apply_geometry_lowering.py b/ufl/algorithms/apply_geometry_lowering.py
index ff4fdfc..8b10a97 100644
--- a/ufl/algorithms/apply_geometry_lowering.py
+++ b/ufl/algorithms/apply_geometry_lowering.py
@@ -135,7 +135,8 @@ class GeometryLoweringApplier(MultiFunction):
 
         domain = o.ufl_domain()
         FJ = self.facet_jacobian(FacetJacobian(domain))
-        # This could in principle use preserve_types[JacobianDeterminant] with minor refactoring:
+        # This could in principle use
+        # preserve_types[JacobianDeterminant] with minor refactoring:
         return inverse_expr(FJ)
 
     @memoized_handler
@@ -144,7 +145,6 @@ class GeometryLoweringApplier(MultiFunction):
             return o
 
         domain = o.ufl_domain()
-        #tdim = domain.topological_dimension()
         FJ = self.facet_jacobian(FacetJacobian(domain))
         detFJ = determinant_expr(FJ)
 
diff --git a/ufl/algorithms/compute_form_data.py b/ufl/algorithms/compute_form_data.py
index f700bf7..55b06ac 100644
--- a/ufl/algorithms/compute_form_data.py
+++ b/ufl/algorithms/compute_form_data.py
@@ -23,6 +23,7 @@ raw input form given by a user."""
 from itertools import chain
 
 from ufl.log import error, info
+from ufl.utils.sequences import max_degree
 
 from ufl.classes import GeometricFacetQuantity, Coefficient, Form
 from ufl.corealg.traversal import traverse_unique_terminals
@@ -55,7 +56,7 @@ def _auto_select_degree(elements):
     """
     # Use max degree of all elements, at least 1 (to work with
     # Lagrange elements)
-    return max({e.degree() for e in elements} - {None} | {1})
+    return max_degree({e.degree() for e in elements} - {None} | {1})
 
 
 def _compute_element_mapping(form):
diff --git a/ufl/algorithms/domain_analysis.py b/ufl/algorithms/domain_analysis.py
index a5ed720..416f087 100644
--- a/ufl/algorithms/domain_analysis.py
+++ b/ufl/algorithms/domain_analysis.py
@@ -18,8 +18,8 @@
 # You should have received a copy of the GNU Lesser General Public License
 # along with UFL. If not, see <http://www.gnu.org/licenses/>.
 
-# import six
 from collections import defaultdict
+from six import iteritems
 from six.moves import zip
 
 import ufl
@@ -28,20 +28,22 @@ from ufl.utils.py23 import as_native_strings
 from ufl.integral import Integral
 from ufl.form import Form
 from ufl.sorting import cmp_expr, sorted_expr
-from ufl.utils.sorting import canonicalize_metadata, sorted_by_key, sorted_by_tuple_key
+from ufl.utils.sorting import canonicalize_metadata, sorted_by_key
 import numbers
 
 
-# @six.python_2_unicode_compatible
 class IntegralData(object):
-    """Utility class with the members
-        (domain, integral_type, subdomain_id, integrals, metadata)
+    """Utility class with the members (domain, integral_type,
+        subdomain_id, integrals, metadata)
 
     where metadata is an empty dictionary that may be used for
     associating metadata with each object.
+
     """
-    __slots__ = as_native_strings(('domain', 'integral_type', 'subdomain_id', 'integrals',
-                 'metadata', 'integral_coefficients', 'enabled_coefficients'))
+    __slots__ = as_native_strings(('domain', 'integral_type', 'subdomain_id',
+                                   'integrals', 'metadata',
+                                   'integral_coefficients',
+                                   'enabled_coefficients'))
 
     def __init__(self, domain, integral_type, subdomain_id, integrals,
                  metadata):
@@ -274,8 +276,12 @@ def build_integral_data(integrals):
 
     # Build list with canonical ordering, iteration over dicts
     # is not deterministic across python versions
+    def keyfunc(item):
+        (d, itype, sid), integrals = item
+        return (d._ufl_sort_key_(), itype, (type(sid).__name__, sid))
+
     integral_datas = []
-    for (d, itype, sid), integrals in sorted_by_tuple_key(itgs):
+    for (d, itype, sid), integrals in sorted(iteritems(itgs), key=keyfunc):
         integral_datas.append(IntegralData(d, itype, sid, integrals, {}))
     return integral_datas
 
diff --git a/ufl/algorithms/estimate_degrees.py b/ufl/algorithms/estimate_degrees.py
index 68715a4..0c400dd 100644
--- a/ufl/algorithms/estimate_degrees.py
+++ b/ufl/algorithms/estimate_degrees.py
@@ -27,6 +27,7 @@ from ufl.integral import Integral
 from ufl.algorithms.multifunction import MultiFunction
 from ufl.corealg.map_dag import map_expr_dags
 from ufl.checks import is_cellwise_constant
+from ufl.constantvalue import IntValue
 
 
 class IrreducibleInt(int):
@@ -221,25 +222,23 @@ class SumDegreeEstimator(MultiFunction):
         return self._add_degrees(v, *ops)
 
     def power(self, v, a, b):
-        """If b is an integer:
+        """If b is a positive integer:
         degree(a**b) == degree(a)*b
         otherwise use the heuristic
-        degree(a**b) == degree(a)*2"""
+        degree(a**b) == degree(a) + 2"""
         f, g = v.ufl_operands
-        try:
-            gi = abs(int(g))
-            if isinstance(a, int):
-                return a*gi
-            else:
-                return tuple(foo*gi for foo in a)
-        except:
-            pass
-        # Something to a non-integer power, this is just a heuristic
-        # with no background
-        if isinstance(a, int):
-            return a*2
-        else:
-            return tuple(foo*2 for foo in a)
+
+        if isinstance(g, IntValue):
+            gi = g.value()
+            if gi >= 0:
+                if isinstance(a, int):
+                    return a*gi
+                else:
+                    return tuple(foo*gi for foo in a)
+
+        # Something to a non-(positive integer) power, e.g. float,
+        # negative integer, Coefficient, etc.
+        return self._add_degrees(v, a, 2)
 
     def atan_2(self, v, a, b):
         """Using the heuristic
diff --git a/ufl/algorithms/formdata.py b/ufl/algorithms/formdata.py
index 445fb3f..2782057 100644
--- a/ufl/algorithms/formdata.py
+++ b/ufl/algorithms/formdata.py
@@ -20,15 +20,13 @@
 #
 # Modified by Anders Logg, 2008.
 
-#import six
 from ufl.utils.formatting import lstr, tstr, estr
 
 
-# @six.python_2_unicode_compatible
 class FormData(object):
-    """
-    Class collecting various information extracted from a Form by
+    """Class collecting various information extracted from a Form by
     calling preprocess.
+
     """
 
     def __init__(self):
@@ -60,7 +58,6 @@ class FormData(object):
         return tstr(geometry + subdomains + functions)
 
 
-# @six.python_2_unicode_compatible
 class ExprData(object):
     """
     Class collecting various information extracted from a Expr by
diff --git a/ufl/argument.py b/ufl/argument.py
index e9f3ac7..0aafa72 100644
--- a/ufl/argument.py
+++ b/ufl/argument.py
@@ -96,11 +96,6 @@ class Argument(FormArgument):
         # use .ufl_function_space().ufl_element() instead.")
         return self._ufl_function_space.ufl_element()
 
-    # def element(self):
-    #    "Deprecated, please use .ufl_function_space().ufl_element() instead."
-    #    deprecate("Argument.element() is deprecated, please use Argument.ufl_element() instead.")
-    #    return self.ufl_element()
-
     def number(self):
         "Return the Argument number."
         return self._number
diff --git a/ufl/cell.py b/ufl/cell.py
index 51f8771..204bb77 100644
--- a/ufl/cell.py
+++ b/ufl/cell.py
@@ -41,11 +41,12 @@ __all_classes__ = as_native_strings(["AbstractCell", "Cell", "TensorProductCell"
 # --- The most abstract cell class, base class for other cell types
 
 class AbstractCell(object):
-    "Representation of an abstract finite element cell with only the dimensions known."
-    __slots__ = as_native_strings((
-        "_topological_dimension",
-        "_geometric_dimension",
-        ))
+    """Representation of an abstract finite element cell with only the
+    dimensions known.
+
+    """
+    __slots__ = as_native_strings(("_topological_dimension",
+                                   "_geometric_dimension"))
 
     def __init__(self, topological_dimension, geometric_dimension):
         # Validate dimensions
diff --git a/ufl/coefficient.py b/ufl/coefficient.py
index 414ef87..c97ab8d 100644
--- a/ufl/coefficient.py
+++ b/ufl/coefficient.py
@@ -84,11 +84,6 @@ class Coefficient(FormArgument):
         "Shortcut to get the finite element of the function space of this coefficient."
         return self._ufl_function_space.ufl_element()
 
-    # def element(self):
-    #    "Deprecated, please use Coefficient.ufl_element() instead."
-    #    deprecate("Coefficient.element() is deprecated, please use Coefficient.ufl_element() instead.")
-    #    return self.ufl_element()
-
     def is_cellwise_constant(self):
         "Return whether this expression is spatially constant over each cell."
         return self.ufl_element().is_cellwise_constant()
diff --git a/ufl/constantvalue.py b/ufl/constantvalue.py
index da5f1fe..37f0e62 100644
--- a/ufl/constantvalue.py
+++ b/ufl/constantvalue.py
@@ -141,8 +141,8 @@ class Zero(ConstantValue):
                     all(isinstance(i, int) for i in index_dimensions)):
                 error("Expecting tuple of integer index dimensions, not %s" % str(index_dimensions))
 
-            # Assuming sorted now to avoid this cost, enable for debuggin:
-            #if sorted(free_indices) != list(free_indices):
+            # Assuming sorted now to avoid this cost, enable for debugging:
+            # if sorted(free_indices) != list(free_indices):
             #    error("Expecting sorted input. Remove this check later for efficiency.")
 
             self.ufl_free_indices = free_indices
@@ -164,8 +164,7 @@ class Zero(ConstantValue):
         r = "Zero(%s, %s, %s)" % (
             repr(self.ufl_shape),
             repr(self.ufl_free_indices),
-            repr(self.ufl_index_dimensions),
-            )
+            repr(self.ufl_index_dimensions))
         return as_native_str(r)
 
     def __eq__(self, other):
diff --git a/ufl/core/expr.py b/ufl/core/expr.py
index a72fb78..42d6e21 100644
--- a/ufl/core/expr.py
+++ b/ufl/core/expr.py
@@ -319,12 +319,6 @@ class Expr(object):
         from ufl.domain import extract_unique_domain
         return extract_unique_domain(self)
 
-    #def is_cellwise_constant(self):  # TODO: Deprecate this and use is_cellwise_constant(expr)
-    #    "Return whether this expression is spatially constant over each cell."
-    #    from ufl.checks import is_cellwise_constant
-    #    deprecate("Expr.is_cellwise_constant() is deprecated, please use is_cellwise_constant(expr) instead.")
-    #    return is_cellwise_constant(self)
-
     # --- Functions for float evaluation ---
 
     def evaluate(self, x, mapping, component, index_values):
@@ -436,67 +430,11 @@ class Expr(object):
 
     # --- Deprecated functions
 
-    #def reconstruct(self, *operands):
-    #    """Return a new object of the same type with new operands.
-    #    Deprecated, please use Expr._ufl_expr_reconstruct_() instead."""
-    #    deprecate("Expr.reconstruct() is deprecated, please use Expr._ufl_expr_reconstruct_() instead.")
-    #    return self._ufl_expr_reconstruct_(*operands)
-
     def geometric_dimension(self):
         "Return the geometric dimension this expression lives in."
         from ufl.domain import find_geometric_dimension
         return find_geometric_dimension(self)
 
-    #def domains(self):
-    #    "Deprecated, please use .ufl_domains() instead."
-    #    deprecate("Expr.domains() is deprecated, please use .ufl_domains() instead.")
-    #    return self.ufl_domains()
-
-    #def cell(self):
-    #    "Deprecated, please use .ufl_domain().ufl_cell() instead."
-    #    deprecate("Expr.cell() is deprecated, please use .ufl_domain() instead.")
-    #    domain = self.ufl_domain()
-    #    return domain.ufl_cell() if domain is not None else None
-
-    #def domain(self):
-    #    "Deprecated, please use .ufl_domain() instead."
-    #    deprecate("Expr.domain() is deprecated, please use .ufl_domain() instead.")
-    #    return self.ufl_domain()
-
-    #def operands(self):
-    #    "Deprecated, please use Expr.ufl_operands instead."
-    #    deprecate("Expr.operands() is deprecated, please use property Expr.ufl_operands instead.")
-    #    return self.ufl_operands
-
-    #def shape(self):
-    #    """Return the tensor shape of the expression.
-    #    Deprecated, please use Expr.ufl_shape instead."""
-    #    deprecate("Expr.shape() is deprecated, please use Expr.ufl_shape instead.")
-    #    return self.ufl_shape
-
-    #def rank(self):
-    #    """Return the tensor rank of the expression.
-    #    Deprecated, please use len(expr.ufl_shape) instead."""
-    #    deprecate("Expr.rank() is deprecated," +
-    #              " please use len(expr.ufl_shape) instead.")
-    #    return len(self.ufl_shape)
-
-    #def free_indices(self):
-    #    "Deprecated, please use property Expr.ufl_free_indices instead."
-    #    from ufl.core.multiindex import Index
-    #    deprecate("Expr.free_indices() is deprecated," +
-    #              " please use property Expr.ufl_free_indices instead.")
-    #    return tuple(Index(count=i) for i in self.ufl_free_indices)
-
-    #def index_dimensions(self):
-    #    "Deprecated, please use property Expr.ufl_index_dimensions instead."
-    #    from ufl.core.multiindex import Index
-    #    from ufl.utils.dicts import EmptyDict
-    #    deprecate("Expr.index_dimensions() is deprecated," +
-    #              " please use property Expr.ufl_index_dimensions instead.")
-    #    idims = {Index(count=i): d for i, d in zip(self.ufl_free_indices, self.ufl_index_dimensions)}
-    #    return idims or EmptyDict
-
 
 # Initializing traits here because Expr is not defined in the class
 # declaration
diff --git a/ufl/core/multiindex.py b/ufl/core/multiindex.py
index 8c8c422..b7dfa97 100644
--- a/ufl/core/multiindex.py
+++ b/ufl/core/multiindex.py
@@ -20,7 +20,6 @@
 #
 # Modified by Massimiliano Leoni, 2016.
 
-#import six
 from six.moves import xrange as range
 
 from ufl.utils.py23 import as_native_str
@@ -46,7 +45,6 @@ class IndexBase(object):
         return str(self).decode("utf-8")
 
 
-# @six.python_2_unicode_compatible
 class FixedIndex(IndexBase):
     """UFL value: An index with a specific value assigned."""
     __slots__ = as_native_strings(("_value", "_hash"))
@@ -91,7 +89,6 @@ class FixedIndex(IndexBase):
         return as_native_str(r)
 
 
-# @six.python_2_unicode_compatible
 class Index(IndexBase):
     """UFL value: An index with no value assigned.
 
@@ -139,7 +136,8 @@ class MultiIndex(Terminal):
             error("Expecting a tuple of indices.")
 
         if all(isinstance(ind, FixedIndex) for ind in indices):
-            # Cache multiindices consisting of purely fixed indices (aka flyweight pattern)
+            # Cache multiindices consisting of purely fixed indices
+            # (aka flyweight pattern)
             key = tuple(ind._value for ind in indices)
             self = MultiIndex._cache.get(key)
             if self is not None:
@@ -147,12 +145,14 @@ class MultiIndex(Terminal):
             self = Terminal.__new__(cls)
             MultiIndex._cache[key] = self
         else:
-            # Create a new object if we have any free indices (too many combinations to cache)
+            # Create a new object if we have any free indices (too
+            # many combinations to cache)
             if not all(isinstance(ind, IndexBase) for ind in indices):
                 error("Expecting only Index and FixedIndex objects.")
             self = Terminal.__new__(cls)
 
-        # Initialize here instead of in __init__ to avoid overwriting self._indices from cached objects
+        # Initialize here instead of in __init__ to avoid overwriting
+        # self._indices from cached objects
         self._init(indices)
         return self
 
diff --git a/ufl/core/operator.py b/ufl/core/operator.py
index f6aef18..228850d 100644
--- a/ufl/core/operator.py
+++ b/ufl/core/operator.py
@@ -58,5 +58,5 @@ class Operator(Expr):
         "Default repr string construction for operators."
         # This should work for most cases
         r = "%s(%s)" % (self._ufl_class_.__name__,
-            ", ".join(repr(op) for op in self.ufl_operands))
+                        ", ".join(repr(op) for op in self.ufl_operands))
         return as_native_str(r)
diff --git a/ufl/domain.py b/ufl/domain.py
index d2bf651..f8d862b 100644
--- a/ufl/domain.py
+++ b/ufl/domain.py
@@ -22,7 +22,6 @@
 # Modified by Kristian B. Oelgaard, 2009
 # Modified by Marie E. Rognes 2012
 
-# import six
 import numbers
 
 from ufl.utils.py23 import as_native_str
@@ -40,7 +39,10 @@ __all_classes__ = as_native_strings(["AbstractDomain", "Mesh", "MeshView", "Tens
 
 
 class AbstractDomain(object):
-    """Symbolic representation of a geometric domain with only a geometric and topological dimension."""
+    """Symbolic representation of a geometric domain with only a geometric
+    and topological dimension.
+
+    """
     def __init__(self, topological_dimension, geometric_dimension):
         # Validate dimensions
         if not isinstance(geometric_dimension, numbers.Integral):
@@ -74,7 +76,6 @@ class AbstractDomain(object):
 #         AbstractDomain.__init__(self, geometric_dimension, geometric_dimension)
 
 
-# @six.python_2_unicode_compatible
 @attach_operators_from_hash_data
 @attach_ufl_id
 class Mesh(AbstractDomain):
@@ -140,23 +141,7 @@ class Mesh(AbstractDomain):
         return (self.geometric_dimension(), self.topological_dimension(),
                 "Mesh", typespecific)
 
-    # Deprecations inherited from Domain
-    #def cell(self):
-    #    deprecate("Mesh.cell() is deprecated, please use .ufl_cell() instead.")
-    #    return self.ufl_cell()
-
-    #def coordinates(self):
-    #    error("Coordinate function support has been removed!\n"
-    #          "Use mesh.ufl_coordinate_element() to get the coordinate element,\n"
-    #          "and SpatialCoordinate(mesh) to represent the coordinate field in a form.")
-
-    #def ufl_coordinates(self):
-    #    error("Coordinate function support has been removed!\n"
-    #          "Use mesh.ufl_coordinate_element() to get the coordinate element,\n"
-    #          "and SpatialCoordinate(mesh) to represent the coordinate field in a form.")
 
-
-# @six.python_2_unicode_compatible
 @attach_operators_from_hash_data
 @attach_ufl_id
 class MeshView(AbstractDomain):
@@ -206,7 +191,6 @@ class MeshView(AbstractDomain):
                 "MeshView", typespecific)
 
 
-# @six.python_2_unicode_compatible
 @attach_operators_from_hash_data
 @attach_ufl_id
 class TensorProductMesh(AbstractDomain):
@@ -278,7 +262,10 @@ _default_domains = {}
 
 
 def default_domain(cell):
-    "Create a singular default Mesh from a cell, always returning the same Mesh object for the same cell."
+    """Create a singular default Mesh from a cell, always returning the
+    same Mesh object for the same cell.
+
+    """
     global _default_domains
     assert isinstance(cell, AbstractCell)
     domain = _default_domains.get(cell)
@@ -316,9 +303,11 @@ def sort_domains(domains):
 
 
 def join_domains(domains):
-    """Take a list of domains and return a tuple with only unique domain objects.
+    """Take a list of domains and return a tuple with only unique domain
+    objects.
 
     Checks that domains with the same id are compatible.
+
     """
     # Use hashing to join domains, ignore None
     domains = set(domains) - set((None,))
diff --git a/ufl/finiteelement/elementlist.py b/ufl/finiteelement/elementlist.py
index e776041..6b7906b 100644
--- a/ufl/finiteelement/elementlist.py
+++ b/ufl/finiteelement/elementlist.py
@@ -26,6 +26,8 @@ elements by calling the function register_element."""
 
 from __future__ import print_function
 
+from numpy import asarray
+
 from ufl.log import warning, error
 from ufl.sobolevspace import L2, H1, H2, HDiv, HCurl, HEin, HDivDiv
 from ufl.utils.formatting import istr
@@ -354,10 +356,10 @@ def canonical_element_description(family, cell, order, form_degree):
             error('Order "%s" invalid for "%s" finite element, '
                   'should be None.' % (order, family))
         kmin, kmax = krange
-        if not (kmin is None or order >= kmin):
+        if not (kmin is None or (asarray(order) >= kmin).all()):
             error('Order "%s" invalid for "%s" finite element.' %
                   (order, family))
-        if not (kmax is None or order <= kmax):
+        if not (kmax is None or (asarray(order) <= kmax).all()):
             error('Order "%s" invalid for "%s" finite element.' %
                   (istr(order), family))
 
diff --git a/ufl/finiteelement/enrichedelement.py b/ufl/finiteelement/enrichedelement.py
index cb702ce..ac047c4 100644
--- a/ufl/finiteelement/enrichedelement.py
+++ b/ufl/finiteelement/enrichedelement.py
@@ -78,7 +78,7 @@ class EnrichedElementBase(FiniteElementBase):
 
         # Cache repr string
         self._repr = as_native_str("%s(%s)" %
-            (class_name, ", ".join(repr(e) for e in self._elements)))
+                                   (class_name, ", ".join(repr(e) for e in self._elements)))
 
     def mapping(self):
         return self._elements[0].mapping()
diff --git a/ufl/finiteelement/finiteelement.py b/ufl/finiteelement/finiteelement.py
index 6cbfb81..a5dcccd 100644
--- a/ufl/finiteelement/finiteelement.py
+++ b/ufl/finiteelement/finiteelement.py
@@ -23,7 +23,6 @@
 # Modified by Anders Logg 2014
 # Modified by Massimiliano Leoni, 2016
 
-# import six
 from ufl.log import error
 from ufl.utils.py23 import as_native_str
 from ufl.utils.py23 import as_native_strings
@@ -35,16 +34,13 @@ from ufl.finiteelement.elementlist import canonical_element_description, simplic
 from ufl.finiteelement.finiteelementbase import FiniteElementBase
 
 
-# @six.python_2_unicode_compatible
 class FiniteElement(FiniteElementBase):
     "The basic finite element class for all simple finite elements."
     # TODO: Move these to base?
-    __slots__ = as_native_strings((
-        "_short_name",
-        "_sobolev_space",
-        "_mapping",
-        "_variant",
-        ))
+    __slots__ = as_native_strings(("_short_name",
+                                   "_sobolev_space",
+                                   "_mapping",
+                                   "_variant"))
 
     def __new__(cls,
                 family,
@@ -169,9 +165,9 @@ class FiniteElement(FiniteElementBase):
         self._short_name = short_name
         self._variant = variant
 
-        # Finite elements on quadrilaterals have an IrreducibleInt as degree
+        # Finite elements on quadrilaterals and hexahedrons have an IrreducibleInt as degree
         if cell is not None:
-            if cell.cellname() == "quadrilateral":
+            if cell.cellname() in ["quadrilateral", "hexahedron"]:
                 from ufl.algorithms.estimate_degrees import IrreducibleInt
                 degree = IrreducibleInt(degree)
 
@@ -193,7 +189,7 @@ class FiniteElement(FiniteElementBase):
         if v is None:
             var_str = ""
         else:
-            var_str = ", variant=%s" % repr(qs)
+            var_str = ", variant=%s" % repr(v)
         self._repr = as_native_str("FiniteElement(%s, %s, %s%s%s)" % (
             repr(self.family()), repr(self.cell()), repr(self.degree()), quad_str, var_str))
         assert '"' not in self._repr
diff --git a/ufl/finiteelement/finiteelementbase.py b/ufl/finiteelement/finiteelementbase.py
index 0b3141c..8752b9c 100644
--- a/ufl/finiteelement/finiteelementbase.py
+++ b/ufl/finiteelement/finiteelementbase.py
@@ -34,16 +34,14 @@ from ufl.cell import AbstractCell, as_cell
 
 class FiniteElementBase(object):
     "Base class for all finite elements."
-    __slots__ = as_native_strings((
-        "_family",
-        "_cell",
-        "_degree",
-        "_quad_scheme",
-        "_value_shape",
-        "_reference_value_shape",
-        "_repr",
-        "__weakref__",
-        ))
+    __slots__ = as_native_strings(("_family",
+                                   "_cell",
+                                   "_degree",
+                                   "_quad_scheme",
+                                   "_value_shape",
+                                   "_reference_value_shape",
+                                   "_repr",
+                                   "__weakref__"))
 
     # TODO: Not all these should be in the base class! In particular
     # family, degree, and quad_scheme do not belong here.
diff --git a/ufl/finiteelement/mixedelement.py b/ufl/finiteelement/mixedelement.py
index 5a54f93..4879076 100644
--- a/ufl/finiteelement/mixedelement.py
+++ b/ufl/finiteelement/mixedelement.py
@@ -31,7 +31,7 @@ from six.moves import xrange as range
 from ufl.log import error
 from ufl.utils.py23 import as_native_strings
 from ufl.permutation import compute_indices
-from ufl.utils.sequences import product
+from ufl.utils.sequences import product, max_degree
 from ufl.utils.dicts import EmptyDict
 from ufl.utils.indexflattening import flatten_multiindex, unflatten_index, shape_to_strides
 from ufl.cell import as_cell
@@ -101,7 +101,7 @@ class MixedElement(FiniteElementBase):
 
         # Initialize element data
         degrees = {e.degree() for e in self._sub_elements} - {None}
-        degree = max(degrees) if degrees else None
+        degree = max_degree(degrees) if degrees else None
         FiniteElementBase.__init__(self, "Mixed", cell, degree, quad_scheme,
                                    value_shape, reference_value_shape)
 
@@ -338,10 +338,14 @@ class VectorElement(MixedElement):
 
 # @six.python_2_unicode_compatible
 class TensorElement(MixedElement):
-    "A special case of a mixed finite element where all elements are equal."
+    """A special case of a mixed finite element where all elements are
+    equal.
+
+    """
     __slots__ = as_native_strings(("_sub_element", "_shape", "_symmetry",
-                 "_sub_element_mapping", "_flattened_sub_element_mapping",
-                 "_mapping"))
+                                   "_sub_element_mapping",
+                                   "_flattened_sub_element_mapping",
+                                   "_mapping"))
 
     def __init__(self, family, cell=None, degree=None, shape=None,
                  symmetry=None, quad_scheme=None):
@@ -392,8 +396,7 @@ class TensorElement(MixedElement):
             if len(i) != len(j):
                 error("Non-matching length of symmetry index tuples.")
             for k in range(len(i)):
-                if not (i[k] >= 0 and j[k] >= 0
-                        and i[k] < shape[k] and j[k] < shape[k]):
+                if not (i[k] >= 0 and j[k] >= 0 and i[k] < shape[k] and j[k] < shape[k]):
                     error("Symmetry dimensions out of bounds.")
 
         # Compute all index combinations for given shape
diff --git a/ufl/finiteelement/restrictedelement.py b/ufl/finiteelement/restrictedelement.py
index 467f993..d82b5cc 100644
--- a/ufl/finiteelement/restrictedelement.py
+++ b/ufl/finiteelement/restrictedelement.py
@@ -22,7 +22,6 @@
 # Modified by Marie E. Rognes 2010, 2012
 # Modified by Massimiliano Leoni, 2016
 
-# import six
 from ufl.utils.py23 import as_native_str
 from ufl.finiteelement.finiteelementbase import FiniteElementBase
 from ufl.log import error
@@ -30,7 +29,6 @@ from ufl.log import error
 valid_restriction_domains = ("interior", "facet", "face", "edge", "vertex")
 
 
-# @six.python_2_unicode_compatible
 class RestrictedElement(FiniteElementBase):
     "Represents the restriction of a finite element to a type of cell entity."
     def __init__(self, element, restriction_domain):
@@ -53,19 +51,16 @@ class RestrictedElement(FiniteElementBase):
             repr(self._element), repr(self._restriction_domain)))
 
     def is_cellwise_constant(self):
-        """Return whether the basis functions of this
-        element is spatially constant over each cell."""
+        """Return whether the basis functions of this element is spatially
+        constant over each cell.
+
+        """
         return self._element.is_cellwise_constant()
 
     def sub_element(self):
         "Return the element which is restricted."
         return self._element
 
-    #def element(self):
-    #    "Deprecated."
-    #    deprecate("RestrictedElement.element() is deprecated, please use .sub_element() instead.")
-    #    return self.sub_element()
-
     def mapping(self):
         return self._element.mapping()
 
@@ -89,8 +84,9 @@ class RestrictedElement(FiniteElementBase):
     def symmetry(self):
         """Return the symmetry dict, which is a mapping :math:`c_0 \\to c_1`
         meaning that component :math:`c_0` is represented by component
-        :math:`c_1`.
-        A component is a tuple of one or more ints."""
+        :math:`c_1`.  A component is a tuple of one or more ints.
+
+        """
         return self._element.symmetry()
 
     def num_sub_elements(self):
diff --git a/ufl/form.py b/ufl/form.py
index e88a288..41ab57a 100644
--- a/ufl/form.py
+++ b/ufl/form.py
@@ -21,11 +21,11 @@
 # Modified by Anders Logg, 2009-2011.
 # Modified by Massimiliano Leoni, 2016.
 
-# import six
 from itertools import chain
 from collections import defaultdict
 
 from ufl.log import error, warning
+from ufl.domain import sort_domains
 from ufl.integral import Integral
 from ufl.checks import is_scalar_constant_expression
 from ufl.equation import Equation
@@ -58,7 +58,7 @@ def _sorted_integrals(integrals):
     all_integrals = []
 
     # Order integrals canonically to increase signature stability
-    for d in sorted(integrals_dict):  # Assuming Domain is sortable
+    for d in sort_domains(integrals_dict):
         for it in sorted(integrals_dict[d]):  # str is sortable
             for si in sorted(integrals_dict[d][it],
                              key=lambda x: (type(x).__name__, x)):  # int/str are sortable
@@ -76,7 +76,6 @@ def _sorted_integrals(integrals):
     return tuple(all_integrals)  # integrals_dict
 
 
-# @six.python_2_unicode_compatible
 class Form(object):
     """Description of a weak form consisting of a sum of integrals over subdomains."""
     __slots__ = (
@@ -142,11 +141,6 @@ class Form(object):
         "Returns whether the form has no integrals."
         return self.integrals() == ()
 
-    #def domains(self):
-    #    "Deprecated, please use .ufl_domains() instead."
-    #    deprecate("Form.domains() is deprecated, please use .ufl_domains() instead.")
-    #    return self.ufl_domains()
-
     def ufl_domains(self):
         """Return the geometric integration domains occuring in the form.
 
@@ -158,27 +152,23 @@ class Form(object):
             self._analyze_domains()
         return self._integration_domains
 
-    #def cell(self):
-    #    "Deprecated, please use .ufl_cell() instead."
-    #    deprecate("Form.cell() is deprecated, please use .ufl_cell() instead.")
-    #    return self.ufl_cell()
-
-    #def domain(self):
-    #    "Deprecated, please use .ufl_domain() instead."
-    #    deprecate("Form.domain() is deprecated, please use .ufl_domain() instead.")
-    #    return self.ufl_domain()
-
     def ufl_cell(self):
-        "Return the single cell this form is defined on, fails if multiple cells are found."
+        """Return the single cell this form is defined on, fails if multiple
+        cells are found.
+
+        """
         return self.ufl_domain().ufl_cell()
 
     def ufl_domain(self):
-        """Return the single geometric integration domain occuring in the form.
+        """Return the single geometric integration domain occuring in the
+        form.
 
         Fails if multiple domains are found.
 
-        NB! This does not include domains of coefficients defined on other
-        meshes, look at form data for that additional information.
+        NB! This does not include domains of coefficients defined on
+        other meshes, look at form data for that additional
+        information.
+
         """
         # Collect all domains
         domains = self.ufl_domains()
@@ -326,11 +316,12 @@ class Form(object):
         return NotImplemented
 
     def __call__(self, *args, **kwargs):
-        """UFL form operator: Evaluate form by replacing arguments and coefficients.
+        """UFL form operator: Evaluate form by replacing arguments and
+        coefficients.
 
-        Replaces form.arguments() with given positional arguments
-        in same number and ordering. Number of positional arguments
-        must be 0 or equal to the number of Arguments in the form.
+        Replaces form.arguments() with given positional arguments in
+        same number and ordering. Number of positional arguments must
+        be 0 or equal to the number of Arguments in the form.
 
         The optional keyword argument coefficients can be set to a dict
         to replace Coefficients with expressions of matching shapes.
@@ -346,6 +337,7 @@ class Form(object):
           M = a(f, f, coefficients={ g: 1 })
 
         Is equivalent to M == grad(f)**2*dx.
+
         """
         repdict = {}
 
@@ -384,7 +376,7 @@ class Form(object):
     def __str__(self):
         "Compute shorter string representation of form. This can be huge for complicated forms."
         # Warning used for making sure we don't use this in the general pipeline:
-        #warning("Calling str on form is potentially expensive and should be avoided except during debugging.")
+        # warning("Calling str on form is potentially expensive and should be avoided except during debugging.")
         # Not caching this because it can be huge
         s = "\n  +  ".join(str(itg) for itg in self.integrals())
         return s or "<empty Form>"
@@ -392,7 +384,7 @@ class Form(object):
     def __repr__(self):
         "Compute repr string of form. This can be huge for complicated forms."
         # Warning used for making sure we don't use this in the general pipeline:
-        #warning("Calling repr on form is potentially expensive and should be avoided except during debugging.")
+        # warning("Calling repr on form is potentially expensive and should be avoided except during debugging.")
         # Not caching this because it can be huge
         itgs = ", ".join(repr(itg) for itg in self.integrals())
         r = "Form([" + itgs + "])"
@@ -491,11 +483,14 @@ def as_form(form):
 
 
 def replace_integral_domains(form, common_domain):  # TODO: Move elsewhere
-    """Given a form and a domain, assign a common integration domain to all integrals.
+    """Given a form and a domain, assign a common integration domain to
+    all integrals.
+
+    Does not modify the input form (``Form`` should always be
+    immutable).  This is to support ill formed forms with no domain
+    specified, sometimes occurring in pydolfin, e.g. assemble(1*dx,
+    mesh=mesh).
 
-    Does not modify the input form (``Form`` should always be immutable).
-    This is to support ill formed forms with no domain specified,
-    sometimes occurring in pydolfin, e.g. assemble(1*dx, mesh=mesh).
     """
     domains = form.ufl_domains()
     if common_domain is not None:
diff --git a/ufl/formatting/ufl2unicode.py b/ufl/formatting/ufl2unicode.py
new file mode 100644
index 0000000..04ece3a
--- /dev/null
+++ b/ufl/formatting/ufl2unicode.py
@@ -0,0 +1,702 @@
+# coding: utf-8
+
+from __future__ import unicode_literals
+from six import unichr
+
+import numbers
+
+import ufl
+from ufl.log import error
+from ufl.corealg.multifunction import MultiFunction
+from ufl.corealg.map_dag import map_expr_dag
+from ufl.core.multiindex import Index, FixedIndex
+from ufl.form import Form
+from ufl.algorithms import compute_form_data
+
+
+class PrecedenceRules(MultiFunction):
+    "An enum-like class for C operator precedence levels."
+    def __init__(self):
+        MultiFunction.__init__(self)
+
+    def highest(self, o):
+        return 0
+    terminal = highest
+    list_tensor = highest
+    component_tensor = highest
+
+    def restricted(self, o):
+        return 5
+    cell_avg = restricted
+    facet_avg = restricted
+
+    def call(self, o):
+        return 10
+    indexed = call
+    min_value = call
+    max_value = call
+    math_function = call
+    bessel_function = call
+
+    def power(self, o):
+        return 12
+
+    def mathop(self, o):
+        return 15
+    derivative = mathop
+    trace = mathop
+    deviatoric = mathop
+    cofactor = mathop
+    skew = mathop
+    sym = mathop
+
+    def not_condition(self, o):
+        return 20
+
+    def product(self, o):
+        return 30
+    division = product
+    # mod = product
+    dot = product
+    inner = product
+    outer = product
+    cross = product
+
+    def add(self, o):
+        return 40
+    # sub = add
+    index_sum = add
+
+    def lt(self, o):
+        return 50
+    le = lt
+    gt = lt
+    ge = lt
+
+    def eq(self, o):
+        return 60
+    ne = eq
+
+    def and_condition(self, o):
+        return 70
+
+    def or_condition(self, o):
+        return 71
+
+    def conditional(self, o):
+        return 72
+
+    def lowest(self, o):
+        return 80
+    operator = lowest
+
+
+_precrules = PrecedenceRules()
+
+
+def precedence(expr):
+    return _precrules(expr)
+
+
+try:
+    import colorama
+    has_colorama = True
+except ImportError:
+    has_colorama = False
+
+
+class UC:
+    "An enum-like class for unicode characters."
+
+    # Letters in this alphabet have contiguous code point numbers
+    bold_math_a = u"𝐚"
+    bold_math_A = u"𝐀"
+
+    thin_space = u"\u2009"
+
+    superscript_plus = u'⁺'
+    superscript_minus = u'⁻'
+    superscript_equals = u'⁼'
+    superscript_left_paren = u'⁽'
+    superscript_right_paren = u'⁾'
+    superscript_digits = ["⁰", "¹", "²", "³", "⁴", "⁵", "⁶", "⁷", "⁸", "⁹"]
+
+    subscript_plus = u'₊'
+    subscript_minus = u'₋'
+    subscript_equals = u'₌'
+    subscript_left_paren = u'₍'
+    subscript_right_paren = u'₎'
+    subscript_digits = ["₀", "₁", "₂", "₃", "₄", "₅", "₆", "₇", "₈", "₉"]
+
+    sqrt = u'√'
+    transpose = u'ᵀ'
+
+    integral = u'∫'
+    integral_double = u'∬'
+    integral_triple = u'∭'
+    integral_contour = u'∮'
+    integral_surface = u'∯'
+    integral_volume = u'∰'
+
+    sum = u'∑'
+    division_slash = '∕'
+    partial = u'∂'
+    epsilon = u'ε'
+    omega = u'ω'
+    Omega = u'Ω'
+    gamma = u'γ'
+    Gamma = u'Γ'
+    nabla = u'∇'
+    for_all = u'∀'
+
+    dot = u'⋅'
+    cross_product = u'⨯'
+    circled_times = u'⊗'
+    nary_product = u'∏'
+
+    ne = u'≠'
+    lt = u'<'
+    le = u'≤'
+    gt = u'>'
+    ge = u'≥'
+
+    logical_and = u'∧'
+    logical_or = u'∨'
+    logical_not = u'¬'
+
+    element_of = u'∈'
+    not_element_of = u'∉'
+
+    left_white_square_bracket = u'⟦'
+    right_white_squared_bracket = u'⟧'
+    left_angled_bracket = u'⟨'
+    right_angled_bracket = u'⟩'
+    left_double_angled_bracket = u'⟪'
+    right_double_angled_bracket = u'⟫'
+
+    combining_right_arrow_above = '\u20D7'
+    combining_overline = '\u0305'
+
+
+def bolden_letter(c):
+    if ord("A") <= ord(c) <= ord("Z"):
+        c = unichr(ord(c) - ord(u"A") + ord(UC.bold_math_A))
+    elif ord("a") <= ord(c) <= ord("z"):
+        c = unichr(ord(c) - ord(u"a") + ord(UC.bold_math_a))
+    return c
+
+
+def superscript_digit(digit):
+    return UC.superscript_digits[ord(digit) - ord("0")]
+
+
+def subscript_digit(digit):
+    return UC.subscript_digits[ord(digit) - ord("0")]
+
+
+def bolden_string(s):
+    return u"".join(bolden_letter(c) for c in s)
+
+
+def overline_string(f):
+    return u"".join("%s%s" % (c, UC.combining_overline) for c in f)
+
+
+def subscript_number(number):
+    assert isinstance(number, int)
+    prefix = UC.subscript_minus if number < 0 else ''
+    number = str(number)
+    return prefix + ''.join(subscript_digit(c) for c in str(number))
+
+
+def superscript_number(number):
+    assert isinstance(number, int)
+    prefix = UC.superscript_minus if number < 0 else ''
+    number = str(number)
+    return prefix + ''.join(superscript_digit(c) for c in str(number))
+
+
+def opfont(opname):
+    return bolden_string(opname)
+
+
+def measure_font(dx):
+    return bolden_string(dx)
+
+
+integral_by_dim = {
+    3: UC.integral_triple,
+    2: UC.integral_double,
+    1: UC.integral,
+}
+
+integral_type_to_codim = {
+    "cell": 0,
+    "exterior_facet": 1,
+    "interior_facet": 1,
+    "vertex": "tdim",
+    "point": "tdim",
+    "custom": 0,
+    "overlap": 0,
+    "interface": 1,
+    "cutcell": 0,
+}
+
+integral_symbols = {
+    "cell": UC.integral_volume,
+    "exterior_facet": UC.integral_surface,
+    "interior_facet": UC.integral_surface,
+    "vertex": UC.integral,
+    "point": UC.integral,
+    "custom": UC.integral,
+    "overlap": UC.integral,
+    "interface": UC.integral,
+    "cutcell": UC.integral,
+}
+
+integral_postfixes = {
+    "cell": "",
+    "exterior_facet": "ext",
+    "interior_facet": "int",
+    "vertex": "vertex",
+    "point": "point",
+    "custom": "custom",
+    "overlap": "overlap",
+    "interface": "interface",
+    "cutcell": "cutcell",
+}
+
+
+def get_integral_symbol(integral_type, domain, subdomain_id):
+    tdim = domain.topological_dimension()
+    codim = integral_type_to_codim[integral_type]
+    itgdim = tdim - codim
+
+    # ipost = integral_postfixes[integral_type]
+    istr = integral_by_dim[itgdim]
+
+    # TODO: Render domain description
+
+    if isinstance(subdomain_id, numbers.Integral):
+        istr += subscript_number(int(subdomain_id))
+    elif subdomain_id == "everywhere":
+        pass
+    elif subdomain_id == "otherwise":
+        istr += "[rest of domain]"
+    elif isinstance(subdomain_id, tuple):
+        istr += ",".join([subscript_number(int(i)) for i in subdomain_id])
+
+    dxstr = ufl.measure.integral_type_to_measure_name[integral_type]
+    dxstr = measure_font(dxstr)
+
+    return istr, dxstr
+
+
+def par(s):
+    return "(%s)" % s
+
+
+def prec(expr):
+    return 0  # FIXME
+    # return precedence[expr._ufl_class_]
+
+
+def is_int(s):
+    try:
+        int(s)
+        return True
+    except ValueError:
+        return False
+
+
+def format_index(ii):
+    if isinstance(ii, FixedIndex):
+        s = "%d" % ii._value
+    elif isinstance(ii, Index):
+        s = "i%s" % subscript_number(ii._count)
+    else:
+        error("Invalid index type %s." % type(ii))
+    return s
+
+
+def ufl2unicode(expression):
+    "Generate Unicode string for a UFL expression or form."
+    if isinstance(expression, Form):
+        form_data = compute_form_data(expression)
+        preprocessed_form = form_data.preprocessed_form
+        return form2unicode(preprocessed_form, form_data)
+    else:
+        return expression2unicode(expression)
+
+
+def expression2unicode(expression, argument_names=None, coefficient_names=None):
+    rules = Expression2UnicodeHandler(argument_names, coefficient_names)
+    return map_expr_dag(rules, expression)
+
+
+def form2unicode(form, formdata):
+    # formname = formdata.name
+    argument_names = None
+    coefficient_names = None
+
+    # Define form as sum of integrals
+    lines = []
+    integrals = form.integrals()
+    for itg in integrals:
+        integrand_string = expression2unicode(
+            itg.integrand(), argument_names, coefficient_names)
+
+        istr, dxstr = get_integral_symbol(itg.integral_type(), itg.ufl_domain(), itg.subdomain_id())
+
+        line = "%s %s %s" % (istr, integrand_string, dxstr)
+        lines.append(line)
+
+    return '\n  + '.join(lines)
+
+
+def binop(expr, a, b, op, sep=" "):
+    eprec = precedence(expr)
+    op0, op1 = expr.ufl_operands
+    aprec = precedence(op0)
+    bprec = precedence(op1)
+    # Assuming left-to-right evaluation, therefore >= and > here:
+    if aprec >= eprec:
+        a = par(a)
+    if bprec > eprec:
+        b = par(b)
+    return sep.join((a, op, b))
+
+
+def mathop(expr, arg, opname):
+    eprec = precedence(expr)
+    aprec = precedence(expr.ufl_operands[0])
+    op = opfont(opname)
+    if aprec > eprec:
+        arg = par(arg)
+        sep = ""
+    else:
+        sep = UC.thin_space
+    return "%s%s%s" % (op, sep, arg)
+
+
+class Expression2UnicodeHandler(MultiFunction):
+    def __init__(self, argument_names=None, coefficient_names=None, colorama_bold=False):
+        MultiFunction.__init__(self)
+        self.argument_names = argument_names
+        self.coefficient_names = coefficient_names
+        self.colorama_bold = colorama_bold and has_colorama
+
+    # --- Terminal objects ---
+
+    def scalar_value(self, o):
+        if o.ufl_shape and self.colorama_bold:
+            return "%s%s%s" % (colorama.Style.BRIGHT, o._value, colorama.Style.RESET_ALL)
+        return "%s" % o._value
+
+    def zero(self, o):
+        if o.ufl_shape and self.colorama_bold:
+            if len(o.ufl_shape) == 1:
+                return "0%s" % UC.combining_right_arrow_above
+            return "%s0%s" % (colorama.Style.BRIGHT, colorama.Style.RESET_ALL)
+        return "0"
+
+    def identity(self, o):
+        if self.colorama_bold:
+            return "%sI%s" % (colorama.Style.BRIGHT, colorama.Style.RESET_ALL)
+        return "I"
+
+    def permutation_symbol(self, o):
+        if self.colorama_bold:
+            return "%s%s%s" % (colorama.Style.BRIGHT, UC.epsilon, colorama.Style.RESET_ALL)
+        return UC.epsilon
+
+    def facet_normal(self, o):
+        return "n%s" % UC.combining_right_arrow_above
+
+    def spatial_coordinate(self, o):
+        return "x%s" % UC.combining_right_arrow_above
+
+    def argument(self, o):
+        # Using ^ for argument numbering and _ for indexing since
+        # indexing is more common than exponentiation
+        if self.argument_names is None:
+            i = o.number()
+            bfn = "v" if i == 0 else "u"
+            if not o.ufl_shape:
+                return bfn
+            elif len(o.ufl_shape) == 1:
+                return "%s%s" % (bfn, UC.combining_right_arrow_above)
+            elif self.colorama_bold:
+                return "%s%s%s" % (colorama.Style.BRIGHT, bfn, colorama.Style.RESET_ALL)
+            else:
+                return bfn
+        return self.argument_names[(o.number(), o.part())]
+
+    def coefficient(self, o):
+        # Using ^ for coefficient numbering and _ for indexing since
+        # indexing is more common than exponentiation
+        if self.coefficient_names is None:
+            i = o.count()
+            var = "w"
+            if len(o.ufl_shape) == 1:
+                var += UC.combining_right_arrow_above
+            elif len(o.ufl_shape) > 1 and self.colorama_bold:
+                var = "%s%s%s" % (colorama.Style.BRIGHT, var, colorama.Style.RESET_ALL)
+            return "%s%s" % (var, superscript_number(i))
+        return self.coefficient_names[o.count()]
+
+    def multi_index(self, o):
+        return ",".join(format_index(i) for i in o)
+
+    def label(self, o):
+        return "l%s" % (subscript_number(o.count()),)
+
+    # --- Non-terminal objects ---
+
+    def variable(self, o, f, l):
+        return "var(%s,%s)" % (f, l)
+
+    def index_sum(self, o, f, i):
+        if 1:  # prec(o.ufl_operands[0]) >? prec(o):
+            f = par(f)
+        return "%s[%s]%s" % (UC.sum, i, f)
+
+    def sum(self, o, a, b):
+        return binop(o, a, b, "+")
+
+    def product(self, o, a, b):
+        return binop(o, a, b, " ", sep="")
+
+    def division(self, o, a, b):
+        if is_int(b):
+            b = subscript_number(int(b))
+            if is_int(a):
+                # Return as a fraction
+                # NOTE: Maybe consider using fractional slash
+                #  with normal numbers if terminals can handle it
+                a = superscript_number(int(a))
+            else:
+                a = par(a)
+            return "%s %s %s" % (a, UC.division_slash, b)
+        return binop(o, a, b, UC.division_slash)
+
+    def abs(self, o, a):
+        return "|%s|" % (a,)
+
+    def transposed(self, o, a):
+        a = par(a)
+        return "%s%s" % (a, UC.transpose)
+
+    def indexed(self, o, A, ii):
+        op0, op1 = o.ufl_operands
+        Aprec = precedence(op0)
+        oprec = precedence(o)
+        if Aprec > oprec:
+            A = par(A)
+        return "%s[%s]" % (A, ii)
+
+    def variable_derivative(self, o, f, v):
+        f = par(f)
+        v = par(v)
+        nom = r"%s%s" % (UC.partial, f)
+        denom = r"%s%s" % (UC.partial, v)
+        return par(r"%s%s%s" % (nom, UC.division_slash, denom))
+
+    def coefficient_derivative(self, o, f, w, v, cd):
+        f = par(f)
+        w = par(w)
+        nom = r"%s%s" % (UC.partial, f)
+        denom = r"%s%s" % (UC.partial, w)
+        return par(r"%s%s%s[%s]" % (nom, UC.division_slash, denom, v))  # TODO: Fix this syntax...
+
+    def grad(self, o, f):
+        return mathop(o, f, "grad")
+
+    def div(self, o, f):
+        return mathop(o, f, "div")
+
+    def nabla_grad(self, o, f):
+        oprec = precedence(o)
+        fprec = precedence(o.ufl_operands[0])
+        if fprec > oprec:
+            f = par(f)
+        return "%s%s%s" % (UC.nabla, UC.thin_space, f)
+
+    def nabla_div(self, o, f):
+        oprec = precedence(o)
+        fprec = precedence(o.ufl_operands[0])
+        if fprec > oprec:
+            f = par(f)
+        return "%s%s%s%s%s" % (UC.nabla, UC.thin_space, UC.dot, UC.thin_space, f)
+
+    def curl(self, o, f):
+        oprec = precedence(o)
+        fprec = precedence(o.ufl_operands[0])
+        if fprec > oprec:
+            f = par(f)
+        return "%s%s%s%s%s" % (UC.nabla, UC.thin_space, UC.cross_product, UC.thin_space, f)
+
+    def math_function(self, o, f):
+        op = opfont(self._name)
+        f = par(f)
+        return "%s%s" % (op, f)
+
+    def sqrt(self, o, f):
+        f = par(f)
+        return "%s%s" % (UC.sqrt, f)
+
+    def exp(self, o, f):
+        op = opfont("exp")
+        f = par(f)
+        return "%s%s" % (op, f)
+
+    def atan2(self, o, f1, f2):
+        f1 = par(f1)
+        f2 = par(f2)
+        op = opfont("arctan2")
+        return "%s(%s, %s)" % (op, f1, f2)
+
+    def bessel_j(self, o, nu, f):
+        op = opfont("J")
+        f = par(f)
+        nu = subscript_number(int(nu))
+        return "%s%s%s" % (op, nu, f)
+
+    def bessel_y(self, o, nu, f):
+        op = opfont("Y")
+        f = par(f)
+        nu = subscript_number(int(nu))
+        return "%s%s%s" % (op, nu, f)
+
+    def bessel_i(self, o, nu, f):
+        op = opfont("I")
+        f = par(f)
+        nu = subscript_number(int(nu))
+        return "%s%s%s" % (op, nu, f)
+
+    def bessel_K(self, o, nu, f):
+        op = opfont("K")
+        f = par(f)
+        nu = subscript_number(int(nu))
+        return "%s%s%s" % (op, nu, f)
+
+    def power(self, o, a, b):
+        if is_int(b):
+            b = superscript_number(int(b))
+            return binop(o, a, b, "", sep="")
+        return binop(o, a, b, "^", sep="")
+
+    def outer(self, o, a, b):
+        return binop(o, a, b, UC.circled_times)
+
+    def inner(self, o, a, b):
+        return "%s%s, %s%s" % (UC.left_angled_bracket, a, b, UC.right_angled_bracket)
+
+    def dot(self, o, a, b):
+        return binop(o, a, b, UC.dot)
+
+    def cross(self, o, a, b):
+        return binop(o, a, b, UC.cross_product)
+
+    def determinant(self, o, A):
+        return "|%s|" % (A,)
+
+    def inverse(self, o, A):
+        A = par(A)
+        return "%s%s" % (A, superscript_number(-1))
+
+    def trace(self, o, A):
+        return mathop(o, A, "tr")
+
+    def deviatoric(self, o, A):
+        return mathop(o, A, "dev")
+
+    def cofactor(self, o, A):
+        return mathop(o, A, "cofac")
+
+    def skew(self, o, A):
+        return mathop(o, A, "skew")
+
+    def sym(self, o, A):
+        return mathop(o, A, "sym")
+
+    def list_tensor(self, o, *ops):
+        l = ", ".join(ops)
+        return "%s%s%s" % ("[", l, "]")
+
+    def component_tensor(self, o, A, ii):
+        return "[%s %s %s]" % (A, UC.for_all, ii)
+
+    def positive_restricted(self, o, f):
+        f = par(f)
+        return "%s%s" % (f, UC.superscript_plus)
+
+    def negative_restricted(self, o, f):
+        f = par(f)
+        return "%s%s" % (f, UC.superscript_minus)
+
+    def cell_avg(self, o, f):
+        f = overline_string(f)
+        return f
+
+    def facet_avg(self, o, f):
+        f = overline_string(f)
+        return f
+
+    def eq(self, o, a, b):
+        return binop(o, a, b, "=")
+
+    def ne(self, o, a, b):
+        return binop(o, a, b, UC.ne)
+
+    def le(self, o, a, b):
+        return binop(o, a, b, UC.le)
+
+    def ge(self, o, a, b):
+        return binop(o, a, b, UC.ge)
+
+    def lt(self, o, a, b):
+        return binop(o, a, b, UC.lt)
+
+    def gt(self, o, a, b):
+        return binop(o, a, b, UC.gt)
+
+    def and_condition(self, o, a, b):
+        return binop(o, a, b, UC.logical_and)
+
+    def or_condition(self, o, a, b):
+        return binop(o, a, b, UC.logical_or)
+
+    def not_condition(self, o, a):
+        a = par(a)
+        return "%s%s" % (UC.logical_not, a)
+
+    def conditional(self, o, c, t, f):
+        c = par(c)
+        t = par(t)
+        f = par(t)
+        If = opfont("if")
+        Else = opfont("else")
+        l = " ".join((t, If, c, Else, f))
+        return l
+
+    def min_value(self, o, a, b):
+        op = opfont("min")
+        return "%s(%s, %s)" % (op, a, b)
+
+    def max_value(self, o, a, b):
+        op = opfont("max")
+        return "%s(%s, %s)" % (op, a, b)
+
+    def expr_list(self, o, *ops):
+        items = ", ".join(ops)
+        return "%s %s %s" % (UC.left_white_square_bracket, items,
+                             UC.right_white_squared_bracket)
+
+    def expr_mapping(self, o, *ops):
+        items = ", ".join(ops)
+        return "%s %s %s" % (UC.left_double_angled_bracket, items,
+                             UC.left_double_angled_bracket)
+
+    def expr(self, o):
+        raise ValueError("Missing handler for type %s" % str(type(o)))
diff --git a/ufl/integral.py b/ufl/integral.py
index c127ff9..c164d41 100644
--- a/ufl/integral.py
+++ b/ufl/integral.py
@@ -21,7 +21,6 @@
 # Modified by Anders Logg, 2008-2009
 # Modified by Massimiliano Leoni, 2016.
 
-# import six
 import ufl
 from ufl.log import error
 from ufl.core.expr import Expr
@@ -35,7 +34,6 @@ from ufl.utils.py23 import as_native_strings
 __all_classes__ = as_native_strings(["Integral"])
 
 
-# @six.python_2_unicode_compatible
 class Integral(object):
     "An integral over a single domain."
     __slots__ = as_native_strings((
@@ -61,12 +59,14 @@ class Integral(object):
     def reconstruct(self, integrand=None,
                     integral_type=None, domain=None, subdomain_id=None,
                     metadata=None, subdomain_data=None):
-        """Construct a new Integral object with some properties replaced with new values.
+        """Construct a new Integral object with some properties replaced with
+        new values.
 
         Example:
             <a = Integral instance>
             b = a.reconstruct(expand_compounds(a.integrand()))
             c = a.reconstruct(metadata={'quadrature_degree':2})
+
         """
         if integrand is None:
             integrand = self.integrand()
@@ -90,11 +90,6 @@ class Integral(object):
         "Return the domain type of this integral."
         return self._integral_type
 
-    #def domain(self):
-    #    "Deprecated, please use .ufl_domain() instead."
-    #    deprecate("Integral.domain() is deprecated, please use .ufl_domain() instead.")
-    #    return self.ufl_domain()
-
     def ufl_domain(self):
         "Return the integration domain of this integral."
         return self._ufl_domain
@@ -136,14 +131,12 @@ class Integral(object):
         return s
 
     def __repr__(self):
-        r = "Integral(%s, %s, %s, %s, %s, %s)" % (
-            repr(self._integrand),
-            repr(self._integral_type),
-            repr(self._ufl_domain),
-            repr(self._subdomain_id),
-            repr(self._metadata),
-            repr(self._subdomain_data),
-            )
+        r = "Integral(%s, %s, %s, %s, %s, %s)" % (repr(self._integrand),
+                                                  repr(self._integral_type),
+                                                  repr(self._ufl_domain),
+                                                  repr(self._subdomain_id),
+                                                  repr(self._metadata),
+                                                  repr(self._subdomain_data))
         return as_native_str(r)
 
     def __eq__(self, other):
@@ -156,8 +149,9 @@ class Integral(object):
                 id_or_none(self._subdomain_data) == id_or_none(other._subdomain_data))
 
     def __hash__(self):
-        # Assuming few collisions by ignoring hash(self._metadata)
-        # (a dict is not hashable but we assume it is immutable in practice)
+        # Assuming few collisions by ignoring hash(self._metadata) (a
+        # dict is not hashable but we assume it is immutable in
+        # practice)
         hashdata = (hash(self._integrand),
                     self._integral_type,
                     hash(self._ufl_domain),
diff --git a/ufl/measure.py b/ufl/measure.py
index 473d972..db6bd57 100644
--- a/ufl/measure.py
+++ b/ufl/measure.py
@@ -21,7 +21,6 @@
 # Modified by Anders Logg 2008-2016
 # Modified by Massimiliano Leoni, 2016.
 
-#import six
 from six import string_types
 import numbers
 
@@ -112,19 +111,18 @@ def measure_names():
     return tuple(sorted(measure_name_to_integral_type.keys()))
 
 
-# @six.python_2_unicode_compatible
 class Measure(object):
-    __slots__ = as_native_strings((
-        "_integral_type",
-        "_domain",
-        "_subdomain_id",
-        "_metadata",
-        "_subdomain_data",
-        ))
+    __slots__ = as_native_strings(("_integral_type",
+                                   "_domain",
+                                   "_subdomain_id",
+                                   "_metadata",
+                                   "_subdomain_data"))
     """Representation of an integration measure.
 
-    The Measure object holds information about integration properties to be
-    transferred to a Form on multiplication with a scalar expression.
+    The Measure object holds information about integration properties
+    to be transferred to a Form on multiplication with a scalar
+    expression.
+
     """
 
     def __init__(self,
@@ -159,8 +157,7 @@ class Measure(object):
 
         # Check that we either have a proper AbstractDomain or none
         self._domain = None if domain is None else as_domain(domain)
-        if not (self._domain is None
-                or isinstance(self._domain, AbstractDomain)):
+        if not (self._domain is None or isinstance(self._domain, AbstractDomain)):
             error("Invalid domain.")
 
         # Store subdomain data
@@ -177,8 +174,7 @@ class Measure(object):
                 if not isinstance(did, numbers.Integral):
                     error("Invalid subdomain_id %s." % (did,))
         else:
-            if not (subdomain_id in ("everywhere",)
-                    or isinstance(subdomain_id, numbers.Integral)):
+            if not (subdomain_id in ("everywhere",) or isinstance(subdomain_id, numbers.Integral)):
                 error("Invalid subdomain_id %s." % (subdomain_id,))
         self._subdomain_id = subdomain_id
 
@@ -194,11 +190,6 @@ class Measure(object):
         """
         return self._integral_type
 
-    #def domain(self):
-    #    "Deprecated, please use .ufl_domain() instead."
-    #    deprecate("Measure.domain() is deprecated, please use .ufl_domain() instead.")
-    #    return self.ufl_domain()
-
     def ufl_domain(self):
         """Return the domain associated with this measure.
 
@@ -212,8 +203,10 @@ class Measure(object):
 
     def metadata(self):
         """Return the integral metadata. This data is not interpreted by UFL.
-        It is passed to the form compiler which can ignore it or use it to
-        compile each integral of a form in a different way."""
+        It is passed to the form compiler which can ignore it or use
+        it to compile each integral of a form in a different way.
+
+        """
         return self._metadata
 
     def reconstruct(self,
@@ -222,7 +215,8 @@ class Measure(object):
                     domain=None,
                     metadata=None,
                     subdomain_data=None):
-        """Construct a new Measure object with some properties replaced with new values.
+        """Construct a new Measure object with some properties replaced with
+        new values.
 
         Example:
             <dm = Measure instance>
@@ -232,6 +226,7 @@ class Measure(object):
         Used by the call operator, so this is equivalent:
             b = dm(2)
             c = dm(0, { "quadrature_degree": 3 })
+
         """
         if subdomain_id is None:
             subdomain_id = self.subdomain_id()
@@ -302,11 +297,12 @@ class Measure(object):
     def __getitem__(self, data):
         """This operator supports legacy syntax in python dolfin programs.
 
-        The old documentation reads:
-        Return a new Measure for same integration type with an attached
-        context for interpreting domain ids. By default this new Measure
-        integrates over everywhere, but it can be restricted with a domain id
-        as usual. Example: dx = dx[boundaries]; L = f*v*dx + g*v+dx(1).
+        The old documentation reads: Return a new Measure for same
+        integration type with an attached context for interpreting
+        domain ids. By default this new Measure integrates over
+        everywhere, but it can be restricted with a domain id as
+        usual. Example: dx = dx[boundaries]; L = f*v*dx + g*v+dx(1).
+
         """
         deprecate("Notation dx[meshfunction] is deprecated. Please use dx(subdomain_data=meshfunction) instead.")
         return self(subdomain_data=data)
@@ -400,7 +396,7 @@ class Measure(object):
 
     def __rmul__(self, integrand):
         """Multiply a scalar expression with measure to construct a form with
-a single integral.
+        a single integral.
 
         This is to implement the notation
 
@@ -426,7 +422,7 @@ a single integral.
         if not is_true_ufl_scalar(integrand):
             error("Can only integrate scalar expressions. The integrand is a "
                   "tensor expression with value shape %s and free indices with labels %s." %
-                    (integrand.ufl_shape, integrand.ufl_free_indices))
+                  (integrand.ufl_shape, integrand.ufl_free_indices))
 
         # If we have a tuple of domain ids, delegate composition to
         # Integral.__add__:
@@ -461,7 +457,6 @@ a single integral.
         return Form([integral])
 
 
-# @six.python_2_unicode_compatible
 class MeasureSum(object):
     """Represents a sum of measures.
 
@@ -506,6 +501,7 @@ class MeasureProduct(object):
 
     This is work in progress and not functional. It needs support
     in other parts of ufl and the rest of the code generation chain.
+
     """
     __slots__ = as_native_strings(("_measures",))
 
@@ -518,8 +514,9 @@ class MeasureProduct(object):
     def __mul__(self, other):
         """Flatten multiplication of product measures.
 
-        This is to ensure that (dm1*dm2)*dm3 is stored as a
-        simple list (dm1,dm2,dm3) in a single MeasureProduct.
+        This is to ensure that (dm1*dm2)*dm3 is stored as a simple
+        list (dm1,dm2,dm3) in a single MeasureProduct.
+
         """
         if isinstance(other, Measure):
             measures = self.sub_measures() + [other]
diff --git a/ufl/sobolevspace.py b/ufl/sobolevspace.py
index f0814d8..2e151a8 100644
--- a/ufl/sobolevspace.py
+++ b/ufl/sobolevspace.py
@@ -25,12 +25,10 @@ symbolic reasoning about the spaces in which finite elements lie."""
 # Modified by Lizao Li 2015
 # Modified by Thomas Gibson 2017
 
-#import six
 from ufl.utils.py23 import as_native_str
 from functools import total_ordering
 
 
-# @six.python_2_unicode_compatible
 @total_ordering
 class SobolevSpace(object):
     """Symbolic representation of a Sobolev space. This implements a
@@ -123,6 +121,7 @@ class SobolevSpace(object):
 class DirectionalSobolevSpace(SobolevSpace):
     """Symbolic representation of a Sobolev space with varying smoothness
     in differerent spatial directions.
+
     """
 
     def __init__(self, orders):
diff --git a/ufl/tensoralgebra.py b/ufl/tensoralgebra.py
index c5f7946..82da808 100644
--- a/ufl/tensoralgebra.py
+++ b/ufl/tensoralgebra.py
@@ -122,10 +122,8 @@ class Transposed(CompoundTensorOperator):
 
 @ufl_type(num_ops=2)
 class Outer(CompoundTensorOperator):
-    __slots__ = as_native_strings((
-        "ufl_free_indices",
-        "ufl_index_dimensions",
-        ))
+    __slots__ = as_native_strings(("ufl_free_indices",
+                                   "ufl_index_dimensions"))
 
     def __new__(cls, a, b):
         ash, bsh = a.ufl_shape, b.ufl_shape
@@ -153,10 +151,8 @@ class Outer(CompoundTensorOperator):
 
 @ufl_type(num_ops=2)
 class Inner(CompoundTensorOperator):
-    __slots__ = as_native_strings((
-        "ufl_free_indices",
-        "ufl_index_dimensions",
-        ))
+    __slots__ = as_native_strings(("ufl_free_indices",
+                                   "ufl_index_dimensions"))
 
     def __new__(cls, a, b):
         # Checks
@@ -174,9 +170,8 @@ class Inner(CompoundTensorOperator):
         return CompoundTensorOperator.__new__(cls)
 
     def __init__(self, a, b):
-        # sort operands for unique representation,
-        # must be independent of various counts etc.
-        # as explained in cmp_expr
+        # sort operands for unique representation, must be independent
+        # of various counts etc.  as explained in cmp_expr
         a, b = sorted_expr((a, b))
 
         CompoundTensorOperator.__init__(self, (a, b))
@@ -194,10 +189,8 @@ class Inner(CompoundTensorOperator):
 
 @ufl_type(num_ops=2)
 class Dot(CompoundTensorOperator):
-    __slots__ = as_native_strings((
-        "ufl_free_indices",
-        "ufl_index_dimensions",
-        ))
+    __slots__ = as_native_strings(("ufl_free_indices",
+                                   "ufl_index_dimensions"))
 
     def __new__(cls, a, b):
         ash = a.ufl_shape
@@ -239,10 +232,8 @@ class Dot(CompoundTensorOperator):
 
 @ufl_type(num_ops=2)
 class Cross(CompoundTensorOperator):
-    __slots__ = as_native_strings((
-        "ufl_free_indices",
-        "ufl_index_dimensions",
-        ))
+    __slots__ = as_native_strings(("ufl_free_indices",
+                                   "ufl_index_dimensions"))
 
     def __new__(cls, a, b):
         ash = a.ufl_shape
diff --git a/ufl/utils/sequences.py b/ufl/utils/sequences.py
index 20ee3e1..8ca0b88 100644
--- a/ufl/utils/sequences.py
+++ b/ufl/utils/sequences.py
@@ -18,9 +18,13 @@
 # You should have received a copy of the GNU Lesser General Public License
 # along with UFL. If not, see <http://www.gnu.org/licenses/>.
 
-from six.moves import zip
+from functools import reduce
+
+from six.moves import map, zip
 from six import string_types
 
+import numpy
+
 
 def product(sequence):
     "Return the product of all elements in a sequence."
@@ -67,3 +71,15 @@ def recursive_chain(lists):
         else:
             for s in recursive_chain(l):
                 yield s
+
+
+def max_degree(degrees):
+    """Maximum degree for mixture of scalar and tuple degrees."""
+    # numpy.maximum broadcasts scalar degrees to tuple degrees if
+    # necessary.  reduce applies numpy.maximum pairwise.
+    degree = reduce(numpy.maximum, map(numpy.asarray, degrees))
+    if degree.ndim:
+        degree = tuple(map(int, degree))  # tuple degree
+    else:
+        degree = int(degree)              # scalar degree
+    return degree
diff --git a/ufl/utils/sorting.py b/ufl/utils/sorting.py
index 6c1dc62..4a45841 100644
--- a/ufl/utils/sorting.py
+++ b/ufl/utils/sorting.py
@@ -78,15 +78,6 @@ def sorted_by_key(mapping):
     return sorted(iteritems(mapping), key=_key)
 
 
-def sorted_by_tuple_key(mapping):
-    "Sort dict items by tuple valued keys, allowing different types as items of the key tuples."
-    # Python3 doesn't allow comparing builtins of different type,
-    # therefore the typename trick here
-    def _tuple_key(x):
-        return tuple((type(k).__name__, k) for k in x[0])
-    return sorted(iteritems(mapping), key=_tuple_key)
-
-
 def canonicalize_metadata(metadata):
     """Assuming metadata to be a dict with string keys and builtin python types as values.
 

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-science/packages/fenics/ufl.git



More information about the debian-science-commits mailing list