[ufl] 01/03: Imported Upstream version 2016.1.0
Johannes Ring
johannr-guest at moszumanska.debian.org
Fri Jun 24 11:55:35 UTC 2016
This is an automated email from the git hooks/post-receive script.
johannr-guest pushed a commit to branch master
in repository ufl.
commit e5f5860571eb7baca1b0bdd6692c9a99cce08d23
Author: Johannes Ring <johannr at simula.no>
Date: Fri Jun 24 13:49:07 2016 +0200
Imported Upstream version 2016.1.0
---
.gitignore | 5 +
ChangeLog | 21 +
README | 37 +-
doc/sphinx/Makefile | 77 +-
doc/sphinx/README | 31 +-
ufl/indexing.py => doc/sphinx/generate-apidoc | 18 +-
doc/sphinx/scripts/README_generate_modules.rst | 29 -
doc/sphinx/scripts/generate_index.py | 97 --
doc/sphinx/scripts/generate_modules.py | 269 ---
doc/sphinx/source/_themes/fenics/README | 2 -
doc/sphinx/source/_themes/fenics/layout.html | 69 -
.../source/_themes/fenics/static/alert_info_32.png | Bin 1168 -> 0 bytes
.../_themes/fenics/static/alert_warning_32.png | Bin 1060 -> 0 bytes
.../source/_themes/fenics/static/bg-page.png | Bin 181 -> 0 bytes
.../source/_themes/fenics/static/bullet_orange.png | Bin 365 -> 0 bytes
doc/sphinx/source/_themes/fenics/static/dolfin.png | Bin 571 -> 0 bytes
doc/sphinx/source/_themes/fenics/static/dorsal.png | Bin 3342 -> 0 bytes
.../source/_themes/fenics/static/fenics.css_t | 388 -----
.../source/_themes/fenics/static/unknown.png | Bin 2802 -> 0 bytes
doc/sphinx/source/_themes/fenics/theme.conf | 12 -
doc/sphinx/source/api-doc/modules.rst | 7 +
doc/sphinx/source/api-doc/ufl.algorithms.rst | 270 +++
doc/sphinx/source/api-doc/ufl.core.rst | 70 +
doc/sphinx/source/api-doc/ufl.corealg.rst | 38 +
doc/sphinx/source/api-doc/ufl.finiteelement.rst | 118 ++
doc/sphinx/source/api-doc/ufl.formatting.rst | 54 +
doc/sphinx/source/api-doc/ufl.rst | 354 ++++
doc/sphinx/source/api-doc/ufl.utils.rst | 102 ++
doc/sphinx/source/conf.py | 162 +-
doc/sphinx/source/index.rst | 110 ++
doc/sphinx/source/releases.rst | 8 +
doc/sphinx/source/releases/next.rst | 11 +
doc/sphinx/source/releases/v1.6.0.rst | 31 +
doc/sphinx/source/user/algorithms.rst | 417 +++++
doc/sphinx/source/user/command_line_utils.rst | 48 +
doc/sphinx/source/user/examples.rst | 545 ++++++
doc/sphinx/source/user/form_language.rst | 1738 ++++++++++++++++++++
doc/sphinx/source/user/internal_representation.rst | 183 +++
doc/sphinx/source/user/introduction.rst | 37 +
doc/sphinx/source/user/user_manual.rst | 17 +
scripts/fixdates | 10 -
scripts/form2ufl | 116 --
scripts/makedoc | 56 -
scripts/ufl2py | 2 +-
setup.py | 32 +-
test/conftest.py | 7 +-
test/mockobjects.py | 3 +-
test/test_algorithms.py | 11 +-
test/test_analyse_demos.py | 4 +-
test/test_apply_algebra_lowering.py | 91 +
test/test_apply_function_pullbacks.py | 366 +++++
test/test_apply_restrictions.py | 4 +-
test/test_arithmetic.py | 1 +
test/test_automatic_differentiation.py | 7 +-
test/test_book_snippets.py | 14 +-
test/test_change_to_local.py | 11 +-
test/test_change_to_reference_frame.py | 292 ++++
test/test_check_arities.py | 8 +-
test/test_classcoverage.py | 10 +-
test/test_conditionals.py | 5 +-
test/test_degree_estimation.py | 3 +-
test/test_derivative.py | 35 +-
test/test_diff.py | 5 +-
test/test_domains.py | 209 +--
test/test_elements.py | 2 +-
test/test_equals.py | 2 +-
test/test_evaluate.py | 4 +-
test/test_expand_indices.py | 4 +-
test/test_ffcforms.py | 2 +-
test/test_form.py | 40 +-
test/test_future_division.py | 1 +
test/test_grad.py | 14 +-
test/test_illegal.py | 3 +-
test/test_indexing.py | 15 +-
test/test_indices.py | 54 +-
test/test_lhs_rhs.py | 2 +-
test/test_literals.py | 3 +-
test/test_measures.py | 65 +-
test/test_mock_expr.py | 28 +-
test/test_new_ad.py | 5 +-
test/test_pickle.py | 2 +-
test/test_piecewise_checks.py | 100 +-
test/test_reference_shapes.py | 8 +-
test/test_scratch.py | 1 +
test/test_signature.py | 25 +-
test/test_simplify.py | 5 +-
test/test_sobolevspace.py | 5 +-
test/test_split.py | 3 +-
test/test_str.py | 1 +
test/test_tensoralgebra.py | 1 +
test/test_utilities.py | 3 +-
ufl/__init__.py | 61 +-
ufl/algebra.py | 7 +-
ufl/algorithms/__init__.py | 20 +-
ufl/algorithms/ad.py | 52 +-
ufl/algorithms/analysis.py | 30 +-
ufl/algorithms/apply_algebra_lowering.py | 178 ++
ufl/algorithms/apply_derivatives.py | 298 +++-
ufl/algorithms/apply_function_pullbacks.py | 226 +++
ufl/algorithms/apply_geometry_lowering.py | 482 ++++++
ufl/algorithms/apply_integral_scaling.py | 89 +
ufl/algorithms/apply_restrictions.py | 17 +-
ufl/algorithms/argument_dependencies.py | 3 +-
ufl/algorithms/change_to_reference.py | 530 +-----
ufl/algorithms/check_arities.py | 28 +-
...agate_restrictions.py => check_restrictions.py} | 19 +-
ufl/algorithms/checks.py | 32 +-
ufl/algorithms/compute_form_data.py | 296 ++--
ufl/algorithms/domain_analysis.py | 96 +-
ufl/algorithms/elementtransformations.py | 81 +-
ufl/algorithms/estimate_degrees.py | 38 +-
ufl/algorithms/expand_compounds.py | 203 +--
ufl/algorithms/expand_indices.py | 17 +-
ufl/algorithms/formdata.py | 38 +-
ufl/algorithms/formfiles.py | 12 +-
ufl/algorithms/formsplitter.py | 68 +
ufl/algorithms/formtransformations.py | 31 +-
ufl/algorithms/forward_ad.py | 76 +-
ufl/algorithms/map_integrands.py | 3 +-
ufl/algorithms/multifunction.py | 1 +
ufl/algorithms/pdiffs.py | 11 +-
ufl/algorithms/predicates.py | 7 +-
ufl/algorithms/renumbering.py | 18 +-
ufl/algorithms/replace.py | 3 +-
ufl/algorithms/signature.py | 60 +-
ufl/algorithms/transformer.py | 7 +-
ufl/algorithms/traversal.py | 3 +-
ufl/argument.py | 115 +-
ufl/assertions.py | 29 +-
ufl/cell.py | 429 +++--
ufl/checks.py | 35 +-
ufl/classes.py | 76 +-
ufl/coefficient.py | 112 +-
ufl/common.py | 36 -
ufl/compound_expressions.py | 54 +-
ufl/conditional.py | 5 +-
ufl/constantvalue.py | 31 +-
ufl/core/compute_expr_hash.py | 3 +-
ufl/core/expr.py | 170 +-
ufl/core/multiindex.py | 14 +-
ufl/core/operator.py | 25 +-
ufl/core/terminal.py | 25 +-
ufl/core/ufl_id.py | 62 +
ufl/core/ufl_type.py | 53 +-
ufl/corealg/map_dag.py | 96 +-
ufl/corealg/multifunction.py | 19 +-
ufl/corealg/traversal.py | 37 +-
ufl/differentiation.py | 56 +-
ufl/domain.py | 648 ++++----
ufl/equation.py | 17 +-
ufl/exprcontainers.py | 9 +-
ufl/exprequals.py | 3 +-
ufl/exproperators.py | 35 +-
ufl/finiteelement/__init__.py | 25 +-
ufl/finiteelement/brokenelement.py | 24 +-
ufl/finiteelement/elementlist.py | 19 +-
ufl/finiteelement/enrichedelement.py | 41 +-
ufl/finiteelement/facetelement.py | 24 +-
ufl/finiteelement/finiteelement.py | 62 +-
ufl/finiteelement/finiteelementbase.py | 94 +-
ufl/finiteelement/hdivcurl.py | 69 +-
ufl/finiteelement/interiorelement.py | 24 +-
ufl/finiteelement/mixedelement.py | 345 ++--
ufl/finiteelement/outerproductelement.py | 150 +-
ufl/finiteelement/restrictedelement.py | 68 +-
ufl/finiteelement/tensorproductelement.py | 38 +-
ufl/finiteelement/traceelement.py | 26 +-
ufl/form.py | 100 +-
ufl/formatting/graph.py | 7 +-
ufl/formatting/latextools.py | 3 +-
ufl/formatting/printing.py | 5 +-
ufl/formatting/ufl2dot.py | 5 +-
ufl/formatting/ufl2latex.py | 22 +-
ufl/formoperators.py | 38 +-
ufl/functionspace.py | 133 ++
ufl/geometry.py | 102 +-
ufl/index_combination_utils.py | 3 +-
ufl/indexed.py | 15 +-
ufl/indexing.py | 3 +-
ufl/indexsum.py | 9 +-
ufl/integral.py | 33 +-
ufl/log.py | 17 +-
ufl/mathfunctions.py | 5 +-
ufl/measure.py | 102 +-
ufl/objects.py | 10 +-
ufl/operators.py | 50 +-
ufl/permutation.py | 7 +-
ufl/precedence.py | 3 +-
ufl/protocols.py | 20 +-
ufl/referencevalue.py | 9 +-
ufl/restriction.py | 3 +-
ufl/sobolevspace.py | 15 +-
ufl/sorting.py | 10 +-
ufl/split_functions.py | 12 +-
ufl/tensoralgebra.py | 11 +-
ufl/tensors.py | 31 +-
ufl/utils/counted.py | 3 +-
ufl/utils/derivativetuples.py | 3 +-
ufl/utils/dicts.py | 3 +-
ufl/utils/formatting.py | 3 +-
ufl/utils/indexflattening.py | 3 +-
ufl/utils/sequences.py | 3 +-
ufl/utils/sorting.py | 22 +-
ufl/utils/stacks.py | 3 +-
ufl/utils/system.py | 3 +-
ufl/utils/timer.py | 3 +-
ufl/utils/ufltypedicts.py | 3 +-
ufl/variable.py | 17 +-
208 files changed, 9527 insertions(+), 4744 deletions(-)
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..85fec14
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,5 @@
+*.pyc
+*.py~
+build/
+test/*_debug.py
+test/UFL.log
diff --git a/ChangeLog b/ChangeLog
index c5ffc8b..3b1fc9e 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,24 @@
+2016.1.0 [2016-06-23]
+ - Add operator A^(i,j) := as_tensor(A, (i,j))
+ - Updates to old manual for publishing on fenics-ufl.readthedocs.org
+ - Bugfix for ufl files with utf-8 encoding
+ - Bugfix in conditional derivatives to avoid inf/nan values in generated
+ code. This bugfix may break ffc if uflacs is not used, to get around
+ that the old workaround in ufl can be enabled by setting
+ ufl.algorithms.apply_derivatives.CONDITIONAL_WORKAROUND = True
+ at the top of your program.
+ - Allow sum([expressions]) where expressions are nonscalar by defining expr+0==expr
+ - Allow form=0; form -= other;
+ - Deprecate .cell(), .domain(), .element() in favour of .ufl_cell(),
+ .ufl_domain(), .ufl_element(), in multiple classes, to allow
+ closer integration with dolfin.
+ - Remove deprecated properties cell.{d,x,n,volume,circumradius,facet_area}.
+ - Remove ancient form2ufl script
+ - Add new class Mesh to replace Domain
+ - Add new class FunctionSpace(mesh, element)
+ - Make FiniteElement classes take Cell, not Domain.
+ - Large reworking of symbolic geometry pipeline
+ - Implement symbolic Piola mappings
1.6.0 [2015-07-28]
- Change approach to attaching __hash__ implementation to accomodate python 3
- Implement new non-recursive traversal based hash computation
diff --git a/README b/README
index c8b56f0..d4cb0eb 100644
--- a/README
+++ b/README
@@ -10,7 +10,7 @@ notation close to mathematical notation.
Authors:
| Martin Sandve Alnæs <martinal at simula.no>
- | Anders Logg <logg at simula.no>
+ | Anders Logg <logg at chalmers.se>
Contributors:
| Kristian B. Ølgaard <k.b.oelgaard at gmail.com>
@@ -18,12 +18,27 @@ Contributors:
| Marie E. Rognes <meg at simula.no>
| Kent-Andre Mardal <kent-and at simula.no>
| Johan Hake <hake at simula.no>
- | David Ham <David.Ham at imperial.ac.uk>
+ | David Ham <david.ham at imperial.ac.uk>
| Florian Rathgeber <f.rathgeber10 at imperial.ac.uk>
| Andrew McRae <a.mcrae12 at imperial.ac.uk>
| Lawrence Mitchell <lawrence.mitchell at imperial.ac.uk>
| Johannes Ring <johannr at simula.no>
+UFL is described in the paper:
+
+Alnæs, M. S., Logg A., Ølgaard, K. B., Rognes, M. E. and Wells,
+G. N. (2014). Unified Form Language: A domain-specific language for
+weak formulations of partial differential equations. *ACM
+Transactions on Mathematical Software* 40(2), Article 9, 37 pages.
+<http://dx.doi.org/doi:10.1145/2566630>,
+<http://arxiv.org/abs/1211.4047>
+
+
+Documentation
+=============
+
+The UFL documentation is hosted at http://fenics-ufl.rtfd.org/.
+
Installation
============
@@ -50,15 +65,13 @@ Directories
- doc/
- The UFL manual resides here.
+ The UFL documentation resides here. See doc/sphinx/README for how to
+ generate the docucmentation.
- test/
- Unit tests for the UFL implementation. Run all tests by typing "python test.py" inside the test/ directory.
-
-- sandbox/
-
- A place for experimental scripts and other unofficial code.
+ Unit tests for the UFL implementation. Run all tests by typing
+ "python test.py" inside the test/ directory.
Utilities
@@ -76,11 +89,13 @@ after installation.
About the Python modules
========================
-The global namespace of the module ufl contains the entire UFL language::
+The global namespace of the module ufl contains the entire UFL
+language::
from ufl import *
-Form compilers may want to import additional implementation details like::
+Form compilers may want to import additional implementation details
+like::
from ufl.classes import *
@@ -108,7 +123,7 @@ Contact
Send feature requests and questions to
- fenics at fenicsproject.org
+ fenics-dev at googlegroups.com
The Git source repository for UFL is located at
diff --git a/doc/sphinx/Makefile b/doc/sphinx/Makefile
index 61d9077..57214e4 100644
--- a/doc/sphinx/Makefile
+++ b/doc/sphinx/Makefile
@@ -7,12 +7,19 @@ SPHINXBUILD = sphinx-build
PAPER =
BUILDDIR = build
+# User-friendly check for sphinx-build
+ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1)
+$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/)
+endif
+
# Internal variables.
PAPEROPT_a4 = -D latex_paper_size=a4
PAPEROPT_letter = -D latex_paper_size=letter
ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source
+# the i18n builder cannot share the environment and doctrees with the others
+I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source
-.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest
+.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest coverage gettext
help:
@echo "Please use \`make <target>' where <target> is one of"
@@ -23,18 +30,26 @@ help:
@echo " json to make JSON files"
@echo " htmlhelp to make HTML files and a HTML help project"
@echo " qthelp to make HTML files and a qthelp project"
+ @echo " applehelp to make an Apple Help Book"
@echo " devhelp to make HTML files and a Devhelp project"
@echo " epub to make an epub"
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
@echo " latexpdf to make LaTeX files and run them through pdflatex"
+ @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx"
@echo " text to make text files"
@echo " man to make manual pages"
+ @echo " texinfo to make Texinfo files"
+ @echo " info to make Texinfo files and run them through makeinfo"
+ @echo " gettext to make PO message catalogs"
@echo " changes to make an overview of all changed/added/deprecated items"
+ @echo " xml to make Docutils-native XML files"
+ @echo " pseudoxml to make pseudoxml-XML files for display purposes"
@echo " linkcheck to check all external links for integrity"
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
+ @echo " coverage to run coverage check of the documentation (if enabled)"
clean:
- -rm -rf $(BUILDDIR)/*
+ rm -rf $(BUILDDIR)/*
html:
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
@@ -72,18 +87,17 @@ qthelp:
@echo
@echo "Build finished; now you can run "qcollectiongenerator" with the" \
".qhcp project file in $(BUILDDIR)/qthelp, like this:"
- @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/DOLFIN.qhcp"
+ @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/UnifiedFormLanguageUFL.qhcp"
@echo "To view the help file:"
- @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/DOLFIN.qhc"
+ @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/UnifiedFormLanguageUFL.qhc"
-devhelp:
- $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
+applehelp:
+ $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp
@echo
- @echo "Build finished."
- @echo "To view the help file:"
- @echo "# mkdir -p $$HOME/.local/share/devhelp/DOLFIN"
- @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/DOLFIN"
- @echo "# devhelp"
+ @echo "Build finished. The help book is in $(BUILDDIR)/applehelp."
+ @echo "N.B. You won't be able to view it unless you put it in" \
+ "~/Library/Documentation/Help or install it in your application" \
+ "bundle."
epub:
$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
@@ -100,7 +114,13 @@ latex:
latexpdf:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo "Running LaTeX files through pdflatex..."
- make -C $(BUILDDIR)/latex all-pdf
+ $(MAKE) -C $(BUILDDIR)/latex all-pdf
+ @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
+
+latexpdfja:
+ $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+ @echo "Running LaTeX files through platex and dvipdfmx..."
+ $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
text:
@@ -113,6 +133,24 @@ man:
@echo
@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
+texinfo:
+ $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
+ @echo
+ @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
+ @echo "Run \`make' in that directory to run these through makeinfo" \
+ "(use \`make info' here to do that automatically)."
+
+info:
+ $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
+ @echo "Running Texinfo files through makeinfo..."
+ make -C $(BUILDDIR)/texinfo info
+ @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
+
+gettext:
+ $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
+ @echo
+ @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
+
changes:
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
@echo
@@ -128,3 +166,18 @@ doctest:
$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
@echo "Testing of doctests in the sources finished, look at the " \
"results in $(BUILDDIR)/doctest/output.txt."
+
+coverage:
+ $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage
+ @echo "Testing of coverage in the sources finished, look at the " \
+ "results in $(BUILDDIR)/coverage/python.txt."
+
+xml:
+ $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml
+ @echo
+ @echo "Build finished. The XML files are in $(BUILDDIR)/xml."
+
+pseudoxml:
+ $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml
+ @echo
+ @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."
diff --git a/doc/sphinx/README b/doc/sphinx/README
index ed81a2f..57dd5aa 100644
--- a/doc/sphinx/README
+++ b/doc/sphinx/README
@@ -1,4 +1,27 @@
-To build the documentation, run `scripts/makedoc` from the
-top-level UFL directory. Running `make html` directly from
-within this directory won't work (unless the makedoc script
-has been run first).
+====================
+Sphinx documentation
+====================
+
+UFL is documented using Sphinx and reStructured text. The
+documnentation is hosted at http://fenics-ufl.readthedocs.org/. The
+online documentation is automatically updated upon pushes to the UFL
+master branch.
+
+
+Updating the API documentation
+==============================
+
+If the UFL API is changed, the script::
+
+ ./generate-apidoc
+
+must be run to update the autodoc file. The script can be run from any
+directory.
+
+
+Building the documentation locally
+==================================
+
+The HTML documentation can be built locally using::
+
+ make html
diff --git a/ufl/indexing.py b/doc/sphinx/generate-apidoc
old mode 100644
new mode 100755
similarity index 61%
copy from ufl/indexing.py
copy to doc/sphinx/generate-apidoc
index 30da4f1..c36179d
--- a/ufl/indexing.py
+++ b/doc/sphinx/generate-apidoc
@@ -1,6 +1,4 @@
-"""This module defines the single index types and some internal index utilities."""
-
-# Copyright (C) 2008-2014 Martin Sandve Alnes and Anders Logg
+# Copyright (C) 2015 Garth N. Wells
#
# This file is part of UFL.
#
@@ -17,5 +15,15 @@
# You should have received a copy of the GNU Lesser General Public License
# along with UFL. If not, see <http://www.gnu.org/licenses/>.
-# TODO: Fix imports and remove this file
-from ufl.core.multiindex import IndexBase, Index, FixedIndex, MultiIndex, indices, as_multi_index
+# This script calls sphinx-apidoc to generate files ready for autodoc
+
+echo ""
+echo "--- Generating UFL autodoc RST files"
+echo ""
+
+# Get location of Sphinx files
+SPHINX_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+SPHINX_SOURCE_DIR=$SPHINX_DIR/source
+
+# Generate .rst files ready for autodoc
+sphinx-apidoc -f -d 1 -o $SPHINX_SOURCE_DIR/api-doc $SPHINX_DIR/../../ufl
diff --git a/doc/sphinx/scripts/README_generate_modules.rst b/doc/sphinx/scripts/README_generate_modules.rst
deleted file mode 100644
index 544cb93..0000000
--- a/doc/sphinx/scripts/README_generate_modules.rst
+++ /dev/null
@@ -1,29 +0,0 @@
-
-Generate Modules
-================
-
-This script parses a directory tree looking for python modules and packages and
-creates ReST files appropriately to create code documentation with Sphinx.
-It also creates a modules index.
-
-
-Usage::
-
- Usage: generate_modules.py [options] <package path> [exclude paths, ...]
-
- Note: By default this script will not overwrite already created files.
-
- Options:
- -h, --help show this help message and exit
- -n HEADER, --doc-header=HEADER
- Documentation Header (default=Project)
- -d DESTDIR, --dest-dir=DESTDIR
- Output destination directory
- -s SUFFIX, --suffix=SUFFIX
- module suffix (default=txt)
- -m MAXDEPTH, --maxdepth=MAXDEPTH
- Maximum depth of submodules to show in the TOC
- (default=4)
- -r, --dry-run Run the script without creating the files
- -f, --force Overwrite all the files
- -t, --no-toc Don't create the table of content file
diff --git a/doc/sphinx/scripts/generate_index.py b/doc/sphinx/scripts/generate_index.py
deleted file mode 100755
index f0ed75f..0000000
--- a/doc/sphinx/scripts/generate_index.py
+++ /dev/null
@@ -1,97 +0,0 @@
-#!/usr/bin/env python
-
-# Copyright (C) 2011 Marie E. Rognes
-#
-# This file is part of UFL.
-#
-# UFL is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Lesser General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# UFL is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with UFL. If not, see <http://www.gnu.org/licenses/>.
-#
-# First added: 2011-06-09
-# Last changed: 2011-06-09
-
-#
-# This is a naive utility script for adding some labels to generated
-# .rst and for creating a main level index. It is used by
-# scripts/makedoc after generating .rst
-#
-
-import os, sys
-
-index_template = """
-
-#############################################
-Documentation for UFL v%s
-#############################################
-
-.. UFL Language Reference
-.. ======================
-..
-.. * (What is accessible?)
-
-UFL Library Reference
-=====================
-
-* :ref:`UFL Programmer's Reference <ufl_package>` (Packages and Modules. Everything.)
-
-.. toctree::
- :hidden:
- :maxdepth: 1
-
- modules
-
-
-"""
-
-def insert_labels(directory, filenames):
- """
- Insert labels based on filename for those files defined by the
- given filenames relative to directory
- """
-
- for name in filenames:
- filename = os.path.join(directory, name)
- file = open(filename)
- text = file.read()
- file.close()
-
- label = "\n.. _%s_package:\n\n" % "_".join(name.split(".")[:-1])
- modded_text = label + text
- print(("Adding label to %s" % filename))
- file = open(filename, "w")
- file.write(modded_text)
- file.close()
-
-def generate_index_file(output_dir, version):
-
- text = index_template % version
- filename = os.path.join(output_dir, "index.rst")
-
- print(("Writing documentation index file to %s" % filename))
- file = open(filename, "w")
- file.write(text)
- file.close()
-
-def main(input_dir, version):
-
- files = ["ufl.rst", "ufl.finiteelement.rst", "ufl.algorithms.rst"]
- insert_labels(input_dir, files)
- generate_index_file(input_dir, version)
-
-if __name__ == '__main__':
-
- if len(sys.argv) != 3:
- print("Usage: python generate_index.py input_directory version")
- exit()
-
- main(sys.argv[1], sys.argv[2])
diff --git a/doc/sphinx/scripts/generate_modules.py b/doc/sphinx/scripts/generate_modules.py
deleted file mode 100755
index 0376207..0000000
--- a/doc/sphinx/scripts/generate_modules.py
+++ /dev/null
@@ -1,269 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-"""
-sphinx-autopackage-script
-
-This script parses a directory tree looking for python modules and packages and
-creates ReST files appropriately to create code documentation with Sphinx.
-It also creates a modules index (named modules.<suffix>).
-"""
-
-# Copyright 2008 Société des arts technologiques (SAT), http://www.sat.qc.ca/
-# Copyright 2010 Thomas Waldmann <tw AT waldmann-edv DOT de>
-# All rights reserved.
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-
-# Modified by Marie E. Rognes (meg at simula.no), 2011
-
-import os
-import optparse
-
-# automodule options
-OPTIONS = ['members',
- 'undoc-members',
- # 'inherited-members', # disabled because there's a bug in sphinx
- 'show-inheritance',
- ]
-
-INIT = '__init__.py'
-
-def makename(package, module):
- """Join package and module with a dot."""
- # Both package and module can be None/empty.
- if package:
- name = package
- if module:
- name += '.' + module
- else:
- name = module
- return name
-
-def write_file(name, text, opts):
- """Write the output file for module/package <name>."""
- if opts.dryrun:
- return
- fname = os.path.join(opts.destdir, "%s.%s" % (name, opts.suffix))
- if not opts.force and os.path.isfile(fname):
- print(('File %s already exists, skipping.' % fname))
- else:
- print(('Creating file %s.' % fname))
- f = open(fname, 'w')
- f.write(text)
- f.close()
-
-def format_heading(level, text):
- """Create a heading of <level> [1, 2 or 3 supported]."""
- underlining = ['=', '-', '~', ][level-1] * len(text)
- return '%s\n%s\n\n' % (text, underlining)
-
-def format_directive(module, package=None):
- """Create the automodule directive and add the options."""
- directive = '.. automodule:: %s\n' % makename(package, module)
-
- # MER: Treat stuff from __init__ a little differently.
- if "__init__" in module:
- directive += ' :%s:\n' % "noindex"
- else:
- for option in OPTIONS:
- directive += ' :%s:\n' % option
- return directive
-
-def create_module_file(package, module, opts):
- """Build the text of the file and write the file."""
- text = format_heading(1, '%s Module' % module)
- text += format_heading(2, ':mod:`%s` Module' % module)
- text += format_directive(module, package)
- write_file(makename(package, module), text, opts)
-
-def create_package_file(root, master_package, subroot, py_files, opts, subs):
- """Build the text of the file and write the file."""
- package = os.path.split(root)[-1]
- text = format_heading(1, '%s Package' % package)
- # add each package's module
- for py_file in py_files:
- if shall_skip(os.path.join(root, py_file)):
- continue
- is_package = py_file == INIT
- py_file = os.path.splitext(py_file)[0]
- py_path = makename(subroot, py_file)
- if is_package:
- heading = ':mod:`%s` Package' % package
- else:
- heading = ':mod:`%s` Module' % py_file
- text += format_heading(2, heading)
- text += format_directive(is_package and subroot or py_path, master_package)
- text += '\n'
-
- # build a list of directories that are packages (they contain an INIT file)
- subs = [sub for sub in subs if os.path.isfile(os.path.join(root, sub, INIT))]
- # if there are some package directories, add a TOC for theses subpackages
- if subs:
- text += format_heading(2, 'Subpackages')
- text += '.. toctree::\n\n'
- for sub in subs:
- text += ' %s.%s\n' % (makename(master_package, subroot), sub)
- text += '\n'
-
- write_file(makename(master_package, subroot), text, opts)
-
-def create_modules_toc_file(master_package, modules, opts, name='modules'):
- """
- Create the module's index.
- """
- text = format_heading(1, '%s Modules' % opts.header)
- text += '.. toctree::\n'
- text += ' :maxdepth: %s\n\n' % opts.maxdepth
-
- modules.sort()
- prev_module = ''
- for module in modules:
- # look if the module is a subpackage and, if yes, ignore it
- if module.startswith(prev_module + '.'):
- continue
- prev_module = module
- text += ' %s\n' % module
-
- write_file(name, text, opts)
-
-def shall_skip(module):
- """
- Check if we want to skip this module.
- """
- # skip it, if there is nothing (or just \n or \r\n) in the file
-
- return (os.path.getsize(module) < 3)
-
-def recurse_tree(path, excludes, opts):
- """
- Look for every file in the directory tree and create the corresponding
- ReST files.
- """
- # use absolute path for root, as relative paths like '../../foo' cause
- # 'if "/." in root ...' to filter out *all* modules otherwise
- path = os.path.abspath(path)
- # check if the base directory is a package and get is name
- if INIT in os.listdir(path):
- package_name = path.split(os.path.sep)[-1]
- else:
- package_name = None
-
- toc = []
- tree = os.walk(path, False)
- for root, subs, files in tree:
- # keep only the Python script files
- py_files = sorted([f for f in files if os.path.splitext(f)[1] == '.py'])
- if INIT in py_files:
- py_files.remove(INIT)
- py_files.insert(0, INIT)
- # remove hidden ('.') and private ('_') directories
- subs = sorted([sub for sub in subs if sub[0] not in ['.', '_']])
- # check if there are valid files to process
- # TODO: could add check for windows hidden files
- if "/." in root or "/_" in root \
- or not py_files \
- or is_excluded(root, excludes):
- continue
- if INIT in py_files:
- # we are in package ...
- if (# ... with subpackage(s)
- subs
- or
- # ... with some module(s)
- len(py_files) > 1
- or
- # ... with a not-to-be-skipped INIT file
- not shall_skip(os.path.join(root, INIT))
- ):
- subroot = root[len(path):].lstrip(os.path.sep).replace(os.path.sep, '.')
- create_package_file(root, package_name, subroot, py_files, opts, subs)
- toc.append(makename(package_name, subroot))
- elif root == path:
- # if we are at the root level, we don't require it to be a package
- for py_file in py_files:
- if not shall_skip(os.path.join(path, py_file)):
- module = os.path.splitext(py_file)[0]
- create_module_file(package_name, module, opts)
- toc.append(makename(package_name, module))
-
- # create the module's index
- if not opts.notoc:
- create_modules_toc_file(package_name, toc, opts)
-
-def normalize_excludes(rootpath, excludes):
- """
- Normalize the excluded directory list:
- * must be either an absolute path or start with rootpath,
- * otherwise it is joined with rootpath
- * with trailing slash
- """
- sep = os.path.sep
- f_excludes = []
- for exclude in excludes:
- if not os.path.isabs(exclude) and not exclude.startswith(rootpath):
- exclude = os.path.join(rootpath, exclude)
- if not exclude.endswith(sep):
- exclude += sep
- f_excludes.append(exclude)
- return f_excludes
-
-def is_excluded(root, excludes):
- """
- Check if the directory is in the exclude list.
-
- Note: by having trailing slashes, we avoid common prefix issues, like
- e.g. an exlude "foo" also accidentally excluding "foobar".
- """
- sep = os.path.sep
- if not root.endswith(sep):
- root += sep
- for exclude in excludes:
- if root.startswith(exclude):
- return True
- return False
-
-def main():
- """
- Parse and check the command line arguments.
- """
- parser = optparse.OptionParser(usage="""usage: %prog [options] <package path> [exclude paths, ...]
-
-Note: By default this script will not overwrite already created files.""")
- parser.add_option("-n", "--doc-header", action="store", dest="header", help="Documentation Header (default=Project)", default="Project")
- parser.add_option("-d", "--dest-dir", action="store", dest="destdir", help="Output destination directory", default="")
- parser.add_option("-s", "--suffix", action="store", dest="suffix", help="module suffix (default=txt)", default="txt")
- parser.add_option("-m", "--maxdepth", action="store", dest="maxdepth", help="Maximum depth of submodules to show in the TOC (default=4)", type="int", default=4)
- parser.add_option("-r", "--dry-run", action="store_true", dest="dryrun", help="Run the script without creating the files")
- parser.add_option("-f", "--force", action="store_true", dest="force", help="Overwrite all the files")
- parser.add_option("-t", "--no-toc", action="store_true", dest="notoc", help="Don't create the table of content file")
- (opts, args) = parser.parse_args()
- if not args:
- parser.error("package path is required.")
- else:
- rootpath, excludes = args[0], args[1:]
- if os.path.isdir(rootpath):
- # check if the output destination is a valid directory
- if opts.destdir and os.path.isdir(opts.destdir):
- excludes = normalize_excludes(rootpath, excludes)
- recurse_tree(rootpath, excludes, opts)
- else:
- print(('%s is not a valid output destination directory.' % opts.destdir))
- else:
- print(('%s is not a valid directory.' % rootpath))
-
-
-if __name__ == '__main__':
- main()
-
diff --git a/doc/sphinx/source/_themes/fenics/README b/doc/sphinx/source/_themes/fenics/README
deleted file mode 100644
index 525d648..0000000
--- a/doc/sphinx/source/_themes/fenics/README
+++ /dev/null
@@ -1,2 +0,0 @@
-This theme is based on the default 'haiku' theme from Sphinx.
-Modified by Anders Logg for the FEniCS Project web pages, 2011.
diff --git a/doc/sphinx/source/_themes/fenics/layout.html b/doc/sphinx/source/_themes/fenics/layout.html
deleted file mode 100644
index 3b2572c..0000000
--- a/doc/sphinx/source/_themes/fenics/layout.html
+++ /dev/null
@@ -1,69 +0,0 @@
-{#
- fenics/layout.html
- ~~~~~~~~~~~~~~~~~~
-
- Sphinx layout template for the fenics theme.
-
- :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
- :license: BSD, see LICENSE for details.
-
- This theme is based on the default 'haiku' theme from Sphinx.
- Modified by Anders Logg for the FEniCS Project web pages, 2011.
-
-#}
-{% extends "basic/layout.html" %}
-{% set script_files = script_files + ['_static/platform_sniff.js'] %}
-{% set css_files = css_files + ['_static/print.css'] %}
-
-{# do not display relbars #}
-{% block relbar1 %}{% endblock %}
-{% block relbar2 %}{% endblock %}
-
-{% macro nav() %}
- <p>
- {%- block fenicsrel1 %}
- {%- endblock %}
- <a href="http://www.fenicsproject.org/new/about">About</a>
- <a href="http://www.fenicsproject.org/new/installation">Installation</a>
- <a href="http://www.fenicsproject.org/new/documentation">Documentation</a>
- <a href="http://www.fenicsproject.org/new/developers">Developers</a>
- <a href="http://www.fenicsproject.org/new/citing">Citing</a>
- <a href="http://www.fenicsproject.org/new/contact">Contact</a>
- {%- block fenicsrel2 %}
- {%- endblock %}
- </p>
-{% endmacro %}
-
-{% block content %}
- <div class="header">
- {%- block fenicsheader %}
- {%- if theme_full_logo != "false" %}
- <a href="{{ pathto('index') }}">
- <img class="logo" src="{{ pathto('_static/' + logo, 1) }}" alt="Logo"/>
- </a>
- {%- else %}
- {%- if logo -%}
- <img class="rightlogo" src="{{ pathto('_static/' + logo, 1) }}" alt="Logo"/>
- {%- endif -%}
- <h1 class="heading"><a href="{{ pathto('index') }}">
- <span>{{ shorttitle|e }}</span></a></h1>
- <h2 class="heading"><span>{{ title|striptags|e }}</span></h2>
- {%- endif %}
- {%- endblock %}
- </div>
- <div class="topnav">
- {{ nav() }}
- </div>
- <div class="content">
- {#{%- if display_toc %}
- <div id="toc">
- <h3>Table Of Contents</h3>
- {{ toc }}
- </div>
- {%- endif %}#}
- {% block body %}{% endblock %}
- </div>
- <div class="bottomnav">
- {{ nav() }}
- </div>
-{% endblock %}
diff --git a/doc/sphinx/source/_themes/fenics/static/alert_info_32.png b/doc/sphinx/source/_themes/fenics/static/alert_info_32.png
deleted file mode 100644
index 05b4fe8..0000000
Binary files a/doc/sphinx/source/_themes/fenics/static/alert_info_32.png and /dev/null differ
diff --git a/doc/sphinx/source/_themes/fenics/static/alert_warning_32.png b/doc/sphinx/source/_themes/fenics/static/alert_warning_32.png
deleted file mode 100644
index f13611c..0000000
Binary files a/doc/sphinx/source/_themes/fenics/static/alert_warning_32.png and /dev/null differ
diff --git a/doc/sphinx/source/_themes/fenics/static/bg-page.png b/doc/sphinx/source/_themes/fenics/static/bg-page.png
deleted file mode 100644
index 0022f00..0000000
Binary files a/doc/sphinx/source/_themes/fenics/static/bg-page.png and /dev/null differ
diff --git a/doc/sphinx/source/_themes/fenics/static/bullet_orange.png b/doc/sphinx/source/_themes/fenics/static/bullet_orange.png
deleted file mode 100644
index ad5d02f..0000000
Binary files a/doc/sphinx/source/_themes/fenics/static/bullet_orange.png and /dev/null differ
diff --git a/doc/sphinx/source/_themes/fenics/static/dolfin.png b/doc/sphinx/source/_themes/fenics/static/dolfin.png
deleted file mode 100644
index ad3c38b..0000000
Binary files a/doc/sphinx/source/_themes/fenics/static/dolfin.png and /dev/null differ
diff --git a/doc/sphinx/source/_themes/fenics/static/dorsal.png b/doc/sphinx/source/_themes/fenics/static/dorsal.png
deleted file mode 100644
index 2637acc..0000000
Binary files a/doc/sphinx/source/_themes/fenics/static/dorsal.png and /dev/null differ
diff --git a/doc/sphinx/source/_themes/fenics/static/fenics.css_t b/doc/sphinx/source/_themes/fenics/static/fenics.css_t
deleted file mode 100644
index 7d3457b..0000000
--- a/doc/sphinx/source/_themes/fenics/static/fenics.css_t
+++ /dev/null
@@ -1,388 +0,0 @@
-/*
- * fenics.css_t
- * ~~~~~~~~~~~~
- *
- * Sphinx stylesheet -- fenics theme.
- *
- * Adapted from http://haiku-os.org/docs/Haiku-doc.css.
- * Original copyright message:
- *
- * Copyright 2008-2009, Haiku. All rights reserved.
- * Distributed under the terms of the MIT License.
- *
- * Authors:
- * Francois Revol <revol at free.fr>
- * Stephan Assmus <superstippi at gmx.de>
- * Braden Ewing <brewin at gmail.com>
- * Humdinger <humdingerb at gmail.com>
- *
- * :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
- * :license: BSD, see LICENSE for details.
- *
- * This theme is based on the default 'haiku' theme from Sphinx.
- * Modified by Anders Logg for the FEniCS Project web pages, 2011.
- */
-
- at import url("basic.css");
-
-html {
- margin: 0px;
- padding: 0px;
-}
-
-body {
- line-height: 1.5;
- margin: auto;
- padding: 0px;
- font-family: "DejaVu Sans", Arial, Helvetica, sans-serif;
- min-width: 59em;
- max-width: 70em;
- color: {{ theme_textcolor }};
-}
-
-div.footer {
- padding: 8px;
- font-size: 11px;
- text-align: center;
- letter-spacing: 0.5px;
-}
-
-/* link colors and text decoration */
-
-a:link {
- font-weight: bold;
- text-decoration: none;
- color: {{ theme_linkcolor }};
-}
-
-a:visited {
- font-weight: bold;
- text-decoration: none;
- color: {{ theme_visitedlinkcolor }};
-}
-
-a:hover, a:active {
- text-decoration: underline;
- color: {{ theme_hoverlinkcolor }};
-}
-
-/* Some headers act as anchors, don't give them a hover effect */
-
-h1 a:hover, a:active {
- text-decoration: none;
- color: {{ theme_headingcolor }};
-}
-
-h2 a:hover, a:active {
- text-decoration: none;
- color: {{ theme_headingcolor }};
-}
-
-h3 a:hover, a:active {
- text-decoration: none;
- color: {{ theme_headingcolor }};
-}
-
-h4 a:hover, a:active {
- text-decoration: none;
- color: {{ theme_headingcolor }};
-}
-
-a.headerlink {
- color: #a7ce38;
- padding-left: 5px;
-}
-
-a.headerlink:hover {
- color: #a7ce38;
-}
-
-/* basic text elements */
-
-div.content {
- margin-top: 20px;
- margin-left: 40px;
- margin-right: 40px;
- margin-bottom: 50px;
- font-size: 0.9em;
-}
-
-/* heading and navigation */
-
-div.header {
- position: relative;
- left: 0px;
- top: 0px;
- height: 85px;
- padding: 0 40px;
- background: #FFF url(bg-page.png) top left repeat-x;
- color: #ffffff;
-}
-
-div.header h1 {
- font-size: 1.6em;
- font-weight: normal;
- letter-spacing: 1px;
- color: {{ theme_headingcolor }};
- border: 0;
- margin: 0;
- padding-top: 15px;
-}
-
-div.header h1 a {
- font-weight: normal;
- color: #ffffff;
-}
-
-div.header h2 {
- font-size: 1.3em;
- font-weight: normal;
- letter-spacing: 1px;
- text-transform: uppercase;
- color: #e0e0e0;
- border: 0;
- margin-top: -3px;
- padding: 0;
-}
-
-div.header img.rightlogo {
- float: right;
-}
-
-div.title {
- font-size: 1.3em;
- font-weight: bold;
- color: {{ theme_headingcolor }};
- border-bottom: dotted thin #e0e0e0;
- margin-bottom: 25px;
- color: #ffffff;
-}
-
-div.topnav {
- background: #e0e0e0;
-}
-
-div.topnav p {
- margin-top: 0;
- margin-left: 40px;
- margin-right: 40px;
- margin-bottom: 0px;
- text-align: right;
- font-size: 0.8em;
-}
-
-div.bottomnav {
- background: #d81e28;
-}
-
-div.bottomnav p {
- margin-right: 40px;
- text-align: right;
- font-size: 0.8em;
-}
-
-a.uplink {
- font-weight: normal;
-}
-
-/* contents box */
-
-table.index {
- margin: 0px 0px 30px 30px;
- padding: 1px;
- border-width: 1px;
- border-style: dotted;
- border-color: #e0e0e0;
-}
-
-table.index tr.heading {
- background-color: #e0e0e0;
- text-align: center;
- font-weight: bold;
- font-size: 1.1em;
-}
-
-table.index tr.index {
- background-color: #eeeeee;
-}
-
-table.index td {
- padding: 5px 20px;
-}
-
-table.index a:link, table.index a:visited {
- font-weight: normal;
- text-decoration: none;
- color: {{ theme_linkcolor }};
-}
-table.index a:hover, table.index a:active {
- text-decoration: underline;
- color: {{ theme_hoverlinkcolor }};
-}
-
-/* FEniCS User Guide styles and layout */
-
-/* Rounded corner boxes */
-/* Common declarations */
-div.admonition {
- -webkit-border-radius: 10px;
- -khtml-border-radius: 10px;
- -moz-border-radius: 10px;
- border-radius: 10px;
- border-style: dotted;
- border-width: thin;
- border-color: #dcdcdc;
- padding: 10px 15px 10px 15px;
- margin-bottom: 15px;
- margin-top: 15px;
-}
-
-div.note {
- padding: 10px 15px 10px 80px;
- background: #e4ffde url(alert_info_32.png) 15px 15px no-repeat;
- min-height: 42px;
-}
-
-div.warning {
- padding: 10px 15px 10px 80px;
- background: #fffbc6 url(alert_warning_32.png) 15px 15px no-repeat;
- min-height: 42px;
-}
-
-div.seealso {
- background: #e4ffde;
-}
-
-/* More layout and styles */
-h1 {
- font-size: 1.3em;
- font-weight: bold;
- color: {{ theme_headingcolor }};
- border-bottom: dotted thin #e0e0e0;
- margin-top: 30px;
-}
-
-h2 {
- font-size: 1.2em;
- font-weight: normal;
- color: #000000;
- border-bottom: dotted thin #e0e0e0;
- margin-top: 30px;
-}
-
-h3 {
- font-size: 1.1em;
- font-weight: normal;
- color: {{ theme_headingcolor }};
- margin-top: 30px;
-}
-
-h4 {
- font-size: 1.0em;
- font-weight: normal;
- color: {{ theme_headingcolor }};
- margin-top: 30px;
-}
-
-p {
- text-align: justify;
-}
-
-p.last {
- margin-bottom: 0;
-}
-
-ol {
- padding-left: 20px;
-}
-
-ul {
- padding-left: 5px;
- margin-top: 3px;
-}
-
-li {
- line-height: 1.3;
-}
-
-div.content ul > li {
- -moz-background-clip:border;
- -moz-background-inline-policy:continuous;
- -moz-background-origin:padding;
- background: transparent url(bullet_orange.png) no-repeat scroll left 0.45em;
- list-style-image: none;
- list-style-type: none;
- padding: 0 0 0 1.666em;
- margin-bottom: 3px;
-}
-
-td {
- vertical-align: top;
-}
-
-tt {
- background-color: #e2e2e2;
- font-size: 1.0em;
- font-family: monospace;
-}
-
-pre {
- border-color: #0c3762;
- border-style: dotted;
- border-width: thin;
- margin: 0 0 12px 0;
- padding: 0.8em;
- background-color: #f0f0f0;
-}
-
-hr {
- border-top: 1px solid #ccc;
- border-bottom: 0;
- border-right: 0;
- border-left: 0;
- margin-bottom: 10px;
- margin-top: 20px;
-}
-
-/* printer only pretty stuff */
- at media print {
- .noprint {
- display: none;
- }
- /* for acronyms we want their definitions inlined at print time */
- acronym[title]:after {
- font-size: small;
- content: " (" attr(title) ")";
- font-style: italic;
- }
- /* and not have mozilla dotted underline */
- acronym {
- border: none;
- }
- div.topnav, div.bottomnav, div.header, table.index {
- display: none;
- }
- div.content {
- margin: 0px;
- padding: 0px;
- }
- html {
- background: #FFF;
- }
-}
-
-.viewcode-back {
- font-family: "DejaVu Sans", Arial, Helvetica, sans-serif;
-}
-
-div.viewcode-block:target {
- background-color: #f4debf;
- border-top: 1px solid #ac9;
- border-bottom: 1px solid #ac9;
- margin: -1px -12px;
- padding: 0 12px;
-}
-
-div.title a:visited {
- color: #ffffff;
-}
diff --git a/doc/sphinx/source/_themes/fenics/static/unknown.png b/doc/sphinx/source/_themes/fenics/static/unknown.png
deleted file mode 100644
index f974136..0000000
Binary files a/doc/sphinx/source/_themes/fenics/static/unknown.png and /dev/null differ
diff --git a/doc/sphinx/source/_themes/fenics/theme.conf b/doc/sphinx/source/_themes/fenics/theme.conf
deleted file mode 100644
index ce9d1dc..0000000
--- a/doc/sphinx/source/_themes/fenics/theme.conf
+++ /dev/null
@@ -1,12 +0,0 @@
-[theme]
-inherit = basic
-stylesheet = fenics.css
-pygments_style = autumn
-
-[options]
-full_logo = false
-textcolor = #333333
-headingcolor = #d81e28
-linkcolor = #d81e28
-visitedlinkcolor = #d81e28
-hoverlinkcolor = #d8363f
diff --git a/doc/sphinx/source/api-doc/modules.rst b/doc/sphinx/source/api-doc/modules.rst
new file mode 100644
index 0000000..ffe8d42
--- /dev/null
+++ b/doc/sphinx/source/api-doc/modules.rst
@@ -0,0 +1,7 @@
+ufl
+===
+
+.. toctree::
+ :maxdepth: 1
+
+ ufl
diff --git a/doc/sphinx/source/api-doc/ufl.algorithms.rst b/doc/sphinx/source/api-doc/ufl.algorithms.rst
new file mode 100644
index 0000000..c61f659
--- /dev/null
+++ b/doc/sphinx/source/api-doc/ufl.algorithms.rst
@@ -0,0 +1,270 @@
+ufl.algorithms package
+======================
+
+Submodules
+----------
+
+ufl.algorithms.ad module
+------------------------
+
+.. automodule:: ufl.algorithms.ad
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+ufl.algorithms.analysis module
+------------------------------
+
+.. automodule:: ufl.algorithms.analysis
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+ufl.algorithms.apply_algebra_lowering module
+--------------------------------------------
+
+.. automodule:: ufl.algorithms.apply_algebra_lowering
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+ufl.algorithms.apply_derivatives module
+---------------------------------------
+
+.. automodule:: ufl.algorithms.apply_derivatives
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+ufl.algorithms.apply_function_pullbacks module
+----------------------------------------------
+
+.. automodule:: ufl.algorithms.apply_function_pullbacks
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+ufl.algorithms.apply_geometry_lowering module
+---------------------------------------------
+
+.. automodule:: ufl.algorithms.apply_geometry_lowering
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+ufl.algorithms.apply_integral_scaling module
+--------------------------------------------
+
+.. automodule:: ufl.algorithms.apply_integral_scaling
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+ufl.algorithms.apply_restrictions module
+----------------------------------------
+
+.. automodule:: ufl.algorithms.apply_restrictions
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+ufl.algorithms.argument_dependencies module
+-------------------------------------------
+
+.. automodule:: ufl.algorithms.argument_dependencies
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+ufl.algorithms.change_to_reference module
+-----------------------------------------
+
+.. automodule:: ufl.algorithms.change_to_reference
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+ufl.algorithms.check_arities module
+-----------------------------------
+
+.. automodule:: ufl.algorithms.check_arities
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+ufl.algorithms.check_restrictions module
+----------------------------------------
+
+.. automodule:: ufl.algorithms.check_restrictions
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+ufl.algorithms.checks module
+----------------------------
+
+.. automodule:: ufl.algorithms.checks
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+ufl.algorithms.compute_form_data module
+---------------------------------------
+
+.. automodule:: ufl.algorithms.compute_form_data
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+ufl.algorithms.domain_analysis module
+-------------------------------------
+
+.. automodule:: ufl.algorithms.domain_analysis
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+ufl.algorithms.elementtransformations module
+--------------------------------------------
+
+.. automodule:: ufl.algorithms.elementtransformations
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+ufl.algorithms.estimate_degrees module
+--------------------------------------
+
+.. automodule:: ufl.algorithms.estimate_degrees
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+ufl.algorithms.expand_compounds module
+--------------------------------------
+
+.. automodule:: ufl.algorithms.expand_compounds
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+ufl.algorithms.expand_indices module
+------------------------------------
+
+.. automodule:: ufl.algorithms.expand_indices
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+ufl.algorithms.formdata module
+------------------------------
+
+.. automodule:: ufl.algorithms.formdata
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+ufl.algorithms.formfiles module
+-------------------------------
+
+.. automodule:: ufl.algorithms.formfiles
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+ufl.algorithms.formtransformations module
+-----------------------------------------
+
+.. automodule:: ufl.algorithms.formtransformations
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+ufl.algorithms.forward_ad module
+--------------------------------
+
+.. automodule:: ufl.algorithms.forward_ad
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+ufl.algorithms.map_integrands module
+------------------------------------
+
+.. automodule:: ufl.algorithms.map_integrands
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+ufl.algorithms.multifunction module
+-----------------------------------
+
+.. automodule:: ufl.algorithms.multifunction
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+ufl.algorithms.pdiffs module
+----------------------------
+
+.. automodule:: ufl.algorithms.pdiffs
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+ufl.algorithms.predicates module
+--------------------------------
+
+.. automodule:: ufl.algorithms.predicates
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+ufl.algorithms.renumbering module
+---------------------------------
+
+.. automodule:: ufl.algorithms.renumbering
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+ufl.algorithms.replace module
+-----------------------------
+
+.. automodule:: ufl.algorithms.replace
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+ufl.algorithms.signature module
+-------------------------------
+
+.. automodule:: ufl.algorithms.signature
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+ufl.algorithms.transformer module
+---------------------------------
+
+.. automodule:: ufl.algorithms.transformer
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+ufl.algorithms.traversal module
+-------------------------------
+
+.. automodule:: ufl.algorithms.traversal
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+
+Module contents
+---------------
+
+.. automodule:: ufl.algorithms
+ :members:
+ :undoc-members:
+ :show-inheritance:
diff --git a/doc/sphinx/source/api-doc/ufl.core.rst b/doc/sphinx/source/api-doc/ufl.core.rst
new file mode 100644
index 0000000..d1e0564
--- /dev/null
+++ b/doc/sphinx/source/api-doc/ufl.core.rst
@@ -0,0 +1,70 @@
+ufl.core package
+================
+
+Submodules
+----------
+
+ufl.core.compute_expr_hash module
+---------------------------------
+
+.. automodule:: ufl.core.compute_expr_hash
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+ufl.core.expr module
+--------------------
+
+.. automodule:: ufl.core.expr
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+ufl.core.multiindex module
+--------------------------
+
+.. automodule:: ufl.core.multiindex
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+ufl.core.operator module
+------------------------
+
+.. automodule:: ufl.core.operator
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+ufl.core.terminal module
+------------------------
+
+.. automodule:: ufl.core.terminal
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+ufl.core.ufl_id module
+----------------------
+
+.. automodule:: ufl.core.ufl_id
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+ufl.core.ufl_type module
+------------------------
+
+.. automodule:: ufl.core.ufl_type
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+
+Module contents
+---------------
+
+.. automodule:: ufl.core
+ :members:
+ :undoc-members:
+ :show-inheritance:
diff --git a/doc/sphinx/source/api-doc/ufl.corealg.rst b/doc/sphinx/source/api-doc/ufl.corealg.rst
new file mode 100644
index 0000000..a9c430a
--- /dev/null
+++ b/doc/sphinx/source/api-doc/ufl.corealg.rst
@@ -0,0 +1,38 @@
+ufl.corealg package
+===================
+
+Submodules
+----------
+
+ufl.corealg.map_dag module
+--------------------------
+
+.. automodule:: ufl.corealg.map_dag
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+ufl.corealg.multifunction module
+--------------------------------
+
+.. automodule:: ufl.corealg.multifunction
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+ufl.corealg.traversal module
+----------------------------
+
+.. automodule:: ufl.corealg.traversal
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+
+Module contents
+---------------
+
+.. automodule:: ufl.corealg
+ :members:
+ :undoc-members:
+ :show-inheritance:
diff --git a/doc/sphinx/source/api-doc/ufl.finiteelement.rst b/doc/sphinx/source/api-doc/ufl.finiteelement.rst
new file mode 100644
index 0000000..f640e43
--- /dev/null
+++ b/doc/sphinx/source/api-doc/ufl.finiteelement.rst
@@ -0,0 +1,118 @@
+ufl.finiteelement package
+=========================
+
+Submodules
+----------
+
+ufl.finiteelement.brokenelement module
+--------------------------------------
+
+.. automodule:: ufl.finiteelement.brokenelement
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+ufl.finiteelement.elementlist module
+------------------------------------
+
+.. automodule:: ufl.finiteelement.elementlist
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+ufl.finiteelement.enrichedelement module
+----------------------------------------
+
+.. automodule:: ufl.finiteelement.enrichedelement
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+ufl.finiteelement.facetelement module
+-------------------------------------
+
+.. automodule:: ufl.finiteelement.facetelement
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+ufl.finiteelement.finiteelement module
+--------------------------------------
+
+.. automodule:: ufl.finiteelement.finiteelement
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+ufl.finiteelement.finiteelementbase module
+------------------------------------------
+
+.. automodule:: ufl.finiteelement.finiteelementbase
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+ufl.finiteelement.hdivcurl module
+---------------------------------
+
+.. automodule:: ufl.finiteelement.hdivcurl
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+ufl.finiteelement.interiorelement module
+----------------------------------------
+
+.. automodule:: ufl.finiteelement.interiorelement
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+ufl.finiteelement.mixedelement module
+-------------------------------------
+
+.. automodule:: ufl.finiteelement.mixedelement
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+ufl.finiteelement.outerproductelement module
+--------------------------------------------
+
+.. automodule:: ufl.finiteelement.outerproductelement
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+ufl.finiteelement.restrictedelement module
+------------------------------------------
+
+.. automodule:: ufl.finiteelement.restrictedelement
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+ufl.finiteelement.tensorproductelement module
+---------------------------------------------
+
+.. automodule:: ufl.finiteelement.tensorproductelement
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+ufl.finiteelement.traceelement module
+-------------------------------------
+
+.. automodule:: ufl.finiteelement.traceelement
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+
+Module contents
+---------------
+
+.. automodule:: ufl.finiteelement
+ :members:
+ :undoc-members:
+ :show-inheritance:
diff --git a/doc/sphinx/source/api-doc/ufl.formatting.rst b/doc/sphinx/source/api-doc/ufl.formatting.rst
new file mode 100644
index 0000000..92b496e
--- /dev/null
+++ b/doc/sphinx/source/api-doc/ufl.formatting.rst
@@ -0,0 +1,54 @@
+ufl.formatting package
+======================
+
+Submodules
+----------
+
+ufl.formatting.graph module
+---------------------------
+
+.. automodule:: ufl.formatting.graph
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+ufl.formatting.latextools module
+--------------------------------
+
+.. automodule:: ufl.formatting.latextools
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+ufl.formatting.printing module
+------------------------------
+
+.. automodule:: ufl.formatting.printing
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+ufl.formatting.ufl2dot module
+-----------------------------
+
+.. automodule:: ufl.formatting.ufl2dot
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+ufl.formatting.ufl2latex module
+-------------------------------
+
+.. automodule:: ufl.formatting.ufl2latex
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+
+Module contents
+---------------
+
+.. automodule:: ufl.formatting
+ :members:
+ :undoc-members:
+ :show-inheritance:
diff --git a/doc/sphinx/source/api-doc/ufl.rst b/doc/sphinx/source/api-doc/ufl.rst
new file mode 100644
index 0000000..24c0d3b
--- /dev/null
+++ b/doc/sphinx/source/api-doc/ufl.rst
@@ -0,0 +1,354 @@
+ufl package
+===========
+
+Subpackages
+-----------
+
+.. toctree::
+
+ ufl.algorithms
+ ufl.core
+ ufl.corealg
+ ufl.finiteelement
+ ufl.formatting
+ ufl.utils
+
+Submodules
+----------
+
+ufl.algebra module
+------------------
+
+.. automodule:: ufl.algebra
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+ufl.argument module
+-------------------
+
+.. automodule:: ufl.argument
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+ufl.assertions module
+---------------------
+
+.. automodule:: ufl.assertions
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+ufl.cell module
+---------------
+
+.. automodule:: ufl.cell
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+ufl.checks module
+-----------------
+
+.. automodule:: ufl.checks
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+ufl.classes module
+------------------
+
+.. automodule:: ufl.classes
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+ufl.coefficient module
+----------------------
+
+.. automodule:: ufl.coefficient
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+ufl.compound_expressions module
+-------------------------------
+
+.. automodule:: ufl.compound_expressions
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+ufl.conditional module
+----------------------
+
+.. automodule:: ufl.conditional
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+ufl.constantvalue module
+------------------------
+
+.. automodule:: ufl.constantvalue
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+ufl.differentiation module
+--------------------------
+
+.. automodule:: ufl.differentiation
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+ufl.domain module
+-----------------
+
+.. automodule:: ufl.domain
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+ufl.equation module
+-------------------
+
+.. automodule:: ufl.equation
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+ufl.exprcontainers module
+-------------------------
+
+.. automodule:: ufl.exprcontainers
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+ufl.exprequals module
+---------------------
+
+.. automodule:: ufl.exprequals
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+ufl.exproperators module
+------------------------
+
+.. automodule:: ufl.exproperators
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+ufl.form module
+---------------
+
+.. automodule:: ufl.form
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+ufl.formoperators module
+------------------------
+
+.. automodule:: ufl.formoperators
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+ufl.functionspace module
+------------------------
+
+.. automodule:: ufl.functionspace
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+ufl.geometry module
+-------------------
+
+.. automodule:: ufl.geometry
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+ufl.index_combination_utils module
+----------------------------------
+
+.. automodule:: ufl.index_combination_utils
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+ufl.indexed module
+------------------
+
+.. automodule:: ufl.indexed
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+ufl.indexing module
+-------------------
+
+.. automodule:: ufl.indexing
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+ufl.indexsum module
+-------------------
+
+.. automodule:: ufl.indexsum
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+ufl.integral module
+-------------------
+
+.. automodule:: ufl.integral
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+ufl.log module
+--------------
+
+.. automodule:: ufl.log
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+ufl.mathfunctions module
+------------------------
+
+.. automodule:: ufl.mathfunctions
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+ufl.measure module
+------------------
+
+.. automodule:: ufl.measure
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+ufl.objects module
+------------------
+
+.. automodule:: ufl.objects
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+ufl.operators module
+--------------------
+
+.. automodule:: ufl.operators
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+ufl.permutation module
+----------------------
+
+.. automodule:: ufl.permutation
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+ufl.precedence module
+---------------------
+
+.. automodule:: ufl.precedence
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+ufl.protocols module
+--------------------
+
+.. automodule:: ufl.protocols
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+ufl.referencevalue module
+-------------------------
+
+.. automodule:: ufl.referencevalue
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+ufl.restriction module
+----------------------
+
+.. automodule:: ufl.restriction
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+ufl.sobolevspace module
+-----------------------
+
+.. automodule:: ufl.sobolevspace
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+ufl.sorting module
+------------------
+
+.. automodule:: ufl.sorting
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+ufl.split_functions module
+--------------------------
+
+.. automodule:: ufl.split_functions
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+ufl.tensoralgebra module
+------------------------
+
+.. automodule:: ufl.tensoralgebra
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+ufl.tensors module
+------------------
+
+.. automodule:: ufl.tensors
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+ufl.variable module
+-------------------
+
+.. automodule:: ufl.variable
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+
+Module contents
+---------------
+
+.. automodule:: ufl
+ :members:
+ :undoc-members:
+ :show-inheritance:
diff --git a/doc/sphinx/source/api-doc/ufl.utils.rst b/doc/sphinx/source/api-doc/ufl.utils.rst
new file mode 100644
index 0000000..96cb88a
--- /dev/null
+++ b/doc/sphinx/source/api-doc/ufl.utils.rst
@@ -0,0 +1,102 @@
+ufl.utils package
+=================
+
+Submodules
+----------
+
+ufl.utils.counted module
+------------------------
+
+.. automodule:: ufl.utils.counted
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+ufl.utils.derivativetuples module
+---------------------------------
+
+.. automodule:: ufl.utils.derivativetuples
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+ufl.utils.dicts module
+----------------------
+
+.. automodule:: ufl.utils.dicts
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+ufl.utils.formatting module
+---------------------------
+
+.. automodule:: ufl.utils.formatting
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+ufl.utils.indexflattening module
+--------------------------------
+
+.. automodule:: ufl.utils.indexflattening
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+ufl.utils.sequences module
+--------------------------
+
+.. automodule:: ufl.utils.sequences
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+ufl.utils.sorting module
+------------------------
+
+.. automodule:: ufl.utils.sorting
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+ufl.utils.stacks module
+-----------------------
+
+.. automodule:: ufl.utils.stacks
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+ufl.utils.system module
+-----------------------
+
+.. automodule:: ufl.utils.system
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+ufl.utils.timer module
+----------------------
+
+.. automodule:: ufl.utils.timer
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+ufl.utils.ufltypedicts module
+-----------------------------
+
+.. automodule:: ufl.utils.ufltypedicts
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+
+Module contents
+---------------
+
+.. automodule:: ufl.utils
+ :members:
+ :undoc-members:
+ :show-inheritance:
diff --git a/doc/sphinx/source/conf.py b/doc/sphinx/source/conf.py
index 41f70ce..d7f3bcc 100644
--- a/doc/sphinx/source/conf.py
+++ b/doc/sphinx/source/conf.py
@@ -1,32 +1,48 @@
# -*- coding: utf-8 -*-
#
+# Unified Form Language (UFL) documentation build configuration file, created by
+# sphinx-quickstart on Tue Nov 3 11:05:14 2015.
+#
+# This file is execfile()d with the current directory set to its
+# containing dir.
+#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default; values that are commented out
# serve to show the default.
-import sys, os
+import sys
+import os
+import shlex
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
-sys.path.insert(0, os.path.abspath( os.path.join('..', '..', '..') ))
+#sys.path.insert(0, os.path.abspath('.'))
-# -- General configuration -----------------------------------------------------
+# -- General configuration ------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
-needs_sphinx = '1.1'
-
-# Add any Sphinx extension module names here, as strings. They can be extensions
-# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
-extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.intersphinx',
- 'sphinx.ext.mathjax']
+#needs_sphinx = '1.0'
+
+# Add any Sphinx extension module names here, as strings. They can be
+# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
+# ones.
+extensions = [
+ 'sphinx.ext.autodoc',
+ 'sphinx.ext.doctest',
+ 'sphinx.ext.coverage',
+ 'sphinx.ext.mathjax',
+ 'sphinx.ext.viewcode',
+]
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
-# The suffix of source filenames.
+# The suffix(es) of source filenames.
+# You can specify multiple suffix as a list of string:
+# source_suffix = ['.rst', '.md']
source_suffix = '.rst'
# The encoding of source files.
@@ -36,21 +52,27 @@ source_suffix = '.rst'
master_doc = 'index'
# General information about the project.
-project = 'UFL'
-copyright = 'FEniCS Project, http://www.fenicsproject.org/'
+project = u'Unified Form Language (UFL)'
+copyright = u'2015, FEniCS Project'
+author = u'FEniCS Project'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
+import ufl
+ufl_version = ufl.__version__
# The short X.Y version.
-version = '1.2'
+version = ufl_version
# The full version, including alpha/beta/rc tags.
-release = '1.2.0'
+release = ufl_version
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
-#language = None
+#
+# This is also used if you do content translation via gettext catalogs.
+# Usually you set "language" from the command line for these cases.
+language = None
# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
@@ -62,7 +84,8 @@ release = '1.2.0'
# directories to ignore when looking for source files.
exclude_patterns = []
-# The reST default role (used for this markup: `text`) to use for all documents.
+# The reST default role (used for this markup: `text`) to use for all
+# documents.
#default_role = None
# If true, '()' will be appended to :func: etc. cross-reference text.
@@ -80,14 +103,20 @@ exclude_patterns = []
pygments_style = 'sphinx'
# A list of ignored prefixes for module index sorting.
-modindex_common_prefix = ["ufl.", "ufl.finiteelement", "ufl.algorithms."]
+#modindex_common_prefix = []
+
+# If true, keep warnings as "system message" paragraphs in the built documents.
+#keep_warnings = False
+
+# If true, `todo` and `todoList` produce output, else they produce nothing.
+todo_include_todos = False
+
-# -- Options for HTML output ---------------------------------------------------
+# -- Options for HTML output ----------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
-html_theme = 'nature'
-#html_theme = 'fenics'
+#html_theme = 'alabaster'
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
@@ -95,11 +124,11 @@ html_theme = 'nature'
#html_theme_options = {}
# Add any paths that contain custom themes here, relative to this directory.
-html_theme_path = ["_themes"]
+#html_theme_path = []
# The name for this set of Sphinx documents. If None, it defaults to
# "<project> v<release> documentation".
-html_title = "FEniCS Project"
+#html_title = None
# A shorter title for the navigation bar. Default is the same as html_title.
#html_short_title = None
@@ -118,6 +147,11 @@ html_title = "FEniCS Project"
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
+# Add any extra paths that contain custom files (such as robots.txt or
+# .htaccess) here, relative to this directory. These files are copied
+# directly to the root of the documentation.
+#html_extra_path = []
+
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.
#html_last_updated_fmt = '%b %d, %Y'
@@ -159,20 +193,46 @@ html_static_path = ['_static']
# This is the file name suffix for HTML files (e.g. ".xhtml").
#html_file_suffix = None
+# Language to be used for generating the HTML full-text search index.
+# Sphinx supports the following languages:
+# 'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja'
+# 'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr'
+#html_search_language = 'en'
+
+# A dictionary with options for the search language support, empty by default.
+# Now only 'ja' uses this config value
+#html_search_options = {'type': 'default'}
+
+# The name of a javascript file (relative to the configuration directory) that
+# implements a search results scorer. If empty, the default will be used.
+#html_search_scorer = 'scorer.js'
+
# Output file base name for HTML help builder.
-htmlhelp_basename = 'UFLdoc'
+htmlhelp_basename = 'UnifiedFormLanguageUFLdoc'
-# -- Options for LaTeX output --------------------------------------------------
+# -- Options for LaTeX output ---------------------------------------------
-# The paper size ('letter' or 'a4').
-#latex_paper_size = 'letter'
+latex_elements = {
+# The paper size ('letterpaper' or 'a4paper').
+#'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt').
-#latex_font_size = '10pt'
+#'pointsize': '10pt',
+
+# Additional stuff for the LaTeX preamble.
+#'preamble': '',
+
+# Latex figure (float) alignment
+#'figure_align': 'htbp',
+}
# Grouping the document tree into LaTeX files. List of tuples
-# (source start file, target name, title, author, documentclass [howto/manual]).
-#latex_documents = []
+# (source start file, target name, title,
+# author, documentclass [howto, manual, or own class]).
+latex_documents = [
+ (master_doc, 'UnifiedFormLanguageUFL.tex', u'Unified Form Language (UFL) Documentation',
+ u'FEniCS Project', 'manual'),
+]
# The name of an image file (relative to this directory) to place at the top of
# the title page.
@@ -188,21 +248,45 @@ htmlhelp_basename = 'UFLdoc'
# If true, show URL addresses after external links.
#latex_show_urls = False
-# Additional stuff for the LaTeX preamble.
-#latex_preamble = ''
-
# Documents to append as an appendix to all manuals.
#latex_appendices = []
# If false, no module index is generated.
#latex_domain_indices = True
-# Parameters affecting the LaTeX PNGs in the HTML files
-pngmath_latex_preamble = r" \usepackage{stmaryrd} "
-# Example configuration for intersphinx: refer to the Python standard library.
-intersphinx_mapping = {'http://docs.python.org/': None}
+# -- Options for manual page output ---------------------------------------
+
+# One entry per manual page. List of tuples
+# (source start file, name, description, authors, manual section).
+man_pages = [
+ (master_doc, 'unifiedformlanguageufl', u'Unified Form Language (UFL) Documentation',
+ [author], 1)
+]
+
+# If true, show URL addresses after external links.
+#man_show_urls = False
+
+
+# -- Options for Texinfo output -------------------------------------------
+
+# Grouping the document tree into Texinfo files. List of tuples
+# (source start file, target name, title, author,
+# dir menu entry, description, category)
+texinfo_documents = [
+ (master_doc, 'UnifiedFormLanguageUFL', u'Unified Form Language (UFL) Documentation',
+ author, 'UnifiedFormLanguageUFL', 'One line description of project.',
+ 'Miscellaneous'),
+]
+
+# Documents to append as an appendix to all manuals.
+#texinfo_appendices = []
+
+# If false, no module index is generated.
+#texinfo_domain_indices = True
+
+# How to display URL addresses: 'footnote', 'no', or 'inline'.
+#texinfo_show_urls = 'footnote'
-# Use docstrings both from __init__ and class when using autodoc for
-# Python classes
-autoclass_content = "both"
+# If true, do not generate a @detailmenu in the "Top" node's menu.
+#texinfo_no_detailmenu = False
diff --git a/doc/sphinx/source/index.rst b/doc/sphinx/source/index.rst
new file mode 100644
index 0000000..9e57734
--- /dev/null
+++ b/doc/sphinx/source/index.rst
@@ -0,0 +1,110 @@
+.. title:: Unified Form Language
+
+***************************
+Unified Form Language (UFL)
+***************************
+
+This is the documentation for the Unified Form Language from the
+FEniCS Project (http://fenicsproject.org). The Unified Form Language
+(UFL) is a domain specific language for declaration of finite element
+discretizations of variational forms. More precisely, it defines a
+flexible interface for choosing finite element spaces and defining
+expressions for weak forms in a notation close to mathematical
+notation. UFL is described in the paper
+
+Alnæs, M. S., Logg A., Ølgaard, K. B., Rognes, M. E. and
+Wells, G. N. (2014). Unified Form Language: A domain-specific language
+for weak formulations of partial differential equations. *ACM
+Transactions on Mathematical Software* 40(2), Article 9, 37
+pages. [http://dx.doi.org/doi:10.1145/2566630>]
+[http://arxiv.org/abs/1211.4047]
+
+UFL is most commonly used as the input language for the FEniCS Form
+Compiler (FFC) and in combination with the problem solving environment
+DOLFIN.
+
+
+
+Installation
+============
+
+Debian/Ubuntu packages
+----------------------
+
+Debian/Ubuntu
+^^^^^^^^^^^^^
+
+A Debian/Ubuntu package ``python-ufl`` is available for UFL:
+
+ sudo apt-get install python-ufl
+
+
+Ubuntu PPA
+^^^^^^^^^^
+
+UFL is available in the FEniCS Project PPA. The version of UFL
+available in the PPA will generally more recent than the Debian/Ubuntu
+package. To install UFL from the PPA:
+
+ sudo add-apt-repository ppa:fenics-packages/fenics
+ sudo apt-get update
+ sudo apt-get install fenics
+
+
+From source
+-----------
+
+Dependencies
+^^^^^^^^^^^^
+
+UFL depends on the Python packages ``numpy`` and ``six``, and
+``setuptools`` is recommended. If ``setuptools`` is available, the UFL
+installer will install missing dependencies automatically.
+
+
+Installation
+^^^^^^^^^^^^
+
+The source for UFL releases can be downloaded from
+http://fenicsproject.org/pub/software/ufl/. To install UFL
+system-wide, from the source directory use:
+
+ python setup.py install
+
+To install into a specified directory, use the ``--prefix`` option.
+
+
+Help and support
+================
+
+Send help requests and questions to fenics-support at googlegroups.com.
+
+Send feature requests and questions to fenics-dev at googlegroups.com
+
+
+Development and reporting bugs
+------------------------------
+
+The git source repository for UFL is located at
+https://bitbucket.org/fenics-project/ufl. For general UFL development
+questions and to make feature requests, use
+fenics-dev at googlegroups.com.
+
+Bugs can be registered at
+https://bitbucket.org/fenics-project/ufl/issues.
+
+
+
+Manual and API reference
+========================
+
+.. toctree::
+ :titlesonly:
+
+ User Manual <user/user_manual>
+ API Reference <api-doc/modules>
+ Releases <releases>
+
+* :ref:`modindex`
+* :ref:`genindex`
+* :ref:`search`
diff --git a/doc/sphinx/source/releases.rst b/doc/sphinx/source/releases.rst
new file mode 100644
index 0000000..57717e7
--- /dev/null
+++ b/doc/sphinx/source/releases.rst
@@ -0,0 +1,8 @@
+Release notes
+=============
+
+.. toctree::
+ :maxdepth: 2
+
+ releases/next
+ releases/v1.6.0
diff --git a/doc/sphinx/source/releases/next.rst b/doc/sphinx/source/releases/next.rst
new file mode 100644
index 0000000..3363ee3
--- /dev/null
+++ b/doc/sphinx/source/releases/next.rst
@@ -0,0 +1,11 @@
+Changes in the next release of UFL
+==================================
+
+- Deprecate ``.cell()``, ``.domain()``, ``.element()`` in favour of
+ ``.ufl_cell()``, ``.ufl_domain()``, ``.ufl_element()``, in multiple
+ classes, to allow closer integration with DOLFIN.
+- Remove deprecated properties
+ ``cell.{d,x,n,volume,circumradius,facet_area}``.
+- Remove ancient ``form2ufl`` script
+- Large reworking of symbolic geometry pipeline
+- Implement symbolic Piola mappings
diff --git a/doc/sphinx/source/releases/v1.6.0.rst b/doc/sphinx/source/releases/v1.6.0.rst
new file mode 100644
index 0000000..2f1355f
--- /dev/null
+++ b/doc/sphinx/source/releases/v1.6.0.rst
@@ -0,0 +1,31 @@
+Changes in UFL 1.6.0
+====================
+
+UFL 1.6.0 was released on 2015-07-28
+
+- Change approach to attaching ``__hash__`` implementation to accomodate
+ Python 3
+- Implement new non-recursive traversal based hash computation
+- Allow ``derivative(M, ListTensor(<scalars>), ...)`` just like list/tuple
+ works
+- Add traits ``is_in_reference_frame``, ``is_restriction``, ``is_evaluation``,
+ ``is_differential``
+- Add missing linear operators to ``ArgumentDependencyExtractor``
+- Add ``_ufl_is_literal_`` type trait
+- Add ``_ufl_is_terminal_modifier_ type`` trait and
+ ``Expr._ufl_terminal_modifiers_`` list
+- Add new types ``ReferenceDiv`` and ``ReferenceCurl``
+- Outer product element support in degree estimation
+- Add ``TraceElement``, ``InteriorElement``, ``FacetElement``,
+ ``BrokenElement``
+- Add ``OuterProductCell`` to valid ``Real`` elements
+- Add ``_cache`` member to form for use by external frameworks
+- Add Sobolev space ``HEin``
+- Add measures ``dI``, ``dO``, ``dC`` for interface, overlap, cutcell
+- Remove ``Measure`` constants
+- Remove ``cell2D`` and ``cell3D``
+- Implement ``reference_value`` in ``apply_restrictions``
+- Rename point integral to vertex integral and kept ``*dP`` syntax
+- Replace lambda functions in ``ufl_type`` with named functions for
+ nicer stack traces
+- Minor bugfixes, removal of unused code and cleanups
diff --git a/doc/sphinx/source/user/algorithms.rst b/doc/sphinx/source/user/algorithms.rst
new file mode 100644
index 0000000..a1bf803
--- /dev/null
+++ b/doc/sphinx/source/user/algorithms.rst
@@ -0,0 +1,417 @@
+**********
+Algorithms
+**********
+
+Algorithms to work with UFL forms and expressions can be found in the
+submodule ``ufl.algorithms``. You can import all of them with
+the line::
+
+ from ufl.algorithms import *
+
+This chapter gives an overview of (most of) the implemented algorithms.
+The intended audience is primarily developers, but advanced users may
+find information here useful for debugging.
+
+While domain specific languages introduce notation to express particular
+ideas more easily, which can reduce the probability of bugs in user code,
+they also add yet another layer of abstraction which can make debugging
+more difficult when the need arises. Many of the utilities described
+here can be useful in that regard.
+
+
+Formatting expressions
+======================
+
+Expressions can be formatted in various ways for inspection, which is
+particularly useful for debugging. We use the following as an example
+form for the formatting sections below::
+
+ element = FiniteElement("CG", triangle, 1)
+ v = TestFunction(element)
+ u = TrialFunction(element)
+ c = Coefficient(element)
+ f = Coefficient(element)
+ a = c*u*v*dx + f*v*ds
+
+
+str
+---
+Compact human readable pretty printing. Useful in interactive Python
+sessions. Example output of ``str(a)``::
+
+ { v_0 * v_1 * w_0 } * dx(<Mesh #-1 with coordinates parameterized by <Lagrange vector element of degree 1 on a triangle: 2 x <CG1 on a triangle>>>[everywhere], {})
+ + { v_0 * w_1 } * ds(<Mesh #-1 with coordinates parameterized by <Lagrange vector element of degree 1 on a triangle: 2 x <CG1 on a triangle>>>[everywhere], {})
+
+repr
+----
+Accurate description of expression, with the property that
+``eval(repr(a)) == a``. Useful to see which representation types
+occur in an expression, especially if ``str(a)`` is ambiguous.
+Example output of ``repr(a)``::
+
+ Form([Integral(Product(Argument(FunctionSpace(Mesh(VectorElement('Lagrange', triangle, 1, dim=2), -1), FiniteElement('Lagrange', triangle, 1)), 0, None), Product(Argument(FunctionSpace(Mesh(VectorElement('Lagrange', triangle, 1, dim=2), -1), FiniteElement('Lagrange', triangle, 1)), 1, None), Coefficient(FunctionSpace(Mesh(VectorElement('Lagrange', triangle, 1, dim=2), -1), FiniteElement('Lagrange', triangle, 1)), 0))), 'cell', Mesh(VectorElement('Lagrange', triangle, 1, dim=2), -1), 'e [...]
+
+
+Tree formatting
+---------------
+
+Ascii tree formatting, useful to inspect the tree structure of
+an expression in interactive Python sessions. Example output of
+``tree_format(a)``::
+
+ Form:
+ Integral:
+ integral type: cell
+ subdomain id: everywhere
+ integrand:
+ Product
+ (
+ Argument(FunctionSpace(Mesh(VectorElement('Lagrange', triangle, 1, dim=2), -1), FiniteElement('Lagrange', triangle, 1)), 0, None)
+ Product
+ (
+ Argument(FunctionSpace(Mesh(VectorElement('Lagrange', triangle, 1, dim=2), -1), FiniteElement('Lagrange', triangle, 1)), 1, None)
+ Coefficient(FunctionSpace(Mesh(VectorElement('Lagrange', triangle, 1, dim=2), -1), FiniteElement('Lagrange', triangle, 1)), 0)
+ )
+ )
+ Integral:
+ integral type: exterior_facet
+ subdomain id: everywhere
+ integrand:
+ Product
+ (
+ Argument(FunctionSpace(Mesh(VectorElement('Lagrange', triangle, 1, dim=2), -1), FiniteElement('Lagrange', triangle, 1)), 0, None)
+ Coefficient(FunctionSpace(Mesh(VectorElement('Lagrange', triangle, 1, dim=2), -1), FiniteElement('Lagrange', triangle, 1)), 1)
+ )
+
+
+Inspecting and manipulating the expression tree
+===============================================
+
+This subsection is mostly for form compiler developers and technically
+interested users.
+
+Traversing expressions
+----------------------
+
+``iter_expressions``
+^^^^^^^^^^^^^^^^^^^^^
+
+Example usage::
+
+ for e in iter_expressions(a):
+ print str(e)
+
+outputs::
+
+ v_0 * v_1 * w_0
+ v_0 * w_1
+
+
+``post_traversal``
+^^^^^^^^^^^^^^^^^^^
+
+TODO: traversal.py
+
+``pre_traversal``
+^^^^^^^^^^^^^^^^^^
+
+TODO: traversal.py
+
+
+``walk``
+^^^^^^^^
+
+TODO: traversal.py
+
+
+``traverse_terminals``
+^^^^^^^^^^^^^^^^^^^^^^^
+
+TODO: traversal.py
+
+
+Extracting information
+----------------------
+
+TODO: analysis.py
+
+
+Transforming expressions
+------------------------
+
+So far the algorithms presented has been about inspecting expressions
+in various ways. Some recurring patterns occur when writing algorithms
+to modify expressions, either to apply mathematical transformations or
+to change their representation. Usually, different expression node types
+need different treatment.
+
+To assist in such algorithms, UFL provides the ``Transformer``
+class. This implements a variant of the Visitor pattern to enable easy
+definition of transformation rules for the types you wish to handle.
+
+Shown here is maybe the simplest transformer possible::
+
+ class Printer(Transformer):
+ def __init__(self):
+ Transformer.__init__(self)
+
+ def expr(self, o, *operands):
+ print "Visiting", str(o), "with operands:"
+ print ", ".join(map(str,operands))
+ return o
+
+ element = FiniteElement("CG", triangle, 1)
+ v = TestFunction(element)
+ u = TrialFunction(element)
+ a = u*v
+
+ p = Printer()
+ p.visit(a)
+
+The call to ``visit`` will traverse ``a`` and call
+``Printer.expr`` on all expression nodes in post--order, with the
+argument ``operands`` holding the return values from visits to the
+operands of ``o``. The output is::
+
+ Visiting v_0 * v_1 with operands:
+ v_0, v_1
+
+:math:`(v^0_h)(v^1_h)`
+
+Implementing ``expr`` above provides a default handler for any
+expression node type. For each subclass of ``Expr`` you can
+define a handler function to override the default by using the name
+of the type in underscore notation, e.g. ``vector_constant``
+for ``VectorConstant``. The constructor of ``Transformer``
+and implementation of ``Transformer.visit`` handles the mapping
+from type to handler function automatically.
+
+Here is a simple example to show how to override default behaviour::
+
+ from ufl.classes import *
+ class CoefficientReplacer(Transformer):
+ def __init__(self):
+ Transformer.__init__(self)
+
+ expr = Transformer.reuse_if_possible
+ terminal = Transformer.always_reuse
+
+ def coefficient(self, o):
+ return FloatValue(3.14)
+
+ element = FiniteElement("CG", triangle, 1)
+ v = TestFunction(element)
+ f = Coefficient(element)
+ a = f*v
+
+ r = CoefficientReplacer()
+ b = r.visit(a)
+ print b
+
+outputs::
+
+ 3.14 * v_0
+
+The output of this code is the transformed expression ``b ==
+3.14*v``. This code also demonstrates how to reuse existing handlers.
+The handler ``Transformer.reuse_if_possible`` will return the
+input object if the operands have not changed, and otherwise reconstruct
+a new instance of the same type but with the new transformed operands.
+The handler ``Transformer.always_reuse`` always reuses the instance
+without recursing into its children, usually applied to terminals.
+To set these defaults with less code, inherit ``ReuseTransformer``
+instead of ``Transformer``. This ensures that the parts of the
+expression tree that are not changed by the transformation algorithms
+always reuse the same instances.
+
+We have already mentioned the difference between pre--traversal
+and post--traversal, and some times you need to combine the
+two. ``Transformer`` makes this easy by checking the number of
+arguments to your handler functions to see if they take transformed
+operands as input or not. If a handler function does not take more
+than a single argument in addition to self, its children are not visited
+automatically, and the handler function must call ``visit`` on its
+operands itself.
+
+Here is an example of mixing pre- and post-traversal::
+
+ class Traverser(ReuseTransformer):
+ def __init__(self):
+ ReuseTransformer.__init__(self)
+
+ def sum(self, o):
+ operands = o.operands()
+ newoperands = []
+ for e in operands:
+ newoperands.append( self.visit(e) )
+ return sum(newoperands)
+
+ element = FiniteElement("CG", triangle, 1)
+ f = Coefficient(element)
+ g = Coefficient(element)
+ h = Coefficient(element)
+ a = f+g+h
+
+ r = Traverser()
+ b = r.visit(a)
+ print b
+
+This code inherits the ``ReuseTransformer`` like explained above,
+so the default behaviour is to recurse into children first and then call
+``Transformer.reuse\_if\_possible`` to reuse or reconstruct each
+expression node. Since ``sum`` only takes ``self`` and the
+expression node instance ``o`` as arguments, its children are not
+visited automatically, and ``sum`` calls on ``self.visit``
+to do this explicitly.
+
+
+Automatic differentiation implementation
+========================================
+
+This subsection is mostly for form compiler developers and technically
+interested users.
+
+TODO: More details about AD algorithms for developers.
+
+
+Forward mode
+------------
+
+TODO: forward_ad.py
+
+
+Reverse mode
+------------
+
+TODO: reverse_ad.py
+
+Mixed derivatives
+-----------------
+
+TODO: ad.py
+
+
+Computational graphs
+====================
+
+This section is for form compiler developers and is probably of no
+interest to end-users.
+
+An expression tree can be seen as a directed acyclic graph (DAG).
+To aid in the implementation of form compilers, UFL includes tools to
+build a linearized\footnote{Linearized as in a linear datastructure,
+do not confuse this with automatic differentiation.} computational graph
+from the abstract expression tree.
+
+A graph can be partitioned into subgraphs based on dependencies of
+subexpressions, such that a quadrature based compiler can easily place
+subexpressions inside the right sets of loops.
+
+% TODO: Finish and test this before writing about it :)
+%The vertices of a graph can be reordered to improve the efficiency
+%of the generated code, an operation usually called operation scheduling.
+
+The computational graph
+-----------------------
+
+TODO: finish graph.py:
+
+ TODO
+
+Consider the expression:
+
+.. math::
+
+ f = (a + b) * (c + d)
+
+where a, b, c, d are arbitrary scalar expressions.
+The *expression tree* for f looks like this::
+
+ a b c d
+ \ / \ /
+ + +
+ \ /
+ *
+
+In UFL f is represented like this expression tree. If a,b,c,d are all
+distinct Coefficient instances, the UFL representation will look like this::
+
+ Coefficient Coefficient Coefficient Coefficient
+ \ / \ /
+ Sum Sum
+ \ /
+ --- Product ---
+
+If we instead have the expression
+
+.. math::
+
+ f = (a + b) * (a - b)
+
+the tree will in fact look like this, with the functions a and b only
+represented once::
+
+ Coefficient Coefficient
+ | \ / |
+ | Sum Product -- IntValue(-1)
+ | | |
+ | Product |
+ | | |
+ |------- Sum -------|
+
+The expression tree is a directed acyclic graph (DAG) where the vertices
+are Expr instances and each edge represents a direct dependency between
+two vertices, i.e. that one vertex is among the operands of another.
+A graph can also be represented in a linearized data structure, consisting
+of an array of vertices and an array of edges. This representation is
+convenient for many algorithms. An example to illustrate this graph
+representation::
+
+ G = V, E
+ V = [a, b, a+b, c, d, c+d, (a+b)*(c+d)]
+ E = [(6,2), (6,5), (5,3), (5,4), (2,0), (2,1)]
+
+In the following this representation of an expression will be called
+the *computational graph*. To construct this graph from a UFL
+expression, simply do::
+
+ G = Graph(expression)
+ V, E = G
+
+The Graph class can build some useful data structures for use in
+algorithms::
+
+ Vin = G.Vin() # Vin[i] = list of vertex indices j such that there is an edge from V[j] to V[i]
+ Vout = G.Vout() # Vout[i] = list of vertex indices j such that there is an edge from V[i] to V[j]
+ Ein = G.Ein() # Ein[i] = list of edge indices j such that E[j] is an edge to V[i], e.g. E[j][1] == i
+ Eout = G.Eout() # Eout[i] = list of edge indices j such that E[j] is an edge from V[i], e.g. E[j][0] == i
+
+The ordering of the vertices in the graph can in principle be arbitrary,
+but here they are ordered such that
+
+.. math::
+
+ v_i \prec v_j, \quad \forall j > i,
+
+where :math:`a \prec b` means that :math:`a` does not depend on :math:`b`
+directly or indirectly.
+
+Another property of the computational graph built by UFL is that no
+identical expression is assigned to more than one vertex. This is
+achieved efficiently by inserting expressions in a dict (a hash map)
+during graph building.
+
+In principle, correct code can be generated for an expression from its
+computational graph simply by iterating over the vertices and generating
+code for each one separately. However, we can do better than that.
+
+
+Partitioning the graph
+----------------------
+
+To help generate better code efficiently, we can partition vertices by
+their dependencies, which allows us to, e.g., place expressions outside
+the quadrature loop if they don't depend (directly or indirectly) on
+the spatial coordinates. This is done simply by::
+
+ P = partition(G) # TODO
diff --git a/doc/sphinx/source/user/command_line_utils.rst b/doc/sphinx/source/user/command_line_utils.rst
new file mode 100644
index 0000000..ea02ce1
--- /dev/null
+++ b/doc/sphinx/source/user/command_line_utils.rst
@@ -0,0 +1,48 @@
+*********************
+Commandline utilities
+*********************
+
+
+Validation and debugging: ``ufl-analyse``
+=========================================
+
+The command ``ufl-analyse`` loads all forms found in a ``.ufl``
+file, tries to discover any errors in them, and prints various kinds of
+information about each form. Basic usage is::
+
+ # ufl-analyse myform.ufl
+
+For more information, type::
+
+ # ufl-analyse --help
+
+Formatting and visualization: ``ufl-convert``
+=============================================
+
+The command ``ufl-convert`` loads all forms found in a ``.ufl``
+file, compiles them into a different form or extracts some information
+from them, and writes the result in a suitable file format.
+
+To try this tool, go to the ``demo/`` directory of the UFL source
+tree. Some of the features to try are basic printing of ``str`` and
+``repr`` string representations of each form::
+
+ # ufl-convert --format=str stiffness.ufl
+ # ufl-convert --format=repr stiffness.ufl
+
+compilation of forms to mathematical notation in LaTeX::
+
+ # ufl-convert --filetype=pdf --format=tex --show=1 stiffness.ufl
+
+LaTeX output of forms after processing with UFL compiler utilities::
+
+ # ufl-convert -tpdf -ftex -s1 --compile=1 stiffness.ufl
+
+and visualization of expression trees using graphviz via compilation of
+forms to the dot format::
+
+ # ufl-convert -tpdf -fdot -s1 stiffness.ufl
+
+Type ``ufl-convert --help`` for more details.
+
+
diff --git a/doc/sphinx/source/user/examples.rst b/doc/sphinx/source/user/examples.rst
new file mode 100644
index 0000000..c9fd8f7
--- /dev/null
+++ b/doc/sphinx/source/user/examples.rst
@@ -0,0 +1,545 @@
+*************
+Example forms
+*************
+
+The following examples illustrate basic usage of the form language
+for the definition of a collection of standard multilinear forms. We
+assume that ``dx`` has been declared as an integral over the interior of
+:math:`\Omega` and that both ``i`` and ``j`` have been declared as a free
+``Index``.
+
+The examples presented below can all be found in the subdirectory
+``demo/`` of the UFL source tree together with numerous
+other examples.
+
+The mass matrix
+===============
+
+As a first example, consider the bilinear form corresponding to a
+mass matrix,
+
+.. math::
+
+ a(v, u) = \int_{\Omega} v \, u \mathop{dx},
+
+which can be implemented in UFL as follows::
+
+ element = FiniteElement("Lagrange", triangle, 1)
+
+ v = TestFunction(element)
+ u = TrialFunction(element)
+
+ a = v*u*dx
+
+This example is implemented in the file ``Mass.ufl`` in the collection
+of demonstration forms included with the UFL source distribution.
+
+Poisson equation
+================
+
+The bilinear and linear forms form for Poisson's equation,
+
+.. math::
+
+ a(v, u) &= \int_{\Omega} \nabla v \cdot \nabla u \mathop{dx}, \\
+ L(v; f) &= \int_{\Omega} v \, f \mathop{dx},
+
+can be implemented as follows::
+
+ element = FiniteElement("Lagrange", triangle, 1)
+
+ v = TestFunction(element)
+ u = TrialFunction(element)
+ f = Coefficient(element)
+
+ a = dot(grad(v), grad(u))*dx
+ L = v*f*dx
+
+Alternatively, index notation can be used to express the scalar product
+like this::
+
+ a = Dx(v, i)*Dx(u, i)*dx
+
+or like this::
+
+ a = v.dx(i)*u.dx(i)*dx
+
+This example is implemented in the file ``Poisson.ufl`` in the collection
+of demonstration forms included with the UFL source distribution.
+
+
+Vector-valued Poisson
+=====================
+
+The bilinear and linear forms for a system of (independent) Poisson
+equations,
+
+.. math::
+
+ a(v, u) &= \int_{\Omega} \nabla v : \nabla u \mathop{dx}, \\
+ L(v; f) &= \int_{\Omega} v \cdot f \mathop{dx},
+
+with :math:`v`, :math:`u` and :math:`f` vector-valued can be implemented
+as follows::
+
+ element = VectorElement("Lagrange", triangle, 1)
+
+ v = TestFunction(element)
+ u = TrialFunction(element)
+ f = Coefficient(element)
+
+ a = inner(grad(v), grad(u))*dx
+ L = dot(v, f)*dx
+
+Alternatively, index notation may be used like this::
+
+ a = Dx(v[i], j)*Dx(u[i], j)*dx
+ L = v[i]*f[i]*dx
+
+or like this::
+
+ a = v[i].dx(j)*u[i].dx(j)*dx
+ L = v[i]*f[i]*dx
+
+This example is implemented in the file ``PoissonSystem.ufl`` in
+the collection of demonstration forms included with the UFL source
+distribution.
+
+
+The strain-strain term of linear elasticity
+===========================================
+
+The strain-strain term of linear elasticity,
+
+.. math::
+
+ a(v, u) = \int_{\Omega} \epsilon(v) : \epsilon(u) \mathop{dx},
+
+where
+
+.. math::
+
+ \epsilon(v) = \frac{1}{2}(\nabla v + (\nabla v)^{\top})
+
+can be implemented as follows::
+
+ element = VectorElement("Lagrange", tetrahedron, 1)
+
+ v = TestFunction(element)
+ u = TrialFunction(element)
+
+ def epsilon(v):
+ Dv = grad(v)
+ return 0.5*(Dv + Dv.T)
+
+ a = inner(epsilon(v), epsilon(u))*dx
+
+Alternatively, index notation can be used to define the form::
+
+ a = 0.25*(Dx(v[j], i) + Dx(v[i], j))* \
+ (Dx(u[j], i) + Dx(u[i], j))*dx
+
+or like this::
+
+ a = 0.25*(v[j].dx(i) + v[i].dx(j))* \
+ (u[j].dx(i) + u[i].dx(j))*dx
+
+This example is implemented in the file ``Elasticity.ufl`` in the
+collection of demonstration forms included with the UFL source
+distribution.
+
+
+The nonlinear term of Navier--Stokes
+====================================
+
+The bilinear form for fixed-point iteration on the nonlinear term of
+the incompressible Navier--Stokes equations,
+
+.. math::
+
+ a(v, u; w) = \int_{\Omega} (w \cdot \nabla u) \cdot v \mathop{dx},
+
+with :math:`w` the frozen velocity from a previous iteration, can be
+implemented as follows::
+
+ element = VectorElement("Lagrange", tetrahedron, 1)
+
+ v = TestFunction(element)
+ u = TrialFunction(element)
+ w = Coefficient(element)
+
+ a = dot(grad(u)*w, v)*dx
+
+alternatively using index notation like this::
+
+ a = v[i]*w[j]*Dx(u[i], j)*dx
+
+or like this::
+
+ a = v[i]*w[j]*u[i].dx(j)*dx
+
+This example is implemented in the file ``NavierStokes.ufl`` in
+the collection of demonstration forms included with the UFL source
+distribution.
+
+The heat equation
+=================
+
+Discretizing the heat equation,
+
+.. math::
+
+ \dot{u} - \nabla \cdot (c \nabla u) = f,
+
+in time using the :math:`\mathrm{dG}(0)` method (backward Euler), we
+obtain the following variational problem for the discrete solution :math:`u_h
+= u_h(x, t)`: Find :math:`u_h^n = u_h(\cdot, t_n)` with
+:math:`u_h^{n-1} = u_h(\cdot, t_{n-1})` given such that
+
+.. math::
+
+ \frac{1}{k_n} \int_{\Omega} v \, (u_h^n - u_h^{n-1}) \mathop{dx} +
+ \int_{\Omega} c \, \nabla v \cdot \nabla u_h^n \mathop{dx} =
+ \int_{\Omega} v \, f^n \mathop{dx}
+
+for all test functions :math:`v`, where :math:`k = t_n - t_{n-1}`
+denotes the time step . In the example below, we implement this
+variational problem with piecewise linear test and trial functions,
+but other choices are possible (just choose another finite element).
+
+Rewriting the variational problem in the standard form :math:`a(v, u_h)
+= L(v)` for all :math:`v`, we obtain the following pair of bilinear and
+linear forms:
+
+.. math::
+
+ a(v, u_h^n; c, k) &= \int_{\Omega} v \, u_h^n \mathop{dx} +
+ k_n \int_{\Omega} c \, \nabla v \cdot \nabla u_h^n \mathop{dx}, \\
+ L(v; u_h^{n-1}, f, k) &= \int_{\Omega} v \, u_h^{n-1} \mathop{dx} + k_n \int_{\Omega} v \, f^n \mathop{dx},
+
+which can be implemented as follows::
+
+ element = FiniteElement("Lagrange", triangle, 1)
+
+ v = TestFunction(element) # Test function
+ u1 = TrialFunction(element) # Value at t_n
+ u0 = Coefficient(element) # Value at t_n-1
+ c = Coefficient(element) # Heat conductivity
+ f = Coefficient(element) # Heat source
+ k = Constant("triangle") # Time step
+
+ a = v*u1*dx + k*c*dot(grad(v), grad(u1))*dx
+ L = v*u0*dx + k*v*f*dx
+
+This example is implemented in the file ``Heat.ufl`` in the collection
+of demonstration forms included with the UFL source distribution.
+
+
+Mixed formulation of Stokes
+===========================
+
+To solve Stokes' equations,
+
+.. math::
+
+ - \Delta u + \nabla p &= f, \\
+ \nabla \cdot u &= 0,
+
+we write the variational problem in standard form :math:`a(v, u) =
+L(v)` for all :math:`v` to obtain the following pair of bilinear and
+linear forms:
+
+.. math::
+
+ a((v, q), (u, p)) &= \int_{\Omega} \nabla v : \nabla u - (\nabla \cdot v) \, p +
+ q \, (\nabla \cdot u) \mathop{dx}, \\
+ L((v, q); f) &= \int_{\Omega} v \cdot f \mathop{dx}.
+
+Using a mixed formulation with Taylor-Hood elements, this can be
+implemented as follows::
+
+ cell = triangle
+ P2 = VectorElement("Lagrange", cell, 2)
+ P1 = FiniteElement("Lagrange", cell, 1)
+ TH = P2 * P1
+
+ (v, q) = TestFunctions(TH)
+ (u, p) = TrialFunctions(TH)
+
+ f = Coefficient(P2)
+
+ a = (inner(grad(v), grad(u)) - div(v)*p + q*div(u))*dx
+ L = dot(v, f)*dx
+
+This example is implemented in the file ``Stokes.ufl`` in the collection
+of demonstration forms included with the UFL source distribution.
+
+Mixed formulation of Poisson
+============================
+
+We next consider the following formulation of Poisson's equation as a
+pair of first order equations for :math:`\sigma \in H(\mathrm{div})`
+and :math:`u \in L_2`:
+
+.. math::
+ \sigma + \nabla u &= 0, \\
+ \nabla \cdot \sigma &= f.
+
+We multiply the two equations by a pair of test functions $\tau$ and
+$w$ and integrate by parts to obtain the following variational
+problem: Find $(\sigma, u) \in V = H(\mathrm{div}) \times L_2$ such that
+
+.. math::
+
+ a((\tau, w), (\sigma, u)) = L((\tau, w)) \quad \forall \, (\tau, w) \in V,
+
+where
+
+.. math::
+
+ a((\tau, w), (\sigma, u)) &= \int_{\Omega} \tau \cdot \sigma - \nabla \cdot \tau \, u
+ + w \nabla \cdot \sigma \mathop{dx},
+ \\
+ L((\tau, w); f) &= \int_{\Omega} w \cdot f \mathop{dx}.
+
+We may implement the corresponding forms in our form language using
+first order BDM H(div)-conforming elements for
+:math:\sigma and piecewise constant :math`L_2`-conforming elements for
+:math:u as follows::
+
+ cell = triangle
+ BDM1 = FiniteElement("Brezzi-Douglas-Marini", cell, 1)
+ DG0 = FiniteElement("Discontinuous Lagrange", cell, 0)
+
+ element = BDM1 * DG0
+
+ (tau, w) = TestFunctions(element)
+ (sigma, u) = TrialFunctions(element)
+
+ f = Coefficient(DG0)
+
+ a = (dot(tau, sigma) - div(tau)*u + w*div(sigma))*dx
+ L = w*f*dx
+
+This example is implemented in the file ``MixedPoisson.ufl`` in
+the collection of demonstration forms included with the UFL source
+distribution.
+
+Poisson equation with DG elements
+=================================
+
+We consider again Poisson's equation, but now in an (interior penalty)
+discontinuous Galerkin formulation: Find :math:`u \in V = L_2` such that
+
+.. math::
+
+ a(v, u) = L(v) \quad \forall v \in V,
+
+where
+
+.. math::
+
+ a(v, u; h) &= \int_{\Omega} \nabla v \cdot \nabla u \mathop{dx} \\
+ &+ \sum_S \int_S
+ - \langle \nabla v \rangle \cdot [[ u ]]_n
+ - [[ v ]]_n \cdot \langle \nabla u \rangle
+ + (\alpha/h) [[ v ]]_n \cdot [[ u ]]_n \mathop{dS} \\
+ &+ \int_{\partial\Omega}
+ - \nabla v \cdot [[ u ]]_n - [[ v ]]_n \cdot \nabla u
+ + (\gamma/h) v u \mathop{ds} \\
+ L(v; f, g) &= \int_{\Omega} v f \mathop{dx} + \int_{\partial\Omega} v g \mathop{ds}.
+
+The corresponding finite element variational problem for discontinuous
+first order elements may be implemented as follows::
+
+ cell = triangle
+ DG1 = FiniteElement("Discontinuous Lagrange", cell, 1)
+
+ v = TestFunction(DG1)
+ u = TrialFunction(DG1)
+
+ f = Coefficient(DG1)
+ g = Coefficient(DG1)
+ #h = MeshSize(cell) # TODO: Do we include MeshSize in UFL?
+ h = Constant(cell)
+ alpha = 1 # TODO: Set to proper value
+ gamma = 1 # TODO: Set to proper value
+
+ a = dot(grad(v), grad(u))*dx \
+ - dot(avg(grad(v)), jump(u))*dS \
+ - dot(jump(v), avg(grad(u)))*dS \
+ + alpha/h('+')*dot(jump(v), jump(u))*dS \
+ - dot(grad(v), jump(u))*ds \
+ - dot(jump(v), grad(u))*ds \
+ + gamma/h*v*u*ds
+ L = v*f*dx + v*g*ds
+
+This example is implemented in the file ``PoissonDG.ufl`` in
+the collection of demonstration forms included with the UFL source
+distribution.
+
+
+Quadrature elements
+===================
+
+*FIXME: The code examples in this section have been mostly converted
+to UFL syntax, but the quadrature elements need some more updating, as
+well as the text. In UFL, I think we should define the element order
+and not the number of points for quadrature elements, and let the form
+compiler choose a quadrature rule. This way the form depends less on
+the cell in use.*
+
+We consider here a nonlinear version of the Poisson's equation to
+illustrate the main point of the ``Quadrature`` finite element
+family. The strong equation looks as follows:
+
+.. math:
+
+ - \nabla \cdot (1+u^2)\nabla u = f.
+
+The linearised bilinear and linear forms for this equation,
+
+.. math:
+
+ a(v, u; u_0) &= \int_{\Omega} (1+u_{0}^2) \nabla v \cdot \nabla u \mathop{dx}
+ + \int_{\Omega} 2u_0 u \nabla v \cdot \nabla u_0 \mathop{dx},
+ \\
+ L(v; u_0, f) &= \int_{\Omega} v \, f \mathop{dx}
+ - \int_{\Omega} (1+u_{0}^2) \nabla v \cdot \nabla u_0 \mathop{dx},
+
+can be implemented in a single form file as follows::
+
+ element = FiniteElement("Lagrange", triangle, 1)
+
+ v = TestFunction(element)
+ u = TrialFunction(element)
+ u0 = Coefficient(element)
+ f = Coefficient(element)
+
+ a = (1+u0**2)*dot(grad(v), grad(u))*dx + 2*u0*u*dot(grad(v), grad(u0))*dx
+ L = v*f*dx - (1+u0**2)*dot(grad(v), grad(u0))*dx
+
+Here, :math:`u_0` represents the solution from the previous Newton-Raphson
+iteration.
+
+The above form will be denoted REF1 and serve as our reference
+implementation for linear elements. A similar form (REF2) using quadratic
+elements will serve as a reference for quadratic elements.
+
+Now, assume that we want to treat the quantities :math:`C = (1 + u_{0}^2)`
+and :math:`\sigma_0 = (1+u_{0}^2) \nabla u_0` as given functions (to be
+computed elsewhere). Substituting into bilinear linear forms, we obtain
+
+.. math:
+ a(v, u) &= \int_{\Omega} \text{C} \nabla v \cdot \nabla u \mathop{dx}
+ + \int_{\Omega} 2u_0 u \nabla v \cdot \nabla u_0 \mathop{dx},
+ \\
+ L(v; \sigma_0, f) &= \int_{\Omega} v \, f \mathop{dx}
+ - \int_{\Omega} \nabla v \cdot \sigma_0 \mathop{dx}.
+
+Then, two additional forms are created to compute the tangent C and
+the gradient of :math:`u_0`. This situation shows up in plasticity and
+other problems where certain quantities need to be computed elsewhere
+(in user-defined functions). The three forms using the standard
+``FiniteElement`` (linear elements) can then be implemented as::
+
+ element = FiniteElement("Lagrange", triangle, 1)
+ DG = FiniteElement("Discontinuous Lagrange", triangle, 0)
+ sig = VectorElement("Discontinuous Lagrange", triangle, 0)
+
+ v = TestFunction(element)
+ u = TrialFunction(element)
+ u0 = Coefficient(element)
+ C = Coefficient(DG)
+ sig0 = Coefficient(sig)
+ f = Coefficient(element)
+
+ a = v.dx(i)*C*u.dx(i)*dx + v.dx(i)*2*u0*u*u0.dx(i)*dx
+ L = v*f*dx - dot(grad(v), sig0)*dx
+
+and::
+
+ element = FiniteElement("Lagrange", triangle, 1)
+ DG = FiniteElement("Discontinuous Lagrange", triangle, 0)
+
+ v = TestFunction(DG)
+ u = TrialFunction(DG)
+ u0= Coefficient(element)
+
+ a = v*u*dx
+ L = v*(1.0 + u0**2)*dx
+
+and::
+
+ element = FiniteElement("Lagrange", triangle, 1)
+ DG = VectorElement("Discontinuous Lagrange", triangle, 0)
+
+ v = TestFunction(DG)
+ u = TrialFunction(DG)
+ u0 = Coefficient(element)
+
+ a = dot(v, u)*dx
+ L = dot(v, grad(u0))*dx
+
+The three forms can be implemented using the ``QuadratureElement``
+in a similar fashion in which only the element declaration is different::
+
+ # QE1NonlinearPoisson.ufl
+ element = FiniteElement("Lagrange", triangle, 1)
+ QE = FiniteElement("Quadrature", triangle, 2)
+ sig = VectorElement("Quadrature", triangle, 2)
+
+and::
+
+ # QE1Tangent.ufl
+ element = FiniteElement("Lagrange", triangle, 1)
+ QE = FiniteElement("Quadrature", triangle, 2)
+
+and::
+
+ # QE1Gradient.ufl
+ element = FiniteElement("Lagrange", triangle, 1)
+ QE = VectorElement("Quadrature", triangle, 2)
+
+Note that we use two points when declaring the ``QuadratureElement``. This
+is because the RHS of the ``Tangent.form`` is second order and therefore
+we need two points for exact integration. Due to consistency issues,
+when passing functions around between the forms, we also need to use
+two points when declaring the ``QuadratureElement`` in the other forms.
+
+Typical values of the relative residual for each Newton iteration for all
+three approaches are shown in Table~\ref{tab:convergence1}. It is noted
+that the convergence rate is quadratic as it should be for all 3 methods.
+
+Relative residuals for each approach for linear elements::
+
+ Iteration REF1 FE1 QE1
+ ========= ==== === ===
+ 1 6.3e-02 6.3e-02 6.3e-02
+ 2 5.3e-04 5.3e-04 5.3e-04
+ 3 3.7e-08 3.7e-08 3.7e-08
+ 4 2.9e-16 2.9e-16 2.5e-16
+
+However, if quadratic elements are used to interpolate the unknown field u,
+the order of all elements in the above forms is increased by 1. This influences
+the convergence rate as seen in Table (tab:convergence2). Clearly, using
+the standard ``FiniteElement`` leads to a poor convergence whereas
+the ``QuadratureElement`` still leads to quadratic convergence.
+
+Relative residuals for each approach for quadratic elements::
+
+ Iteration REF2 FE2 QE2
+ ========= ==== === ===
+ 1 2.6e-01 3.9e-01 2.6e-01
+ 2 1.1e-02 4.6e-02 1.1e-02
+ 3 1.2e-05 1.1e-02 1.6e-05
+ 4 1.1e-11 7.2e-04 9.1e-09
+
+
+More examples
+=============
+
+Feel free to send additional demo form files for your favourite PDE to
+the UFL mailing list.
+
+%TODO: Modify rest of FFC example forms to UFL syntax and add here.
diff --git a/doc/sphinx/source/user/form_language.rst b/doc/sphinx/source/user/form_language.rst
new file mode 100644
index 0000000..2f1418f
--- /dev/null
+++ b/doc/sphinx/source/user/form_language.rst
@@ -0,0 +1,1738 @@
+*************
+Form language
+*************
+
+UFL consists of a set of operators and atomic expressions that can be
+used to express variational forms and functionals. Below we will define
+all these operators and atomic expressions in detail.
+
+UFL is built on top of the Python language, and any Python code is
+valid in the definition of a form.
+In particular, comments (lines starting with ``#`` and functions (keyword ``def``, see _user-defined
+below) are useful in the definition of a form. However, it is usually a
+good idea to avoid using advanced Python features in the form definition,
+to stay close to the mathematical notation.
+
+The entire form language can be imported in Python with the line:
+
+.. code-block:: python
+
+ from ufl import *
+
+which is assumed in all examples below and can be omitted in ``.ufl``
+files. This can be useful for experimenting with the language in an
+interactive Python interpreter.
+
+
+Forms and integrals
+===================
+
+UFL is designed to express forms in the following generalized format:
+
+.. math::
+
+ a(\mathbf{v}; \mathbf{w})
+ =
+ \sum_{k=1}^{n_c} \int_{\Omega_k}
+ I^c_k(\mathbf{v}; \mathbf{w}) dx
+ + \sum_{k=1}^{n_e} \int_{\partial\Omega_k}
+ I^e_k(\mathbf{v}; \mathbf{w}) ds
+ + \sum_{k=1}^{n_i} \int_{\Gamma_k}
+ I^i_k(\mathbf{v}; \mathbf{w}) dS.
+
+Here the form :math:`a` depends on the *form arguments* :math:`\mathbf{v} = (v_1,
+\ldots, v_r)` and the *form coefficients* :math:`\mathbf{w} = (w_1, \ldots, w_n)`,
+and its expression is a sum of integrals. Each term of a valid form
+expression must be a scalar-valued expression integrated exactly once. How
+to define form arguments and integrand expressions is detailed in the
+rest of this chapter.
+
+Integrals are expressed through multiplication with a measure,
+representing an integral over either of:
+
+ * the interior of the domain :math:`\Omega` (``dx``, cell integral);
+
+ * the boundary :math:`\partial\Omega` of :math:`\Omega` (``ds``, exterior facet integral);
+
+ * the set of interior facets :math:`\Gamma` (``dS``, interior facet integral).
+
+(Note that newer versions of UFL supports several other integral
+types currently not documented here).
+As a basic example, assume ``v`` is a scalar-valued expression and
+consider the integral of ``v`` over the interior of :math:`\Omega`. This
+may be expressed as::
+
+ a = v*dx
+
+and the integral of ``v`` over :math:`\partial\Omega` is written as::
+
+ a = v*ds
+
+Alternatively, measures can be redefined to represent numbered subsets of
+a domain, such that a form can take on different expressions on different
+parts of the domain. If ``c``, ``e0`` and ``e1`` are scalar-valued
+expressions, then::
+
+ a = c*dx + e0*ds(0) + e1*ds(1)
+
+represents
+
+.. math::
+
+ a = \int_\Omega c\,dx + \int_{\partial\Omega_0} e_0 \, ds + \int_{\partial\Omega_1} e_1 \, ds.
+
+where
+
+.. math::
+
+ \partial\Omega_0 \subset \partial\Omega, \qquad \partial\Omega_1 \subset \partial\Omega.
+
+.. note::
+
+ The domain :math:`\Omega`, its subdomains and boundaries are not known
+ to UFL. These are defined in a problem solving environment such as DOLFIN,
+ which uses UFL to specify forms.
+
+
+Finite element spaces
+=====================
+
+Before defining forms which can be integrated, it is necessary to describe
+the finite element spaces over which the integration takes place.
+UFL can represent very flexible general hierarchies of mixed finite elements,
+and has predefined names for most common element families.
+A finite element space is defined by an element domain, shape functions and nodal variables.
+In UFL, the element domain is called a ``Cell``.
+
+Cells
+-----
+
+A polygonal cell is defined by a shape name and a geometric dimension, written as::
+
+ cell = Cell(shape, gdim)
+
+Valid shapes are "interval", "triangle", "tetrahedron", "quadrilateral",
+and "hexahedron". Some examples::
+
+ # Regular triangle cell
+ cell = Cell("triangle")
+
+ # Triangle cell embedded in 3D space
+ cell = Cell("triangle", 3)
+
+Objects for regular cells of all basic shapes are predefined::
+
+ # Predefined linear cells
+ cell = interval
+ cell = triangle
+ cell = tetrahedron
+ cell = quadrilateral
+ cell = hexahedron
+
+In the rest of this document, a variable name ``cell`` will be used where
+any cell is a valid argument, to make the examples dimension independent
+wherever possible. Using a variable ``cell`` to hold the cell type used
+in a form is highly recommended, since this makes most form definitions
+dimension independent.
+
+
+Element families
+----------------
+
+UFL predefines a set of names of known element families. When defining
+a finite element below, the argument ``family`` is a string and its
+possible values include:
+
+* ``"Lagrange"`` or ``"CG"``, representing standard scalar
+ Lagrange finite elements (continuous piecewise polynomial functions);
+
+* ``"Discontinuous Lagrange"`` or ``"DG"``, representing
+ scalar discontinuous Lagrange finite elements (discontinuous piecewise
+ polynomial functions);
+
+* ``"Crouzeix-Raviart"`` or ``"CR"``, representing scalar
+ Crouzeix--Raviart elements;
+
+* ``"Brezzi-Douglas-Marini"`` or ``"BDM"``, representing
+ vector-valued Brezzi--Douglas--Marini H(div) elements;
+
+* ``"Brezzi-Douglas-Fortin-Marini`` or ``"BDFM"``, representing
+ vector-valued Brezzi--Douglas--Fortin--Marini H(div) elements;
+
+* ``"Raviart-Thomas"`` or ``"RT"``, representing
+ vector-valued Raviart--Thomas H(div) elements.
+
+* ``"Nedelec 1st kind H(div)"`` or ``"N1div"``,
+ representing vector-valued Nedelec H(div) elements
+ (of the first kind).
+
+* ``"Nedelec 2st kind H(div)"`` or ``"N2div"``,
+ representing vector-valued Nedelec H(div) elements
+ (of the second kind).
+
+* ``"Nedelec 1st kind H(curl)"`` or ``"N1curl"``, representing
+ vector-valued Nedelec H(curl) elements
+ (of the first kind).
+
+* ``"Nedelec 2st kind H(curl)"`` or ``"N2curl"``,
+ representing vector-valued Nedelec H(curl) elements
+ (of the second kind).
+
+* ``"Quadrature"`` or ``"Q"``, representing artificial ``finite elements``
+ with degrees of freedom being function evaluation at quadrature points;
+
+* ``"Boundary Quadrature"`` or ``"BQ"``, representing artificial
+ "finite elements" with degrees of freedom being function evaluation
+ at quadrature points on the boundary;
+
+Note that new versions of UFL also support notation from the Periodic Table
+of Finite Elements, currently not documented here.
+
+
+Basic elements
+--------------
+
+A ``FiniteElement``, sometimes called a basic element, represents a
+finite element in some family on a given cell with a certain polynomial
+degree. Valid families and cells are explained above.
+The notation is::
+
+ element = FiniteElement(family, cell, degree)
+
+Some examples::
+
+ element = FiniteElement("Lagrange", interval, 3)
+ element = FiniteElement("DG", tetrahedron, 0)
+ element = FiniteElement("BDM", triangle, 1)
+
+
+Vector elements
+---------------
+
+A ``VectorElement`` represents a combination of basic elements such that
+each component of a vector is represented by the basic element. The size
+is usually omitted, the default size equals the geometry dimension.
+The notation is::
+
+ element = VectorElement(family, cell, degree[, size])
+
+Some examples::
+
+ # A quadratic "P2" vector element on a triangle
+ element = VectorElement("CG", triangle, 2)
+ # A linear 3D vector element on a 1D interval
+ element = VectorElement("CG", interval, 1, size=3)
+ # A six-dimensional piecewise constant element on a tetrahedron
+ element = VectorElement("DG", tetrahedron, 0, size=6)
+
+
+Tensor elements
+---------------
+
+A ``TensorElement`` represents a combination of basic elements such that
+each component of a tensor is represented by the basic element. The
+shape is usually omitted, the default shape is (d, d) where d is the
+geometry dimension. The notation is::
+
+ element = TensorElement(family, cell, degree[, shape, symmetry])
+
+Any shape tuple consisting of positive integers is valid,
+and the optional symmetry can either be set to ``True``
+which means standard matrix symmetry (like :math:`A_{ij} = A_{ji}`),
+or a ``dict`` like ``{ (0,1):(1,0), (0,2):(2,0) }``
+where the ``dict`` keys are index tuples that are
+represented by the corresponding ``dict`` value.
+
+Examples::
+
+ element = TensorElement("CG", cell, 2)
+ element = TensorElement("DG", cell, 0, shape=(6,6))
+ element = TensorElement("DG", cell, 0, symmetry=True)
+ element = TensorElement("DG", cell, 0, symmetry={(0,0): (1,1)})
+
+
+Mixed elements
+--------------
+
+A ``MixedElement`` represents an arbitrary combination of other elements.
+``VectorElement`` and ``TensorElement`` are special cases of a
+``MixedElement`` where all sub-elements are equal.
+
+General notation for an arbitrary number of subelements::
+
+ element = MixedElement(element1, element2[, element3, ...])
+
+Shorthand notation for two subelements::
+
+ element = element1 * element2
+
+.. note::
+
+ The ``*`` operator is left-associative, such that::
+
+ element = element1 * element2 * element3
+
+ represents ``(e1 * e2) * e3``, i.e. this is a mixed element with two
+ sub-elements ``(e1 * e2)`` and ``e3``.
+
+See `Form arguments`_ for details on how defining
+functions on mixed spaces can differ from functions on other
+finite element spaces.
+
+Examples::
+
+ # Taylor-Hood element
+ V = VectorElement("Lagrange", cell, 2)
+ P = FiniteElement("Lagrange", cell, 1)
+ TH = V * P
+
+ # A tensor-vector-scalar element
+ T = TensorElement("Lagrange", cell, 2, symmetry=True)
+ V = VectorElement("Lagrange", cell, 1)
+ P = FiniteElement("DG", cell, 0)
+ ME = MixedElement(T, V, P)
+
+EnrichedElement
+---------------
+
+The data type ``EnrichedElement`` represents the vector sum of two
+(or more) finite elements.
+
+Example: The Mini element can be constructed as::
+
+ P1 = VectorElement("Lagrange", "triangle", 1)
+ B = VectorElement("Bubble", "triangle", 3)
+ Q = FiniteElement("Lagrange", "triangle", 1)
+
+ Mini = (P1 + B) * Q
+
+Form arguments
+==============
+
+Form arguments are divided in two groups, arguments and
+coefficients. An ``Argument`` represents an
+arbitrary basis function in a given discrete finite element space,
+while a ``Coefficient`` represents a function in a discrete finite
+element space that will be provided by the user at a later stage. The
+number of ``Argument``\ s that occur in a ``Form`` equals
+the "arity" of the form.
+
+Basis functions
+---------------
+
+The data type ``Argument`` represents a basis function on a
+given finite element. An ``Argument`` must be created for a
+previously declared finite element (simple or mixed)::
+
+ v = Argument(element)
+
+Note that more than one ``Argument`` can be declared for the same
+``FiniteElement``. Basis functions are associated with the arguments of
+a multilinear form in the order of declaration.
+
+For a ``MixedElement``, the function ``Arguments`` can be used to
+construct tuples of ``Argument``\ s, as illustrated here for a mixed
+Taylor--Hood element::
+
+ v, q = Arguments(TH)
+ u, p = Arguments(TH)
+
+For a ``Argument`` on a ``MixedElement`` (or ``VectorElement``
+or ``TensorElement``), the function ``split`` can be used to extract
+basis function values on subspaces, as illustrated here for a mixed
+Taylor--Hood element::
+
+ vq = Argument(TH)
+ v, q = split(up)
+
+A shorthand for this is in place called ``Arguments``::
+
+ v, q = Arguments(TH)
+
+For convenience, ``TestFunction`` and ``TrialFunction`` are special
+instances of ``Argument`` with the property that a ``TestFunction``
+will always be the first argument in a form and ``TrialFunction`` will
+always be the second argument in a form (order of declaration does
+not matter). Their usage is otherwise the same as for ``Argument``::
+
+ v = TestFunction(element)
+ u = TrialFunction(element)
+ v, q = TestFunctions(TH)
+ u, p = TrialFunctions(TH)
+
+
+Meshes and function spaces
+--------------------------
+
+Note that newer versions of UFL introduce the concept of a
+Mesh and a FunctionSpace. These are currently not documented here.
+
+
+Coefficient functions
+---------------------
+
+The data type ``Coefficient`` represents a function belonging to a given
+finite element space, that is, a linear combination of basis functions
+of the finite element space. A ``Coefficient`` must be declared for a
+previously declared ``FiniteElement``::
+
+ f = Coefficient(element)
+
+Note that the order in which ``Coefficient``\ s are declared is important,
+directly reflected in the ordering they have among the arguments to each
+``Form`` they are part of.
+
+``Coefficient`` is used to represent user-defined functions, including, e.g.,
+source terms, body forces, variable coefficients and stabilization terms.
+UFL treats each ``Coefficient`` as a linear combination of unknown basis
+functions with unknown coefficients, that is, UFL knows nothing about
+the concrete basis functions of the element and nothing about the value
+of the function.
+
+.. note::
+
+ Note that more than one function can be declared for the same
+ ``FiniteElement``. The following example declares two ``Argument``\ s
+ and two ``Coefficient``\ s for the same ``FiniteElement``::
+
+ v = Argument(element)
+ u = Argument(element)
+ f = Coefficient(element)
+ g = Coefficient(element)
+
+For a ``Coefficient`` on a ``MixedElement`` (or ``VectorElement`` or
+``TensorElement``), the function ``split`` can be used to extract function
+values on subspaces, as illustrated here for a mixed Taylor--Hood element::
+
+ up = Coefficient(TH)
+ u, p = split(up)
+
+A shorthand for this is in place called ``Coefficients``::
+
+ u, p = Coefficient(TH)
+
+Spatially constant (or discontinuous piecewise constant) functions can
+conveniently be represented by ``Constant``, ``VectorConstant``, and
+``TensorConstant``::
+
+ c0 = Constant(cell)
+ v0 = VectorConstant(cell)
+ t0 = TensorConstant(cell)
+
+These three lines are equivalent with first defining
+DG0 elements and then defining a ``Coefficient``
+on each, illustrated here::
+
+ DG0 = FiniteElement("Discontinuous Lagrange", cell, 0)
+ DG0v = VectorElement("Discontinuous Lagrange", cell, 0)
+ DG0t = TensorElement("Discontinuous Lagrange", cell, 0)
+
+ c1 = Coefficient(DG0)
+ v1 = Coefficient(DG0v)
+ t1 = Coefficient(DG0t)
+
+Basic Datatypes
+===============
+
+UFL expressions can depend on some other quantities in addition to the
+functions and basis functions described above.
+
+Literals and geometric quantities
+---------------------------------
+
+Some atomic quantities are derived from the cell. For example, the
+(global) spatial coordinates are available as a vector valued expression
+``SpatialCoordinate(cell)``::
+
+ # Linear form for a load vector with a sin(y) coefficient
+ v = TestFunction(element)
+ x = SpatialCoordinate(cell)
+ L = sin(x[1])*v*dx
+
+Another quantity is the (outwards pointing) facet normal ``FacetNormal(cell)``.
+The normal vector is only defined on the boundary, so it can't be used
+in a cell integral.
+
+Example functional ``M``, an integral of the normal component of a
+function ``g`` over the boundary::
+
+ n = FacetNormal(cell)
+ g = Coefficient(VectorElement("CG", cell, 1))
+ M = dot(n, g)*ds
+
+Python scalars (int, float) can be used anywhere a scalar expression
+is allowed. Another literal constant type is ``Identity`` which
+represents an :math:`n\times n` unit matrix of given size :math:`n`,
+as in this example::
+
+ # Geometric dimension
+ d = cell.geometric_dimension()
+
+ # d x d identiy matrix
+ I = Identity(d)
+
+ # Kronecker delta
+ delta_ij = I[i,j]
+
+
+Indexing and tensor components
+==============================
+
+UFL supports index notation, which is often a convenient way to
+express forms. The basic principle of index notation is that summation
+is implicit over indices repeated twice in each term of an expression.
+The following examples illustrate the index notation, assuming that
+each of the variables ``i`` and ``j`` have been declared as
+a free ``Index``:
+
+* ``v[i]*w[i]``: :math:`\sum_{i=0}^{n-1} v_i w_i = \mathbf{v}\cdot\mathbf{w}`
+
+* ``Dx(v, i)*Dx(w, i)``:
+ :math:`\sum_{i=0}^{d-1} \frac{\partial v}{\partial x_i} \frac{\partial w}{\partial x_i}
+ = \nabla v \cdot \nabla w`
+
+* ``Dx(v[i], i)``: :math:`\sum_{i=0}^{d-1}
+ \frac{\partial v_i}{\partial x_i} = \nabla \cdot v`
+
+* ``Dx(v[i], j)*Dx(w[i], j)``: :math:`\sum_{i=0}^{n-1} \sum_{j=0}^{d-1}
+ \frac{\partial v_i}{\partial x_j} \frac{\partial w_i}{\partial x_j}
+ = \nabla \mathbf{v} : \nabla \mathbf{w}`
+
+Here we will try to very briefly summarize the basic concepts of tensor
+algebra and index notation, just enough to express the operators in UFL.
+
+Assuming an Euclidean space in :math:`d` dimensions with :math:`1 \le
+d 3`, and a set of orthonormal basis vectors :math:`\mathbf{i}_i` for :math:`i
+\in {0, \ldots, d-1 }`, we can define the dot product of any two basis
+functions as
+
+.. math::
+
+ \mathbf{i}_{i} \cdot \mathbf{i}_{j} = \delta_{ij},
+
+where :math:`\delta_{ij}` is the Kronecker delta
+
+.. math::
+
+ \delta_{ij}
+ \equiv
+ \begin{cases}
+ 1, \quad i = j, \\
+ 0, \quad \text{otherwise}.
+ \end{cases}
+
+A rank 1 tensor (vector) quantity :math:`\mathbf{v}` can be represented in
+terms of unit vectors and its scalar components in that basis. In tensor
+algebra it is common to assume implicit summation over indices repeated
+twice in a product:
+
+.. math::
+
+ \mathbf{v} = v_k \mathbf{i}_k \equiv \sum_k v_k \mathbf{i}_k.
+
+Similarly, a rank two tensor (matrix) quantity :math:`\mathbf{A}` can
+be represented in terms of unit matrices, that is outer products of
+unit vectors:
+
+.. math::
+
+ \mathbf{A} = A_{ij} \mathbf{i}_i \mathbf{i}_j \equiv \sum_i \sum_j A_{ij} \mathbf{i}_i \mathbf{i}_j .
+
+This generalizes to tensors of arbitrary rank:
+
+.. math::
+
+ \mathcal{C} &= C_\iota \mathbf{i}_{\iota_0} \otimes \cdots \otimes \mathbf{i}_{\iota_{r-1}} \\
+ &\equiv \sum_{\iota_0} \cdots \sum_{\iota_{r-1}}
+ C_\iota \mathbf{i}_{\iota_0}\otimes\cdots \otimes \mathbf{i}_{\iota_{r-1}},
+
+where :math:`\mathcal{C}` is a rank :math:`r` tensor and :math:`\iota`
+is a multi-index of length :math:`r`.
+
+When writing equations on paper, a mathematician can easily switch
+between the :math:`\mathbf{v}` and :math:`v_i` representations without
+stating it explicitly. This is possible because of flexible notation
+and conventions. In a programming language, we can't use the boldface
+notation which associates :math:`\mathbf{v}` and :math:`v` by convention,
+and we can't always interpret such conventions unambiguously. Therefore,
+UFL requires that an expression is explicitly mapped from its tensor
+representation (:math:`\mathbf{v}`, :math:`\mathbf{A}`) to its component
+representation (:math:`v_i`, :math:`A_{ij}`) and back. This is done using
+``Index`` objects, the indexing operator (``v[i]``), and the function
+``as_tensor``. More details on these follow.
+
+In the following descriptions of UFL operator syntax, i-l and p-s are
+assumed to be predefined indices, and unless otherwise specified the name
+v refers to some vector valued expression, and the name A refers to some
+matrix valued expression. The name C refers to a tensor expression of
+arbitrary rank.
+
+Defining indices
+----------------
+
+A set of indices ``i``, ``j``, ``k``, ``l`` and ``p``, ``q``, ``r``,
+``s`` are predefined, and these should be enough for many applications.
+Examples will usually use these objects instead of creating new ones to
+conserve space.
+
+The data type ``Index`` represents an index used for subscripting
+derivatives or taking components of non-scalar expressions.
+To create indices, you can either make a single using ``Index()``
+or make several at once conveniently using ``indices(n)``::
+
+ i = Index()
+ j, k, l = indices(3)
+
+Each of these represents an ``index range`` determined by the context;
+if used to subscript a tensor-valued expression, the range is given
+by the shape of the expression, and if used to subscript a derivative,
+the range is given by the dimension :math:`d` of the underlying shape
+of the finite element space. As we shall see below, indices can be a
+powerful tool when used to define forms in tensor notation.
+
+
+.. note:: Advanced usage
+
+ If using UFL inside DOLFIN or another larger programming environment,
+ it is a good idea to define your indices explicitly just before your
+ form uses them, to avoid name collisions. The definition of the
+ predefined indices is simply::
+
+ i, j, k, l = indices(4)
+ p, q, r, s = indices(4)
+
+.. note:: Advanced usage
+
+ Note that in the old FFC notation, the definition ::
+
+ i = Index(0)
+
+ meant that the value of the index remained constant. This does not mean
+ the same in UFL, and this notation is only meant for internal usage.
+ Fixed indices are simply integers instead::
+
+ i = 0
+
+
+Taking components of tensors
+----------------------------
+
+Basic fixed indexing of a vector valued expression v or matrix valued
+expression A:
+
+* ``v[0]``: component access, representing the scalar value of the first
+ component of v
+
+* ``A[0,1]``: component access, representing the scalar value of the
+ first row, second column of A
+
+
+Basic indexing:
+
+* ``v[i]``: component access, representing the scalar value of some
+ component of v
+* ``A[i,j]``: component access, representing the scalar value of some
+ component i,j of A
+
+More advanced indexing:
+
+* ``A[i,0]``: component access, representing the scalar value of some
+ component i of the first column of A
+
+* ``A[i,:]``: row access, representing some row i of A, i.e. rank(A[i,:]) == 1
+
+* ``A[:,j]``: column access, representing some column j of A,
+ i.e. rank(A[:,j]) == 1
+
+* ``C[...,0]``: subtensor access, representing the subtensor of A with
+ the last axis fixed, e.g., A[...,0] == A[:,0]
+
+* ``C[j,...]``: subtensor access, representing the subtensor of A with
+ the last axis fixed, e.g., A[j,...] == A[j,:]
+
+
+Making tensors from components
+------------------------------
+
+If you have expressions for scalar components of a tensor and wish to
+convert them to a tensor, there are two ways to do it. If you have a
+single expression with free indices that should map to tensor axes,
+like mapping :math:`v_k` to :math:`\mathbf{v}` or :math:`A_{ij}` to
+:math:`\mathbf{A}`, the following examples show how this is done::
+
+ vk = Identity(cell.d)[0,k]
+ v = as_tensor(vk, (k,))
+
+ Aij = v[i]*u[j]
+ A = as_tensor(Aij, (i,j))
+
+Here ``v`` will represent unit vector :math:`\mathbf{i}_0`, and ``A``
+will represent the outer product of ``v`` and ``u``.
+
+If you have multiple expressions without indices, you can build tensors
+from them just as easily, as illustrated here::
+
+ v = as_vector([1.0, 2.0, 3.0])
+ A = as_matrix([[u[0], 0], [0, u[1]]])
+ B = as_matrix([[a+b for b in range(2)] for a in range(2)])
+
+Here ``v``, ``A`` and ``B`` will represent the expressions
+
+.. math::
+
+ \mathbf{v} &= \mathbf{i}_0 + 2 \mathbf{i}_1 + 3 \mathbf{i}_2, \\
+ \mathbf{A} &= \begin{bmatrix} u_0 & 0 \\ 0 & u_1 \end{bmatrix}, \\
+ \mathbf{B} &= \begin{bmatrix} 0 & 1 \\ 1 & 2 \end{bmatrix}.
+
+Note that the function ``as_tensor`` generalizes from vectors to tensors
+of arbitrary rank, while the alternative functions ``as_vector`` and
+``as_matrix`` work the same way but are only for constructing vectors
+and matrices. They are included for readability and convenience.
+
+
+Implicit summation
+------------------
+
+Implicit summation can occur in only a few situations. A product of
+two terms that shares the same free index is implicitly treated as a
+sum over that free index:
+
+* ``v[i]*v[i]``: :math:`\sum_i v_i v_i`
+* ``A[i,j]*v[i]*v[j]``: :math:`\sum_j (\sum_i A_{ij} v_i) v_j`
+
+A tensor valued expression indexed twice with the same free index is
+treated as a sum over that free index:
+
+* ``A[i,i]``: :math:`\sum_i A_{ii}`
+* ``C[i,j,j,i]``: :math:`\sum_i \sum_j C_{ijji}`
+
+The spatial derivative, in the direction of a free index, of an expression
+with the same free index, is treated as a sum over that free index:
+
+* ``v[i].dx(i)``: :math:`\sum_i v_i`
+* ``A[i,j].dx(i)``: :math:`\sum_i \frac{d(A_{ij})}{dx_i}`
+
+Note that these examples are some times written :math:`v_{i,i}` and
+:math:`A_{ij,i}` in pen-and-paper index notation.
+
+
+Basic algebraic operators
+=========================
+
+The basic algebraic operators ``+``, ``-``, ``*``, ``/`` can be used
+freely on UFL expressions. They do have some requirements on their
+operands, summarized here:
+
+Addition or subtraction, ``a + b`` or ``a - b``:
+
+* The operands a and b must have the same shape.
+* The operands a and b must have the same set of free indices.
+
+Division, ``a / b``:
+
+* The operand b must be a scalar expression.
+
+* The operand b must have no free indices.
+
+* The operand a can be non-scalar with free indices, in which division
+ represents scalar division of all components with the scalar b.
+
+Multiplication, ``a * b``:
+
+* The only non-scalar operations allowed is scalar-tensor,
+ matrix-vector and matrix-matrix multiplication.
+
+* If either of the operands have any free indices, both must be scalar.
+
+* If any free indices are repeated, summation is implied.
+
+
+Basic nonlinear functions
+=========================
+
+Some basic nonlinear functions are also available, their meaning mostly
+obvious.
+
+* ``abs(f)``: the absolute value of f.
+
+* ``sign(f)``: the sign of f (+1 or -1).
+
+* ``pow(f, g)`` or ``f**g``: f to the power g, :math:`f^g`
+
+* ``sqrt(f)``: square root, :math:`\sqrt{f}`
+
+* ``exp(f)``: exponential of f
+
+* ``ln(f)``: natural logarithm of f
+
+* ``cos(f)``: cosine of f
+
+* ``sin(f)``: sine of f
+
+* ``tan(f)``: tangent of f
+
+* ``cosh(f)``: hyperbolic cosine of f
+
+* ``sinh(f)``: hyperbolic sine of f
+
+* ``tanh(f)``: hyperbolic tangent of f
+
+* ``acos(f)``: inverse cosine of f
+
+* ``asin(f)``: inverse sine of f
+
+* ``atan(f)``: inverse tangent of f
+
+* ``atan2(f1, f2)``: inverse tangent of (f1/f2)
+
+* ``erf(f)``: error function of f, :math:`{2\over\sqrt{\pi}} \int_0^f \exp(-t^2) \mathop{dt}`
+
+* ``bessel_J(nu, f)``: Bessel function of the first kind, :math:`J_\nu(f)`
+
+* ``bessel_Y(nu, f)``: Bessel function of the second kind, :math:`Y_\nu(f)`
+
+* ``bessel_I(nu, f)``: Modified Bessel function of the first kind, :math:`I_\nu(f)`
+
+* ``bessel_K(nu, f)``: Modified Bessel function of the second kind, :math:`K_\nu(f)`
+
+These functions do not accept non-scalar operands or operands with free
+indices or ``Argument`` dependencies.
+
+
+Tensor algebra operators
+========================
+
+``transpose``
+-------------
+
+The transpose of a matrix A can be written as::
+
+ AT = transpose(A)
+ AT = A.T
+ AT = as_matrix(A[i,j], (j,i))
+
+The definition of the transpose is
+
+.. math::
+
+ \mathtt{AT[i,j]} \leftrightarrow (A^{\top})_{ij} = A_{ji}
+
+For transposing higher order tensor expressions, index notation can
+be used::
+
+ AT = as_tensor(A[i,j,k,l], (l,k,j,i))
+
+``tr``
+------
+
+The trace of a matrix A is the sum of the diagonal entries. This can
+be written as::
+
+ t = tr(A)
+ t = A[i,i]
+
+The definition of the trace is
+
+.. math::
+
+ \mathtt{tr(A)} \leftrightarrow \mathrm{tr} \mathbf{A} = A_{ii} = \sum_{i=0}^{n-1} A_{ii}.
+
+``dot``
+-------
+
+The dot product of two tensors a and b can be written::
+
+ # General tensors
+ f = dot(a, b)
+
+ # Vectors a and b
+ f = a[i]*b[i]
+
+ # Matrices a and b
+ f = as_matrix(a[i,k]*b[k,j], (i,j))
+
+The definition of the dot product of unit vectors is (assuming an
+orthonormal basis for a Euclidean space):
+
+.. math::
+
+ \mathbf{i}_i \cdot \mathbf{i}_j = \delta_{ij}
+
+where :math:`\delta_{ij}` is the Kronecker delta function.
+The dot product of higher order tensors follow from this, as illustrated
+with the following examples.
+
+An example with two vectors
+
+.. math::
+
+ \mathbf{v} \cdot \mathbf{u} = (v_i \mathbf{i}_i) \cdot (u_j \mathbf{i}_j)
+ = v_i u_j (\mathbf{i}_i \cdot \mathbf{i}_j) = v_i u_j \delta_{ij} = v_i u_i
+
+An example with a tensor of rank two
+
+.. math::
+
+ \mathbf{A} \cdot \mathbf{B}
+ &= (A_{ij} \mathbf{i}_i \mathbf{i}_j) \cdot (B_{kl} \mathbf{i}_k \mathbf{i}_l) \\
+ &= (A_{ij}B_{kl}) \mathbf{i}_i(\mathbf{i}_j \cdot \mathbf{i}_k) \mathbf{i}_l \\
+ &= (A_{ij}B_{kl}\delta_{jk}) \mathbf{i}_i \mathbf{i}_l \\
+ &= A_{ik}B_{kl} \mathbf{i}_i \mathbf{i}_l.
+
+This is the same as a matrix-matrix multiplication.
+
+An example with a vector and a tensor of rank two
+
+.. math::
+
+ \mathbf{v} \cdot \mathbf{A}
+ &= (v_j \mathbf{i}_j) \cdot (A_{kl} \mathbf{i}_k \mathbf{i}_l) \\
+ &= (v_j A_{kl}) (\mathbf{i}_j \cdot \mathbf{i}_k) \mathbf{i}_l \\
+ &= (v_j A_{kl}\delta_{jk}) \mathbf{i}_l \\
+ &= v_k A_{kl} \mathbf{i}_l
+
+This is the same as a vector-matrix multiplication.
+
+This generalizes to tensors of arbitrary rank:
+The dot product applies to the last axis of a and the first axis of b.
+The tensor rank of the product is rank(a)+rank(b)-2.
+
+``inner``
+---------
+
+The inner product is a contraction over all axes of a and b, that is
+the sum of all component-wise products. The operands must have exactly the
+same dimensions. For two vectors it is equivalent to the dot product.
+
+If :math:`\mathbf{A}` and :math:`\mathbf{B}` are rank two tensors and
+:math:`\mathcal{C}` and :math:`\mathcal{D}` are rank 3 tensors
+their inner products are
+
+.. math::
+ \mathbf{A} : \mathbf{B} &= A_{ij} B_{ij}
+ \\
+ \mathcal{C} : \mathcal{D} &= C_{ijk} D_{ijk}
+
+Using UFL notation, the following sets of declarations are equivalent::
+
+ # Vectors
+ f = dot(a, b)
+ f = inner(a, b)
+ f = a[i]*b[i]
+
+ # Matrices
+ f = inner(A, B)
+ f = A[i,j]*B[i,j]
+
+ # Rank 3 tensors
+ f = inner(C, D)
+ f = C[i,j,k]*D[i,j,k]
+
+
+``outer``
+---------
+
+The outer product of two tensors a and b can be written::
+
+ A = outer(a, b)
+
+The general definition of the outer product of two tensors
+:math:`\mathcal{C}` of rank :math:`r` and :math:`\mathcal{D}` of rank
+:math:`s` is
+
+.. math::
+
+ \mathcal{C} \otimes \mathcal{D}
+ =
+ C_{\iota^a_0 \ldots \iota^a_{r-1}} D_{\iota^b_0 \ldots\iota^b_{s-1}}
+ \mathbf{i}_{\iota^a_0}\otimes\cdots\otimes\mathbf{i}_{\iota^a_{r-2}}
+ \otimes
+ \mathbf{i}_{\iota^b_1} \otimes \cdots \otimes \mathbf{i}_{\iota^b_{s-1}}
+
+Some examples with vectors and matrices are easier to understand:
+
+.. math::
+
+ \mathbf{v} \otimes \mathbf{u} = v_i u_j \mathbf{i}_i \mathbf{i}_j, \\
+ \mathbf{v} \otimes \mathbf{v} = v_i B_{kl} \mathbf{i}_i \mathbf{i}_k \mathbf{i}_l, \\
+ \mathbf{A} \otimes \mathbf{B} = A_{ij} B_{kl} \mathbf{i}_i \mathbf{i}_j \mathbf{i}_k \mathbf{i}_l .
+
+The outer product of vectors is often written simply as:
+
+.. math::
+
+ \mathbf{v} \otimes \mathbf{u} = \mathbf{v} \mathbf{u},
+
+which is what we have done with :math:`\mathbf{i}_i \mathbf{i}_j` above.
+
+The rank of the outer product is the sum of the ranks of the operands.
+
+``cross``
+---------
+
+The operator ``cross`` accepts as arguments two logically vector-valued
+expressions and returns a vector which is the cross product (vector
+product) of the two vectors:
+
+.. math::
+
+ \mathtt{cross(v, w)} \leftrightarrow \mathbf{v} \times \mathbf{w}
+ = (v_1 w_2 - v_2 w_1, v_2 w_0 - v_0 w_2, v_0 w_1 - v_1 w_0)
+
+Note that this operator is only defined for vectors of length three.
+
+``det``
+-------
+
+The determinant of a matrix A can be written::
+
+ d = det(A)
+
+``dev``
+-------
+
+The deviatoric part of matrix A can be written::
+
+ B = dev(A)
+
+``sym``
+-------
+
+The symmetric part of A can be written::
+
+ B = sym(A)
+
+The definition is
+
+.. math::
+
+ {\rm sym} \mathbf{A} = \frac{1}{2}(\mathbf{A} + \mathbf{A}^T)
+
+``skew``
+--------
+
+The skew symmetric part of A can be written::
+
+ B = skew(A)
+
+The definition is
+
+.. math::
+
+ {\rm skew} \mathbf{A} = \frac{1}{2}(\mathbf{A} - \mathbf{A}^T)
+
+
+``cofac``
+---------
+
+The cofactor of a matrix A can be written::
+
+ B = cofac(A)
+
+The definition is
+
+.. math::
+
+ {\rm cofac} \mathbf{A} = \det (\mathbf{A}) \mathbf{A}^{-1}
+
+The implementation of this is currently rather crude, with a hardcoded
+symbolic expression for the cofactor. Therefore, this is limited to 1x1,
+2x2 and 3x3 matrices.
+
+``inv``
+-------
+
+The inverse of matrix A can be written::
+
+ Ainv = inv(A)
+
+The implementation of this is currently rather crude, with a hardcoded
+symbolic expression for the inverse. Therefore, this is limited to 1x1,
+2x2 and 3x3 matrices.
+
+
+Differential Operators
+======================
+
+Three different kinds of derivatives are currently supported: spatial
+derivatives, derivatives w.r.t. user defined variables, and derivatives
+of a form or functional w.r.t. a function.
+
+
+Basic spatial derivatives
+-------------------------
+
+Spatial derivatives hold a special physical meaning in partial differential equations
+and there are several ways to express those. The basic way is::
+
+ # Derivative w.r.t. x_2
+ f = Dx(v, 2)
+ f = v.dx(2)
+ # Derivative w.r.t. x_i
+ g = Dx(v, i)
+ g = v.dx(i)
+
+If ``v`` is a scalar expression, ``f`` here is the scalar derivative of
+``v`` with respect to spatial direction z. If ``v`` has no free indices, ``g``
+is the scalar derivative in spatial direction :math:`x_i`, and ``g``
+has the free index ``i``. This can be expressed compactly as :math:`v_{,i}`:
+
+.. math::
+
+ f = \frac{\partial v}{\partial x_2} = v_{,2}, \\
+ g = \frac{\partial v}{\partial x_i} = v_{,i}.
+
+If the expression to be differentiated w.r.t. :math:`x_i` has ``i``
+as a free-index, implicit summation is implied::
+
+ # Sum of derivatives w.r.t. x_i for all i
+ g = Dx(v[i], i)
+ g = v[i].dx(i)
+
+Here ``g`` will represent the sum of derivatives
+w.r.t. :math:`x_i` for all ``i``, that is
+
+.. math::
+
+ g = \sum_i \frac{\partial v}{\partial x_i} = v_{i,i}.
+
+.. note::
+
+ `v[i].dx(i)` and :math:`v_{i,i}` with compact notation denote implicit summation.
+
+
+Compound spatial derivatives
+----------------------------
+
+UFL implements several common differential operators. The notation is
+simple and their names should be self-explanatory::
+
+ Df = grad(f)
+ df = div(f)
+ cf = curl(v)
+ rf = rot(f)
+
+The operand ``f`` can have no free indices.
+
+Gradient
+--------
+
+The gradient of a scalar :math:`u` is defined as
+
+.. math::
+
+ \mathrm{grad}(u) \equiv \nabla u =
+ \sum_{k=0}^{d-1} \frac{\partial u}{\partial x_k} \mathbf{i}_k,
+
+which is a vector of all spatial partial derivatives of :math:`u`.
+
+The gradient of a vector :math:`\mathbf{v}` is defined as
+
+.. math::
+
+ \mathrm{grad}(\mathbf{v}) \equiv \nabla \mathbf{v}
+ = \frac{\partial v_i}{\partial x_j} \mathbf{i}_i \mathbf{i}_j,
+
+which written componentwise is
+
+.. math::
+
+ \mathbf{A} = \nabla \mathbf{v}, \qquad A_{ij} = v_{i,j}
+
+In general for a tensor :math:`\mathbf{A}` of rank :math:`r` the definition is
+
+.. math::
+
+ {\rm grad}(\mathbf{A}) \equiv \nabla \mathbf{A}
+ = (\frac{\partial}{\partial x_i}) (A_\iota\mathbf{i}_{\iota_0}
+ \otimes\cdots\otimes \mathbf{i}_{\iota_{r-1}}) \otimes \mathbf{i}_i
+ = \frac{\partial A_\iota}{\partial x_i} \mathbf{i}_{\iota_0}
+ \otimes \cdots \otimes \mathbf{i}_{\iota_{r-1}} \otimes \mathbf{i}_i,
+
+where :math:`\iota` is a multiindex of length :math:`r`.
+
+In UFL, the following pairs of declarations are equivalent::
+
+ Dfi = grad(f)[i]
+ Dfi = f.dx(i)
+
+ Dvi = grad(v)[i, j]
+ Dvi = v[i].dx(j)
+
+ DAi = grad(A)[..., i]
+ DAi = A.dx(i)
+
+for a scalar expression ``f``, a vector expression ``v``, and a tensor
+expression ``A`` of arbitrary rank.
+
+Divergence
+----------
+
+The divergence of any nonscalar (vector or tensor) expression :math:`\mathbf{A}`
+is defined as the contraction of the partial derivative over the last
+axis of the expression.
+
+The divergence of a vector :math:`\mathbf{v}` is defined as
+
+.. math::
+
+ \mathrm{div}(\mathbf{v}) \equiv \nabla\cdot\mathbf{v}
+ = \sum_{k=0}^{d-1}\frac{\partial v_i}{\partial x_i}
+
+In UFL, the following declarations are equivalent::
+
+ dv = div(v)
+ dv = v[i].dx(i)
+
+ dA = div(A)
+ dA = A[..., i].dx(i)
+
+for a vector expression v and a tensor expression A.
+
+Curl and rot
+------------
+
+The operator ``curl`` or ``rot`` accepts as argument a vector-valued expression
+and returns its curl:
+
+.. math::
+
+ \mathrm{curl}(\mathbf{v}) = \nabla \times \mathbf{v}
+ = (\frac{\partial v_2}{\partial x_1} - \frac{\partial v_1}{\partial x_2},
+ \frac{\partial v_0}{\partial x_2} - \frac{\partial v_2}{\partial x_0},
+ \frac{\partial v_1}{\partial x_0} - \frac{\partial v_0}{\partial x_1}).
+
+.. note::
+ The `curl` or `rot` operator is only defined for vectors of length three.
+
+In UFL, the following declarations are equivalent::
+
+ omega = curl(v)
+ omega = rot(v)
+
+
+Variable derivatives
+--------------------
+
+UFL also supports differentiation with respect to user defined
+variables. A user defined variable can be any expression that is defined
+as a variable.
+
+
+The notation is illustrated here::
+
+ # Define some arbitrary expression
+ u = Coefficient(element)
+ w = sin(u**2)
+
+ # Annotate expression w as a variable that can be used by "diff"
+ w = variable(w)
+
+ # This expression is a function of w
+ F = I + diff(u, x)
+
+ # The derivative of expression f w.r.t. the variable w
+ df = diff(f, w)
+
+Note that the variable ``w`` still represents the same expression.
+
+This can be useful for example to implement material laws in
+hyperelasticity where the stress tensor is derived from a Helmholtz
+strain energy function.
+
+Currently, UFL does not implement time in any particular way,
+but differentiation w.r.t. time can be done without this support
+through the use of a constant variable t::
+
+ t = variable(Constant(cell))
+ f = sin(x[0])**2 * cos(t)
+ dfdt = diff(f, t)
+
+
+Functional derivatives
+----------------------
+
+The third and final kind of derivative are derivatives of functionals
+or forms w.r.t. to a ``Coefficient``. This is described in more detail in the
+section `AD`_ about form transformations.
+
+DG operators
+============
+
+UFL provides operators for implementation of discontinuous Galerkin
+methods. These include the evaluation of the jump and average
+of a function (or in general an expression) over the interior facets
+(edges or faces) of a mesh.
+
+Restriction: ``v('+')`` and ``v('-')``
+-----------------------------------------------------------
+
+When integrating over interior facets (``*dS``), one may restrict
+expressions to the positive or negative side of the facet::
+
+ element = FiniteElement("Discontinuous Lagrange", "tetrahedron", 0)
+
+ v = TestFunction(element)
+ u = TrialFunction(element)
+
+ f = Coefficient(element)
+
+ a = f('+')*dot(grad(v)('+'), grad(u)('-'))*dS
+
+Restriction may be applied to functions of any finite element space but
+will only have effect when applied to expressions that are discontinuous
+across facets.
+
+Jump: ``jump(v)``
+-----------------
+
+The operator ``jump`` may be used to express the jump of a
+function across a common facet of two cells. Two versions of the
+``jump`` operator are provided.
+
+If called with only one argument, then the ``jump`` operator
+evaluates to the difference between the restrictions of the given
+expression on the positive and negative sides of the facet:
+
+.. math::
+
+ \mathtt{jump(v)} \leftrightarrow [[ v ]] = v^+ - v^-
+
+If the expression ``v`` is scalar, then ``jump(v)`` will also be
+scalar, and if ``v`` is vector-valued, then ``jump(v)`` will also be
+vector-valued.
+
+If called with two arguments, ``jump(v, n)`` evaluates to the
+jump in ``v`` weighted by ``n``. Typically, ``n`` will
+be chosen to represent the unit outward normal of the facet (as seen
+from each of the two neighboring cells). If ``v`` is scalar, then
+``jump(v, n)`` is given by
+
+.. math::
+
+ \mathtt{jump(v, n)} \leftrightarrow [[ v ]]_n = v^+ n^+ + v^- n^-
+
+If ``v`` is vector-valued, then ``jump(v, n)`` is given by
+
+.. math::
+
+ \mathtt{jump(v, n)} \leftrightarrow [[ v ]]_n = v^+ \cdot n^+ + v^- \cdot n^-
+
+Thus, if the expression ``v`` is scalar, then ``jump(v, n)`` will
+be vector-valued, and if ``v`` is vector-valued, then ``jump(v, n)`` will be scalar.
+
+Average: ``avg(v)``
+-------------------
+
+The operator ``avg`` may be used to express the average
+of an expression across a common facet of two cells:
+
+.. math::
+
+ \mathtt{avg(v)} \leftrightarrow [[ v ]] = \frac{1}{2} (v^+ + v^-)
+
+The expression ``avg(v)`` has the same value shape as the expression ``v``.
+
+Conditional Operators
+=====================
+
+Conditional
+-----------
+
+UFL has limited support for branching, but for some PDEs it is needed.
+The expression ``c`` in::
+
+ c = conditional(condition, true_value, false_value)
+
+evaluates to ``true_value`` at run-time if ``condition``
+evaluates to true, or to ``false_value`` otherwise.
+
+This corresponds to the C++ syntax ``(condition ? true_value: false_value)``,
+or the Python syntax ``(true_value if condition else false_value)``.
+
+Conditions
+----------
+
+* ``eq(a, b)`` must be used in place of the notation ``a == b``
+* ``ne(a, b)`` must be used in place of the notation ``a != b``
+* ``le(a, b)`` is equivalent to ``a <= b``
+* ``ge(a, b)`` is equivalent to ``a >= b``
+* ``lt(a, b)`` is equivalent to ``a < b``
+* ``gt(a, b)`` is equivalent to ``a > b``
+
+.. note::
+
+ Because of details in the way Python behaves, we cannot overload
+ the == operator hence these named operators.
+
+.. _user-defined:
+
+User-defined operators
+======================
+
+A user may define new operators, using standard Python syntax. As an
+example, consider the strain-rate operator :math:`\epsilon` of linear elasticity,
+defined by
+
+.. math::
+
+ \epsilon(v) = \frac{1}{2} (\nabla v + (\nabla v)^{\top}).
+
+This operator can be implemented as a function using the Python ``def``
+keyword::
+
+ def epsilon(v):
+ return 0.5*(grad(v) + grad(v).T)
+
+Alternatively, using the shorthand ``lambda`` notation, the
+strain operator may be defined as follows::
+
+ epsilon = lambda v: 0.5*(grad(v) + grad(v).T)
+
+
+Form Transformations
+====================
+
+When you have defined a ``Form``, you can derive new related
+forms from it automatically. UFL defines a set of common
+form transformations described in this section.
+
+
+Replacing arguments of a Form
+-----------------------------
+
+The function ``replace`` lets you replace terminal objects with
+other values, using a mapping defined by a Python dict. This can be
+used for example to replace a ``Coefficient`` with a fixed value for
+optimized runtime evaluation.
+
+Example::
+
+ f = Coefficient(element)
+ g = Coefficient(element)
+ c = Constant(cell)
+ a = f*g*v*dx
+ b = replace(a, { f: 3.14, g: c })
+
+The replacement values must have the same basic properties as the original
+values, in particular value shape and free indices.
+
+
+Action of a form on a function
+------------------------------
+
+The action of a bilinear form :math:`a` is defined as
+
+.. math::
+
+ b(v; w) = a(v, w)
+
+The action of a linear form :math:`L` is defined as
+
+.. math::
+
+ f(;w) = L(w)
+
+This operation is implemented in UFL simply by replacing the rightmost
+basis function (trial function for `a`, test function for `L`)
+in a ``Form``, and is used like this::
+
+ L = action(a, w)
+ f = action(L, w)
+
+To give a concrete example, these declarations are equivalent::
+
+ a = inner(grad(u), grad(v))*dx
+ L = action(a, w)
+
+ a = inner(grad(u), grad(v))*dx
+ L = inner(grad(w), grad(v))*dx
+
+If a is a rank 2 form used to assemble the matrix A, L is a rank 1
+form that can be used to assemble the vector :math:`b = Ax` directly.
+This can be used to define both the form of a matrix and the form of its
+action without code duplication, and for the action of a Jacobi matrix
+computed using derivative.
+
+If L is a rank 1 form used to assemble the vector b, f is a functional
+that can be used to assemble the scalar value :math:`f = b \cdot w`
+directly. This operation is sometimes used in, e.g., error control with L
+being the residual equation and w being the solution to the dual problem.
+(However, the discrete vector for the assembled residual equation will
+typically be available, so doing the dot product using linear algebra
+would be faster than using this feature.)
+
+
+Energy norm of a bilinear form
+-------------------------------
+
+The functional representing the energy norm :math:`|v|_A = v^T A v` of
+a matrix A assembled from a form :math:`a` can be computed with::
+
+ f = energy_norm(a, w)
+
+which is equivalent to::
+
+ f = action(action(a, w), w)
+
+
+Adjoint of a bilinear form
+---------------------------
+
+The adjoint :math:`a'` of a bilinear form :math:`a` is defined as
+
+.. math::
+
+ a'(u,v) = a(v,u).
+
+This operation is implemented in UFL simply by swapping test and trial
+functions in a ``Form``, and is used like this::
+
+ aprime = adjoint(a)
+
+
+Linear and bilinear parts of a form
+-----------------------------------
+
+Some times it is useful to write an equation on the format
+
+.. math::
+
+ a(v,u) - L(v) = 0.
+
+Before assembly, we need to extract the forms corresponding to the left
+hand side and right hand side. This corresponds to extracting the bilinear and linear
+terms of the form respectively, or the terms that depend on both a test
+and a trial function on one side and the terms that depend on only a
+test function on the other.
+
+This is easily done in UFL using ``lhs`` and ``rhs``::
+
+ b = u*v*dx - f*v*dx
+ a, L = lhs(b), rhs(b)
+
+Note that ``rhs`` multiplies the extracted terms by -1,
+corresponding to moving them from left to right, so this is equivalent to::
+
+ a = u*v*dx
+ L = f*v*dx
+
+As a slightly more complicated example, this formulation::
+
+ F = v*(u - w)*dx + k*dot(grad(v), grad(0.5*(w + u)))*dx
+ a, L = lhs(F), rhs(F)
+
+is equivalent to::
+
+ a = v*u*dx + k*dot(grad(v), 0.5*grad(u))*dx
+ L = v*w*dx - k*dot(grad(v), 0.5*grad(w))*dx
+
+
+.. _AD:
+
+Automatic functional differentiation
+------------------------------------
+
+UFL can compute derivatives of functionals or forms w.r.t. to a
+``Coefficient``. This functionality can be used for example to linearize
+your nonlinear residual equation automatically, or derive a linear system
+from a functional, or compute sensitivity vectors w.r.t. some coefficient.
+
+A functional can be differentiated to obtain a linear form,
+
+.. math::
+
+ F(v; w) = \frac{d}{dw} f(;w)
+
+and a linear form can be differentiated to obtain the bilinear form
+corresponding to its Jacobi matrix.
+
+.. note::
+ Note that by "linear form" we only mean a form that is linear
+ in its test function, not in the function you differentiate with respect to.
+
+.. math::
+
+ J(v, u; w) = \frac{d}{dw} F(v; w).
+
+The UFL code to express this is (for a simple functional
+:math:`f(w)=\int_\Omega \frac 1 2 w^2\,dx`)::
+
+ f = (w**2)/2 * dx
+ F = derivative(f, w, v)
+ J = derivative(F, w, u)
+
+which is equivalent to::
+
+ f = (w**2)/2 * dx
+ F = w*v*dx
+ J = u*v*dx
+
+Assume in the following examples that::
+
+ v = TestFunction(element)
+ u = TrialFunction(element)
+ w = Coefficient(element)
+
+The stiffness matrix can be computed from the functional
+:math:`\int_\Omega \nabla w : \nabla w \, dx`, by::
+
+ f = inner(grad(w), grad(w))/2 * dx
+ F = derivative(f, w, v)
+ J = derivative(F, w, u)
+
+which is equivalent to::
+
+ f = inner(grad(w), grad(w))/2 * dx
+ F = inner(grad(w), grad(v)) * dx
+ J = inner(grad(u), grad(v)) * dx
+
+Note that here the basis functions are provided explicitly, which is
+some times necessary, e.g., if part of the form is linearlized manually
+as in::
+
+ g = Coefficient(element)
+ f = inner(grad(w), grad(w))*dx
+ F = derivative(f, w, v) + dot(w-g,v)*dx
+ J = derivative(F, w, u)
+
+Derivatives can also be computed w.r.t. functions in mixed spaces.
+Consider this example, an implementation of the harmonic map equations
+using automatic differentiation::
+
+ X = VectorElement("Lagrange", cell, 1)
+ Y = FiniteElement("Lagrange", cell, 1)
+
+ x = Coefficient(X)
+ y = Coefficient(Y)
+
+ L = inner(grad(x), grad(x))*dx + dot(x,x)*y*dx
+
+ F = derivative(L, (x,y))
+ J = derivative(F, (x,y))
+
+Here ``L`` is defined as a functional with two coefficient functions
+``x`` and ``y`` from separate finite element spaces. However, ``F`` and
+``J`` become linear and bilinear forms respectively with basis functions
+defined on the mixed finite element::
+
+ M = X + Y
+
+There is a subtle difference between defining ``x`` and ``y``
+separately and this alternative implementation
+(reusing the elements ``X``, ``Y``, ``M``)::
+
+ u = Coefficient(M)
+ x, y = split(u)
+
+ L = inner(grad(x), grad(x))*dx + dot(x,x)*y*dx
+
+ F = derivative(L, u)
+ J = derivative(F, u)
+
+The difference is that the forms here have *one* coefficient function
+``u`` in the mixed space, and the forms above have *two* coefficient
+functions ``x`` and ``y``.
+
+TODO: Move this to implementation part?
+If you wonder how this is all done, a brief explanation follows.
+Recall that a ``Coefficient`` represents a
+sum of unknown coefficients multiplied with unknown
+basis functions in some finite element space.
+
+.. math::
+
+ w(x) = \sum_k w_k \phi_k(x)
+
+Also recall that a ``Argument`` represents any (unknown) basis
+function in some finite element space.
+
+.. math::
+
+ v(x) = \phi_k(x), \qquad \phi_k \in V_h .
+
+A form :math:`L(v; w)` implemented in UFL is intended for discretization
+like
+
+.. math::
+
+ b_i = L(\phi_i; \sum_k w_k \phi_k), \qquad \forall \phi_i \in V_h .
+
+The Jacobi matrix :math:`A_{ij}` of this vector can be obtained by
+differentiation of :math:`b_i` w.r.t. :math:`w_j`, which can be written
+
+.. math::
+
+ A_{ij} = \frac{d b_i}{d w_j} = a(\phi_i, \phi_j; \sum_k w_k \phi_k), \qquad \forall \phi_i \in V_h, \quad \forall \phi_j \in V_h ,
+
+for some form `a`. In UFL, the form `a` can be obtained by
+differentiating `L`. To manage this, we note that as long as the domain
+:math:\Omega is independent of :math:`w_j`, :math:`\int_\Omega` commutes with :math:`\frac{d}{d
+w_j}`, and we can differentiate the integrand expression instead, e.g.,
+
+.. math::
+
+ L(v; w) = \int_\Omega I_c(v; w) \, dx + \int_{\partial\Omega} I_e(v; w) \, ds, \\
+ \frac{d}{d w_j} L(v; w) = \int_\Omega \frac{d I_c}{d w_j} \, dx + \int_{\partial\Omega} \frac{d I_e}{d w_j} \, ds.
+
+In addition, we need that
+
+.. math::
+
+ \frac{d w}{d w_j} = \phi_j, \qquad \forall \phi_j \in V_h ,
+
+which in UFL can be represented as
+
+.. math::
+
+ w &= \mathtt{Coefficient(element)}, \\
+ v &= \mathtt{Argument(element)}, \\
+ \frac{dw}{d w_j} &= v,
+
+since :math:`w` represents the sum and :math:`v` represents any and all
+basis functions in :math:`V_h`.
+
+Other operators have well defined derivatives, and by repeatedly applying
+the chain rule we can differentiate the integrand automatically.
+
+
+Combining form transformations
+------------------------------
+
+Form transformations can be combined freely. Note that to do this,
+derivatives are usually be evaluated before applying e.g. the action of
+a form, because ``derivative`` changes the arity of the form::
+
+ element = FiniteElement("CG", cell, 1)
+ w = Coefficient(element)
+ f = w**4/4*dx(0) + inner(grad(w), grad(w))*dx(1)
+ F = derivative(f, w)
+ J = derivative(F, w)
+ Ja = action(J, w)
+ Jp = adjoint(J)
+ Jpa = action(Jp, w)
+ g = Coefficient(element)
+ Jnorm = energy_norm(J, g)
+
+
+Form files
+==========
+
+UFL forms and elements can be collected in a *form file* with the
+extension *.ufl*. Form compilers will typically execute this file with
+the global UFL namespace available, and extract forms and elements
+that are defined after execution. The compilers do not compile all
+forms and elements that are defined in file, but only those that
+are "exported". A finite element with the variable name ``element``
+is exported by default, as are forms with the names ``M``, ``L``, and
+``a``. The default form names are intended for a functional, linear form,
+and bilinear form respectively.
+
+To export multiple forms and elements or use other names, an explicit
+list with the forms and elements to export can be defined. Simply write::
+
+ elements = [V, P, TH]
+ forms = [a, L, F, J, L2, H1]
+
+at the end of the file to export the elements and forms held by these
+variables.
diff --git a/doc/sphinx/source/user/internal_representation.rst b/doc/sphinx/source/user/internal_representation.rst
new file mode 100644
index 0000000..8a6b268
--- /dev/null
+++ b/doc/sphinx/source/user/internal_representation.rst
@@ -0,0 +1,183 @@
+*******************************
+Internal representation details
+*******************************
+
+FIXME: This chapter is very much outdated.
+Most of the concepts are still the same but
+a lot of the details are different.
+
+
+This chapter explains how UFL forms and expressions are represented
+in detail. Most operations are mirrored by a representation class,
+e.g., ``Sum`` and ``Product``, which are subclasses
+of ``Expr``. You can import all of them from the submodule
+``ufl.classes`` by::
+
+ from ufl.classes import *
+
+Structure of a form
+===================
+
+Each ``Form`` owns multiple ``Integral`` instances, each associated
+with a different ``Measure``. An ``Integral`` owns a ``Measure``
+and an ``Expr``, which represents the integrand expression. The
+``Expr`` is the base class of all expressions. It has two direct
+subclasses ``Terminal`` and ``Operator``.
+
+Subclasses of ``Terminal`` represent atomic quantities which
+terminate the expression tree, e.g. they have no subexpressions.
+Subclasses of ``Operator`` represent operations on one or more
+other expressions, which may usually be ``Expr`` subclasses of
+arbitrary type. Different ``Operator``\ s may have restrictions
+on some properties of their arguments.
+
+All the types mentioned here are conceptually immutable, i.e. they
+should never be modified over the course of their entire lifetime. When a
+modified expression, measure, integral, or form is needed, a new instance
+must be created, possibly sharing some data with the old one. Since the
+shared data is also immutable, sharing can cause no problems.
+
+General properties of expressions
+=================================
+
+Any UFL expression has certain properties, defined by functions that
+every ``Expr`` subclass must implement. In the following, ``u``
+represents an arbitrary UFL expression, i.e. an instance of an
+arbitrary ``Expr`` subclass.
+
+``operands``
+------------
+
+``u.operands()`` returns a tuple with all the operands of u, which should
+all be ``Expr`` instances.
+
+``reconstruct``
+---------------
+
+``u.reconstruct(operands)`` returns a new ``Expr`` instance
+representing the same operation as ``u`` but with other
+operands. Terminal objects may simply return ``self`` since all
+``Expr`` instance are immutable. An important invariant is that
+``u.reconstruct(u.operands()) == u``.
+
+``cell``
+--------
+
+``u.cell()`` returns the first ``Cell`` instance found in ``u``. It
+is currently assumed in UFL that no two different cells are used in
+a single form. Not all expression define a cell, in which case this
+returns ``None`` and ``u`` is spatially constant. Note that this
+property is used in some algorithms.
+
+
+``shape``
+---------
+
+``u.shape()`` returns a tuple of integers, which is the tensor shape
+of ``u``.
+
+
+``free_indices``
+-----------------
+
+``u.free_indices()`` returns a tuple of ``Index`` objects, which
+are the unassigned, free indices of ``u``.
+
+
+``index_dimensions``
+---------------------
+
+``u.index_dimensions()`` returns a ``dict`` mapping from each
+``Index`` instance in ``u.free_indices()`` to the integer dimension
+of the value space each index can range over.
+
+
+``str(u)``
+----------
+
+``str(u)`` returns a human-readable string representation of ``u``.
+
+
+``repr(u)``
+-----------
+
+``repr(u)`` returns a Python string representation of ``u``, such
+that ``eval(repr(u)) == u`` holds in Python.
+
+
+``hash(u)``
+-----------
+
+``hash(u)`` returns a hash code for ``u``, which is used extensively
+(indirectly) in algorithms whenever ``u`` is placed in a Python
+``dict`` or ``set``.
+
+
+``u == v``
+----------
+
+``u == v`` returns true if and only if ``u`` and ``v`` represents
+the same expression in the exact same way. This is used extensively
+(indirectly) in algorithms whenever ``u`` is placed in a Python
+``dict`` or ``set``.
+
+
+About other relational operators
+--------------------------------
+
+In general, UFL expressions are not possible to fully evaluate since the
+cell and the values of form arguments are not available. Implementing
+relational operators for immediate evaluation is therefore impossible.
+
+Overloading relational operators as a part of the form language is not
+possible either, since it interferes with the correct use of container
+types in Python like ``dict`` or ``set``.
+
+
+Elements
+========
+
+All finite element classes have a common base class
+``FiniteElementBase``. The class hierarchy looks like this:
+
+TODO: Class figure.
+
+TODO: Describe all FiniteElementBase subclasses here.
+
+
+Terminals
+=========
+
+All ``Terminal`` subclasses have some non-``Expr`` data attached
+to them. ``ScalarValue`` has a Python scalar, ``Coefficient``
+has a ``FiniteElement``, etc.
+
+Therefore, a unified implementation of ``reconstruct`` is
+not possible, but since all ``Expr`` instances are immutable,
+``reconstruct`` for terminals can simply return self. This feature
+and the immutability property is used extensively in algorithms.
+
+Operators
+=========
+
+All instances of ``Operator`` subclasses are fully specified
+by their type plus the tuple of ``Expr`` instances that are
+the operands. Their constructors should take these operands as the
+positional arguments, and only that. This way, a unified implementation
+of ``reconstruct`` is possible, by simply calling the constructor
+with new operands. This feature is used extensively in algorithms.
+
+Extending UFL
+=============
+
+Adding new types to the UFL class hierarchy must be done with care. If
+you can get away with implementing a new operator as a combination of
+existing ones, that is the easiest route. The reason is that only some
+of the properties of an operator is represented by the ``Expr``
+subclass. Other properties are part of the various algorithms in
+UFL. One example is derivatives, which are defined in the differentiation
+algorithm, and how to render a type to the ``LaTeX`` or dot formats. These
+properties could be merged into the class hierarchy, but other properties
+like how to map a UFL type to some ``ffc`` or ``dolfin`` type
+cannot be part of UFL. So before adding a new class, consider that doing
+so may require changes in multiple algorithms and even other projects.
diff --git a/doc/sphinx/source/user/introduction.rst b/doc/sphinx/source/user/introduction.rst
new file mode 100644
index 0000000..27d01ec
--- /dev/null
+++ b/doc/sphinx/source/user/introduction.rst
@@ -0,0 +1,37 @@
+************
+Introduction
+************
+
+The Unified Form Language (UFL) is a domain specific language for
+defining discrete variational forms and functionals in a notation close
+to pen-and-paper formulation.
+
+UFL is part of the FEniCS Project, and is usually used in combination
+with other components from this project to compute solutions to partial
+differential equations. The form compiler FFC uses UFL as its
+end-user interface, producing implementations of the UFC interface as
+output. See DOLFIN for more details about using UFL in an integrated
+problem solving environment.
+
+This manual is intended for different audiences. If you are an end user
+and all you want to do is to solve your PDEs with the FEniCS framework,
+you should read :doc:`form_language`, and also :doc:`examples`. These two
+sections explain how to use all operators available in the language and
+present a number of examples to illustrate the use of the form language in applications.
+
+The remaining chapters contain more technical details intended for developers
+who need to understand what is happening behind the scenes and modify
+or extend UFL in the future.
+
+:doc:`internal_representation` describes the implementation of the language, in particular
+how expressions are represented internally by UFL. This can also be
+useful knowledge to understand error messages and debug errors in your
+form files.
+
+:doc:`algorithms` explains the many algorithms available to work with UFL expressions,
+mostly intended to aid developers of form compilers. The algorithms include
+helper functions for easy and efficient iteration over expression
+trees, formatting tools to present expressions as text or
+images of different kinds, utilities to analyse properties of expressions
+or checking their validity, automatic differentiation algorithms, as
+well as algorithms to work with the computational graphs of expressions.
diff --git a/doc/sphinx/source/user/user_manual.rst b/doc/sphinx/source/user/user_manual.rst
new file mode 100644
index 0000000..3cbcb1d
--- /dev/null
+++ b/doc/sphinx/source/user/user_manual.rst
@@ -0,0 +1,17 @@
+.. UFL user manual
+
+.. _ufl_user_manual:
+
+###############
+UFL user manual
+###############
+
+.. toctree::
+ :maxdepth: 1
+
+ introduction
+ form_language
+ examples
+ internal_representation
+ algorithms
+ command_line_utils
diff --git a/scripts/fixdates b/scripts/fixdates
deleted file mode 100755
index 80175f5..0000000
--- a/scripts/fixdates
+++ /dev/null
@@ -1,10 +0,0 @@
-#!/usr/bin/env python
-import sys, os, glob, re, commands
-
-r = re.compile(r"(20..-..-..)")
-for fn in glob.glob("*.py"):
- st, op = commands.getstatusoutput("bzr log -l1 --line " + fn)
- m = r.search(op)
- date, = m.groups()
-
- st, op = commands.getstatusoutput("sed -i 's/Last changed.*/Last changed: %s/' %s" % (date, fn))
diff --git a/scripts/form2ufl b/scripts/form2ufl
deleted file mode 100755
index 51e1966..0000000
--- a/scripts/form2ufl
+++ /dev/null
@@ -1,116 +0,0 @@
-#!/usr/bin/env python
-#
-# Simple conversion script from old FFC .form format to UFL format
-
-__authors__ = "Anders Logg"
-__date__ = "2008-08-01 -- 2009-03-15"
-
-import sys
-import re
-from os.path import exists
-
-def help():
- print("Simple conversion script from old FFC .form format to UFL format.")
- print("")
- print("Usage: form2ufl <filename>.form")
-
-def error(message=None):
- if not message is None:
- print(message)
- else:
- help()
- sys.exit(1)
-
-def split_at_closing_brace(s):
- num_left = num_right = 0
- for (i, c) in enumerate(s):
- if c == "(":
- num_left += 1
- elif c == ")":
- num_right += 1
- if num_left == num_right:
- return s[:i + 1], s[i + 1:]
- return s, ""
-
-def replace_transp(match):
- expr1, expr2 = split_at_closing_brace(match.groups()[0])
- return expr1 + ".T" + expr2
-
-def replace_mult(match):
- start, end = split_at_closing_brace(match.groups()[0])
- expr1 = start.split(",")[0][1:].strip()
- expr2 = start.split(",")[1][:-1].strip()
- if "+" in expr1:
- expr1 = "(%s)" % expr1
- if "+" in expr2:
- expr2 = "(%s)" % expr2
- return expr1 + "*" + expr2 + end
-
-def form2ufl(code):
-
- # List of simple replacements
- simple_replacements = ((r"\.form", ".ufl"),
- (r"\bdot\b", "inner"),
- (r"\bD\b", "Dx"),
- (r"\bmodulus\b", "abs"),
- (r'"interval"', "interval"),
- (r'"triangle"', "triangle"),
- (r'"tetrahedron"', "tetrahedron"),
- (r'MeshSize', "Constant"),
- (r'FacetNormal', "VectorConstant"),
- (r"Nedelec", "N1curl"),
- (r"VectorQuadratureElement\(", 'VectorElement("Quadrature", '),
- (r"QuadratureElement\(", 'FiniteElement("Quadrature", '))
-
- # List of complex replacements
- complex_replacements = ((r"\btransp(.*)", replace_transp),
- (r"\bmult(.*)", replace_mult))
-
- # Iterate over replacemens
- for (a, b) in simple_replacements + complex_replacements:
- code = re.sub(a, b, code)
-
- return code
-
-def main(args):
-
- # Check command-line arguments
- if not len(args) == 1:
- error()
-
- # Get prefix and filenames
- words = args[0].split(".form")
- if not (len(words) == 2 and words[1] == ""):
- error()
- prefix = words[0]
- form_filename = prefix + ".form"
- ufl_filename = prefix + ".ufl"
-
- # Check if outfile exists
- if exists(ufl_filename):
- error("File already exists: " + ufl_filename)
-
- print("Converting %s --> %s" % (form_filename, ufl_filename))
-
- # Read file
- try:
- file = open(form_filename, "r")
- code = file.read()
- file.close()
- except:
- error("Unable to read file: " + form_filename)
-
- # Convert to UFL
- code = form2ufl(code)
-
- # Write file
- try:
- file = open(ufl_filename, "w")
- file.write(code)
- file.close()
- except:
- error("Unable to write to file: " + ufl_filename)
-
-if __name__ == "__main__":
- main(sys.argv[1:])
- sys.exit(0)
diff --git a/scripts/makedoc b/scripts/makedoc
deleted file mode 100755
index ffdc1d5..0000000
--- a/scripts/makedoc
+++ /dev/null
@@ -1,56 +0,0 @@
-# Copyright (C) 2011 Marie E. Rognes
-#
-# This file is part of UFL.
-#
-# UFL is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Lesser General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# UFL is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with UFL. If not, see <http://www.gnu.org/licenses/>.
-#
-# First added: 2011-06-09
-# Last changed: 2011-06-27
-
-#
-# This is a utility script for generating .rst and .html
-# documentation for UFL.
-#
-# Run from the top level UFL directory:
-#
-# ./scripts/makedoc
-#
-
-echo ""
-echo "--- Generating UFL html documentation"
-echo ""
-
-SPHINX_DIR=./doc/sphinx
-SPHINX_SCRIPT_DIR=$SPHINX_DIR/scripts
-SPHINX_SOURCE_DIR=$SPHINX_DIR/source
-
-# Generate .rst files
-$SPHINX_SCRIPT_DIR/generate_modules.py ufl --dest-dir=$SPHINX_SOURCE_DIR --suffix=rst --force
-
-echo ""
-echo "--- reSTructured text files generated in doc/sphinx/source/"
-echo ""
-
-# Generate index (and add some labels)
-VERSION=`grep '__version__' ufl/__init__.py | cut -d'"' -f2`
-$SPHINX_SCRIPT_DIR/generate_index.py $SPHINX_SOURCE_DIR $VERSION
-
-# Run sphinx make html
-cd $SPHINX_DIR
-make clean
-make html
-
-echo ""
-echo "--- HTML files generated in $SPHINX_DIR/build/html"
-echo ""
diff --git a/scripts/ufl2py b/scripts/ufl2py
index 51f3893..7a222ab 100755
--- a/scripts/ufl2py
+++ b/scripts/ufl2py
@@ -33,6 +33,7 @@ if not args:
filenames = args
header = """#!/usr/bin/env python
+# -*- coding: utf-8 -*-
from ufl import *
set_level(DEBUG)
"""
@@ -54,4 +55,3 @@ for filename in filenames:
pyname = "%s.py" % basename
with file(pyname, "w") as f:
f.write(code)
-
diff --git a/setup.py b/setup.py
index 7c3400b..49e4ac2 100755
--- a/setup.py
+++ b/setup.py
@@ -1,10 +1,17 @@
#!/usr/bin/env python
+# -*- coding: utf-8 -*-
from __future__ import print_function
-from distutils.core import setup
+
+try:
+ from setuptools import setup
+except ImportError:
+ from distutils.core import setup
+
from os.path import join as pjoin, split as psplit
import re
import sys
import platform
+import codecs
if sys.version_info < (2, 7):
print("Python 2.7 or higher required, please upgrade.")
@@ -13,8 +20,7 @@ if sys.version_info < (2, 7):
scripts = [pjoin("scripts", "ufl-analyse"),
pjoin("scripts", "ufl-convert"),
pjoin("scripts", "ufl-version"),
- pjoin("scripts", "ufl2py"),
- pjoin("scripts", "form2ufl")]
+ pjoin("scripts", "ufl2py")]
if platform.system() == "Windows" or "bdist_wininst" in sys.argv:
# In the Windows command prompt we can't execute Python scripts
@@ -29,20 +35,27 @@ if platform.system() == "Windows" or "bdist_wininst" in sys.argv:
batch_files.append(batch_file)
scripts.extend(batch_files)
+# __init__.py has UTF-8 characters. Works in Python 2 and 3.
version = re.findall('__version__ = "(.*)"',
- open('ufl/__init__.py', 'r').read())[0]
+ codecs.open('ufl/__init__.py', 'r', encoding='utf-8').read())[0]
+
+url = "https://bitbucket.org/fenics-project/ufl/"
+tarball = None
+if not 'dev' in version:
+ tarball = url + "downloads/ufl-%s.tar.gz" % version
setup(name="UFL",
version = version,
description = "Unified Form Language",
- author = "Martin Sandve Alnes, Anders Logg",
- author_email = "fenics at fenicsproject.org",
- url = "http://www.fenicsproject.org",
+ author = "Martin Sandve Alnæs, Anders Logg",
+ author_email = "fenics-dev at googlegroups.com",
+ url = url,
+ download_url = tarball,
classifiers=[
'Development Status :: 5 - Production/Stable',
'Intended Audience :: Developers',
'Intended Audience :: Science/Research',
- 'Programming Language :: Python :: 2.5',
+ 'Programming Language :: Python :: 2.7',
'License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)',
'Topic :: Scientific/Engineering :: Mathematics',
'Topic :: Software Development :: Compilers',
@@ -59,9 +72,10 @@ setup(name="UFL",
"ufl.formatting",
],
package_dir = {"ufl": "ufl"},
+ install_requires = ["numpy", "six"],
data_files = [(pjoin("share", "man", "man1"),
[pjoin("doc", "man", "man1", "ufl-analyse.1.gz"),
pjoin("doc", "man", "man1", "ufl-convert.1.gz"),
pjoin("doc", "man", "man1", "ufl-version.1.gz"),
pjoin("doc", "man", "man1", "ufl2py.1.gz"),
- pjoin("doc", "man", "man1", "form2ufl.1.gz")])])
+ ])])
diff --git a/test/conftest.py b/test/conftest.py
index 64c64b8..2f45f33 100644
--- a/test/conftest.py
+++ b/test/conftest.py
@@ -1,3 +1,4 @@
+# -*- coding: utf-8 -*-
import pytest
@@ -33,11 +34,11 @@ class Tester:
def assertEqualTotalShape(self, value, expected):
self.assertEqual(value.ufl_shape, expected.ufl_shape)
- self.assertEqual(set(value.free_indices()), set(expected.free_indices()))
- self.assertEqual(value.index_dimensions(), expected.index_dimensions())
+ self.assertEqual(value.ufl_free_indices, expected.ufl_free_indices)
+ self.assertEqual(value.ufl_index_dimensions, expected.ufl_index_dimensions)
def assertSameIndices(self, expr, free_indices):
- self.assertEqual(expr.free_indices(), free_indices)
+ self.assertEqual(expr.ufl_free_indices, tuple(sorted(i.count() for i in free_indices)))
def assertEqualAfterPreprocessing(self, a, b):
a2 = compute_form_data(a*dx).preprocessed_form
diff --git a/test/mockobjects.py b/test/mockobjects.py
index 21858fb..2ed55ea 100644
--- a/test/mockobjects.py
+++ b/test/mockobjects.py
@@ -1,3 +1,4 @@
+# -*- coding: utf-8 -*-
from ufl import *
@@ -11,7 +12,7 @@ class MockMesh:
return self._ufl_id
def ufl_domain(self):
- return Domain(triangle, label="MockMesh_id_%d" % self.ufl_id(), data=self)
+ return Mesh(triangle, ufl_id=self.ufl_id(), cargo=self)
def ufl_measure(self, integral_type="dx", subdomain_id="everywhere", metadata=None, subdomain_data=None):
return Measure(integral_type, subdomain_id=subdomain_id, metadata=metadata, domain=self, subdomain_data=subdomain_data)
diff --git a/test/test_algorithms.py b/test/test_algorithms.py
index 80375c5..3790622 100755
--- a/test/test_algorithms.py
+++ b/test/test_algorithms.py
@@ -1,6 +1,7 @@
#!/usr/bin/env py.test
+# -*- coding: utf-8 -*-
-__authors__ = "Martin Sandve Alnes"
+__authors__ = "Martin Sandve Alnæs"
__date__ = "2008-03-12 -- 2009-01-28"
# Modified by Anders Logg, 2008
@@ -124,17 +125,17 @@ def test_adjoint():
assert u2.number() < v2.number()
a = u * v * dx
- a_arg_degrees = [arg.element().degree() for arg in extract_arguments(a)]
+ a_arg_degrees = [arg.ufl_element().degree() for arg in extract_arguments(a)]
assert a_arg_degrees == [2, 1]
b = adjoint(a)
- b_arg_degrees = [arg.element().degree() for arg in extract_arguments(b)]
+ b_arg_degrees = [arg.ufl_element().degree() for arg in extract_arguments(b)]
assert b_arg_degrees == [1, 2]
c = adjoint(a, (u2, v2))
- c_arg_degrees = [arg.element().degree() for arg in extract_arguments(c)]
+ c_arg_degrees = [arg.ufl_element().degree() for arg in extract_arguments(c)]
assert c_arg_degrees == [1, 2]
d = adjoint(b)
- d_arg_degrees = [arg.element().degree() for arg in extract_arguments(d)]
+ d_arg_degrees = [arg.ufl_element().degree() for arg in extract_arguments(d)]
assert d_arg_degrees == [2, 1]
diff --git a/test/test_analyse_demos.py b/test/test_analyse_demos.py
index eed8351..8873558 100755
--- a/test/test_analyse_demos.py
+++ b/test/test_analyse_demos.py
@@ -1,6 +1,7 @@
#!/usr/bin/env py.test
+# -*- coding: utf-8 -*-
-__authors__ = "Martin Sandve Alnes"
+__authors__ = "Martin Sandve Alnæs"
__date__ = "2008-09-28 -- 2008-09-28"
import os
@@ -73,4 +74,3 @@ def test_each_demo_with_validate_form():
except:
excepted = 1
assert excepted == 0, filename
-
diff --git a/test/test_apply_algebra_lowering.py b/test/test_apply_algebra_lowering.py
new file mode 100755
index 0000000..c86ed5e
--- /dev/null
+++ b/test/test_apply_algebra_lowering.py
@@ -0,0 +1,91 @@
+#!/usr/bin/env pytest
+# -*- coding: utf-8 -*-
+
+import pytest
+from ufl import *
+from ufl.compound_expressions import *
+from ufl.algorithms.renumbering import renumber_indices
+
+ at pytest.fixture
+def A0(request):
+ return Coefficient(FiniteElement("CG", interval, 1))
+
+ at pytest.fixture
+def A1(request):
+ return Coefficient(TensorElement("CG", interval, 1))
+
+ at pytest.fixture
+def A2(request):
+ return Coefficient(TensorElement("CG", triangle, 1))
+
+ at pytest.fixture
+def A3(request):
+ return Coefficient(TensorElement("CG", tetrahedron, 1))
+
+ at pytest.fixture
+def A21(request):
+ return Coefficient(TensorElement("CG", triangle, 1, shape=(2,1)))
+
+ at pytest.fixture
+def A31(request):
+ return Coefficient(TensorElement("CG", triangle, 1, shape=(3,1)))
+
+ at pytest.fixture
+def A32(request):
+ return Coefficient(TensorElement("CG", triangle, 1, shape=(3,2)))
+
+def test_determinant0(A0):
+ assert determinant_expr(A0) == A0
+
+def test_determinant1(A1):
+ assert determinant_expr(A1) == A1[0,0]
+
+def test_determinant2(A2):
+ assert determinant_expr(A2) == A2[0,0]*A2[1,1] - A2[0,1]*A2[1,0]
+
+def test_determinant3(A3):
+ assert determinant_expr(A3) == (A3[0,0]*(A3[1,1]*A3[2,2] - A3[1,2]*A3[2,1])
+ +A3[0,1]*(A3[1,2]*A3[2,0] - A3[1,0]*A3[2,2])
+ +A3[0,2]*(A3[1,0]*A3[2,1] - A3[1,1]*A3[2,0]))
+
+def test_pseudo_determinant21(A21):
+ i = Index()
+ assert renumber_indices(determinant_expr(A21)) == renumber_indices(sqrt(A21[i,0]*A21[i,0]))
+
+def test_pseudo_determinant31(A31):
+ i = Index()
+ assert renumber_indices(determinant_expr(A31)) == renumber_indices(sqrt((A31[i,0]*A31[i,0])))
+
+def test_pseudo_determinant32(A32):
+ i = Index()
+ c = cross_expr(A32[:,0], A32[:,1])
+ assert renumber_indices(determinant_expr(A32)) == renumber_indices(sqrt(c[i]*c[i]))
+
+
+def test_inverse0(A0):
+ expected = 1.0/A0 # stays scalar
+ assert inverse_expr(A0) == renumber_indices(expected)
+
+def test_inverse1(A1):
+ expected = as_tensor(((1.0/A1[0,0],),)) # reshaped into 1x1 tensor
+ assert inverse_expr(A1) == renumber_indices(expected)
+
+def xtest_inverse2(A2):
+ expected = todo
+ assert inverse_expr(A2) == renumber_indices(expected)
+
+def xtest_inverse3(A3):
+ expected = todo
+ assert inverse_expr(A3) == renumber_indices(expected)
+
+def xtest_pseudo_inverse21(A21):
+ expected = todo
+ assert renumber_indices(inverse_expr(A21)) == renumber_indices(expected)
+
+def xtest_pseudo_inverse31(A31):
+ expected = todo
+ assert renumber_indices(inverse_expr(A31)) == renumber_indices(expected)
+
+def xtest_pseudo_inverse32(A32):
+ expected = todo
+ assert renumber_indices(inverse_expr(A32)) == renumber_indices(expected)
diff --git a/test/test_apply_function_pullbacks.py b/test/test_apply_function_pullbacks.py
new file mode 100755
index 0000000..d00ed13
--- /dev/null
+++ b/test/test_apply_function_pullbacks.py
@@ -0,0 +1,366 @@
+#!/usr/bin/env py.test
+# -*- coding: utf-8 -*-
+
+from __future__ import print_function
+
+from pytest import raises
+from ufl import *
+from ufl.algorithms.apply_function_pullbacks import apply_function_pullbacks, apply_single_function_pullbacks
+from ufl.algorithms.renumbering import renumber_indices
+from ufl.classes import Jacobian, JacobianInverse, JacobianDeterminant, ReferenceValue, CellOrientation
+
+def check_single_function_pullback(g, mappings):
+ expected = mappings[g]
+ actual = apply_single_function_pullbacks(g)
+ rexp = renumber_indices(expected)
+ ract = renumber_indices(actual)
+ if not rexp == ract:
+ print()
+ print("In check_single_function_pullback:")
+ print("input:")
+ print(repr(g))
+ print("expected:")
+ print(str(rexp))
+ print("actual:")
+ print(str(ract))
+ print("signatures:")
+ print((expected**2*dx).signature())
+ print((actual**2*dx).signature())
+ print()
+ assert ract == rexp
+
+def test_apply_single_function_pullbacks_triangle3d():
+ triangle3d = Cell("triangle", geometric_dimension=3)
+ cell = triangle3d
+ domain = as_domain(cell)
+
+ U0 = FiniteElement("DG", cell, 0)
+ U = FiniteElement("CG", cell, 1)
+ V = VectorElement("CG", cell, 1)
+ Vd = FiniteElement("RT", cell, 1)
+ Vc = FiniteElement("N1curl", cell, 1)
+ T = TensorElement("CG", cell, 1)
+ S = TensorElement("CG", cell, 1, symmetry=True)
+
+ Um = U*U
+ Vm = U*V
+ Vdm = V*Vd
+ Vcm = Vd*Vc
+ Tm = Vc*T
+ Sm = T*S
+
+ Vd0 = Vd*U0 # case from failing ffc demo
+
+ W = S*T*Vc*Vd*V*U
+
+ u = Coefficient(U)
+ v = Coefficient(V)
+ vd = Coefficient(Vd)
+ vc = Coefficient(Vc)
+ t = Coefficient(T)
+ s = Coefficient(S)
+
+ um = Coefficient(Um)
+ vm = Coefficient(Vm)
+ vdm = Coefficient(Vdm)
+ vcm = Coefficient(Vcm)
+ tm = Coefficient(Tm)
+ sm = Coefficient(Sm)
+
+ vd0m = Coefficient(Vd0) # case from failing ffc demo
+
+ w = Coefficient(W)
+
+ ru = ReferenceValue(u)
+ rv = ReferenceValue(v)
+ rvd = ReferenceValue(vd)
+ rvc = ReferenceValue(vc)
+ rt = ReferenceValue(t)
+ rs = ReferenceValue(s)
+
+ rum = ReferenceValue(um)
+ rvm = ReferenceValue(vm)
+ rvdm = ReferenceValue(vdm)
+ rvcm = ReferenceValue(vcm)
+ rtm = ReferenceValue(tm)
+ rsm = ReferenceValue(sm)
+
+ rvd0m = ReferenceValue(vd0m)
+
+ rw = ReferenceValue(w)
+ assert len(w) == 9 + 9 + 3 + 3 + 3 + 1
+ assert len(rw) == 6 + 9 + 2 + 2 + 3 + 1
+ assert len(w) == 28
+ assert len(rw) == 23
+
+ assert len(vd0m) == 4
+ assert len(rvd0m) == 3
+
+ # Geometric quantities we need:
+ J = Jacobian(domain)
+ detJ = JacobianDeterminant(domain)
+ Jinv = JacobianInverse(domain)
+ #o = CellOrientation(domain)
+ i, j, k, l = indices(4)
+
+ # Contravariant H(div) Piola mapping:
+ M_hdiv = ((1.0/detJ) * J) # Not applying cell orientation here
+ # Covariant H(curl) Piola mapping: Jinv.T
+
+ mappings = {
+ # Simple elements should get a simple representation
+ u: ru,
+ v: rv,
+ vd: as_vector(M_hdiv[i,j]*rvd[j], i),
+ vc: as_vector(Jinv[j,i]*rvc[j], i),
+ t: rt,
+ s: as_tensor([[rs[0], rs[1], rs[2]],
+ [rs[1], rs[3], rs[4]],
+ [rs[2], rs[4], rs[5]]]),
+ # Mixed elements become a bit more complicated
+ um: rum,
+ vm: rvm,
+ vdm: as_vector([
+ # V
+ rvdm[0],
+ rvdm[1],
+ rvdm[2],
+ # Vd
+ M_hdiv[0,j]*as_vector([rvdm[3], rvdm[4]])[j],
+ M_hdiv[1,j]*as_vector([rvdm[3], rvdm[4]])[j],
+ M_hdiv[2,j]*as_vector([rvdm[3], rvdm[4]])[j],
+ ]),
+ vcm: as_vector([
+ # Vd
+ M_hdiv[0,j]*as_vector([rvcm[0], rvcm[1]])[j],
+ M_hdiv[1,j]*as_vector([rvcm[0], rvcm[1]])[j],
+ M_hdiv[2,j]*as_vector([rvcm[0], rvcm[1]])[j],
+ # Vc
+ Jinv[i,0]*as_vector([rvcm[2], rvcm[3]])[i],
+ Jinv[i,1]*as_vector([rvcm[2], rvcm[3]])[i],
+ Jinv[i,2]*as_vector([rvcm[2], rvcm[3]])[i],
+ ]),
+ tm: as_vector([
+ # Vc
+ Jinv[i,0]*as_vector([rtm[0], rtm[1]])[i],
+ Jinv[i,1]*as_vector([rtm[0], rtm[1]])[i],
+ Jinv[i,2]*as_vector([rtm[0], rtm[1]])[i],
+ # T
+ rtm[2], rtm[3], rtm[4],
+ rtm[5], rtm[6], rtm[7],
+ rtm[8], rtm[9], rtm[10],
+ ]),
+ sm: as_vector([
+ # T
+ rsm[0], rsm[1], rsm[2],
+ rsm[3], rsm[4], rsm[5],
+ rsm[6], rsm[7], rsm[8],
+ # S
+ rsm[ 9], rsm[10], rsm[11],
+ rsm[10], rsm[12], rsm[13],
+ rsm[11], rsm[13], rsm[14],
+ ]),
+ # Case from failing ffc demo:
+ vd0m: as_vector([
+ M_hdiv[0,j]*as_vector([rvd0m[0],rvd0m[1]])[j],
+ M_hdiv[1,j]*as_vector([rvd0m[0],rvd0m[1]])[j],
+ M_hdiv[2,j]*as_vector([rvd0m[0],rvd0m[1]])[j],
+ rvd0m[2]
+ ]),
+ # This combines it all:
+ w: as_vector([
+ # S
+ rw[0], rw[1], rw[2],
+ rw[1], rw[3], rw[4],
+ rw[2], rw[4], rw[5],
+ # T
+ rw[6], rw[7], rw[8],
+ rw[9], rw[10], rw[11],
+ rw[12], rw[13], rw[14],
+ # Vc
+ Jinv[i,0]*as_vector([rw[15], rw[16]])[i],
+ Jinv[i,1]*as_vector([rw[15], rw[16]])[i],
+ Jinv[i,2]*as_vector([rw[15], rw[16]])[i],
+ # Vd
+ M_hdiv[0,j]*as_vector([rw[17], rw[18]])[j],
+ M_hdiv[1,j]*as_vector([rw[17], rw[18]])[j],
+ M_hdiv[2,j]*as_vector([rw[17], rw[18]])[j],
+ # V
+ rw[19],
+ rw[20],
+ rw[21],
+ # U
+ rw[22],
+ ]),
+ }
+
+ # Check functions of various elements outside a mixed context
+ check_single_function_pullback(u, mappings)
+ check_single_function_pullback(v, mappings)
+ check_single_function_pullback(vd, mappings)
+ check_single_function_pullback(vc, mappings)
+ check_single_function_pullback(t, mappings)
+ check_single_function_pullback(s, mappings)
+
+ # Check functions of various elements inside a mixed context
+ check_single_function_pullback(um, mappings)
+ check_single_function_pullback(vm, mappings)
+ check_single_function_pullback(vdm, mappings)
+ check_single_function_pullback(vcm, mappings)
+ check_single_function_pullback(tm, mappings)
+ check_single_function_pullback(sm, mappings)
+
+ # Check the ridiculous mixed element W combining it all
+ check_single_function_pullback(w, mappings)
+
+
+def test_apply_single_function_pullbacks_triangle():
+ cell = triangle
+ domain = as_domain(cell)
+
+ U = FiniteElement("CG", cell, 1)
+ V = VectorElement("CG", cell, 1)
+ Vd = FiniteElement("RT", cell, 1)
+ Vc = FiniteElement("N1curl", cell, 1)
+ T = TensorElement("CG", cell, 1)
+ S = TensorElement("CG", cell, 1, symmetry=True)
+
+ Um = U*U
+ Vm = U*V
+ Vdm = V*Vd
+ Vcm = Vd*Vc
+ Tm = Vc*T
+ Sm = T*S
+
+ W = S*T*Vc*Vd*V*U
+
+ u = Coefficient(U)
+ v = Coefficient(V)
+ vd = Coefficient(Vd)
+ vc = Coefficient(Vc)
+ t = Coefficient(T)
+ s = Coefficient(S)
+
+ um = Coefficient(Um)
+ vm = Coefficient(Vm)
+ vdm = Coefficient(Vdm)
+ vcm = Coefficient(Vcm)
+ tm = Coefficient(Tm)
+ sm = Coefficient(Sm)
+
+ w = Coefficient(W)
+
+ ru = ReferenceValue(u)
+ rv = ReferenceValue(v)
+ rvd = ReferenceValue(vd)
+ rvc = ReferenceValue(vc)
+ rt = ReferenceValue(t)
+ rs = ReferenceValue(s)
+
+ rum = ReferenceValue(um)
+ rvm = ReferenceValue(vm)
+ rvdm = ReferenceValue(vdm)
+ rvcm = ReferenceValue(vcm)
+ rtm = ReferenceValue(tm)
+ rsm = ReferenceValue(sm)
+
+ rw = ReferenceValue(w)
+
+ assert len(w) == 4 + 4 + 2 + 2 + 2 + 1
+ assert len(rw) == 3 + 4 + 2 + 2 + 2 + 1
+ assert len(w) == 15
+ assert len(rw) == 14
+
+ # Geometric quantities we need:
+ J = Jacobian(domain)
+ detJ = JacobianDeterminant(domain)
+ Jinv = JacobianInverse(domain)
+ i, j, k, l = indices(4)
+
+ # Contravariant H(div) Piola mapping:
+ M_hdiv = (1.0/detJ) * J
+ # Covariant H(curl) Piola mapping: Jinv.T
+
+ mappings = {
+ # Simple elements should get a simple representation
+ u: ru,
+ v: rv,
+ vd: as_vector(M_hdiv[i,j]*rvd[j], i),
+ vc: as_vector(Jinv[j,i]*rvc[j], i),
+ t: rt,
+ s: as_tensor([[rs[0], rs[1]], [rs[1], rs[2]]]),
+ # Mixed elements become a bit more complicated
+ um: rum,
+ vm: rvm,
+ vdm: as_vector([
+ # V
+ rvdm[0],
+ rvdm[1],
+ # Vd
+ M_hdiv[0,j]*as_vector([rvdm[2], rvdm[3]])[j],
+ M_hdiv[1,j]*as_vector([rvdm[2], rvdm[3]])[j],
+ ]),
+ vcm: as_vector([
+ # Vd
+ M_hdiv[0,j]*as_vector([rvcm[0], rvcm[1]])[j],
+ M_hdiv[1,j]*as_vector([rvcm[0], rvcm[1]])[j],
+ # Vc
+ Jinv[i,0]*as_vector([rvcm[2], rvcm[3]])[i],
+ Jinv[i,1]*as_vector([rvcm[2], rvcm[3]])[i],
+ ]),
+ tm: as_vector([
+ # Vc
+ Jinv[i,0]*as_vector([rtm[0], rtm[1]])[i],
+ Jinv[i,1]*as_vector([rtm[0], rtm[1]])[i],
+ # T
+ rtm[2], rtm[3],
+ rtm[4], rtm[5],
+ ]),
+ sm: as_vector([
+ # T
+ rsm[0], rsm[1],
+ rsm[2], rsm[3],
+ # S
+ rsm[4], rsm[5],
+ rsm[5], rsm[6],
+ ]),
+ # This combines it all:
+ w: as_vector([
+ # S
+ rw[0], rw[1],
+ rw[1], rw[2],
+ # T
+ rw[3], rw[4],
+ rw[5], rw[6],
+ # Vc
+ Jinv[i,0]*as_vector([rw[7], rw[8]])[i],
+ Jinv[i,1]*as_vector([rw[7], rw[8]])[i],
+ # Vd
+ M_hdiv[0,j]*as_vector([rw[9], rw[10]])[j],
+ M_hdiv[1,j]*as_vector([rw[9], rw[10]])[j],
+ # V
+ rw[11],
+ rw[12],
+ # U
+ rw[13],
+ ]),
+ }
+
+ # Check functions of various elements outside a mixed context
+ check_single_function_pullback(u, mappings)
+ check_single_function_pullback(v, mappings)
+ check_single_function_pullback(vd, mappings)
+ check_single_function_pullback(vc, mappings)
+ check_single_function_pullback(t, mappings)
+ check_single_function_pullback(s, mappings)
+
+ # Check functions of various elements inside a mixed context
+ check_single_function_pullback(um, mappings)
+ check_single_function_pullback(vm, mappings)
+ check_single_function_pullback(vdm, mappings)
+ check_single_function_pullback(vcm, mappings)
+ check_single_function_pullback(tm, mappings)
+ check_single_function_pullback(sm, mappings)
+
+ # Check the ridiculous mixed element W combining it all
+ check_single_function_pullback(w, mappings)
diff --git a/test/test_apply_restrictions.py b/test/test_apply_restrictions.py
old mode 100644
new mode 100755
index 97204ce..aafa24c
--- a/test/test_apply_restrictions.py
+++ b/test/test_apply_restrictions.py
@@ -1,8 +1,10 @@
+#!/usr/bin/env py.test
+# -*- coding: utf-8 -*-
from pytest import raises
from ufl import *
from ufl.algorithms.apply_restrictions import apply_restrictions
-from ufl.algorithms import renumber_indices
+from ufl.algorithms.renumbering import renumber_indices
def test_apply_restrictions():
cell = triangle
diff --git a/test/test_arithmetic.py b/test/test_arithmetic.py
index baa433f..dea5aa9 100755
--- a/test/test_arithmetic.py
+++ b/test/test_arithmetic.py
@@ -1,4 +1,5 @@
#!/usr/bin/env py.test
+# -*- coding: utf-8 -*-
import pytest
diff --git a/test/test_automatic_differentiation.py b/test/test_automatic_differentiation.py
index d14fd38..2aba149 100755
--- a/test/test_automatic_differentiation.py
+++ b/test/test_automatic_differentiation.py
@@ -1,4 +1,5 @@
#!/usr/bin/env py.test
+# -*- coding: utf-8 -*-
"""
These tests should cover the behaviour of the automatic differentiation
@@ -17,7 +18,7 @@ import ufl
from ufl import *
import ufl.algorithms
-from ufl.common import unique_post_traversal
+from ufl.corealg.traversal import unique_post_traversal
from ufl.conditional import Conditional
from ufl.algorithms import expand_derivatives
@@ -324,7 +325,7 @@ def _test_zero_derivatives_of_noncompounds_produce_the_right_types_and_shapes(se
for var in (u, v, w):
if debug: print('\n', 'shapes: ', t.ufl_shape, var.ufl_shape, '\n')
if debug: print('\n', 't: ', str(t), '\n')
- if debug: print('\n', 't ind: ', str(t.free_indices()), '\n')
+ if debug: print('\n', 't ind: ', str(t.ufl_free_indices), '\n')
if debug: print('\n', 'var: ', str(var), '\n')
before = derivative(t, var)
if debug: print('\n', 'before: ', str(before), '\n')
@@ -495,7 +496,7 @@ def xtest_derivative_grad_coeff_with_variation_components(self, d_expr):
dw = collection.shared_objects.dw
for g, dg in ((v, dv), (w, dw)):
# Pick a single component
- ii = (0,)*(g.rank())
+ ii = (0,)*(len(g.ufl_shape))
f = g[ii]
df = dg[ii]
diff --git a/test/test_book_snippets.py b/test/test_book_snippets.py
index 902be3f..f5bb316 100755
--- a/test/test_book_snippets.py
+++ b/test/test_book_snippets.py
@@ -1,4 +1,5 @@
#!/usr/bin/env py.test
+# -*- coding: utf-8 -*-
"""
This file contains snippets from the FEniCS book,
and allows us to test that these can still run
@@ -159,7 +160,8 @@ def test_uflcode_824(self):
def test_uflcode_886(self):
cell = triangle
# ...
- g = sin(cell.x[0])
+ x = SpatialCoordinate(cell) # Original: x = cell.x
+ g = sin(x[0])
v = variable(g)
f = exp(v**2)
h = diff(f, v)
@@ -342,7 +344,7 @@ def test_python_1843(self):
if e._ufl_is_terminal_:
return e
ops = [apply_ad(o, ad_routine) for o in e.ufl_operands]
- e = e.reconstruct(*ops)
+ e = e._ufl_expr_reconstruct_(*ops)
if isinstance(e, Derivative):
e = ad_routine(e)
return e
@@ -417,7 +419,7 @@ def test_python_2024(self):
cell = triangle
element = FiniteElement("Lagrange", cell, 1)
- x = cell.x
+ x = SpatialCoordinate(cell) # Original: x = cell.x
if 0:
print((m(Argument(element, 2))))
print((m(x)))
@@ -436,7 +438,7 @@ def test_python_2087(self):
self.mapping = mapping
def operator(self, e, *ops):
- return e.reconstruct(*ops)
+ return e._ufl_expr_reconstruct_(*ops)
def terminal(self, e):
return self.mapping.get(e, e)
@@ -471,7 +473,7 @@ def test_python_2189(self):
def test_python_2328(self):
cell = triangle
- x = cell.x
+ x = SpatialCoordinate(cell) # Original: x = cell.x
e = x[0] + x[1]
#print e((0.5, 0.7)) # prints 1.2
# ...
@@ -479,7 +481,7 @@ def test_python_2328(self):
def test_python_2338(self):
cell = triangle
- x = cell.x
+ x = SpatialCoordinate(cell) # Original: x = cell.x
# ...
c = Constant(cell)
e = c*(x[0] + x[1])
diff --git a/test/test_change_to_local.py b/test/test_change_to_local.py
index a7f81fd..b123c05 100755
--- a/test/test_change_to_local.py
+++ b/test/test_change_to_local.py
@@ -1,4 +1,5 @@
#!/usr/bin/env py.test
+# -*- coding: utf-8 -*-
"""
Tests of the change to local representaiton algorithms.
"""
@@ -7,13 +8,15 @@ import pytest
from ufl import *
from ufl.classes import ReferenceGrad, JacobianInverse
-from ufl.algorithms import tree_format, change_to_reference_grad, renumber_indices
+from ufl.algorithms import tree_format, change_to_reference_grad
+from ufl.algorithms.renumbering import renumber_indices
def test_change_to_reference_grad():
- domain = Domain(triangle)
- U = FiniteElement("CG", domain, 1)
- V = VectorElement("CG", domain, 1)
+ cell = triangle
+ domain = Mesh(cell)
+ U = FunctionSpace(domain, FiniteElement("CG", cell, 1))
+ V = FunctionSpace(domain, VectorElement("CG", cell, 1))
u = Coefficient(U)
v = Coefficient(V)
Jinv = JacobianInverse(domain)
diff --git a/test/test_change_to_reference_frame.py b/test/test_change_to_reference_frame.py
new file mode 100755
index 0000000..df3b4cf
--- /dev/null
+++ b/test/test_change_to_reference_frame.py
@@ -0,0 +1,292 @@
+#!/usr/bin/env py.test
+# -*- coding: utf-8 -*-
+"""Tests of the change to reference frame algorithm."""
+
+import pytest
+
+from ufl import *
+
+from ufl.classes import Form, Integral, Expr, ReferenceGrad, ReferenceValue
+
+'''
+from ufl.classes import ReferenceGrad, JacobianInverse
+from ufl.algorithms import tree_format, change_to_reference_grad
+
+from six.moves import xrange as range
+
+from ufl.log import error, warning
+from ufl.assertions import ufl_assert
+
+from ufl.core.multiindex import Index, indices
+from ufl.corealg.multifunction import MultiFunction
+from ufl.corealg.map_dag import map_expr_dag
+
+from ufl.classes import (Expr, FormArgument, GeometricQuantity,
+ Terminal, ReferenceGrad, Grad, Restricted, ReferenceValue,
+ Jacobian, JacobianInverse, JacobianDeterminant,
+ FacetJacobian, FacetJacobianInverse, FacetJacobianDeterminant,
+ CellFacetJacobian,
+ CellEdgeVectors, FacetEdgeVectors,
+ FacetNormal, CellNormal, ReferenceNormal,
+ CellVolume, FacetArea,
+ CellOrientation, FacetOrientation, QuadratureWeight,
+ SpatialCoordinate, Indexed, MultiIndex, FixedIndex)
+
+from ufl.constantvalue import as_ufl, Identity
+from ufl.tensoralgebra import Transposed
+from ufl.tensors import as_tensor, as_vector, as_scalar, ComponentTensor
+from ufl.operators import sqrt, max_value, min_value, sign
+from ufl.permutation import compute_indices
+
+from ufl.algorithms.transformer import ReuseTransformer, apply_transformer
+from ufl.compound_expressions import determinant_expr, cross_expr, inverse_expr
+from ufl.finiteelement import FiniteElement, EnrichedElement, VectorElement, MixedElement, OuterProductElement, OuterProductVectorElement, TensorElement, FacetElement, InteriorElement, BrokenElement, TraceElement
+'''
+
+
+def change_integral_to_reference_frame(form, context):
+ if False: # TODO: integral.is_in_reference_frame():
+ # TODO: Assume reference frame integral is written purely in
+ # reference frame or tramsform integrand here as well?
+ return integrand
+ else:
+ # Change integrand expression to reference frame
+ integrand = change_to_reference_frame(integral.integrand())
+
+ # Compute and apply integration scaling factor
+ scale = compute_integrand_scaling_factor(integral.ufl_domain(),
+ integral.integral_type())
+
+ return integral.reconstruct(integrand * scale) # TODO: , reference=True)
+
+
+def change_expr_to_reference_frame(expr):
+ expr = ReferenceValue(expr)
+ return expr
+
+
+def change_to_reference_frame(expr):
+ if isinstance(expr, Form):
+ return change_form_to_reference_frame(expr)
+ elif isinstance(expr, Integral):
+ return change_integral_to_reference_frame(expr)
+ elif isinstance(expr, Expr):
+ return change_expr_to_reference_frame(expr)
+ else:
+ error("Invalid type.")
+
+
+def test_change_unmapped_form_arguments_to_reference_frame():
+ U = FiniteElement("CG", triangle, 1)
+ V = VectorElement("CG", triangle, 1)
+ T = TensorElement("CG", triangle, 1)
+
+ expr = Coefficient(U)
+ assert change_to_reference_frame(expr) == ReferenceValue(expr)
+ expr = Coefficient(V)
+ assert change_to_reference_frame(expr) == ReferenceValue(expr)
+ expr = Coefficient(T)
+ assert change_to_reference_frame(expr) == ReferenceValue(expr)
+
+
+def test_change_hdiv_form_arguments_to_reference_frame():
+ V = FiniteElement("RT", triangle, 1)
+
+ expr = Coefficient(V)
+ assert change_to_reference_frame(expr) == ReferenceValue(expr)
+
+
+def test_change_hcurl_form_arguments_to_reference_frame():
+ V = FiniteElement("RT", triangle, 1)
+
+ expr = Coefficient(V)
+ assert change_to_reference_frame(expr) == ReferenceValue(expr)
+
+ '''
+ # user input
+ grad(f + g)('+')
+ # change to reference frame
+ -> (K*rgrad(rv(M)*rv(f) + rv(M)*rv(g)))('+')
+ # apply derivatives
+ -> (K*(rv(M)*rgrad(rv(f)) + rv(M)*rgrad(rv(g))))('+')
+ # apply restrictions
+ -> K('+')*(rv(M('+'))*rgrad(rv(f('+'))) + rv(M('+'))*rgrad(rv(g('+'))))
+
+
+
+ # user input
+ grad(f + g)('+')
+
+ # some derivatives applied before processing
+ (grad(f) + grad(g))('+')
+
+ # ... replace to get fully defined form arguments here ...
+
+ # expand compounds
+ # * context options:
+ # - keep {types} without rewriting to lower level types
+ # - preserve div and curl if applied directly to terminal
+ # (ffc context may set this to off)
+ # * output invariants:
+ # - no compound operator types left in expression (simplified language)
+ # - div and curl rewritten in terms of grad (optionally unless applied directly to terminal)
+ -> (grad(f) + grad(g))('+')
+
+ # change to reference frame
+ # * context options:
+ # - keep {types} without rewriting to lower level types (e.g. JacobianInverse)
+ # (ffc context may initially add all code snippets expressions)
+ # - keep {types} in global frame (e.g. Coefficient)
+ # (ffc context may initially add Coefficient and Argument here to refrain from changing)
+ # - skip integral scaling
+ # (ffc context may turn skipping on to preserve current behaviour)
+ # * output invariants:
+ # - ReferenceValue bound directly to terminals where applicable
+ # - grad replaced by mapping expression of rgrad
+ # - div replaced by mapping expression of rdiv
+ # - curl replaced by mapping expression of rcurl
+ -> as_tensor(IndexSum(K[i,j]*rgrad(as_tensor(rv(M)[k,l]*rv(f)[l], (l,))
+ + as_tensor(rv(M)[r,s]*rv(g)[s], (s,)))[j],
+ j),
+ (i,))('+')
+
+ # apply derivatives
+ # * context options:
+ # - N/A?
+ # * output invariants:
+ # - grad,div,curl, bound directly to terminals
+ # - rgrad,rdiv,rcurl bound directly to referencevalue objects (rgrad(global_f) invalid)
+ -> (K*(rv(M)*rgrad(rv(f)) + rv(M)*rgrad(rv(g))))('+')
+
+ # apply restrictions
+ # * context options:
+ # - N/A?
+ # * output invariants:
+ # - *_restricted bound directly to terminals
+ # - all terminals that must be restricted to make sense are restricted
+ -> K('+')*(rv(M('+'))*rgrad(rv(f('+'))) + rv(M('+'))*rgrad(rv(g('+'))))
+
+ # final modified terminal structure:
+ t = terminal | restricted(terminal) # choice of terminal
+ r = rval(t) | rgrad(r) # in reference frame: value or n-gradient
+ g = t | grad(g) # in global frame: value or n-gradient
+ v = r | g # value in either frame
+ e = v | cell_avg(v) | facet_avg(v) | at_cell_midpoint(v) | at_facet_midpoint(v)
+ # evaluated at point or averaged over cell entity
+ m = e | indexed(e) # scalar component of
+ '''
+
+def new_analyse_modified_terminal(expr):
+ assert expr._ufl_is_terminal_ or expr._ufl_is_terminal_modifier_type_
+ m = expr
+
+ # The outermost expression may index to get a specific scalar value
+ if isinstance(m, Indexed):
+ unindexed, multi_index = m.ufl_operands
+ indices = tuple(int(i) for i in multi_index)
+ else:
+ unindexed = m
+ indices = ()
+
+ # The evaluation mode is one of current point,
+ # a cell entity midpoint, or averaging over a cell entity
+ if unindexed._ufl_is_evaluation_type_: # averages and point evaluations
+ v, = v.ufl_operand
+ evaluation = unindexed.ufl_handler_name
+ else:
+ v = unindexed
+ evaluation = "current_point"
+
+ # We're either in reference frame or global, checks below ensure we don't mix the two
+ frame = "reference" if v._ufl_is_reference_type_ else "global"
+
+ # Peel off derivatives (grad^n,div,curl,div(grad),grad(div) etc.)
+ t = v
+ derivatives = []
+ while t._ufl_is_derivative_type_:
+ # ensure frame consistency
+ assert t._ufl_is_reference_type_ == v._ufl_is_reference_type_
+ derivatives.append(t._ufl_class_)
+ t, = t.ufl_operands
+ core = t
+ derivatives = tuple(derivatives)
+
+ # This can be an intermediate step to use derivatives instead of ngrads:
+ num_derivatives = len(derivatives)
+
+ # If we had a reference type before unwrapping terminal,
+ # there should be a ReferenceValue inside all the derivatives
+ if v._ufl_is_reference_type_:
+ assert isinstance(t, ReferenceValue)
+ t, = t.ufl_operands
+
+ # At the core we may have a restriction
+ if t._ufl_is_restriction_type_:
+ restriction = t.side()
+ t, = t.ufl_operands
+ else:
+ restriction = ""
+
+ # And then finally the terminal
+ assert t._ufl_is_terminal_
+ terminal = t
+
+ # This will only be correct for derivatives = grad^n
+ gdim = terminal.ufl_domain().geometric_dimension()
+ derivatives_shape = (gdim,)*num_derivatives
+
+ # Get shapes
+ expr_shape = expr.ufl_shape
+ unindexed_shape = unindexed.ufl_shape
+ core_shape = core.ufl_shape
+
+ # Split indices
+ core_indices = indices[:len(core_shape)]
+ derivative_indices = indices[len(core_shape):]
+
+ # Apply paranoid dimension checking
+ assert len(indices) == len(unindexed_shape)
+ assert all(0 <= i for i in indices)
+ assert all(i < j for i,j in zip(indices, unindexed_shape))
+ assert len(core_indices) == len(core_shape)
+ assert all(0 <= i for i in core_indices)
+ assert all(i < j for i,j in zip(core_indices, core_shape))
+ assert len(derivative_indices) == len(derivatives_shape) # This will fail for e.g. div(grad(f))
+ assert all(0 <= i for i in derivative_indices)
+ assert all(i < j for i,j in zip(derivative_indices, derivatives_shape))
+
+ # Return values:
+ mt = ModifiedTerminal(
+ # TODO: Use keyword args
+ expr,
+ indices,
+ evaluation,
+ frame,
+ num_derivatives,
+ derivatives,
+ restriction,
+ terminal
+ )
+ return mt
+
+
+'''
+New form preprocessing pipeline:
+
+Preferably introduce these changes:
+1) Create new FormArgument Expression without element or domain
+2) Create new FormArgument Constant without domain
+3) Drop replace
+--> but just applying replace first is fine
+
+i) group and join integrals by (domain, type, subdomain_id),
+ii) process integrands:
+ a) apply_coefficient_completion # replace coefficients to ensure proper elements and domains
+ b) lower_compound_operators # expand_compounds
+ c) change_to_reference_frame # change f->rv(f), m->M*rv(m), grad(f)->K*rgrad(rv(f)), grad(grad(f))->K*rgrad(K*rgrad(rv(f))), grad(expr)->K*rgrad(expr)
+ # if grad(expr)->K*rgrad(expr) should be valid, then rgrad must be applicable to quite generic expressions
+ d) apply_derivatives # one possibility is to add an apply_mapped_derivatives AD algorithm which includes mappings
+ e) apply_geometry_lowering
+ f) apply_restrictions # requiring grad(f)('+') instead of grad(f('+')) would simplify a lot...
+iii) extract final metadata about elements and coefficient ordering
+'''
diff --git a/test/test_check_arities.py b/test/test_check_arities.py
old mode 100644
new mode 100755
index 0f1693c..e2bfb46
--- a/test/test_check_arities.py
+++ b/test/test_check_arities.py
@@ -1,12 +1,14 @@
-
+#!/usr/bin/env py.test
+# -*- coding: utf-8 -*-
import pytest
from ufl import *
from ufl.algorithms.compute_form_data import compute_form_data
def test_check_arities():
# Code from bitbucket issue #49
- D = Domain(tetrahedron)
- V = VectorElement("P", D, 2)
+ cell = tetrahedron
+ D = Mesh(cell)
+ V = FunctionSpace(D, VectorElement("P", cell, 2))
dv = TestFunction(V)
du = TrialFunction(V)
diff --git a/test/test_classcoverage.py b/test/test_classcoverage.py
index 8a80964..5115bc2 100755
--- a/test/test_classcoverage.py
+++ b/test/test_classcoverage.py
@@ -1,8 +1,9 @@
#!/usr/bin/env py.test
+# -*- coding: utf-8 -*-
from __future__ import print_function
-__authors__ = "Martin Sandve Alnes"
+__authors__ = "Martin Sandve Alnæs"
__date__ = "2008-09-06 -- 2009-02-10"
import pytest
@@ -31,12 +32,12 @@ def _test_object(a, shape, free_indices):
s = str(a)
# Check that some properties are at least available
- fi = a.free_indices()
+ fi = a.ufl_free_indices
sh = a.ufl_shape
- ce = a.cell()
# Compare with provided properties
if free_indices is not None:
+ free_indices = [i.count() for i in free_indices]
if len(set(fi) ^ set(free_indices)) != 0:
print(type(a))
print(a)
@@ -64,9 +65,6 @@ def _test_object2(a):
# Can't really test str more than that it exists
s = str(a)
- # Check that some properties are at least available
- ce = a.cell()
-
def _test_form(a):
# Test reproduction via repr string
r = repr(a)
diff --git a/test/test_conditionals.py b/test/test_conditionals.py
index dfe4bf1..4d5f4f0 100755
--- a/test/test_conditionals.py
+++ b/test/test_conditionals.py
@@ -1,6 +1,7 @@
#!/usr/bin/env py.test
+# -*- coding: utf-8 -*-
-__authors__ = "Martin Sandve Alnes"
+__authors__ = "Martin Sandve Alnæs"
__date__ = "2008-08-20 -- 2012-11-30"
import pytest
@@ -163,5 +164,3 @@ def test_ge_produces_ufl_expr(f, g):
# Protection from misuse in boolean python expression context:
with pytest.raises(UFLException):
bool(expr1)
-
-
diff --git a/test/test_degree_estimation.py b/test/test_degree_estimation.py
index 5651329..214e42b 100755
--- a/test/test_degree_estimation.py
+++ b/test/test_degree_estimation.py
@@ -1,6 +1,7 @@
#!/usr/bin/env py.test
+# -*- coding: utf-8 -*-
-__authors__ = "Martin Sandve Alnes"
+__authors__ = "Martin Sandve Alnæs"
__date__ = "2008-03-12 -- 2009-01-28"
import pytest
diff --git a/test/test_derivative.py b/test/test_derivative.py
index 2bd1d3c..dcdeff7 100755
--- a/test/test_derivative.py
+++ b/test/test_derivative.py
@@ -1,6 +1,7 @@
#!/usr/bin/env py.test
+# -*- coding: utf-8 -*-
-__authors__ = "Martin Sandve Alnes"
+__authors__ = "Martin Sandve Alnæs"
__date__ = "2009-02-17 -- 2009-02-17"
import pytest
@@ -39,8 +40,8 @@ def assertEqualBySampling(actual, expected):
amapping = dict((c, make_value(c)) for c in chain(ad.original_form.coefficients(), ad.original_form.arguments()))
bmapping = dict((c, make_value(c)) for c in chain(bd.original_form.coefficients(), bd.original_form.arguments()))
- acell = actual.cell()
- bcell = expected.cell()
+ acell = actual.ufl_domain().ufl_cell()
+ bcell = expected.ufl_domain().ufl_cell()
assert acell == bcell
if acell.geometric_dimension() == 1:
x = (0.3,)
@@ -390,11 +391,11 @@ def test_coefficient_derivatives(self):
dv = TestFunction(V)
- f = Coefficient(V).reconstruct(count=0)
- g = Coefficient(V).reconstruct(count=1)
- df = Coefficient(V).reconstruct(count=2)
- dg = Coefficient(V).reconstruct(count=3)
- u = Coefficient(V).reconstruct(count=4)
+ f = Coefficient(V, count=0)
+ g = Coefficient(V, count=1)
+ df = Coefficient(V, count=2)
+ dg = Coefficient(V, count=3)
+ u = Coefficient(V, count=4)
cd = { f: df, g: dg }
integrand = inner(f, g)
@@ -413,10 +414,10 @@ def test_vector_coefficient_derivatives(self):
dv = TestFunction(V)
- df = Coefficient(VV).reconstruct(count=0)
- g = Coefficient(V).reconstruct(count=1)
- f = Coefficient(V).reconstruct(count=2)
- u = Coefficient(V).reconstruct(count=3)
+ df = Coefficient(VV, count=0)
+ g = Coefficient(V, count=1)
+ f = Coefficient(V, count=2)
+ u = Coefficient(V, count=3)
cd = { f: df }
integrand = inner(f, g)
@@ -437,11 +438,11 @@ def test_vector_coefficient_derivatives_of_product(self):
dv = TestFunction(V)
- df = Coefficient(VV).reconstruct(count=0)
- g = Coefficient(V).reconstruct(count=1)
- dg = Coefficient(VV).reconstruct(count=2)
- f = Coefficient(V).reconstruct(count=3)
- u = Coefficient(V).reconstruct(count=4)
+ df = Coefficient(VV, count=0)
+ g = Coefficient(V, count=1)
+ dg = Coefficient(VV, count=2)
+ f = Coefficient(V, count=3)
+ u = Coefficient(V, count=4)
cd = { f: df, g: dg }
integrand = f[i]*g[i]
diff --git a/test/test_diff.py b/test/test_diff.py
index d6dbead..ec0b444 100755
--- a/test/test_diff.py
+++ b/test/test_diff.py
@@ -1,6 +1,7 @@
#!/usr/bin/env py.test
+# -*- coding: utf-8 -*-
-__authors__ = "Martin Sandve Alnes"
+__authors__ = "Martin Sandve Alnæs"
__date__ = "2009-02-17 -- 2014-10-14"
import pytest
@@ -193,5 +194,3 @@ def testDiffX():
assert round(df11 - 2 * 4 * 3, 7) == 0
# TODO: More tests involving wrapper types and indices
-
-
diff --git a/test/test_domains.py b/test/test_domains.py
index 9f22ca7..6e23708 100755
--- a/test/test_domains.py
+++ b/test/test_domains.py
@@ -1,4 +1,5 @@
#!/usr/bin/env py.test
+# -*- coding: utf-8 -*-
"""
Tests of domain language and attaching domains to forms.
"""
@@ -6,7 +7,7 @@ Tests of domain language and attaching domains to forms.
import pytest
from ufl import *
-from ufl.geometry import as_domain
+from ufl.domain import as_domain, default_domain
from ufl.algorithms import compute_form_data
all_cells = (interval, triangle, tetrahedron,
@@ -17,9 +18,12 @@ from mockobjects import MockMesh, MockMeshFunction
def test_construct_domains_from_cells():
for cell in all_cells:
- D1 = Domain(cell)
+ D0 = Mesh(cell)
+ D1 = default_domain(cell)
D2 = as_domain(cell)
- assert D1 is not D2
+ assert D0 is not D1
+ assert D0 is not D2
+ assert D1 is D2
if 0:
print()
for D in (D1, D2):
@@ -27,6 +31,8 @@ def test_construct_domains_from_cells():
print(('str', str(D)))
print(('repr', repr(D)))
print()
+ assert D0 != D1
+ assert D0 != D2
assert D1 == D2
@@ -39,9 +45,9 @@ def test_as_domain_from_cell_is_equal():
def test_construct_domains_with_names():
for cell in all_cells:
- D2 = Domain(cell, label="D2")
- D3 = Domain(cell, label="D3")
- D3b = Domain(cell, label="D3")
+ D2 = Mesh(cell, ufl_id=2)
+ D3 = Mesh(cell, ufl_id=3)
+ D3b = Mesh(cell, ufl_id=3)
assert D2 != D3
assert D3 == D3b
@@ -49,24 +55,24 @@ def test_construct_domains_with_names():
def test_domains_sort_by_name():
# This ordering is rather arbitrary, but at least this shows sorting is
# working
- domains1 = [Domain(cell, label="D%s" % cell.cellname())
+ domains1 = [Mesh(cell, ufl_id=hash(cell.cellname()))
for cell in all_cells]
- domains2 = [Domain(cell, label="D%s" % cell.cellname())
+ domains2 = [Mesh(cell, ufl_id=hash(cell.cellname()))
for cell in sorted(all_cells)]
sdomains = sorted(domains1, key=lambda D: (D.geometric_dimension(),
- D.topological_dimension(),
- D.cell(),
- D.label()))
+ D.topological_dimension(),
+ D.ufl_cell(),
+ D.ufl_id()))
assert sdomains != domains1
assert sdomains == domains2
def test_topdomain_creation():
- D = Domain(interval)
+ D = Mesh(interval)
assert D.geometric_dimension() == 1
- D = Domain(triangle)
+ D = Mesh(triangle)
assert D.geometric_dimension() == 2
- D = Domain(tetrahedron)
+ D = Mesh(tetrahedron)
assert D.geometric_dimension() == 3
@@ -76,108 +82,115 @@ def test_cell_legacy_case():
V = FiniteElement("CG", triangle, 1)
f = Coefficient(V)
- assert f.domains() == (D, )
+ assert f.ufl_domains() == (D, )
M = f * dx
- assert M.domains() == (D, )
+ assert M.ufl_domains() == (D, )
def test_simple_domain_case():
# Creating domain from just cell with label like new dolfin will do
- D = Domain(triangle, label="foo")
+ D = Mesh(triangle, ufl_id=3)
- V = FiniteElement("CG", D, 1)
+ V = FunctionSpace(D, FiniteElement("CG", D.ufl_cell(), 1))
f = Coefficient(V)
- assert f.domains() == (D, )
+ assert f.ufl_domains() == (D, )
M = f * dx
- assert M.domains() == (D, )
-
+ assert M.ufl_domains() == (D, )
-def test_creating_domains_with_coordinate_fields():
- # P2 field for coordinates
- D = Domain(triangle)
- P2 = VectorElement("CG", D, 2)
- x = Coefficient(P2)
- assert x.domains() == (D, )
+def test_creating_domains_with_coordinate_fields(): # FIXME: Rewrite for new approach
# Definition of higher order domain, element, coefficient, form
- E = Domain(x)
- V = FiniteElement("CG", E, 1)
+
+ # Mesh with P2 representation of coordinates
+ cell = triangle
+ P2 = VectorElement("CG", cell, 2)
+ domain = Mesh(P2)
+
+ # Piecewise linear function space over quadratic mesh
+ element = FiniteElement("CG", cell, 1)
+ V = FunctionSpace(domain, element)
+
f = Coefficient(V)
M = f * dx
- assert f.domains() == (E, )
- assert M.domains() == (E, )
+ assert f.ufl_domains() == (domain, )
+ assert M.ufl_domains() == (domain, )
# Test the gymnastics that dolfin will have to go through
- V2 = eval(V.reconstruction_signature())
- E2 = V2.domain().reconstruct(coordinates=x)
- V2 = V2.reconstruct(domain=E2)
- f2 = f.reconstruct(element=V2)
+ domain2 = Mesh(P2, ufl_id=domain.ufl_id())
+ V2 = FunctionSpace(domain2, eval(repr(V.ufl_element())))
+ f2 = Coefficient(V2, count=f.count())
assert f == f2
+ assert domain == domain2
assert V == V2
- assert E == E2
def test_join_domains():
- from ufl.geometry import join_domains
- mesh1 = MockMesh(11)
- mesh2 = MockMesh(13)
+ from ufl.domain import join_domains
+ mesh7 = MockMesh(7)
+ mesh8 = MockMesh(8)
triangle3 = Cell("triangle", geometric_dimension=3)
- xa = Coefficient(VectorElement("CG", Domain(triangle, label="A"), 1))
- xb = Coefficient(VectorElement("CG", Domain(triangle, label="B"), 1))
+ xa = VectorElement("CG", triangle, 1)
+ xb = VectorElement("CG", triangle, 1)
# Equal domains are joined
- assert 1 == len(join_domains([Domain(triangle), Domain(triangle)]))
- assert 1 == len(join_domains([Domain(triangle, label="A"),
- Domain(triangle, label="A")]))
- assert 1 == len(join_domains([Domain(triangle, label="A", data=mesh1),
- Domain(triangle, label="A", data=mesh1)]))
- assert 1 == len(join_domains([Domain(xa), Domain(xa)]))
+ assert 1 == len(join_domains([Mesh(triangle, ufl_id=7),
+ Mesh(triangle, ufl_id=7)]))
+ assert 1 == len(join_domains([Mesh(triangle, ufl_id=7, cargo=mesh7),
+ Mesh(triangle, ufl_id=7, cargo=mesh7)]))
+ assert 1 == len(join_domains([Mesh(xa, ufl_id=3), Mesh(xa, ufl_id=3)]))
# Different domains are not joined
- assert 2 == len(join_domains([Domain(triangle, label="A"),
- Domain(triangle, label="B")]))
- assert 2 == len(join_domains([Domain(triangle, label="A"),
- Domain(quadrilateral, label="B")]))
- assert 2 == len(join_domains([Domain(xa),
- Domain(xb)]))
+ assert 2 == len(join_domains([Mesh(triangle), Mesh(triangle)]))
+ assert 2 == len(join_domains([Mesh(triangle, ufl_id=7),
+ Mesh(triangle, ufl_id=8)]))
+ assert 2 == len(join_domains([Mesh(triangle, ufl_id=7),
+ Mesh(quadrilateral, ufl_id=8)]))
+ assert 2 == len(join_domains([Mesh(xa, ufl_id=7), Mesh(xa, ufl_id=8)]))
+ assert 2 == len(join_domains([Mesh(xa), Mesh(xb)]))
# Incompatible cells require labeling
- # self.assertRaises(UFLException, lambda: join_domains([Domain(triangle), Domain(triangle3)])) # FIXME: Figure out
- # self.assertRaises(UFLException, lambda: join_domains([Domain(triangle),
- # Domain(quadrilateral)])) # FIXME: Figure out
+ # self.assertRaises(UFLException, lambda: join_domains([Mesh(triangle), Mesh(triangle3)])) # FIXME: Figure out
+ # self.assertRaises(UFLException, lambda: join_domains([Mesh(triangle),
+ # Mesh(quadrilateral)])) # FIXME: Figure out
# Incompatible coordinates require labeling
- xc = Coefficient(VectorElement("CG", Domain(triangle), 1))
- xd = Coefficient(VectorElement("CG", Domain(triangle), 1))
+ xc = Coefficient(FunctionSpace(Mesh(triangle), VectorElement("CG", triangle, 1)))
+ xd = Coefficient(FunctionSpace(Mesh(triangle), VectorElement("CG", triangle, 1)))
with pytest.raises(UFLException):
- join_domains([Domain(xc), Domain(xd)])
+ join_domains([Mesh(xc), Mesh(xd)])
# Incompatible data is checked if and only if the domains are the same
- assert 2 == len(join_domains([Domain(triangle, label="A", data=mesh1),
- Domain(triangle, label="B", data=mesh2)]))
- assert 2 == len(join_domains([Domain(triangle, label="A", data=mesh1),
- Domain(triangle3, label="B", data=mesh2)]))
- assert 2 == len(join_domains([Domain(triangle, label="A", data=mesh1),
- Domain(quadrilateral, label="B", data=mesh2)]))
+ assert 2 == len(join_domains([Mesh(triangle, ufl_id=7, cargo=mesh7),
+ Mesh(triangle, ufl_id=8, cargo=mesh8)]))
+ assert 2 == len(join_domains([Mesh(triangle, ufl_id=7, cargo=mesh7),
+ Mesh(quadrilateral, ufl_id=8, cargo=mesh8)]))
+ # Geometric dimensions must match
with pytest.raises(UFLException):
- join_domains([Domain(triangle, label="A", data=mesh1),
- Domain(triangle, label="A", data=mesh2)])
+ join_domains([Mesh(triangle),
+ Mesh(triangle3)])
+ with pytest.raises(UFLException):
+ join_domains([Mesh(triangle, ufl_id=7, cargo=mesh7),
+ Mesh(triangle3, ufl_id=8, cargo=mesh8)])
+ # Cargo and mesh ids must match
+ with pytest.raises(UFLException):
+ Mesh(triangle, ufl_id=7, cargo=mesh8)
# Nones are removed
- assert 1 == len(
- join_domains([None, Domain(triangle), None, Domain(triangle), None]))
- assert 2 == len(join_domains([Domain(triangle, label="A"), None,
- Domain(quadrilateral, label="B")]))
- assert None not in join_domains([Domain(triangle, label="A"), None,
- Domain(tetrahedron, label="B")])
+ assert 2 == len(join_domains([None, Mesh(triangle, ufl_id=3),
+ None, Mesh(triangle, ufl_id=3),
+ None, Mesh(triangle, ufl_id=4)]))
+ assert 2 == len(join_domains([Mesh(triangle, ufl_id=7), None,
+ Mesh(quadrilateral, ufl_id=8)]))
+ assert None not in join_domains([Mesh(triangle3, ufl_id=7), None,
+ Mesh(tetrahedron, ufl_id=8)])
def test_everywhere_integrals_with_backwards_compatibility():
- D = Domain(triangle)
+ D = Mesh(triangle)
- V = FiniteElement("CG", D, 1)
+ V = FunctionSpace(D, FiniteElement("CG", triangle, 1))
f = Coefficient(V)
a = f * dx
@@ -192,12 +205,14 @@ def test_everywhere_integrals_with_backwards_compatibility():
itg1 = ida.integrals[0].integrand()
itg2 = a.integrals()[0].integrand()
assert type(itg1) == type(itg2)
- assert itg1.element() == itg2.element()
+ assert itg1.ufl_element() == itg2.ufl_element()
+
+def xtest_mixed_elements_on_overlapping_regions(): # Old sketch, not working
-def xtest_mixed_elements_on_overlapping_regions():
# Create domain and both disjoint and overlapping regions
- D = Domain(tetrahedron, label='D')
+ cell = tetrahedron
+ D = Mesh(cell, label='D')
DD = Region(D, (0, 4), "DD")
DL = Region(D, (1, 2), "DL")
DR = Region(D, (2, 3), "DR")
@@ -227,16 +242,16 @@ def xtest_mixed_elements_on_overlapping_regions():
# Check that we can get the domain for each value component of the mixed
# space
- assert M.domain(0) == D
- assert M.domain(1) == DD
- assert M.domain(2) == DD
+ assert M.ufl_domain(0) == D
+ assert M.ufl_domain(1) == DD
+ assert M.ufl_domain(2) == DD
- assert M.domain(3) == DL # Vector element
- assert M.domain(4) == DL
- assert M.domain(5) == DL
+ assert M.ufl_domain(3) == DL # Vector element
+ assert M.ufl_domain(4) == DL
+ assert M.ufl_domain(5) == DL
- assert M.domain(6) == DR
- # assert M.domain() == None # FIXME: What?
+ assert M.ufl_domain(6) == DR
+ # assert M.ufl_domain() == None # FIXME: What?
# Create a mixed function and fetch components with names for more
# readable test code below
@@ -274,11 +289,11 @@ def xtest_mixed_elements_on_overlapping_regions():
# fail
-def xtest_form_domain_model():
+def xtest_form_domain_model(): # Old sketch, not working
# Create domains with different celltypes
- # TODO: Figure out PyDOLFIN integration with Domain
- DA = Domain(tetrahedron, label='DA')
- DB = Domain(hexahedron, label='DB')
+ # TODO: Figure out PyDOLFIN integration with Mesh
+ DA = Mesh(tetrahedron, label='DA')
+ DB = Mesh(hexahedron, label='DB')
# Check python protocol behaviour
assert DA != DB
@@ -291,7 +306,7 @@ def xtest_form_domain_model():
assert DA.name() == 'DA'
assert DA.geometric_dimension() == 3
assert DA.topological_dimension() == 3
- assert DA.cell() == tetrahedron
+ assert DA.ufl_cell() == tetrahedron
# Check region/domain getters
assert DA.top_domain() == DA
@@ -326,9 +341,9 @@ def xtest_form_domain_model():
VBR = FiniteElement("CG", DBR, 1)
# Check that regions are available through elements
- assert VA.domain() == DA
- assert VAL.domain() == DAL
- assert VAR.domain() == DAR
+ assert VA.ufl_domain() == DA
+ assert VAL.ufl_domain() == DAL
+ assert VAR.ufl_domain() == DAR
# Create functions in each space on DA
fa = Coefficient(VA)
@@ -349,12 +364,12 @@ def xtest_form_domain_model():
# Create measure proxy objects from strings and ints, requiring
# domains and regions to be part of their integrands
- dxb = dx('DB') # Get Domain by name
+ dxb = dx('DB') # Get Mesh by name
dxbl = dx(Region(DB, (1, 4), 'DBL2'))
# Provide a region with different name but same subdomain ids as
# DBL
dxbr = dx((1, 4))
- # Assume unique Domain and provide subdomain ids explicitly
+ # Assume unique Mesh and provide subdomain ids explicitly
# Not checking measure objects in detail, as long as
# they carry information to construct integrals below
@@ -392,7 +407,7 @@ def xtest_form_domain_model():
def xtest_subdomain_stuff(): # Old sketch, not working
- D = Domain(triangle)
+ D = Mesh(triangle)
D1 = D[1]
D2 = D[2]
diff --git a/test/test_elements.py b/test/test_elements.py
index edfc87f..a2e66d0 100755
--- a/test/test_elements.py
+++ b/test/test_elements.py
@@ -1,4 +1,5 @@
#!/usr/bin/env py.test
+# -*- coding: utf-8 -*-
# Last changed: 2014-02-24
@@ -193,4 +194,3 @@ def test_radau():
element = FiniteElement("Radau", cell, degree)
assert element == eval(repr(element))
-
diff --git a/test/test_equals.py b/test/test_equals.py
index c79f315..abc9d0b 100755
--- a/test/test_equals.py
+++ b/test/test_equals.py
@@ -1,4 +1,5 @@
#!/usr/bin/env py.test
+# -*- coding: utf-8 -*-
"""
Test of expression comparison.
@@ -81,4 +82,3 @@ def test_comparison_of_deeply_nested_expression():
assert a == b
assert not a == c
assert not b == c
-
diff --git a/test/test_evaluate.py b/test/test_evaluate.py
index f3c3af4..30e3a34 100755
--- a/test/test_evaluate.py
+++ b/test/test_evaluate.py
@@ -1,6 +1,7 @@
#!/usr/bin/env py.test
+# -*- coding: utf-8 -*-
-__authors__ = "Martin Sandve Alnes"
+__authors__ = "Martin Sandve Alnæs"
__date__ = "2009-02-13 -- 2009-02-13"
import pytest
@@ -363,4 +364,3 @@ def test_cofac():
def test_inv():
pass # TODO
-
diff --git a/test/test_expand_indices.py b/test/test_expand_indices.py
index 2c946fc..bbc91f0 100755
--- a/test/test_expand_indices.py
+++ b/test/test_expand_indices.py
@@ -1,6 +1,7 @@
#!/usr/bin/env py.test
+# -*- coding: utf-8 -*-
-__authors__ = "Martin Sandve Alnes"
+__authors__ = "Martin Sandve Alnæs"
__date__ = "2009-03-19 -- 2012-03-20"
# Modified by Anders Logg, 2008
@@ -12,6 +13,7 @@ from pprint import *
from ufl import *
from ufl.algorithms import *
+from ufl.algorithms.renumbering import renumber_indices
from ufl.classes import Sum, Product
# TODO: Test expand_indices2 throuroughly for correctness, then efficiency:
diff --git a/test/test_ffcforms.py b/test/test_ffcforms.py
index c68ffc4..16fbcc5 100755
--- a/test/test_ffcforms.py
+++ b/test/test_ffcforms.py
@@ -1,4 +1,5 @@
#!/usr/bin/env py.test
+# -*- coding: utf-8 -*-
"""Unit tests including all demo forms from FFC 0.5.0. The forms are
modified (with comments) to work with the UFL notation which differs
from the FFC notation in some places."""
@@ -394,4 +395,3 @@ def testVectorLaplaceGradCurl():
VectorLagrange = VectorElement("Lagrange", shape, order + 1)
[a, L] = HodgeLaplaceGradCurl(GRAD * CURL, VectorLagrange)
-
diff --git a/test/test_form.py b/test/test_form.py
index fededdd..9815b7d 100755
--- a/test/test_form.py
+++ b/test/test_form.py
@@ -1,3 +1,5 @@
+#!/usr/bin/env py.test
+# -*- coding: utf-8 -*-
import pytest
from ufl import *
@@ -6,16 +8,14 @@ from ufl import *
@pytest.fixture
def element():
cell = triangle
- domain = Domain(cell)
- element = FiniteElement("Lagrange", domain, 1)
+ element = FiniteElement("Lagrange", cell, 1)
return element
@pytest.fixture
def mass():
cell = triangle
- domain = Domain(cell)
- element = FiniteElement("Lagrange", domain, 1)
+ element = FiniteElement("Lagrange", cell, 1)
v = TestFunction(element)
u = TrialFunction(element)
return u * v * dx
@@ -24,8 +24,7 @@ def mass():
@pytest.fixture
def stiffness():
cell = triangle
- domain = Domain(cell)
- element = FiniteElement("Lagrange", domain, 1)
+ element = FiniteElement("Lagrange", cell, 1)
v = TestFunction(element)
u = TrialFunction(element)
return inner(grad(u), grad(v)) * dx
@@ -34,8 +33,7 @@ def stiffness():
@pytest.fixture
def convection():
cell = triangle
- domain = Domain(cell)
- element = VectorElement("Lagrange", domain, 1)
+ element = VectorElement("Lagrange", cell, 1)
v = TestFunction(element)
u = TrialFunction(element)
w = Coefficient(element)
@@ -45,8 +43,7 @@ def convection():
@pytest.fixture
def load():
cell = triangle
- domain = Domain(cell)
- element = FiniteElement("Lagrange", domain, 1)
+ element = FiniteElement("Lagrange", cell, 1)
f = Coefficient(element)
v = TestFunction(element)
return f * v * dx
@@ -55,8 +52,7 @@ def load():
@pytest.fixture
def boundary_load():
cell = triangle
- domain = Domain(cell)
- element = FiniteElement("Lagrange", domain, 1)
+ element = FiniteElement("Lagrange", cell, 1)
f = Coefficient(element)
v = TestFunction(element)
return f * v * ds
@@ -92,18 +88,19 @@ def test_form_coefficients(element):
def test_form_domains():
cell = triangle
- domain = Domain(cell)
- element = FiniteElement("Lagrange", domain, 1)
+ domain = Mesh(cell)
+ element = FiniteElement("Lagrange", cell, 1)
+ V = FunctionSpace(domain, element)
- v = TestFunction(element)
- f = Coefficient(element)
+ v = TestFunction(V)
+ f = Coefficient(V)
x = SpatialCoordinate(domain)[0]
- assert (x * dx).domains() == (domain,)
- assert (v * dx).domains() == (domain,)
- assert (f * dx).domains() == (domain,)
- assert (x * v * f * dx).domains() == (domain,)
- assert (1 * dx(domain)).domains() == (domain,)
+ assert (x * dx).ufl_domains() == (domain,)
+ assert (v * dx).ufl_domains() == (domain,)
+ assert (f * dx).ufl_domains() == (domain,)
+ assert (x * v * f * dx).ufl_domains() == (domain,)
+ assert (1 * dx(domain)).ufl_domains() == (domain,)
def test_form_empty(mass):
@@ -120,4 +117,3 @@ def form_integrals(mass, boundary_load):
assert isinstance(boundary_load.integrals_by_type("cell"), tuple)
assert len(boundary_load.integrals_by_type("cell")) == 0
assert len(boundary_load.integrals_by_type("exterior_facet")) == 1
-
diff --git a/test/test_future_division.py b/test/test_future_division.py
index b8c77d5..2519da1 100755
--- a/test/test_future_division.py
+++ b/test/test_future_division.py
@@ -1,4 +1,5 @@
#!/usr/bin/env py.test
+# -*- coding: utf-8 -*-
# This file must be separate from the other arithmetic
# tests to test the effect of this future statment
diff --git a/test/test_grad.py b/test/test_grad.py
index 2c0b2ed..7080d67 100755
--- a/test/test_grad.py
+++ b/test/test_grad.py
@@ -1,4 +1,5 @@
#!/usr/bin/env py.test
+# -*- coding: utf-8 -*-
"""
Test use of grad in various situations.
@@ -94,18 +95,17 @@ def _test_grad_div_curl_properties(self, cell):
assert v.dx(i).ufl_shape == (d,)
assert t.dx(i).ufl_shape == (d, d)
- assert s.dx(i).free_indices() == (i,)
- assert v.dx(i).free_indices() == (i,)
- assert t.dx(i).free_indices() == (i,)
+ assert s.dx(i).ufl_free_indices == (i.count(),)
+ assert v.dx(i).ufl_free_indices == (i.count(),)
+ assert t.dx(i).ufl_free_indices == (i.count(),)
self.assertEqual(s.dx(i, j).ufl_shape, ())
self.assertEqual(v.dx(i, j).ufl_shape, (d,))
self.assertEqual(t.dx(i, j).ufl_shape, (d, d))
- # This comparison is unstable w.r.t. sorting of i,j
- self.assertTrue(s.dx(i, j).free_indices() in [(i, j), (j, i)])
- self.assertTrue(v.dx(i, j).free_indices() in [(i, j), (j, i)])
- self.assertTrue(t.dx(i, j).free_indices() in [(i, j), (j, i)])
+ self.assertTrue(s.dx(i, j).ufl_free_indices == tuple(sorted([i.count(), j.count()])))
+ self.assertTrue(v.dx(i, j).ufl_free_indices == tuple(sorted([i.count(), j.count()])))
+ self.assertTrue(t.dx(i, j).ufl_free_indices == tuple(sorted([i.count(), j.count()])))
a0 = s.dx(0)*dx
a1 = s.dx(0)**2*dx
diff --git a/test/test_illegal.py b/test/test_illegal.py
index 5ef3edc..cca7478 100755
--- a/test/test_illegal.py
+++ b/test/test_illegal.py
@@ -1,4 +1,5 @@
#!/usr/bin/env py.test
+# -*- coding: utf-8 -*-
import pytest
@@ -85,5 +86,3 @@ def test_add_vectorexpr_b(vg, v, u, vf, b):
tmp = vg + v + u + vf
with pytest.raises(UFLException):
tmp + b
-
-
diff --git a/test/test_indexing.py b/test/test_indexing.py
index f05a934..1faa019 100755
--- a/test/test_indexing.py
+++ b/test/test_indexing.py
@@ -1,4 +1,5 @@
-
+#!/usr/bin/env py.test
+# -*- coding: utf-8 -*-
import pytest
from ufl import *
from ufl.classes import *
@@ -27,23 +28,23 @@ def test_annotated_literals():
assert z.ufl_shape == ()
assert z.ufl_free_indices == ()
assert z.ufl_index_dimensions == ()
- assert z.free_indices() == ()
- assert z.index_dimensions() == {}
+ assert z.free_indices() == () # Deprecated interface
+ assert z.index_dimensions() == {} # Deprecated interface
z = Zero((3,))
assert z.ufl_shape == (3,)
assert z.ufl_free_indices == ()
assert z.ufl_index_dimensions == ()
- assert z.free_indices() == ()
- assert z.index_dimensions() == {}
+ assert z.free_indices() == () # Deprecated interface
+ assert z.index_dimensions() == {} # Deprecated interface
i = Index(count=2)
j = Index(count=4)
# z = Zero((), (2, 4), (3, 5))
z = Zero((), (j, i), {i: 3, j: 5})
assert z.ufl_shape == ()
- assert z.free_indices() == (i, j)
- assert z.index_dimensions() == {i: 3, j: 5}
+ assert z.free_indices() == (i, j) # Deprecated interface
+ assert z.index_dimensions() == {i: 3, j: 5} # Deprecated interface
assert z.ufl_free_indices == (2, 4)
assert z.ufl_index_dimensions == (3, 5)
diff --git a/test/test_indices.py b/test/test_indices.py
index effd94b..6ffba1d 100755
--- a/test/test_indices.py
+++ b/test/test_indices.py
@@ -1,4 +1,5 @@
#!/usr/bin/env py.test
+# -*- coding: utf-8 -*-
import pytest
@@ -82,10 +83,10 @@ def test_indexed_function2(self):
bfun = cos(f[0])
left = u[i] + f[i]
right = v[i] * bfun
- assert len(left.free_indices()) == 1
- assert left.free_indices()[0] == i
- assert len(right.free_indices()) == 1
- assert right.free_indices()[0] == i
+ assert len(left.ufl_free_indices) == 1
+ assert left.ufl_free_indices[0] == i.count()
+ assert len(right.ufl_free_indices) == 1
+ assert right.ufl_free_indices[0] == i.count()
b = left * right * dx
def test_indexed_function3(self):
@@ -106,10 +107,10 @@ def test_vector_from_indices(self):
uu = as_vector(v[j], j)
w = v + u
ww = vv + uu
- assert vv.rank() == 1
- assert uu.rank() == 1
- assert w.rank() == 1
- assert ww.rank() == 1
+ assert len(vv.ufl_shape) == 1
+ assert len(uu.ufl_shape) == 1
+ assert len(w.ufl_shape) == 1
+ assert len(ww.ufl_shape) == 1
def test_matrix_from_indices(self):
element = VectorElement("CG", "triangle", 1)
@@ -121,10 +122,10 @@ def test_matrix_from_indices(self):
C = A + A
C = B + B
D = A + B
- assert A.rank() == 2
- assert B.rank() == 2
- assert C.rank() == 2
- assert D.rank() == 2
+ assert len(A.ufl_shape) == 2
+ assert len(B.ufl_shape) == 2
+ assert len(C.ufl_shape) == 2
+ assert len(D.ufl_shape) == 2
def test_vector_from_list(self):
element = VectorElement("CG", "triangle", 1)
@@ -134,8 +135,8 @@ def test_vector_from_list(self):
# create vector from list
vv = as_vector([u[0], v[0]])
ww = vv + vv
- assert vv.rank() == 1
- assert ww.rank() == 1
+ assert len(vv.ufl_shape) == 1
+ assert len(ww.ufl_shape) == 1
def test_matrix_from_list(self):
element = VectorElement("CG", "triangle", 1)
@@ -150,10 +151,10 @@ def test_matrix_from_list(self):
C = A + A
C = B + B
D = A + B
- assert A.rank() == 2
- assert B.rank() == 2
- assert C.rank() == 2
- assert D.rank() == 2
+ assert len(A.ufl_shape) == 2
+ assert len(B.ufl_shape) == 2
+ assert len(C.ufl_shape) == 2
+ assert len(D.ufl_shape) == 2
def test_tensor(self):
element = VectorElement("CG", "triangle", 1)
@@ -164,21 +165,21 @@ def test_tensor(self):
# define the components of a fourth order tensor
Cijkl = u[i]*v[j]*f[k]*g[l]
- assert Cijkl.rank() == 0
- assert set(Cijkl.free_indices()) == {i, j, k, l}
+ assert len(Cijkl.ufl_shape) == 0
+ assert set(Cijkl.ufl_free_indices) == {i.count(), j.count(), k.count(), l.count()}
# make it a tensor
C = as_tensor(Cijkl, (i, j, k, l))
- assert C.rank() == 4
+ assert len(C.ufl_shape) == 4
self.assertSameIndices(C, ())
# get sub-matrix
A = C[:,:, 0, 0]
- assert A.rank() == 2
+ assert len(A.ufl_shape) == 2
self.assertSameIndices(A, ())
A = C[:,:, i, j]
- assert A.rank() == 2
- assert set(A.free_indices()) == {i, j}
+ assert len(A.ufl_shape) == 2
+ assert set(A.ufl_free_indices) == {i.count(), j.count()}
# legal?
vv = as_vector([u[i], v[i]])
@@ -236,7 +237,7 @@ def test_spatial_derivative(self):
a = v.dx(i, j)
#self.assertSameIndices(a, (i,j))
- assert set(a.free_indices()) == {j, i}
+ assert set(a.ufl_free_indices) == {j.count(), i.count()}
self.assertNotIsInstance(a, IndexSum)
assert a.ufl_shape == (d,)
@@ -246,8 +247,7 @@ def test_spatial_derivative(self):
assert a.ufl_shape == ()
a = (v[i]*u[j]).dx(0, 1)
- # indices change place because of sorting, I guess this may be ok
- assert set(a.free_indices()) == {i, j}
+ assert set(a.ufl_free_indices) == {i.count(), j.count()}
self.assertNotIsInstance(a, IndexSum)
assert a.ufl_shape == ()
diff --git a/test/test_lhs_rhs.py b/test/test_lhs_rhs.py
index c88ecc1..d73f956 100755
--- a/test/test_lhs_rhs.py
+++ b/test/test_lhs_rhs.py
@@ -1,4 +1,5 @@
#!/usr/bin/env py.test
+# -*- coding: utf-8 -*-
__authors__ = "Marie E. Rognes"
@@ -70,4 +71,3 @@ def test_lhs_rhs_slightly_obscure():
F = f * w * dx
a, L = system(F)
assert(len(L.integrals()) == 1)
-
diff --git a/test/test_literals.py b/test/test_literals.py
index 2fd6ea7..10a9e80 100755
--- a/test/test_literals.py
+++ b/test/test_literals.py
@@ -1,6 +1,7 @@
#!/usr/bin/env py.test
+# -*- coding: utf-8 -*-
-__authors__ = "Martin Sandve Alnes"
+__authors__ = "Martin Sandve Alnæs"
__date__ = "2011-04-14 -- 2011-04-14"
import pytest
diff --git a/test/test_measures.py b/test/test_measures.py
index 637b3ff..a4c02c1 100755
--- a/test/test_measures.py
+++ b/test/test_measures.py
@@ -1,4 +1,5 @@
-#!/use/bin/env py.test
+#!/usr/bin/env py.test
+# -*- coding: utf-8 -*-
"""
Tests of the various ways Measure objects can be created and used.
@@ -63,12 +64,12 @@ def test_construct_forms_from_default_measures():
assert dS_v.integral_type() == "interior_facet_vert"
# Check that defaults are set properly
- assert dx.domain() == None
+ assert dx.ufl_domain() == None
assert dx.metadata() == {}
# Check that we can create a basic form with default measure
one = as_ufl(1)
- a = one * dx(Domain(triangle))
+ a = one * dx(Mesh(triangle))
def test_foo():
@@ -78,19 +79,19 @@ def test_foo():
tdim = 2
cell = Cell("triangle", gdim)
mymesh = MockMesh(9)
- mydomain = Domain(cell, label="Omega", data=mymesh)
+ mydomain = Mesh(cell, ufl_id=9, cargo=mymesh)
assert cell.topological_dimension() == tdim
assert cell.geometric_dimension() == gdim
assert cell.cellname() == "triangle"
assert mydomain.topological_dimension() == tdim
assert mydomain.geometric_dimension() == gdim
- assert mydomain.cell() == cell
- assert mydomain.label() == "Omega"
- assert mydomain.data() == mymesh
+ assert mydomain.ufl_cell() == cell
+ assert mydomain.ufl_id() == 9
+ assert mydomain.ufl_cargo() == mymesh
# Define a coefficient for use in tests below
- V = FiniteElement("CG", mydomain, 1)
+ V = FunctionSpace(mydomain, FiniteElement("CG", cell, 1))
f = Coefficient(V)
# Test definition of a custom measure with explicit parameters
@@ -99,7 +100,7 @@ def test_foo():
domain=mydomain,
subdomain_id=3,
metadata=metadata)
- assert mydx.domain().label() == mydomain.label()
+ assert mydx.ufl_domain().ufl_id() == mydomain.ufl_id()
assert mydx.metadata() == metadata
M = f * mydx
@@ -108,26 +109,26 @@ def test_foo():
# domain=None,
# subdomain_id="everywhere",
# metadata=None)
- assert dx.domain() == None
+ assert dx.ufl_domain() == None
assert dx.subdomain_id() == "everywhere"
# Set subdomain_id to "everywhere", still no domain set
dxe = dx()
- assert dxe.domain() == None
+ assert dxe.ufl_domain() == None
assert dxe.subdomain_id() == "everywhere"
# Set subdomain_id to 5, still no domain set
dx5 = dx(5)
- assert dx5.domain() == None
+ assert dx5.ufl_domain() == None
assert dx5.subdomain_id() == 5
# Check that original dx is untouched
- assert dx.domain() == None
+ assert dx.ufl_domain() == None
assert dx.subdomain_id() == "everywhere"
# Set subdomain_id to (2,3), still no domain set
dx23 = dx((2, 3))
- assert dx23.domain() == None
+ assert dx23.ufl_domain() == None
assert dx23.subdomain_id(), (2 == 3)
# Map metadata to metadata, ffc interprets as before
@@ -135,7 +136,7 @@ def test_foo():
# assert dxm.metadata() == {"dummy":123}
assert dxm.metadata() == {"dummy": 123} # Deprecated, TODO: Remove
- assert dxm.domain() == None
+ assert dxm.ufl_domain() == None
assert dxm.subdomain_id() == "everywhere"
# dxm = dx(metadata={"dummy":123})
@@ -143,7 +144,7 @@ def test_foo():
dxm = dx(metadata={"dummy": 123})
assert dxm.metadata() == {"dummy": 123}
- assert dxm.domain() == None
+ assert dxm.ufl_domain() == None
assert dxm.subdomain_id() == "everywhere"
dxi = dx(metadata={"quadrature_degree": 3})
@@ -167,20 +168,12 @@ def test_foo():
dSd = dS[interior_facet_domains]
# Current behaviour: no domain created, measure domain data is a single
# object not a full dict
- assert dxd.domain() == None
- assert dsd.domain() == None
- assert dSd.domain() == None
+ assert dxd.ufl_domain() == None
+ assert dsd.ufl_domain() == None
+ assert dSd.ufl_domain() == None
assert dxd.subdomain_data() is cell_domains
assert dsd.subdomain_data() is exterior_facet_domains
assert dSd.subdomain_data() is interior_facet_domains
- # Considered behaviour at one point:
- # assert dxd.domain().label() == "MockMesh"
- # assert dsd.domain().label() == "MockMesh"
- # assert dSd.domain().label() == "MockMesh"
- # assert dxd.domain().data() == { "mesh": mesh, "cell": cell_domains }
- # assert dsd.domain().data() == { "mesh": mesh, "exterior_facet": exterior_facet_domains }
- # assert dSd.domain().data() == { "mesh": mesh, "interior_facet":
- # interior_facet_domains }
# Create some forms with these measures (used in checks below):
Mx = f * dxd
@@ -189,24 +182,24 @@ def test_foo():
M = f * dxd + f ** 2 * dsd + f('+') * dSd
# Test extracting domain data from a form for each measure:
- domain, = Mx.domains()
- assert domain.label() == mydomain.label()
- assert domain.data() == mymesh
+ domain, = Mx.ufl_domains()
+ assert domain.ufl_id() == mydomain.ufl_id()
+ assert domain.ufl_cargo() == mymesh
assert Mx.subdomain_data()[mydomain]["cell"] == cell_domains
- domain, = Ms.domains()
- assert domain.data() == mymesh
+ domain, = Ms.ufl_domains()
+ assert domain.ufl_cargo() == mymesh
assert Ms.subdomain_data()[mydomain][
"exterior_facet"] == exterior_facet_domains
- domain, = MS.domains()
- assert domain.data() == mymesh
+ domain, = MS.ufl_domains()
+ assert domain.ufl_cargo() == mymesh
assert MS.subdomain_data()[mydomain][
"interior_facet"] == interior_facet_domains
# Test joining of these domains in a single form
- domain, = M.domains()
- assert domain.data() == mymesh
+ domain, = M.ufl_domains()
+ assert domain.ufl_cargo() == mymesh
assert M.subdomain_data()[mydomain]["cell"] == cell_domains
assert M.subdomain_data()[mydomain][
"exterior_facet"] == exterior_facet_domains
diff --git a/test/test_mock_expr.py b/test/test_mock_expr.py
index 004e2c4..41c6d1a 100755
--- a/test/test_mock_expr.py
+++ b/test/test_mock_expr.py
@@ -1,4 +1,5 @@
#!/usr/bin/env py.test
+# -*- coding: utf-8 -*-
import pytest
@@ -10,39 +11,39 @@ class MockExpr(Expr):
def __init__(self, shape=None, free_indices=None, index_dimensions=None, cell=None):
Expr.__init__(self)
self.fields = []
-
+
if not shape is None:
self._shape = shape
self.fields.append("shape")
-
+
if not free_indices is None:
self._free_indices = free_indices
self.fields.append("free_indices")
-
+
if not index_dimensions is None:
self._index_dimensions = index_dimensions
self.fields.append("index_dimensions")
-
+
if not cell is None:
self._cell = cell
self.fields.append("cell")
-
+
def shape(self):
assert hasattr(self, "_shape")
return self._shape
-
+
def free_indices(self):
assert hasattr(self, "_free_indices")
return self._free_indices
-
+
def index_dimensions(self):
assert hasattr(self, "_index_dimensions")
return self._index_dimensions
-
+
def cell(self):
assert hasattr(self, "_cell")
return self._cell
-
+
def matches(self, other):
for field in self.fields:
a = getattr(self, field)()
@@ -50,10 +51,10 @@ class MockExpr(Expr):
if not a == b:
return False
return True
-
+
def __repr__(self):
return "MockExpr(%s)" % ", ".join("%s=%s" % (k, repr(getattr(self, "_%s" % k))) for k in self.fields)
-
+
def __iter__(self):
raise NotImplementedError
@@ -61,16 +62,15 @@ def test_mock_expr():
a = MockExpr(shape=(1,))
b = MockExpr(shape=(2,))
assert not a.matches(b)
-
+
i, j = indices(2)
c = MockExpr(shape=(1, 2), free_indices=(i, j), index_dimensions={i:2, j:3}, cell=triangle)
d = MockExpr(shape=(1, 2), free_indices=(i, j), index_dimensions={i:2, j:3}, cell=triangle)
assert c.matches(d)
-
+
e = FiniteElement("CG", triangle, 1)
f = Coefficient(e)
g = MockExpr(shape=(), free_indices=(), index_dimensions={}, cell=triangle)
assert g.matches(f)
h = MockExpr(shape=(1,), free_indices=(), index_dimensions={}, cell=triangle)
assert not h.matches(f)
-
diff --git a/test/test_new_ad.py b/test/test_new_ad.py
index fd7ef26..4862f19 100755
--- a/test/test_new_ad.py
+++ b/test/test_new_ad.py
@@ -1,4 +1,5 @@
-
+#!/usr/bin/env py.test
+# -*- coding: utf-8 -*-
import pytest
from ufl import *
@@ -6,7 +7,7 @@ from ufl import *
from ufl.tensors import as_tensor
from ufl.classes import Grad
from ufl.algorithms import tree_format
-from ufl.algorithms import renumber_indices
+from ufl.algorithms.renumbering import renumber_indices
from ufl.algorithms.apply_derivatives import apply_derivatives, GenericDerivativeRuleset, \
GradRuleset, VariableRuleset, GateauxDerivativeRuleset
diff --git a/test/test_pickle.py b/test/test_pickle.py
index f4b982a..145df65 100755
--- a/test/test_pickle.py
+++ b/test/test_pickle.py
@@ -1,4 +1,5 @@
#!/usr/bin/env py.test
+# -*- coding: utf-8 -*-
"""Pickle all the unit test forms from FFC 0.5.0"""
__author__ = "Anders Logg (logg at simula.no) et al."
@@ -550,4 +551,3 @@ def testFormData():
form_data_restore = pickle.loads(form_data_pickle)
assert(str(form_data) == str(form_data_restore))
-
diff --git a/test/test_piecewise_checks.py b/test/test_piecewise_checks.py
index e3ee4d6..892b633 100755
--- a/test/test_piecewise_checks.py
+++ b/test/test_piecewise_checks.py
@@ -1,4 +1,5 @@
#!/usr/bin/env py.test
+# -*- coding: utf-8 -*-
"""
Test the is_cellwise_constant function on all relevant terminal types.
@@ -7,6 +8,7 @@ Test the is_cellwise_constant function on all relevant terminal types.
import pytest
from ufl import *
from ufl.classes import *
+from ufl.checks import is_cellwise_constant
def get_domains():
@@ -18,15 +20,14 @@ def get_domains():
tetrahedron,
hexahedron,
]
- return [Domain(cell) for cell in all_cells]
+ return [Mesh(cell) for cell in all_cells]
def get_nonlinear():
domains_with_quadratic_coordinates = []
for D in get_domains():
- V = VectorElement("CG", D, 2)
- x = Coefficient(V)
- E = Domain(x)
+ V = VectorElement("CG", D.ufl_cell(), 2)
+ E = Mesh(V)
domains_with_quadratic_coordinates.append(E)
return domains_with_quadratic_coordinates
@@ -48,9 +49,8 @@ def domains(request):
domains = get_domains()
domains_with_linear_coordinates = []
for D in domains:
- V = VectorElement("CG", D, 1)
- x = Coefficient(V)
- E = Domain(x)
+ V = VectorElement("CG", D.ufl_cell(), 1)
+ E = Mesh(V)
domains_with_linear_coordinates.append(E)
all_domains = domains + domains_with_linear_coordinates + get_nonlinear()
@@ -64,13 +64,12 @@ def affine_domains(request):
triangle,
tetrahedron,
]
- affine_domains = [Domain(cell) for cell in affine_cells]
+ affine_domains = [Mesh(cell) for cell in affine_cells]
affine_domains_with_linear_coordinates = []
for D in affine_domains:
- V = VectorElement("CG", D, 1)
- x = Coefficient(V)
- E = Domain(x)
+ V = VectorElement("CG", D.ufl_cell(), 1)
+ E = Mesh(V)
affine_domains_with_linear_coordinates.append(E)
all_affine_domains = affine_domains + \
@@ -86,12 +85,11 @@ def affine_facet_domains(request):
quadrilateral,
tetrahedron,
]
- affine_facet_domains = [Domain(cell) for cell in affine_facet_cells]
+ affine_facet_domains = [Mesh(cell) for cell in affine_facet_cells]
affine_facet_domains_with_linear_coordinates = []
for D in affine_facet_domains:
- V = VectorElement("CG", D, 1)
- x = Coefficient(V)
- E = Domain(x)
+ V = VectorElement("CG", D.ufl_cell(), 1)
+ E = Mesh(V)
affine_facet_domains_with_linear_coordinates.append(E)
all_affine_facet_domains = affine_facet_domains + \
@@ -106,12 +104,11 @@ def nonaffine_domains(request):
quadrilateral,
hexahedron,
]
- nonaffine_domains = [Domain(cell) for cell in nonaffine_cells]
+ nonaffine_domains = [Mesh(cell) for cell in nonaffine_cells]
nonaffine_domains_with_linear_coordinates = []
for D in nonaffine_domains:
- V = VectorElement("CG", D, 1)
- x = Coefficient(V)
- E = Domain(x)
+ V = VectorElement("CG", D.ufl_cell(), 1)
+ E = Mesh(V)
nonaffine_domains_with_linear_coordinates.append(E)
all_nonaffine_domains = nonaffine_domains + \
@@ -125,12 +122,11 @@ def nonaffine_facet_domains(request):
nonaffine_facet_cells = [
hexahedron,
]
- nonaffine_facet_domains = [Domain(cell) for cell in nonaffine_facet_cells]
+ nonaffine_facet_domains = [Mesh(cell) for cell in nonaffine_facet_cells]
nonaffine_facet_domains_with_linear_coordinates = []
for D in nonaffine_facet_domains:
- V = VectorElement("CG", D, 1)
- x = Coefficient(V)
- E = Domain(x)
+ V = VectorElement("CG", D.ufl_cell(), 1)
+ E = Mesh(V)
nonaffine_facet_domains_with_linear_coordinates.append(E)
all_nonaffine_facet_domains = nonaffine_facet_domains + \
@@ -142,48 +138,48 @@ def nonaffine_facet_domains(request):
def test_always_cellwise_constant_geometric_quantities(domains):
"Test geometric quantities that are always constant over a cell."
e = CellVolume(domains)
- assert e.is_cellwise_constant()
+ assert is_cellwise_constant(e)
e = Circumradius(domains)
- assert e.is_cellwise_constant()
+ assert is_cellwise_constant(e)
e = FacetArea(domains)
- assert e.is_cellwise_constant()
+ assert is_cellwise_constant(e)
e = MinFacetEdgeLength(domains)
- assert e.is_cellwise_constant()
+ assert is_cellwise_constant(e)
e = MaxFacetEdgeLength(domains)
- assert e.is_cellwise_constant()
+ assert is_cellwise_constant(e)
def test_coordinates_never_cellwise_constant(domains):
e = SpatialCoordinate(domains)
- assert not e.is_cellwise_constant()
+ assert not is_cellwise_constant(e)
e = CellCoordinate(domains)
- assert not e.is_cellwise_constant()
+ assert not is_cellwise_constant(e)
def test_coordinates_never_cellwise_constant_vertex():
# The only exception here:
- domains = Domain(Cell("vertex", 3))
- assert domains.cell().cellname() == "vertex"
+ domains = Mesh(Cell("vertex", 3))
+ assert domains.ufl_cell().cellname() == "vertex"
e = SpatialCoordinate(domains)
- assert e.is_cellwise_constant()
+ assert is_cellwise_constant(e)
e = CellCoordinate(domains)
- assert e.is_cellwise_constant()
+ assert is_cellwise_constant(e)
def mappings_are_cellwise_constant(domain, test):
e = Jacobian(domain)
- assert e.is_cellwise_constant() == test
+ assert is_cellwise_constant(e) == test
e = JacobianDeterminant(domain)
- assert e.is_cellwise_constant() == test
+ assert is_cellwise_constant(e) == test
e = JacobianInverse(domain)
- assert e.is_cellwise_constant() == test
+ assert is_cellwise_constant(e) == test
if domain.topological_dimension() != 1:
e = FacetJacobian(domain)
- assert e.is_cellwise_constant() == test
+ assert is_cellwise_constant(e) == test
e = FacetJacobianDeterminant(domain)
- assert e.is_cellwise_constant() == test
+ assert is_cellwise_constant(e) == test
e = FacetJacobianInverse(domain)
- assert e.is_cellwise_constant() == test
+ assert is_cellwise_constant(e) == test
def test_mappings_are_cellwise_constant_on_linear_affine_cells(affine_domains):
@@ -200,7 +196,7 @@ def test_mappings_are_cellwise_not_constant_on_nonlinear_cells(nonlinear_domains
def facetnormal_cellwise_constant(domain, test):
e = FacetNormal(domain)
- assert e.is_cellwise_constant() == test
+ assert is_cellwise_constant(e) == test
def test_facetnormal_cellwise_constant_affine(affine_facet_domains):
@@ -217,26 +213,26 @@ def test_facetnormal_not_cellwise_constant_nonlinear(nonlinear_domains):
def test_coefficient_sometimes_cellwise_constant(domains_not_linear):
e = Constant(domains_not_linear)
- assert e.is_cellwise_constant()
+ assert is_cellwise_constant(e)
- V = FiniteElement("DG", domains_not_linear, 0)
+ V = FiniteElement("DG", domains_not_linear.ufl_cell(), 0)
e = Coefficient(V)
- assert e.is_cellwise_constant()
- V = FiniteElement("R", domains_not_linear, 0)
+ assert is_cellwise_constant(e)
+ V = FiniteElement("R", domains_not_linear.ufl_cell(), 0)
e = Coefficient(V)
- assert e.is_cellwise_constant()
+ assert is_cellwise_constant(e)
# This should be true, but that has to wait for a fix of issue #13
# e = TestFunction(V)
- # assert e.is_cellwise_constant()
- # V = FiniteElement("R", domains_not_linear, 0)
+ # assert is_cellwise_constant(e)
+ # V = FiniteElement("R", domains_not_linear.ufl_cell(), 0)
# e = TestFunction(V)
- # assert e.is_cellwise_constant()
+ # assert is_cellwise_constant(e)
def test_coefficient_mostly_not_cellwise_constant(domains_not_linear):
- V = FiniteElement("DG", domains_not_linear, 1)
+ V = FiniteElement("DG", domains_not_linear.ufl_cell(), 1)
e = Coefficient(V)
- assert not e.is_cellwise_constant()
+ assert not is_cellwise_constant(e)
e = TestFunction(V)
- assert not e.is_cellwise_constant()
+ assert not is_cellwise_constant(e)
diff --git a/test/test_reference_shapes.py b/test/test_reference_shapes.py
old mode 100644
new mode 100755
index c7c8032..22d3a21
--- a/test/test_reference_shapes.py
+++ b/test/test_reference_shapes.py
@@ -1,4 +1,5 @@
#!/usr/bin/env py.test
+# -*- coding: utf-8 -*-
import pytest
@@ -27,9 +28,12 @@ def test_reference_shapes():
T = TensorElement("CG", cell, 1)
assert T.value_shape() == (3, 3)
- assert T.reference_value_shape() == (9,)
+ assert T.reference_value_shape() == (3,3)
+
+ S = TensorElement("CG", cell, 1, symmetry=True)
+ assert S.value_shape() == (3, 3)
+ assert S.reference_value_shape() == (6,)
M = MixedElement(V, U, W)
assert M.value_shape() == (7,)
assert M.reference_value_shape() == (5,)
-
diff --git a/test/test_scratch.py b/test/test_scratch.py
index fbd1625..fcbed29 100755
--- a/test/test_scratch.py
+++ b/test/test_scratch.py
@@ -1,4 +1,5 @@
#!/usr/bin/env py.test
+# -*- coding: utf-8 -*-
"""
This is a template file you can copy when making a new test case.
Begin by copying this file to a filename matching test_*.py.
diff --git a/test/test_signature.py b/test/test_signature.py
index 3839f9b..6c9138c 100755
--- a/test/test_signature.py
+++ b/test/test_signature.py
@@ -1,4 +1,5 @@
#!/usr/bin/env py.test
+# -*- coding: utf-8 -*-
"""
Test the computation of form signatures.
"""
@@ -7,7 +8,7 @@ import pytest
from ufl import *
-from ufl.common import EmptyDictType
+from ufl.utils.dicts import EmptyDictType
from ufl.classes import MultiIndex, FixedIndex
from ufl.algorithms.signature import compute_multiindex_hashdata, \
compute_terminal_hashdata
@@ -39,8 +40,8 @@ def test_domain_signatures_of_cell2domains(self):
# Signature data holds when constructing two domains from a cell:
D1 = as_domain(cell)
D2 = as_domain(cell)
- self.assertEqual(D1.signature_data({D1:0}),
- D2.signature_data({D2:0}))
+ self.assertEqual(D1._ufl_signature_data_({D1:0}),
+ D2._ufl_signature_data_({D2:0}))
def compute_unique_terminal_hashdatas(hashdatas):
count = 0
@@ -248,12 +249,12 @@ def test_domain_signature_data_does_not_depend_on_domain_label_value(self):
s1s = set()
s2s = set()
for cell in cells:
- d0 = Domain(cell)
- d1 = Domain(cell, label="domain1")
- d2 = Domain(cell, label="domain2")
- s0 = d0.signature_data({ d0: 0 })
- s1 = d1.signature_data({ d1: 0 })
- s2 = d2.signature_data({ d2: 0 })
+ d0 = Mesh(cell)
+ d1 = Mesh(cell, ufl_id=1)
+ d2 = Mesh(cell, ufl_id=2)
+ s0 = d0._ufl_signature_data_({ d0: 0 })
+ s1 = d1._ufl_signature_data_({ d1: 0 })
+ s2 = d2._ufl_signature_data_({ d2: 0 })
assert s0 == s1
assert s0 == s2
s0s.add(s0)
@@ -266,15 +267,15 @@ def test_domain_signature_data_does_not_depend_on_domain_label_value(self):
def test_terminal_hashdata_does_not_depend_on_domain_label_value(self):
reprs = set()
hashes = set()
- labels = ["domain1", "domain2"]
+ ufl_ids = [1, 2]
cells = [triangle, quadrilateral]
- domains = [Domain(cell, label=label) for cell in cells for label in labels]
+ domains = [Mesh(cell, ufl_id=ufl_id) for cell in cells for ufl_id in ufl_ids]
nreps = 2
num_exprs = 2
def forms():
for rep in range(nreps):
for domain in domains:
- V = FiniteElement("CG", domain, 2)
+ V = FunctionSpace(domain, FiniteElement("CG", domain.ufl_cell(), 2))
f = Coefficient(V, count=0)
v = TestFunction(V)
x = SpatialCoordinate(domain)
diff --git a/test/test_simplify.py b/test/test_simplify.py
index 2b03451..1863d7b 100755
--- a/test/test_simplify.py
+++ b/test/test_simplify.py
@@ -1,4 +1,5 @@
#!/usr/bin/env py.test
+# -*- coding: utf-8 -*-
import pytest
from ufl.classes import Sum, Product
@@ -119,8 +120,4 @@ def test_indexing(self):
Bij = u[i]*v[j]
Bij2 = as_tensor(Bij, (i, j))[i, j]
Bij3 = as_tensor(Bij, (i, j))
- #print "BIJ: ", Bij.free_indices()
- #print "BIJ2: ", Bij2.free_indices()
- #print "BIJ: ", Bij
- #print "BIJ2: ", Bij2
assert Bij2 == Bij
diff --git a/test/test_sobolevspace.py b/test/test_sobolevspace.py
index f6a0458..0b32494 100755
--- a/test/test_sobolevspace.py
+++ b/test/test_sobolevspace.py
@@ -1,11 +1,13 @@
#!/usr/bin/env py.test
+# -*- coding: utf-8 -*-
__authors__ = "David Ham"
__date__ = "2014-03-04"
import pytest
from ufl import FiniteElement, triangle
-from ufl.sobolevspace import H2, H1, HDiv, HCurl, L2, SobolevSpace
+from ufl.sobolevspace import SobolevSpace
+from ufl import H2, H1, HDiv, HCurl, L2
# TODO: Add construction of all elements with periodic table notation here.
@@ -97,4 +99,3 @@ def test_contains_hcurl():
assert hcurl_element not in H1
assert hcurl_element not in HDiv
assert hcurl_element not in H2
-
diff --git a/test/test_split.py b/test/test_split.py
index cfc513d..7e9ca7a 100755
--- a/test/test_split.py
+++ b/test/test_split.py
@@ -1,8 +1,9 @@
#!/usr/bin/env py.test
+# -*- coding: utf-8 -*-
from ufl import *
-__authors__ = "Martin Sandve Alnes"
+__authors__ = "Martin Sandve Alnæs"
__date__ = "2009-03-14 -- 2009-03-14"
import pytest
diff --git a/test/test_str.py b/test/test_str.py
index e112dba..54b37f8 100755
--- a/test/test_str.py
+++ b/test/test_str.py
@@ -1,4 +1,5 @@
#!/usr/bin/env py.test
+# -*- coding: utf-8 -*-
import pytest
diff --git a/test/test_tensoralgebra.py b/test/test_tensoralgebra.py
index 5158f46..471cdb4 100755
--- a/test/test_tensoralgebra.py
+++ b/test/test_tensoralgebra.py
@@ -1,4 +1,5 @@
#!/usr/bin/env py.test
+# -*- coding: utf-8 -*-
"""
Test tensor algebra operators.
"""
diff --git a/test/test_utilities.py b/test/test_utilities.py
index 4193b9b..790af1c 100755
--- a/test/test_utilities.py
+++ b/test/test_utilities.py
@@ -1,4 +1,5 @@
#!/usr/bin/env py.test
+# -*- coding: utf-8 -*-
"""
Test internal utility functions.
@@ -150,7 +151,7 @@ def test_index_flattening():
def test_stackdict():
- from ufl.common import StackDict
+ from ufl.utils.stacks import StackDict
d = StackDict(a=1)
assert d["a"] == 1
d.push("a", 2)
diff --git a/ufl/__init__.py b/ufl/__init__.py
index ed86f58..8c53b68 100644
--- a/ufl/__init__.py
+++ b/ufl/__init__.py
@@ -1,3 +1,4 @@
+# -*- coding: utf-8 -*-
"""The Unified Form Language is an embedded domain specific language
for definition of variational forms intended for finite element
discretization. More precisely, it defines a fixed interface for choosing
@@ -38,15 +39,14 @@ The development version can be found in the repository at
A very brief overview of the language contents follows:
-* Domains::
+* Cells::
- Domain, ProductDomain
+ AbstractCell, Cell, TensorProductCell, OuterProductCell,
+ vertex, interval, triangle, tetrahedron, quadrilateral, hexahedron
-* Cells::
+* Domains::
- Cell, ProductCell, OuterProductCell,
- interval, triangle, tetrahedron,
- quadrilateral, hexahedron
+ AbstractDomain, Mesh, MeshView, TensorProductMesh
* Sobolev spaces::
@@ -58,13 +58,18 @@ A very brief overview of the language contents follows:
MixedElement, VectorElement, TensorElement
EnrichedElement, RestrictedElement,
TensorProductElement, OuterProductElement,
- OuterProductVectorElement, HDiv, HCurl
+ HDivElement, HCurlElement
BrokenElement, TraceElement
FacetElement, InteriorElement
+* Function spaces::
+
+ FunctionSpace
+
* Arguments::
- Argument, TestFunction, TrialFunction
+ Argument, TestFunction, TrialFunction,
+ Arguments, TestFunctions, TrialFunctions
* Coefficients::
@@ -84,6 +89,7 @@ A very brief overview of the language contents follows:
FacetNormal, CellNormal,
CellVolume, Circumradius, MinCellEdgeLength, MaxCellEdgeLength,
FacetArea, MinFacetEdgeLength, MaxFacetEdgeLength,
+ Jacobian, JacobianDeterminant, JacobianInverse
* Indices::
@@ -139,7 +145,7 @@ A very brief overview of the language contents follows:
* Integral measures::
dx, ds, dS, dP,
- dc, dC, dO, dI,
+ dc, dC, dO, dI, dX
ds_b, ds_t, ds_tb, ds_v, dS_h, dS_v
* Form transformations::
@@ -150,7 +156,7 @@ A very brief overview of the language contents follows:
sensitivity_rhs, derivative
"""
-# Copyright (C) 2008-2014 Martin Sandve Alnes and Anders Logg
+# Copyright (C) 2008-2015 Martin Sandve Alnæs and Anders Logg
#
# This file is part of UFL.
#
@@ -173,7 +179,7 @@ A very brief overview of the language contents follows:
# Modified by Andrew T. T. McRae, 2014
# Modified by Lawrence Mitchell, 2014
-__version__ = "1.6.0"
+__version__ = "2016.1.0"
########## README
# Imports here should be what the user sees when doing "from ufl import *",
@@ -184,20 +190,22 @@ __version__ = "1.6.0"
# Utility functions (product is the counterpart of the built-in
# python function sum, can be useful for users as well?)
-from ufl.common import product
+from ufl.utils.sequences import product
# Output control
from ufl.log import get_handler, get_logger, set_handler, set_level, add_logfile, \
UFLException, DEBUG, INFO, WARNING, ERROR, CRITICAL
# Types for geometric quantities
-from ufl.cell import as_cell, Cell, ProductCell, OuterProductCell
-from ufl.domain import as_domain, Domain, ProductDomain
+
+from ufl.cell import as_cell, AbstractCell, Cell, TensorProductCell, OuterProductCell
+from ufl.domain import as_domain, AbstractDomain, Mesh, MeshView, TensorProductMesh
from ufl.geometry import (
SpatialCoordinate,
FacetNormal, CellNormal,
CellVolume, Circumradius, MinCellEdgeLength, MaxCellEdgeLength,
FacetArea, MinFacetEdgeLength, MaxFacetEdgeLength,
+ Jacobian, JacobianDeterminant, JacobianInverse
)
# Sobolev spaces
@@ -207,12 +215,15 @@ from ufl.sobolevspace import L2, H1, H2, HDiv, HCurl
from ufl.finiteelement import FiniteElementBase, FiniteElement, \
MixedElement, VectorElement, TensorElement, EnrichedElement, \
RestrictedElement, TensorProductElement, OuterProductElement, \
- OuterProductVectorElement, HDiv, HCurl, BrokenElement, TraceElement, \
+ HDivElement, HCurlElement, BrokenElement, TraceElement, \
FacetElement, InteriorElement
# Hook to extend predefined element families
from ufl.finiteelement.elementlist import register_element, show_elements #, ufl_elements
+# Function spaces
+from ufl.functionspace import FunctionSpace, MixedFunctionSpace
+
# Arguments
from ufl.argument import Argument, TestFunction, TrialFunction, \
Arguments, TestFunctions, TrialFunctions
@@ -257,7 +268,7 @@ from ufl.operators import rank, shape, \
elem_mult, elem_div, elem_pow, elem_op
# Measure classes
-from ufl.measure import Measure, register_integral_type, integral_types
+from ufl.measure import Measure, register_integral_type, integral_types, custom_integral_types
# Form class
from ufl.form import Form, replace_integral_domains
@@ -271,7 +282,7 @@ import ufl.measureoperators as __measureoperators
# Representations of transformed forms
from ufl.formoperators import replace, derivative, action, energy_norm, rhs, lhs,\
- system, functional, adjoint, sensitivity_rhs #, dirichlet_functional
+ system, functional, adjoint, sensitivity_rhs, block_split #, dirichlet_functional
# Predefined convenience objects
from ufl.objects import (
@@ -279,7 +290,7 @@ from ufl.objects import (
quadrilateral, hexahedron, facet,
i, j, k, l, p, q, r, s,
dx, ds, dS, dP,
- dc, dC, dO, dI,
+ dc, dC, dO, dI, dX,
ds_b, ds_t, ds_tb, ds_v, dS_h, dS_v
)
@@ -290,19 +301,21 @@ __all__ = [
'product',
'get_handler', 'get_logger', 'set_handler', 'set_level', 'add_logfile',
'UFLException', 'DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL',
- 'as_cell', 'Cell', 'ProductCell', 'OuterProductCell',
- 'as_domain', 'Domain', 'ProductDomain',
+ 'as_cell', 'AbstractCell', 'Cell', 'TensorProductCell', 'OuterProductCell',
+ 'as_domain', 'AbstractDomain', 'Mesh', 'MeshView', 'TensorProductMesh',
'L2', 'H1', 'H2', 'HCurl', 'HDiv',
'SpatialCoordinate',
'CellVolume', 'Circumradius', 'MinCellEdgeLength', 'MaxCellEdgeLength',
'FacetArea', 'MinFacetEdgeLength', 'MaxFacetEdgeLength',
'FacetNormal', 'CellNormal',
+ 'Jacobian', 'JacobianDeterminant', 'JacobianInverse',
'FiniteElementBase', 'FiniteElement',
'MixedElement', 'VectorElement', 'TensorElement', 'EnrichedElement',
'RestrictedElement', 'TensorProductElement', 'OuterProductElement',
- 'OuterProductVectorElement', 'HDiv', 'HCurl',
+ 'HDivElement', 'HCurlElement',
'BrokenElement', 'TraceElement', 'FacetElement', 'InteriorElement',
'register_element', 'show_elements',
+ 'FunctionSpace',
'Argument', 'TestFunction', 'TrialFunction',
'Arguments', 'TestFunctions', 'TrialFunctions',
'Coefficient', 'Coefficients',
@@ -328,11 +341,11 @@ __all__ = [
'jump', 'avg', 'cell_avg', 'facet_avg',
'elem_mult', 'elem_div', 'elem_pow', 'elem_op',
'Form',
- 'Integral', 'Measure', 'register_integral_type', 'integral_types',
- 'replace', 'replace_integral_domains', 'derivative', 'action', 'energy_norm', 'rhs', 'lhs',
+ 'Integral', 'Measure', 'register_integral_type', 'integral_types', 'custom_integral_types',
+ 'replace', 'replace_integral_domains', 'derivative', 'action', 'energy_norm', 'rhs', 'lhs', 'block_split',
'system', 'functional', 'adjoint', 'sensitivity_rhs',
'dx', 'ds', 'dS', 'dP',
- 'dc', 'dC', 'dO', 'dI',
+ 'dc', 'dC', 'dO', 'dI', 'dX',
'ds_b', 'ds_t', 'ds_tb', 'ds_v', 'dS_h', 'dS_v',
'vertex', 'interval', 'triangle', 'tetrahedron',
'quadrilateral', 'hexahedron', 'facet',
diff --git a/ufl/algebra.py b/ufl/algebra.py
index 2798a90..c4d004f 100644
--- a/ufl/algebra.py
+++ b/ufl/algebra.py
@@ -1,6 +1,7 @@
+# -*- coding: utf-8 -*-
"Basic algebra operations."
-# Copyright (C) 2008-2014 Martin Sandve Alnes
+# Copyright (C) 2008-2015 Martin Sandve Alnæs
#
# This file is part of UFL.
#
@@ -24,7 +25,6 @@ from six import iteritems
from ufl.log import error, warning
from ufl.assertions import ufl_assert
-from ufl.common import product, mergedicts2, subdict, EmptyDict
from ufl.core.expr import Expr
from ufl.core.operator import Operator
from ufl.constantvalue import Zero, zero, ScalarValue, IntValue, as_ufl
@@ -333,7 +333,8 @@ class Abs(Operator):
def __init__(self, a):
Operator.__init__(self, (a,))
ufl_assert(isinstance(a, Expr), "Expecting Expr instance.")
- if not isinstance(a, Expr): error("Expecting Expr instances.")
+ if not isinstance(a, Expr):
+ error("Expecting Expr instances.")
def evaluate(self, x, mapping, component, index_values):
a = self.ufl_operands[0].evaluate(x, mapping, component, index_values)
diff --git a/ufl/algorithms/__init__.py b/ufl/algorithms/__init__.py
index 6a924ec..7efdea8 100644
--- a/ufl/algorithms/__init__.py
+++ b/ufl/algorithms/__init__.py
@@ -1,6 +1,7 @@
+# -*- coding: utf-8 -*-
"This module collects algorithms and utility functions operating on UFL objects."
-# Copyright (C) 2008-2014 Martin Sandve Alnes
+# Copyright (C) 2008-2015 Martin Sandve Alnæs
#
# This file is part of UFL.
#
@@ -20,6 +21,10 @@
# Modified by Anders Logg, 2008-2009.
+# FIXME: Clean up this to become a more official set of supported algorithms.
+# Currently contains too much stuff that's not recommended to use.
+
+
# Utilities for traversing over expression trees in different ways
from ufl.algorithms.traversal import iter_expressions
@@ -57,19 +62,14 @@ from ufl.algorithms.checks import validate_form
from ufl.corealg.multifunction import MultiFunction
from ufl.algorithms.transformer import Transformer, is_post_handler, \
apply_transformer, \
- ReuseTransformer, ufl2ufl, \
- CopyTransformer, ufl2uflcopy, \
+ ReuseTransformer, \
VariableStripper, strip_variables
from ufl.algorithms.replace import Replacer, replace
from ufl.algorithms.change_to_reference import change_to_reference_grad
-from ufl.algorithms.expand_compounds import CompoundExpander, expand_compounds, \
- CompoundExpanderPreDiff, expand_compounds_prediff, \
- CompoundExpanderPostDiff, expand_compounds_postdiff
+from ufl.algorithms.expand_compounds import expand_compounds
from ufl.algorithms.estimate_degrees import SumDegreeEstimator, estimate_total_polynomial_degree
from ufl.algorithms.argument_dependencies import ArgumentDependencyExtracter, extract_argument_dependencies, NotMultiLinearException
-from ufl.algorithms.renumbering import renumber_indices
from ufl.algorithms.expand_indices import expand_indices, purge_list_tensors
-from ufl.algorithms.propagate_restrictions import propagate_restrictions
# Utilities for transforming complete Forms into other Forms
from ufl.algorithms.formtransformations import (
@@ -77,8 +77,10 @@ from ufl.algorithms.formtransformations import (
compute_form_lhs, compute_form_rhs,
compute_form_functional, compute_form_arities)
+from ufl.algorithms.formsplitter import FormSplitter
+
# Utilities for Automatic Functional Differentiation
-from ufl.algorithms.ad import expand_derivatives #, compute_diff, propagate_spatial_derivatives, compute_form_derivative
+from ufl.algorithms.ad import expand_derivatives
# Utilities for form file handling
from ufl.algorithms.formfiles import read_ufl_file, load_ufl_file, load_forms
diff --git a/ufl/algorithms/ad.py b/ufl/algorithms/ad.py
index 61b35f5..a2e57d3 100644
--- a/ufl/algorithms/ad.py
+++ b/ufl/algorithms/ad.py
@@ -1,6 +1,7 @@
+# -*- coding: utf-8 -*-
"""Front-end for AD routines."""
-# Copyright (C) 2008-2014 Martin Sandve Alnes
+# Copyright (C) 2008-2015 Martin Sandve Alnæs
#
# This file is part of UFL.
#
@@ -19,53 +20,26 @@
#
# Modified by Anders Logg, 2009.
-from ufl.log import debug, error
-from ufl.assertions import ufl_assert
-from ufl.classes import Terminal, Derivative
-from ufl.algorithms.map_integrands import map_integrands
-from ufl.algorithms.expand_compounds import expand_compounds, expand_compounds_postdiff
-from ufl.algorithms.forward_ad import apply_nested_forward_ad
+from ufl.log import warning
+from ufl.algorithms.apply_algebra_lowering import apply_algebra_lowering
from ufl.algorithms.apply_derivatives import apply_derivatives
-def expand_derivatives(form, dim=None,
- apply_expand_compounds_before=True,
- apply_expand_compounds_after=False,
- use_alternative_wrapper_algorithm=False):
+def expand_derivatives(form, **kwargs):
"""Expand all derivatives of expr.
In the returned expression g which is mathematically
equivalent to expr, there are no VariableDerivative
or CoefficientDerivative objects left, and Grad
objects have been propagated to Terminal nodes.
-
- Note: Passing dim is now unnecessary, and the argument is ignored.
"""
+ # For a deprecation period (I see that dolfin-adjoint passes some args here)
+ if kwargs:
+ warning("Deprecation: expand_derivatives no longer takes any keyword arguments")
- def _expand_derivatives(expression):
- #print '_expand_derivatives:', expression
- # Expand compound expressions or not, in the future this
- # should be removed from here and applied on the outside.
- if apply_expand_compounds_before:
- expression = expand_compounds(expression)
- #print 'after expand_compounds', expression
-
- # Apply recursive forward mode AD
- #expression = apply_nested_forward_ad(expression)
- expression = apply_derivatives(expression)
-
- # FIXME: Form compilers assume expand_compounds have been applied.
- # This means quite a bit of work to handle all compounds
- # through the entire jit chain. For now, just test if we
- # can apply compounds afterwards, to focus on fixing issues
- # in the AD algorithm for compounds. Since this is optional,
- # alternative form compilers can then disable expand_compounds alltogether.
- if apply_expand_compounds_after:
- # FIXME: Test expand_compounds_postdiff, it should make this algorithm viable for existing FFC code
- #expression = expand_compounds(expression)
- expression = expand_compounds_postdiff(expression)
- return expression
-
+ # Lower abstractions for tensor-algebra types into index notation
+ form = apply_algebra_lowering(form)
+ # Apply differentiation
+ form = apply_derivatives(form)
- # Apply chosen algorithm to all integrands
- return map_integrands(_expand_derivatives, form)
+ return form
diff --git a/ufl/algorithms/analysis.py b/ufl/algorithms/analysis.py
index 6d6fd3c..96ce18d 100644
--- a/ufl/algorithms/analysis.py
+++ b/ufl/algorithms/analysis.py
@@ -1,6 +1,7 @@
+# -*- coding: utf-8 -*-
"""Utility algorithms for inspection of and information extraction from UFL objects in various ways."""
-# Copyright (C) 2008-2014 Martin Sandve Alnes
+# Copyright (C) 2008-2015 Martin Sandve Alnæs
#
# This file is part of UFL.
#
@@ -26,8 +27,7 @@ from collections import namedtuple
from ufl.log import error, warning, info
from ufl.assertions import ufl_assert
-from ufl.sorting import topological_sorting
-from ufl.utils.sorting import sorted_by_count
+from ufl.utils.sorting import sorted_by_count, topological_sorting
from ufl.core.expr import Expr
from ufl.core.terminal import Terminal, FormArgument
@@ -36,11 +36,10 @@ from ufl.argument import Argument
from ufl.coefficient import Coefficient
from ufl.variable import Variable
from ufl.core.multiindex import Index, MultiIndex
-from ufl.geometry import Domain
from ufl.integral import Measure, Integral
from ufl.form import Form
from ufl.algorithms.traversal import iter_expressions
-from ufl.corealg.traversal import pre_traversal, traverse_terminals
+from ufl.corealg.traversal import unique_pre_traversal, traverse_unique_terminals
# TODO: Some of these can possibly be optimised by implementing inlined stack based traversal algorithms
@@ -68,7 +67,7 @@ def __unused__extract_classes(a):
The argument a can be a Form, Integral or Expr."""
return set(o._ufl_class_
for e in iter_expressions(a)
- for o in pre_traversal(e))
+ for o in unique_pre_traversal(e))
def extract_type(a, ufl_type):
"""Build a set of all objects of class ufl_type found in a.
@@ -76,11 +75,11 @@ def extract_type(a, ufl_type):
if issubclass(ufl_type, Terminal):
# Optimization
return set(o for e in iter_expressions(a)
- for o in traverse_terminals(e)
+ for o in traverse_unique_terminals(e)
if isinstance(o, ufl_type))
else:
return set(o for e in iter_expressions(a)
- for o in pre_traversal(e)
+ for o in unique_pre_traversal(e)
if isinstance(o, ufl_type))
def has_type(a, ufl_type):
@@ -88,9 +87,9 @@ def has_type(a, ufl_type):
The argument a can be a Form, Integral or Expr."""
if issubclass(ufl_type, Terminal):
# Optimization
- traversal = traverse_terminals
+ traversal = traverse_unique_terminals
else:
- traversal = pre_traversal
+ traversal = unique_pre_traversal
return any(isinstance(o, ufl_type) for e in iter_expressions(a) for o in traversal(e))
def has_exact_type(a, ufl_type):
@@ -99,9 +98,9 @@ def has_exact_type(a, ufl_type):
tc = ufl_type._ufl_typecode_
if issubclass(ufl_type, Terminal):
# Optimization
- traversal = traverse_terminals
+ traversal = traverse_unique_terminals
else:
- traversal = pre_traversal
+ traversal = unique_pre_traversal
return any(o._ufl_typecode_ == tc for e in iter_expressions(a) for o in traversal(e))
def extract_arguments(a):
@@ -153,7 +152,7 @@ The arguments found are:\n%s""" % "\n".join(" %s" % f for f in coefficients)
def extract_elements(form):
"Build sorted tuple of all elements used in form."
args = chain(*extract_arguments_and_coefficients(form))
- return tuple(f.element() for f in args)
+ return tuple(f.ufl_element() for f in args)
def extract_unique_elements(form):
@@ -164,7 +163,8 @@ def extract_unique_elements(form):
def extract_sub_elements(elements):
"Build sorted tuple of all sub elements (including parent element)."
sub_elements = tuple(chain(*[e.sub_elements() for e in elements]))
- if not sub_elements: return tuple(elements)
+ if not sub_elements:
+ return tuple(elements)
return tuple(elements) + extract_sub_elements(sub_elements)
@@ -194,7 +194,7 @@ def sort_elements(elements):
"""
# Set nodes
- nodes = elements
+ nodes = sorted(elements)
# Set edges
edges = dict((node, []) for node in nodes)
diff --git a/ufl/algorithms/apply_algebra_lowering.py b/ufl/algorithms/apply_algebra_lowering.py
new file mode 100644
index 0000000..6e934c6
--- /dev/null
+++ b/ufl/algorithms/apply_algebra_lowering.py
@@ -0,0 +1,178 @@
+# -*- coding: utf-8 -*-
+"""Algorithm for expanding compound expressions into
+equivalent representations using basic operators."""
+
+# Copyright (C) 2008-2015 Martin Sandve Alnæs and Anders Logg
+#
+# This file is part of UFL.
+#
+# UFL is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# UFL is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with UFL. If not, see <http://www.gnu.org/licenses/>.
+#
+# Modified by Anders Logg, 2009-2010
+
+from ufl.log import error, warning
+from ufl.assertions import ufl_assert
+
+from ufl.classes import Product, Grad
+from ufl.core.multiindex import indices, Index, FixedIndex
+from ufl.tensors import as_tensor, as_matrix, as_vector
+
+from ufl.compound_expressions import deviatoric_expr, determinant_expr, cofactor_expr, adj_expr, inverse_expr
+
+from ufl.corealg.multifunction import MultiFunction
+from ufl.algorithms.map_integrands import map_integrand_dags
+
+
+class LowerCompoundAlgebra(MultiFunction):
+ """Expands high level compound operators (e.g. inner) to equivalent
+ representations using basic operators (e.g. index notation)."""
+ def __init__(self):
+ MultiFunction.__init__(self)
+
+ expr = MultiFunction.reuse_if_untouched
+
+ # ------------ Compound tensor operators
+
+ def trace(self, o, A):
+ i = Index()
+ return A[i, i]
+
+ def transposed(self, o, A):
+ i, j = indices(2)
+ return as_tensor(A[i, j], (j, i))
+
+ def _square_matrix_shape(self, A):
+ sh = A.ufl_shape
+ ufl_assert(sh[0] == sh[1], "Expecting square matrix.")
+ ufl_assert(sh[0] is not None, "Unknown dimension.")
+ return sh
+
+ def deviatoric(self, o, A):
+ return deviatoric_expr(A)
+
+ def skew(self, o, A):
+ i, j = indices(2)
+ return as_matrix( (A[i, j] - A[j, i]) / 2, (i, j) )
+
+ def sym(self, o, A):
+ i, j = indices(2)
+ return as_matrix( (A[i, j] + A[j, i]) / 2, (i, j) )
+
+ def cross(self, o, a, b):
+ def c(i, j):
+ return Product(a[i], b[j]) - Product(a[j], b[i])
+ return as_vector((c(1, 2), c(2, 0), c(0, 1)))
+
+ def altenative_dot(self, o, a, b): # TODO: Test this
+ ash = a.ufl_shape
+ bsh = b.ufl_shape
+ ai = indices(len(ash)-1)
+ bi = indices(len(bsh)-1)
+ # Simplification for tensors where the dot-sum dimension has length 1
+ if ash[-1] == 1:
+ k = (FixedIndex(0),)
+ else:
+ k = (Index(),)
+ # Potentially creates a single IndexSum over a Product
+ s = a[ai+k]*b[k+bi]
+ return as_tensor(s, ai+bi)
+
+ def dot(self, o, a, b):
+ ai = indices(len(a.ufl_shape)-1)
+ bi = indices(len(b.ufl_shape)-1)
+ k = (Index(),)
+ # Creates a single IndexSum over a Product
+ s = a[ai+k]*b[k+bi]
+ return as_tensor(s, ai+bi)
+
+ def alternative_inner(self, o, a, b): # TODO: Test this
+ ash = a.ufl_shape
+ bsh = b.ufl_shape
+ ufl_assert(ash == bsh)
+ # Simplification for tensors with one or more dimensions of length 1
+ ii = []
+ zi = FixedIndex(0)
+ for n in ash:
+ if n == 1:
+ ii.append(zi)
+ else:
+ ii.append(Index())
+ ii = tuple(ii)
+ #ii = indices(len(a.ufl_shape))
+ # Potentially creates multiple IndexSums over a Product
+ s = a[ii]*b[ii]
+ return s
+
+ def inner(self, o, a, b):
+ ufl_assert(a.ufl_shape == b.ufl_shape)
+ ii = indices(len(a.ufl_shape))
+ # Creates multiple IndexSums over a Product
+ s = a[ii]*b[ii]
+ return s
+
+ def outer(self, o, a, b):
+ ii = indices(len(a.ufl_shape))
+ jj = indices(len(b.ufl_shape))
+ # Create a Product with no shared indices
+ s = a[ii]*b[jj]
+ return as_tensor(s, ii+jj)
+
+ def determinant(self, o, A):
+ return determinant_expr(A)
+
+ def cofactor(self, o, A):
+ return cofactor_expr(A)
+
+ def inverse(self, o, A):
+ return inverse_expr(A)
+
+ # ------------ Compound differential operators
+
+ def div(self, o, a):
+ i = Index()
+ return a[..., i].dx(i)
+
+ def nabla_div(self, o, a):
+ i = Index()
+ return a[i, ...].dx(i)
+
+ def nabla_grad(self, o, a):
+ sh = a.ufl_shape
+ if sh == ():
+ return Grad(a)
+ else:
+ j = Index()
+ ii = tuple(indices(len(sh)))
+ return as_tensor(a[ii].dx(j), (j,) + ii)
+
+ def curl(self, o, a):
+ # o = curl a = "[a.dx(1), -a.dx(0)]" if a.ufl_shape == ()
+ # o = curl a = "cross(nabla, (a0, a1, 0))[2]" if a.ufl_shape == (2,)
+ # o = curl a = "cross(nabla, a)" if a.ufl_shape == (3,)
+ def c(i, j):
+ return a[j].dx(i) - a[i].dx(j)
+ sh = a.ufl_shape
+ if sh == ():
+ return as_vector((a.dx(1), -a.dx(0)))
+ if sh == (2,):
+ return c(0, 1)
+ if sh == (3,):
+ return as_vector((c(1, 2), c(2, 0), c(0, 1)))
+ error("Invalid shape %s of curl argument." % (sh,))
+
+
+def apply_algebra_lowering(expr):
+ """Expands high level compound operators (e.g. inner) to equivalent
+ representations using basic operators (e.g. index notation)."""
+ return map_integrand_dags(LowerCompoundAlgebra(), expr)
diff --git a/ufl/algorithms/apply_derivatives.py b/ufl/algorithms/apply_derivatives.py
index 157474b..82d59d1 100644
--- a/ufl/algorithms/apply_derivatives.py
+++ b/ufl/algorithms/apply_derivatives.py
@@ -1,6 +1,7 @@
+# -*- coding: utf-8 -*-
"""This module contains the apply_derivatives algorithm which computes the derivatives of a form of expression."""
-# Copyright (C) 2008-2014 Martin Sandve Alnes
+# Copyright (C) 2008-2015 Martin Sandve Alnæs
#
# This file is part of UFL.
#
@@ -27,17 +28,19 @@ from ufl.core.multiindex import MultiIndex, Index, FixedIndex, indices
from ufl.tensors import as_tensor, as_scalar, as_scalars, unit_indexed_tensor, unwrap_list_tensor
from ufl.classes import ConstantValue, Identity, Zero, FloatValue
-from ufl.classes import Coefficient, FormArgument
-from ufl.classes import Grad, Variable
+from ufl.classes import Coefficient, FormArgument, ReferenceValue
+from ufl.classes import Grad, ReferenceGrad, Variable
from ufl.classes import Indexed, ListTensor, ComponentTensor
from ufl.classes import ExprList, ExprMapping
from ufl.classes import Product, Sum, IndexSum
+from ufl.classes import Jacobian, JacobianInverse
+from ufl.classes import SpatialCoordinate
from ufl.constantvalue import is_true_ufl_scalar, is_ufl_scalar
-from ufl.operators import dot, inner, outer, lt, eq, conditional, sign, \
- sqrt, exp, ln, cos, sin, tan, cosh, sinh, tanh, acos, asin, atan, atan_2, \
- erf, bessel_J, bessel_Y, bessel_I, bessel_K, \
- cell_avg, facet_avg
+from ufl.operators import (dot, inner, outer, lt, eq, conditional, sign,
+ sqrt, exp, ln, cos, sin, tan, cosh, sinh, tanh, acos, asin, atan, atan_2,
+ erf, bessel_J, bessel_Y, bessel_I, bessel_K,
+ cell_avg, facet_avg)
from math import pi
@@ -45,6 +48,7 @@ from ufl.corealg.multifunction import MultiFunction
from ufl.corealg.map_dag import map_expr_dag
from ufl.algorithms.map_integrands import map_integrand_dags
+from ufl.checks import is_cellwise_constant
# TODO: Add more rulesets?
# - DivRuleset
@@ -53,6 +57,12 @@ from ufl.algorithms.map_integrands import map_integrand_dags
# - ReferenceDivRuleset
+# Set this to True to enable previously default workaround
+# for bug in FFC handling of conditionals, uflacs does not
+# have this bug.
+CONDITIONAL_WORKAROUND = False
+
+
class GenericDerivativeRuleset(MultiFunction):
def __init__(self, var_shape):
MultiFunction.__init__(self)
@@ -77,11 +87,15 @@ class GenericDerivativeRuleset(MultiFunction):
error("FIXME: Unimplemented differentiation handler for type {0}.".format(o._ufl_class_.__name__))
- # --- Helper functions for creating zeros with the right shapes
+ # --- Some types just don't have any derivative, this is just to make algorithm structure generic
def non_differentiable_terminal(self, o):
"Labels and indices are not differentiable. It's convenient to return the non-differentiated object."
return o
+ label = non_differentiable_terminal
+ multi_index = non_differentiable_terminal
+
+ # --- Helper functions for creating zeros with the right shapes
def independent_terminal(self, o):
"Return a zero with the right shape for terminals independent of differentiation variable."
@@ -99,11 +113,7 @@ class GenericDerivativeRuleset(MultiFunction):
# --- Default rules for terminals
- # Some types just don't have any derivative, this is just to make algorithm structure generic
- label = non_differentiable_terminal
- multi_index = non_differentiable_terminal
-
- # Literals are assumed independent of the differentiation variable by default
+ # Literals are by definition independent of any differentiation variable
constant_value = independent_terminal
# Rules for form arguments must be specified in specialized rule set
@@ -114,43 +124,43 @@ class GenericDerivativeRuleset(MultiFunction):
# These types are currently assumed independent, but for non-affine domains
# this no longer holds and we want to implement rules for them.
- facet_normal = independent_terminal
- spatial_coordinate = independent_terminal
- cell_coordinate = independent_terminal
+ #facet_normal = independent_terminal
+ #spatial_coordinate = independent_terminal
+ #cell_coordinate = independent_terminal
# Measures of cell entities, assuming independent although
# this will not be true for all of these for non-affine domains
- cell_volume = independent_terminal
- circumradius = independent_terminal
- facet_area = independent_terminal
+ #cell_volume = independent_terminal
+ #circumradius = independent_terminal
+ #facet_area = independent_terminal
#cell_surface_area = independent_terminal
- min_cell_edge_length = independent_terminal
- max_cell_edge_length = independent_terminal
- min_facet_edge_length = independent_terminal
- max_facet_edge_length = independent_terminal
+ #min_cell_edge_length = independent_terminal
+ #max_cell_edge_length = independent_terminal
+ #min_facet_edge_length = independent_terminal
+ #max_facet_edge_length = independent_terminal
# Other stuff
- cell_orientation = independent_terminal
- quadrature_weigth = independent_terminal
+ #cell_orientation = independent_terminal
+ #quadrature_weigth = independent_terminal
# These types are currently not expected to show up in AD pass.
# To make some of these available to the end-user, they need to be implemented here.
- facet_coordinate = unexpected
- cell_origin = unexpected
- facet_origin = unexpected
- cell_facet_origin = unexpected
- jacobian = unexpected
- jacobian_determinant = unexpected
- jacobian_inverse = unexpected
- facet_jacobian = unexpected
- facet_jacobian_determinant = unexpected
- facet_jacobian_inverse = unexpected
- cell_facet_jacobian = unexpected
- cell_facet_jacobian_determinant = unexpected
- cell_facet_jacobian_inverse = unexpected
- cell_edge_vectors = unexpected
- facet_edge_vectors = unexpected
- cell_normal = unexpected # TODO: Expecting rename
+ #facet_coordinate = unexpected
+ #cell_origin = unexpected
+ #facet_origin = unexpected
+ #cell_facet_origin = unexpected
+ #jacobian = unexpected
+ #jacobian_determinant = unexpected
+ #jacobian_inverse = unexpected
+ #facet_jacobian = unexpected
+ #facet_jacobian_determinant = unexpected
+ #facet_jacobian_inverse = unexpected
+ #cell_facet_jacobian = unexpected
+ #cell_facet_jacobian_determinant = unexpected
+ #cell_facet_jacobian_inverse = unexpected
+ #cell_edge_vectors = unexpected
+ #facet_edge_vectors = unexpected
+ #cell_normal = unexpected # TODO: Expecting rename
#cell_normals = unexpected
#facet_tangents = unexpected
#cell_tangents = unexpected
@@ -160,7 +170,7 @@ class GenericDerivativeRuleset(MultiFunction):
# --- Default rules for operators
- def variable(self, o, df, l):
+ def variable(self, o, df, unused_l):
return df
# --- Indexing and component handling
@@ -183,7 +193,7 @@ class GenericDerivativeRuleset(MultiFunction):
return Indexed(C, MultiIndex(tuple(Cind)))
# Otherwise a more generic approach
- r = Ap.rank() - len(ii)
+ r = len(Ap.ufl_shape) - len(ii)
if r:
kk = indices(r)
op = Indexed(Ap, MultiIndex(ii.indices() + kk))
@@ -394,9 +404,9 @@ class GenericDerivativeRuleset(MultiFunction):
# --- Restrictions
def restricted(self, o, fp):
- # Restriction and differentiation commutes, at least for the derivatives we support.
+ # Restriction and differentiation commutes
if isinstance(fp, ConstantValue):
- return fp # TODO: Necessary? Can't restriction simplify directly instead?
+ return fp # TODO: Add simplification to Restricted instead?
else:
return fp(o._side) # (f+-)' == (f')+-
@@ -410,15 +420,22 @@ class GenericDerivativeRuleset(MultiFunction):
# Should not be used anywhere...
return None
- def conditional(self, o, dc, dt, df):
+ def conditional(self, o, unused_dc, dt, df):
+ global CONDITIONAL_WORKAROUND
if isinstance(dt, Zero) and isinstance(df, Zero):
# Assuming dt and df have the same indices here, which should be the case
return dt
- else:
- # Placing t[1],f[1] outside here to avoid getting arguments inside conditionals
- c, t, f = o.ufl_operands
+ elif CONDITIONAL_WORKAROUND:
+ # Placing t[1],f[1] outside here to avoid getting arguments inside conditionals.
+ # This will fail when dt or df become NaN or Inf in floating point computations!
+ c = o.ufl_operands[0]
dc = conditional(c, 1, 0)
return dc*dt + (1.0 - dc)*df
+ else:
+ # Not placing t[1],f[1] outside, allowing arguments inside conditionals.
+ # This will make legacy ffc fail, but should work with uflacs.
+ c = o.ufl_operands[0]
+ return conditional(c, dt, df)
def max_value(self, o, df, dg):
#d/dx max(f, g) =
@@ -447,27 +464,39 @@ class GradRuleset(GenericDerivativeRuleset):
# --- Specialized rules for geometric quantities
def geometric_quantity(self, o):
- "dg/dx = 0 if piecewise constant, otherwise Grad(g)"
- if o.is_cellwise_constant():
+ """Default for geometric quantities is dg/dx = 0 if piecewise constant, otherwise keep Grad(g).
+ Override for specific types if other behaviour is needed."""
+ if is_cellwise_constant(o):
return self.independent_terminal(o)
else:
# TODO: Which types does this involve? I don't think the form compilers will handle this.
return Grad(o)
+ def jacobian_inverse(self, o):
+ # grad(K) == K_ji rgrad(K)_rj
+ if is_cellwise_constant(o):
+ return self.independent_terminal(o)
+ ufl_assert(o._ufl_is_terminal_, "ReferenceValue can only wrap a terminal")
+ r = indices(len(o.ufl_shape))
+ i, j = indices(2)
+ Do = as_tensor(o[j,i]*ReferenceGrad(o)[r + (j,)], r + (i,))
+ return Do
+
+ # TODO: Add more explicit geometry type handlers here, with non-affine domains several should be non-zero.
+
def spatial_coordinate(self, o):
"dx/dx = I"
return self._Id
def cell_coordinate(self, o):
"dX/dx = inv(dx/dX) = inv(J) = K"
- return JacobianInverse(o.domain())
-
- # TODO: Add more geometry types here, with non-affine domains several should be non-zero.
+ # FIXME: Is this true for manifolds? What about orientation?
+ return JacobianInverse(o.ufl_domain())
# --- Specialized rules for form arguments
def coefficient(self, o):
- if o.is_cellwise_constant():
+ if is_cellwise_constant(o):
return self.independent_terminal(o)
return Grad(o)
@@ -475,12 +504,37 @@ class GradRuleset(GenericDerivativeRuleset):
return Grad(o)
def _argument(self, o): # TODO: Enable this after fixing issue#13, unless we move simplification to a separate stage?
- if o.is_cellwise_constant():
+ if is_cellwise_constant(o):
# Collapse gradient of cellwise constant function to zero
return AnnotatedZero(o.ufl_shape + self._var_shape, arguments=(o,)) # TODO: Missing this type
else:
return Grad(o)
+ # --- Rules for values or derivatives in reference frame
+
+ def reference_value(self, o):
+ # grad(o) == grad(rv(f)) -> K_ji*rgrad(rv(f))_rj
+ f = o.ufl_operands[0]
+ ufl_assert(f._ufl_is_terminal_, "ReferenceValue can only wrap a terminal")
+ domain = f.ufl_domain()
+ K = JacobianInverse(domain)
+ r = indices(len(o.ufl_shape))
+ i, j = indices(2)
+ Do = as_tensor(K[j,i]*ReferenceGrad(o)[r + (j,)], r + (i,))
+ return Do
+
+ def reference_grad(self, o):
+ # grad(o) == grad(rgrad(rv(f))) -> K_ji*rgrad(rgrad(rv(f)))_rj
+ f = o.ufl_operands[0]
+ valid_operand = f._ufl_is_in_reference_frame_ or isinstance(f, (JacobianInverse, SpatialCoordinate))
+ ufl_assert(valid_operand, "ReferenceGrad can only wrap a reference frame type!")
+ domain = f.ufl_domain()
+ K = JacobianInverse(domain)
+ r = indices(len(o.ufl_shape))
+ i, j = indices(2)
+ Do = as_tensor(K[j,i]*ReferenceGrad(o)[r + (j,)], r + (i,))
+ return Do
+
# --- Nesting of gradients
def grad(self, o):
@@ -496,7 +550,7 @@ class GradRuleset(GenericDerivativeRuleset):
pass
# TODO: Not sure how to detect that gradient of f is cellwise constant.
# Can we trust element degrees?
- #if o.is_cellwise_constant():
+ #if is_cellwise_constant(o):
# return self.terminal(o)
# TODO: Maybe we can ask "f.has_derivatives_of_order(n)" to check
# if we should make a zero here?
@@ -507,6 +561,69 @@ class GradRuleset(GenericDerivativeRuleset):
facet_avg = GenericDerivativeRuleset.independent_operator
+class ReferenceGradRuleset(GenericDerivativeRuleset):
+ def __init__(self, topological_dimension):
+ GenericDerivativeRuleset.__init__(self, var_shape=(topological_dimension,))
+ self._Id = Identity(topological_dimension)
+
+ # --- Specialized rules for geometric quantities
+
+ def geometric_quantity(self, o):
+ "dg/dX = 0 if piecewise constant, otherwise ReferenceGrad(g)"
+ if is_cellwise_constant(o):
+ return self.independent_terminal(o)
+ else:
+ # TODO: Which types does this involve? I don't think the form compilers will handle this.
+ return ReferenceGrad(o)
+
+ def spatial_coordinate(self, o):
+ "dx/dX = J"
+ # Don't convert back to J, otherwise we get in a loop
+ return ReferenceGrad(o)
+
+ def cell_coordinate(self, o):
+ "dX/dX = I"
+ return self._Id
+
+ # TODO: Add more geometry types here, with non-affine domains several should be non-zero.
+
+ # --- Specialized rules for form arguments
+
+ def coefficient(self, o):
+ error("Coefficient should be wrapped in ReferenceValue by now")
+
+ def argument(self, o):
+ error("Argument should be wrapped in ReferenceValue by now")
+
+ def reference_value(self, o):
+ ufl_assert(o.ufl_operands[0]._ufl_is_terminal_, "ReferenceValue can only wrap a terminal")
+ return ReferenceGrad(o)
+
+ def _argument(self, o): # TODO: Enable this after fixing issue#13, unless we move simplification to a separate stage?
+ if is_cellwise_constant(o):
+ # Collapse gradient of cellwise constant function to zero
+ return AnnotatedZero(o.ufl_shape + self._var_shape, arguments=(o,)) # TODO: Missing this type
+ else:
+ return ReferenceGrad(o)
+
+ # --- Nesting of gradients
+
+ def grad(self, o):
+ error("Grad should have been transformed by this point, but got {0}.".format(type(o).__name__))
+
+ def reference_grad(self, o):
+ "Represent ref_grad(ref_grad(f)) as RefGrad(RefGrad(f))."
+
+ # Check that o is a "differential terminal"
+ ufl_assert(isinstance(o.ufl_operands[0], (ReferenceGrad, ReferenceValue, Terminal)),
+ "Expecting only grads applied to a terminal.")
+
+ return ReferenceGrad(o)
+
+ cell_avg = GenericDerivativeRuleset.independent_operator
+ facet_avg = GenericDerivativeRuleset.independent_operator
+
+
class VariableRuleset(GenericDerivativeRuleset):
def __init__(self, var):
GenericDerivativeRuleset.__init__(self, var_shape=var.ufl_shape)
@@ -580,6 +697,30 @@ class VariableRuleset(GenericDerivativeRuleset):
"Expecting only grads applied to a terminal.")
return self.independent_terminal(o)
+ # --- Rules for values or derivatives in reference frame
+
+ def reference_value(self, o):
+ # d/dv(o) == d/dv(rv(f)) = 0 if v is not f, or rv(dv/df)
+ v = self._variable
+ if isinstance(v, Coefficient) and o.ufl_operands[0] == v:
+ if v.ufl_element().mapping() != "identity":
+ # FIXME: This is a bit tricky, instead of Identity it is
+ # actually inverse(transform), or we should rather not
+ # convert to reference frame in the first place
+ error("Missing implementation: To handle derivatives of rv(f) w.r.t. f for" +
+ " mapped elements, rewriting to reference frame should not happen first...")
+ # dv/dv = identity of rank 2*rank(v)
+ return self._Id
+ else:
+ # df/v = 0
+ return self.independent_terminal(o)
+
+ def reference_grad(self, o):
+ "Variable derivative of a gradient of a terminal must be 0."
+ ufl_assert(isinstance(o.ufl_operands[0], (ReferenceGrad, ReferenceValue)),
+ "Unexpected argument to reference_grad.")
+ return self.independent_terminal(o)
+
cell_avg = GenericDerivativeRuleset.independent_operator
facet_avg = GenericDerivativeRuleset.independent_operator
@@ -611,6 +752,14 @@ class GateauxDerivativeRuleset(GenericDerivativeRuleset):
# Explicitly defining dg/dw == 0
geometric_quantity = GenericDerivativeRuleset.independent_terminal
+ def cell_avg(self, o, fp):
+ # Cell average of a single function and differentiation commutes, D_f[v](cell_avg(f)) = cell_avg(v)
+ return cell_avg(fp)
+
+ def facet_avg(self, o, fp):
+ # Facet average of a single function and differentiation commutes, D_f[v](facet_avg(f)) = facet_avg(v)
+ return facet_avg(fp)
+
# Explicitly defining da/dw == 0
argument = GenericDerivativeRuleset.independent_terminal
@@ -657,13 +806,23 @@ class GateauxDerivativeRuleset(GenericDerivativeRuleset):
dosum += prod
return dosum
- def cell_avg(self, o, fp):
- # Cell average of a single function and differentiation commutes, D_f[v](cell_avg(f)) = cell_avg(v)
- return cell_avg(fp)
-
- def facet_avg(self, o, fp):
- # Facet average of a single function and differentiation commutes, D_f[v](facet_avg(f)) = facet_avg(v)
- return cell_avg(fp)
+ def reference_value(self, o):
+ error("Currently no support for ReferenceValue in CoefficientDerivative.")
+ # TODO: This is implementable for regular derivative(M(f),f,v) but too messy
+ # if customized coefficient derivative relations are given by the user.
+ # We would only need this to allow the user to write derivative(...ReferenceValue...,...).
+ #f, = o.ufl_operands
+ #ufl_assert(f._ufl_is_terminal_, "ReferenceValue can only wrap terminals directly.")
+ #if f is w: # FIXME: check all cases like in coefficient
+ # return ReferenceValue(v) # FIXME: requires that v is an Argument with the same element mapping!
+ #else:
+ # return self.independent_terminal(o)
+
+ def reference_grad(self, o):
+ error("Currently no support for ReferenceGrad in CoefficientDerivative.")
+ # TODO: This is implementable for regular derivative(M(f),f,v) but too messy
+ # if customized coefficient derivative relations are given by the user.
+ # We would only need this to allow the user to write derivative(...ReferenceValue...,...).
def grad(self, g):
# If we hit this type, it has already been propagated
@@ -728,7 +887,8 @@ class GateauxDerivativeRuleset(GenericDerivativeRuleset):
# Analyse differentiation variable coefficient
if isinstance(w, FormArgument):
- if not w == o: continue
+ if not w == o:
+ continue
wshape = w.ufl_shape
if isinstance(v, FormArgument):
@@ -780,7 +940,7 @@ class GateauxDerivativeRuleset(GenericDerivativeRuleset):
# Make sure we have a tuple to match the self._v tuple
if not isinstance(oprimes, tuple):
oprimes = (oprimes,)
- ufl_assert(len(oprimes) == len(self._v), "Got a tuple of arguments, "+\
+ ufl_assert(len(oprimes) == len(self._v), "Got a tuple of arguments, "+
"expecting a matching tuple of coefficient derivatives.")
# Compute dg/dw_j = dg/dw_h : v.
@@ -818,6 +978,10 @@ class DerivativeRuleDispatcher(MultiFunction):
rules = GradRuleset(o.ufl_shape[-1])
return map_expr_dag(rules, f)
+ def reference_grad(self, o, f):
+ rules = ReferenceGradRuleset(o.ufl_shape[-1]) # FIXME: Look over this and test better.
+ return map_expr_dag(rules, f)
+
def variable_derivative(self, o, f, dummy_v):
rules = VariableRuleset(o.ufl_operands[1])
return map_expr_dag(rules, f)
@@ -846,7 +1010,7 @@ class DerivativeRuleDispatcher(MultiFunction):
return Indexed(C, MultiIndex(tuple(Cind)))
# Otherwise a more generic approach
- r = Ap.rank() - len(ii)
+ r = len(Ap.ufl_shape) - len(ii)
if r:
kk = indices(r)
op = Indexed(Ap, MultiIndex(ii.indices() + kk))
diff --git a/ufl/algorithms/apply_function_pullbacks.py b/ufl/algorithms/apply_function_pullbacks.py
new file mode 100644
index 0000000..9a90ee2
--- /dev/null
+++ b/ufl/algorithms/apply_function_pullbacks.py
@@ -0,0 +1,226 @@
+# -*- coding: utf-8 -*-
+"""Algorithm for replacing gradients in an expression with reference gradients and coordinate mappings."""
+
+# Copyright (C) 2008-2015 Martin Sandve Alnæs
+#
+# This file is part of UFL.
+#
+# UFL is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# UFL is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# 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 xrange as range
+
+from ufl.log import error
+from ufl.assertions import ufl_assert
+
+from ufl.core.multiindex import indices
+from ufl.corealg.multifunction import MultiFunction, memoized_handler
+from ufl.algorithms.map_integrands import map_integrand_dags
+
+from ufl.classes import (ReferenceValue,
+ Jacobian, JacobianInverse, JacobianDeterminant,
+ Index)
+
+from ufl.constantvalue import as_ufl, Identity
+from ufl.tensors import as_tensor, as_vector
+
+from ufl.finiteelement import (FiniteElement, EnrichedElement, VectorElement, MixedElement,
+ OuterProductElement, TensorElement,
+ FacetElement, InteriorElement, BrokenElement, TraceElement)
+from ufl.utils.sequences import product
+
+def sub_elements_with_mappings(element):
+ "Return an ordered list of the largest subelements that have a defined mapping."
+ if element.mapping() != "undefined":
+ return [element]
+ elements = []
+ for subelm in element.sub_elements():
+ if subelm.mapping() != "undefined":
+ elements.append(subelm)
+ else:
+ elements.extend(sub_elements_with_mappings(subelm))
+ return elements
+
+def create_nested_lists(shape):
+ if len(shape) == 0:
+ return [None]
+ elif len(shape) == 1:
+ return [None]*shape[0]
+ else:
+ return [create_nested_lists(shape[1:]) for i in range(shape[0])]
+
+def reshape_to_nested_list(components, shape):
+ if len(shape) == 0:
+ assert len(components) == 1
+ return [components[0]]
+ elif len(shape) == 1:
+ assert len(components) == shape[0]
+ return components
+ else:
+ n = product(shape[1:])
+ return [reshape_to_nested_list(components[n*i:n*(i+1)], shape[1:]) for i in range(shape[0])]
+
+def apply_single_function_pullbacks(g):
+ element = g.ufl_element()
+ mapping = element.mapping()
+
+ r = ReferenceValue(g)
+
+ gsh = g.ufl_shape
+ rsh = r.ufl_shape
+
+ # Shortcut the "identity" case which includes Expression and Constant from dolfin that may be ill-formed without a domain (until we get that fixed)
+ if mapping == "identity":
+ assert rsh == gsh
+ return r
+
+ gsize = product(gsh)
+ rsize = product(rsh)
+
+ # Create some geometric objects for reuse
+ domain = g.ufl_domain()
+ J = Jacobian(domain)
+ detJ = JacobianDeterminant(domain)
+ Jinv = JacobianInverse(domain)
+
+ tdim = domain.topological_dimension()
+ gdim = domain.geometric_dimension()
+
+ # Create contravariant transform for reuse
+ # (note that detJ is the _signed_ (pseudo-)determinant)
+ transform_hdiv = (1.0/detJ) * J
+
+ # Shortcut simple cases for a more efficient representation,
+ # including directly Piola-mapped elements and mixed elements
+ # of any combination of affinely mapped elements without symmetries
+ if mapping == "symmetries":
+ fcm = element.flattened_sub_element_mapping()
+ assert gsize >= rsize
+ assert len(fcm) == gsize
+ assert sorted(set(fcm)) == sorted(range(rsize))
+ g_components = [r[fcm[i]] for i in range(gsize)]
+ g_components = reshape_to_nested_list(g_components, gsh)
+ f = as_tensor(g_components)
+ assert f.ufl_shape == g.ufl_shape
+ return f
+ elif mapping == "contravariant Piola":
+ assert transform_hdiv.ufl_shape == (gsize, rsize)
+ i, j = indices(2)
+ f = as_vector(transform_hdiv[i, j]*r[j], i)
+ #f = as_tensor(transform_hdiv[i, j]*r[k,j], (k,i)) # FIXME: Handle Vector(Piola) here?
+ assert f.ufl_shape == g.ufl_shape
+ return f
+ elif mapping == "covariant Piola":
+ assert Jinv.ufl_shape == (rsize, gsize)
+ i, j = indices(2)
+ f = as_vector(Jinv[j, i]*r[j], i)
+ #f = as_tensor(Jinv[j, i]*r[k,j], (k,i)) # FIXME: Handle Vector(Piola) here?
+ assert f.ufl_shape == g.ufl_shape
+ return f
+
+
+ # By placing components in a list and using as_vector at the end, we're
+ # assuming below that both global function g and its reference value r
+ # have vector shape, which is the case for most elements with the exceptions:
+ # - TensorElements
+ # - All cases with scalar subelements and without symmetries are covered by the shortcut above
+ # (ONLY IF REFERENCE VALUE SHAPE PRESERVES TENSOR RANK)
+ # - All cases with scalar subelements and without symmetries are covered by the shortcut above
+ # - VectorElements of vector-valued basic elements (FIXME)
+ # - TensorElements with symmetries (FIXME)
+ # - Tensor-valued FiniteElements (the new Regge elements)
+ assert len(gsh) == 1
+ assert len(rsh) == 1
+
+ g_components = [None]*gsize
+ gpos = 0
+ rpos = 0
+ for subelm in sub_elements_with_mappings(element):
+ gm = product(subelm.value_shape())
+ rm = product(subelm.reference_value_shape())
+
+ mp = subelm.mapping()
+ if mp == "identity":
+ assert gm == rm
+ for i in range(gm):
+ g_components[gpos + i] = r[rpos + i]
+
+ elif mp == "symmetries":
+ """
+ tensor_element.value_shape() == (2,2)
+ tensor_element.reference_value_shape() == (3,)
+ tensor_element.symmetry() == { (1,0): (0,1) }
+ tensor_element.component_mapping() == { (0,0): 0, (0,1): 1, (1,0): 1, (1,1): 2 }
+ tensor_element.flattened_component_mapping() == { 0: 0, 1: 1, 2: 1, 3: 2 }
+ """
+ fcm = subelm.flattened_sub_element_mapping()
+ assert gm >= rm
+ assert len(fcm) == gm
+ assert sorted(set(fcm)) == sorted(range(rm))
+ for i in range(gm):
+ g_components[gpos + i] = r[rpos + fcm[i]]
+
+ elif mp == "contravariant Piola":
+ assert transform_hdiv.ufl_shape == (gm, rm)
+ # Get reference value vector corresponding to this subelement:
+ rv = as_vector([r[rpos+k] for k in range(rm)])
+ # Apply transform with IndexSum over j for each row
+ j = Index()
+ for i in range(gm):
+ g_components[gpos + i] = transform_hdiv[i, j]*rv[j]
+
+ elif mp == "covariant Piola":
+ assert Jinv.ufl_shape == (rm, gm)
+ # Get reference value vector corresponding to this subelement:
+ rv = as_vector([r[rpos+k] for k in range(rm)])
+ # Apply transform with IndexSum over j for each row
+ j = Index()
+ for i in range(gm):
+ g_components[gpos + i] = Jinv[j, i]*rv[j]
+
+ else:
+ error("Unknown subelement mapping type %s for element %s." % (mp, str(subelm)))
+
+ gpos += gm
+ rpos += rm
+
+ # Wrap up components in a vector, must return same shape as input function g
+ assert len(gsh) == 1
+ f = as_vector(g_components)
+ assert f.ufl_shape == g.ufl_shape
+ return f
+
+
+class FunctionPullbackApplier(MultiFunction):
+ def __init__(self):
+ MultiFunction.__init__(self)
+
+ expr = MultiFunction.reuse_if_untouched
+
+ def terminal(self, t):
+ return t
+
+ @memoized_handler
+ def form_argument(self, o):
+ # Represent 0-derivatives of form arguments on reference element
+ return apply_single_function_pullbacks(o)
+
+def apply_function_pullbacks(expr):
+ """Change representation of coefficients and arguments in expression
+ by applying Piola mappings where applicable and representing all
+ form arguments in reference value.
+
+ @param expr:
+ An Expr.
+ """
+ return map_integrand_dags(FunctionPullbackApplier(), expr)
diff --git a/ufl/algorithms/apply_geometry_lowering.py b/ufl/algorithms/apply_geometry_lowering.py
new file mode 100644
index 0000000..cbd642e
--- /dev/null
+++ b/ufl/algorithms/apply_geometry_lowering.py
@@ -0,0 +1,482 @@
+# -*- coding: utf-8 -*-
+"""Algorithm for lowering abstractions of geometric types.
+
+This means replacing high-level types with expressions
+of mostly the Jacobian and reference cell data.
+"""
+
+# Copyright (C) 2013-2015 Martin Sandve Alnæs
+#
+# This file is part of UFL.
+#
+# UFL is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# UFL is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# 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 xrange as range
+
+from ufl.log import error, warning
+from ufl.assertions import ufl_assert
+
+from ufl.core.multiindex import Index, indices
+from ufl.corealg.multifunction import MultiFunction, memoized_handler
+from ufl.corealg.map_dag import map_expr_dag
+
+from ufl.classes import (Expr, Form, Integral,
+ ReferenceGrad, ReferenceValue,
+ Jacobian, JacobianInverse, JacobianDeterminant,
+ CellOrientation, CellOrigin, CellCoordinate,
+ FacetJacobian, FacetJacobianDeterminant,
+ CellFacetJacobian,
+ CellEdgeVectors, FacetEdgeVectors,
+ FacetNormal, CellNormal, ReferenceNormal,
+ ReferenceCellVolume, ReferenceFacetVolume,
+ CellVolume, FacetArea,
+ SpatialCoordinate)
+#FacetJacobianInverse,
+#FacetOrientation, QuadratureWeight,
+
+from ufl.tensors import as_tensor, as_vector
+from ufl.operators import sqrt, max_value, min_value
+
+from ufl.compound_expressions import determinant_expr, cross_expr, inverse_expr
+
+
+class GeometryLoweringApplier(MultiFunction):
+ def __init__(self, preserve_types=()):
+ MultiFunction.__init__(self)
+ # Store preserve_types as boolean lookup table
+ self._preserve_types = [False]*Expr._ufl_num_typecodes_
+ for cls in preserve_types:
+ self._preserve_types[cls._ufl_typecode_] = True
+
+ expr = MultiFunction.reuse_if_untouched
+
+ def terminal(self, t):
+ return t
+
+ @memoized_handler
+ def jacobian(self, o):
+ if self._preserve_types[o._ufl_typecode_]:
+ return o
+ domain = o.ufl_domain()
+ if domain.ufl_coordinate_element().mapping() != "identity":
+ error("Piola mapped coordinates are not implemented.")
+ # Note: No longer supporting domain.coordinates(), always preserving SpatialCoordinate object.
+ # However if Jacobians are not preserved, using ReferenceGrad(SpatialCoordinate(domain)) to represent them.
+ x = self.spatial_coordinate(SpatialCoordinate(domain))
+ return ReferenceGrad(x)
+
+ @memoized_handler
+ def _future_jacobian(self, o):
+ # If we're not using Coefficient to represent the spatial coordinate,
+ # we can just as well just return o here too unless we add representation
+ # of basis functions and dofs to the ufl layer (which is nice to avoid).
+ return o
+
+ @memoized_handler
+ def jacobian_inverse(self, o):
+ if self._preserve_types[o._ufl_typecode_]:
+ return o
+
+ domain = o.ufl_domain()
+ J = self.jacobian(Jacobian(domain))
+ # TODO: This could in principle use preserve_types[JacobianDeterminant] with minor refactoring:
+ K = inverse_expr(J)
+ return K
+
+ @memoized_handler
+ def jacobian_determinant(self, o):
+ if self._preserve_types[o._ufl_typecode_]:
+ return o
+
+ domain = o.ufl_domain()
+ J = self.jacobian(Jacobian(domain))
+ detJ = determinant_expr(J)
+
+ # TODO: Is "signing" the determinant for manifolds the cleanest approach?
+ # The alternative is to have a specific type for the unsigned pseudo-determinant.
+ if domain.topological_dimension() < domain.geometric_dimension():
+ co = CellOrientation(domain)
+ detJ = co*detJ
+
+ return detJ
+
+ @memoized_handler
+ def facet_jacobian(self, o):
+ if self._preserve_types[o._ufl_typecode_]:
+ return o
+
+ domain = o.ufl_domain()
+ J = self.jacobian(Jacobian(domain))
+ RFJ = CellFacetJacobian(domain)
+ i, j, k = indices(3)
+ return as_tensor(J[i, k]*RFJ[k, j], (i, j))
+
+ @memoized_handler
+ def facet_jacobian_inverse(self, o):
+ if self._preserve_types[o._ufl_typecode_]:
+ return o
+
+ domain = o.ufl_domain()
+ FJ = self.facet_jacobian(FacetJacobian(domain))
+ # This could in principle use preserve_types[JacobianDeterminant] with minor refactoring:
+ return inverse_expr(FJ)
+
+ @memoized_handler
+ def facet_jacobian_determinant(self, o):
+ if self._preserve_types[o._ufl_typecode_]:
+ return o
+
+ domain = o.ufl_domain()
+ FJ = self.facet_jacobian(FacetJacobian(domain))
+ detFJ = determinant_expr(FJ)
+
+ # TODO: Should we "sign" the facet jacobian determinant for manifolds?
+ # It's currently used unsigned in apply_integral_scaling.
+ #if domain.topological_dimension() < domain.geometric_dimension():
+ # co = CellOrientation(domain)
+ # detFJ = co*detFJ
+
+ return detFJ
+
+ @memoized_handler
+ def spatial_coordinate(self, o):
+ "Fall through to coordinate field of domain if it exists."
+ if self._preserve_types[o._ufl_typecode_]:
+ return o
+ if o.ufl_domain().ufl_coordinate_element().mapping() != "identity":
+ error("Piola mapped coordinates are not implemented.")
+ # No longer supporting domain.coordinates(), always preserving SpatialCoordinate object.
+ return o
+
+ @memoized_handler
+ def cell_coordinate(self, o):
+ "Compute from physical coordinates if they are known, using the appropriate mappings."
+ if self._preserve_types[o._ufl_typecode_]:
+ return o
+
+ domain = o.ufl_domain()
+ K = self.jacobian_inverse(JacobianInverse(domain))
+ x = self.spatial_coordinate(SpatialCoordinate(domain))
+ x0 = CellOrigin(domain)
+ i, j = indices(2)
+ X = as_tensor(K[i, j] * (x[j] - x0[j]), (i,))
+ return X
+
+ @memoized_handler
+ def facet_cell_coordinate(self, o):
+ if self._preserve_types[o._ufl_typecode_]:
+ return o
+
+ error("Missing computation of facet reference coordinates "
+ "from physical coordinates via mappings.")
+
+ @memoized_handler
+ def cell_volume(self, o):
+ if self._preserve_types[o._ufl_typecode_]:
+ return o
+
+ domain = o.ufl_domain()
+ if not domain.is_piecewise_linear_simplex_domain():
+ # Don't lower for non-affine cells, instead leave it to form compiler
+ warning("Only know how to compute the cell volume of an affine cell.")
+ return o
+
+ r = self.jacobian_determinant(JacobianDeterminant(domain))
+ r0 = ReferenceCellVolume(domain)
+ return abs(r * r0)
+
+ @memoized_handler
+ def facet_area(self, o):
+ if self._preserve_types[o._ufl_typecode_]:
+ return o
+
+ domain = o.ufl_domain()
+ if not domain.is_piecewise_linear_simplex_domain():
+ # Don't lower for non-affine cells, instead leave it to form compiler
+ warning("Only know how to compute the facet area of an affine cell.")
+ return o
+
+ r = self.facet_jacobian_determinant(FacetJacobianDeterminant(domain))
+ r0 = ReferenceFacetVolume(domain)
+ return abs(r * r0)
+
+ @memoized_handler
+ def circumradius(self, o):
+ if self._preserve_types[o._ufl_typecode_]:
+ return o
+
+ domain = o.ufl_domain()
+ if not domain.is_piecewise_linear_simplex_domain():
+ # Don't lower for non-affine cells, instead leave it to form compiler
+ warning("Only know how to compute the circumradius of an affine cell.")
+ return o
+
+ cellname = domain.ufl_cell().cellname()
+ cellvolume = self.cell_volume(CellVolume(domain))
+
+ if cellname == "interval":
+ r = 0.5 * cellvolume
+
+ elif cellname == "triangle":
+ J = self.jacobian(Jacobian(domain))
+ trev = CellEdgeVectors(domain)
+ num_edges = 3
+ i, j, k = indices(3)
+ elen = [sqrt((J[i, j]*trev[edge, j])*(J[i, k]*trev[edge, k]))
+ for edge in range(num_edges)]
+
+ r = (elen[0] * elen[1] * elen[2]) / (4.0 * cellvolume)
+
+ elif cellname == "tetrahedron":
+ J = self.jacobian(Jacobian(domain))
+ trev = CellEdgeVectors(domain)
+ num_edges = 6
+ i, j, k = indices(3)
+ elen = [sqrt((J[i, j]*trev[edge, j])*(J[i, k]*trev[edge, k]))
+ for edge in range(num_edges)]
+
+ # elen[3] = length of edge 3
+ # la, lb, lc = lengths of the sides of an intermediate triangle
+ la = elen[3] * elen[2]
+ lb = elen[4] * elen[1]
+ lc = elen[5] * elen[0]
+ # p = perimeter
+ p = (la + lb + lc)
+ # s = semiperimeter
+ s = p / 2
+ # area of intermediate triangle with Herons formula
+ triangle_area = sqrt(s * (s - la) * (s - lb) * (s - lc))
+ r = triangle_area / (6.0 * cellvolume)
+
+ else:
+ error("Unhandled cell type %s." % cellname)
+
+ return r
+
+ @memoized_handler
+ def min_cell_edge_length(self, o):
+ if self._preserve_types[o._ufl_typecode_]:
+ return o
+
+ domain = o.ufl_domain()
+ if not domain.is_piecewise_linear_simplex_domain():
+ # Don't lower for non-affine cells, instead leave it to form compiler
+ warning("Only know how to compute the min_cell_edge_length of an affine cell.")
+ return o
+
+ cellname = domain.ufl_cell().cellname()
+
+ J = self.jacobian(Jacobian(domain))
+ trev = CellEdgeVectors(domain)
+ num_edges = trev.ufl_shape[0]
+ i, j, k = indices(3)
+ elen = [sqrt((J[i, j]*trev[edge, j])*(J[i, k]*trev[edge, k]))
+ for edge in range(num_edges)]
+
+ if cellname == "triangle":
+ return min_value(elen[0], min_value(elen[1], elen[2]))
+ elif cellname == "tetrahedron":
+ min1 = min_value(elen[0], min_value(elen[1], elen[2]))
+ min2 = min_value(elen[3], min_value(elen[4], elen[5]))
+ return min_value(min1, min2)
+ else:
+ error("Unhandled cell type %s." % cellname)
+
+ @memoized_handler
+ def max_cell_edge_length(self, o):
+ if self._preserve_types[o._ufl_typecode_]:
+ return o
+
+ domain = o.ufl_domain()
+ if not domain.is_piecewise_linear_simplex_domain():
+ # Don't lower for non-affine cells, instead leave it to form compiler
+ warning("Only know how to compute the max_cell_edge_length of an affine cell.")
+ return o
+
+ cellname = domain.ufl_cell().cellname()
+
+ J = self.jacobian(Jacobian(domain))
+ trev = CellEdgeVectors(domain)
+ num_edges = trev.ufl_shape[0]
+ i, j, k = indices(3)
+ elen = [sqrt((J[i, j]*trev[edge, j])*(J[i, k]*trev[edge, k]))
+ for edge in range(num_edges)]
+
+ if cellname == "triangle":
+ return max_value(elen[0], max_value(elen[1], elen[2]))
+ elif cellname == "tetrahedron":
+ max1 = max_value(elen[0], max_value(elen[1], elen[2]))
+ max2 = max_value(elen[3], max_value(elen[4], elen[5]))
+ return max_value(max1, max2)
+ else:
+ error("Unhandled cell type %s." % cellname)
+
+ @memoized_handler
+ def min_facet_edge_length(self, o):
+ if self._preserve_types[o._ufl_typecode_]:
+ return o
+
+ domain = o.ufl_domain()
+ if not domain.is_piecewise_linear_simplex_domain():
+ # Don't lower for non-affine cells, instead leave it to form compiler
+ warning("Only know how to compute the min_facet_edge_length of an affine cell.")
+ return o
+
+ cellname = domain.ufl_cell().cellname()
+
+ if cellname == "triangle":
+ return self.facet_area(FacetArea(domain))
+ elif cellname == "tetrahedron":
+ J = self.jacobian(Jacobian(domain))
+ trev = FacetEdgeVectors(domain)
+ num_edges = 3
+ i, j, k = indices(3)
+ elen = [sqrt((J[i, j]*trev[edge, j])*(J[i, k]*trev[edge, k]))
+ for edge in range(num_edges)]
+ return min_value(elen[0], min_value(elen[1], elen[2]))
+ else:
+ error("Unhandled cell type %s." % cellname)
+
+ @memoized_handler
+ def max_facet_edge_length(self, o):
+ if self._preserve_types[o._ufl_typecode_]:
+ return o
+
+ domain = o.ufl_domain()
+ if not domain.is_piecewise_linear_simplex_domain():
+ # Don't lower for non-affine cells, instead leave it to form compiler
+ warning("Only know how to compute the max_facet_edge_length of an affine cell.")
+ return o
+
+ cellname = domain.ufl_cell().cellname()
+
+ if cellname == "triangle":
+ return self.facet_area(FacetArea(domain))
+ elif cellname == "tetrahedron":
+ J = self.jacobian(Jacobian(domain))
+ trev = FacetEdgeVectors(domain)
+ num_edges = 3
+ i, j, k = indices(3)
+ elen = [sqrt((J[i, j]*trev[edge, j])*(J[i, k]*trev[edge, k]))
+ for edge in range(num_edges)]
+ return max_value(elen[0], max_value(elen[1], elen[2]))
+ else:
+ error("Unhandled cell type %s." % cellname)
+
+ @memoized_handler
+ def cell_normal(self, o):
+ if self._preserve_types[o._ufl_typecode_]:
+ return o
+
+ domain = o.ufl_domain()
+ gdim = domain.geometric_dimension()
+ tdim = domain.topological_dimension()
+
+ if tdim == gdim - 1: # n-manifold embedded in n-1 space
+ i = Index()
+ J = self.jacobian(Jacobian(domain))
+
+ if tdim == 2:
+ # Surface in 3D
+ t0 = as_vector(J[i, 0], i)
+ t1 = as_vector(J[i, 1], i)
+ cell_normal = cross_expr(t0, t1)
+ elif tdim == 1:
+ # Line in 2D (cell normal is 'up' for a line pointing to the 'right')
+ cell_normal = as_vector((-J[1, 0], J[0, 0]))
+ else:
+ error("Cell normal not implemented for tdim %d, gdim %d" % (tdim, gdim))
+
+ # Return normalized vector, sign corrected by cell orientation
+ co = CellOrientation(domain)
+ return co * cell_normal / sqrt(cell_normal[i]*cell_normal[i])
+ else:
+ error("What do you want cell normal in gdim={0}, tdim={1} to be?".format(gdim, tdim))
+
+ @memoized_handler
+ def facet_normal(self, o):
+ if self._preserve_types[o._ufl_typecode_]:
+ return o
+
+ domain = o.ufl_domain()
+ tdim = domain.topological_dimension()
+
+ if tdim == 1:
+ # Special-case 1D (possibly immersed), for which we say that
+ # n is just in the direction of J.
+ J = self.jacobian(Jacobian(domain)) # dx/dX
+ ndir = J[:, 0]
+
+ gdim = domain.geometric_dimension()
+ if gdim == 1:
+ nlen = abs(ndir[0])
+ else:
+ i = Index()
+ nlen = sqrt(ndir[i]*ndir[i])
+
+ rn = ReferenceNormal(domain) # +/- 1.0 here
+ n = rn[0] * ndir / nlen
+ r = n
+ else:
+ # Recall that the covariant Piola transform u -> J^(-T)*u preserves
+ # tangential components. The normal vector is characterised by
+ # having zero tangential component in reference and physical space.
+ Jinv = self.jacobian_inverse(JacobianInverse(domain))
+ i, j = indices(2)
+
+ rn = ReferenceNormal(domain)
+ # compute signed, unnormalised normal; note transpose
+ ndir = as_vector(Jinv[j, i] * rn[j], i)
+
+ # normalise
+ i = Index()
+ n = ndir / sqrt(ndir[i]*ndir[i])
+ r = n
+
+ ufl_assert(r.ufl_shape == o.ufl_shape,
+ "Inconsistent dimensions (in=%d, out=%d)." % (o.ufl_shape[0], r.ufl_shape[0]))
+ return r
+
+def apply_geometry_lowering(form, preserve_types=()):
+ """Change GeometricQuantity objects in expression to the lowest level GeometricQuantity objects.
+
+ Assumes the expression is preprocessed or at least that derivatives have been expanded.
+
+ @param form:
+ An Expr or Form.
+ """
+ if isinstance(form, Form):
+ newintegrals = [apply_geometry_lowering(integral, preserve_types)
+ for integral in form.integrals()]
+ return Form(newintegrals)
+
+ elif isinstance(form, Integral):
+ integral = form
+ if integral.integral_type() in ("custom", "vertex"):
+ automatic_preserve_types = [SpatialCoordinate, Jacobian]
+ else:
+ automatic_preserve_types = [CellCoordinate]
+ preserve_types = set(preserve_types) | set(automatic_preserve_types)
+
+ mf = GeometryLoweringApplier(preserve_types)
+ newintegrand = map_expr_dag(mf, integral.integrand())
+ return integral.reconstruct(integrand=newintegrand)
+
+ elif isinstance(form, Expr):
+ expr = form
+ mf = GeometryLoweringApplier(preserve_types)
+ return map_expr_dag(mf, expr)
+
+ else:
+ error("Invalid type %s" % (form.__class__.__name__,))
diff --git a/ufl/algorithms/apply_integral_scaling.py b/ufl/algorithms/apply_integral_scaling.py
new file mode 100644
index 0000000..f36b02e
--- /dev/null
+++ b/ufl/algorithms/apply_integral_scaling.py
@@ -0,0 +1,89 @@
+# -*- coding: utf-8 -*-
+"""Algorithm for replacing gradients in an expression with reference gradients and coordinate mappings."""
+
+# Copyright (C) 2013-2015 Martin Sandve Alnæs
+#
+# This file is part of UFL.
+#
+# UFL is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# UFL is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# 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 xrange as range
+
+from ufl.log import error, warning
+from ufl.assertions import ufl_assert
+
+from ufl.classes import JacobianDeterminant, FacetJacobianDeterminant, QuadratureWeight, Form, Integral
+
+
+def compute_integrand_scaling_factor(integral):
+ """Change integrand geometry to the right representations."""
+
+ domain = integral.ufl_domain()
+ integral_type = integral.integral_type()
+ #co = CellOrientation(domain)
+ weight = QuadratureWeight(domain)
+ tdim = domain.topological_dimension()
+ #gdim = domain.geometric_dimension()
+
+ if integral_type == "cell":
+ scale = abs(JacobianDeterminant(domain)) * weight
+
+ elif integral_type.startswith("exterior_facet"):
+ if tdim > 1:
+ # Scaling integral by facet jacobian determinant and quadrature weight
+ scale = FacetJacobianDeterminant(domain) * weight
+ else:
+ # No need to scale 'integral' over a vertex
+ scale = 1
+
+ elif integral_type.startswith("interior_facet"):
+ if tdim > 1:
+ # Scaling integral by facet jacobian determinant from one side and quadrature weight
+ scale = FacetJacobianDeterminant(domain)('+') * weight
+ else:
+ # No need to scale 'integral' over a vertex
+ scale = 1
+
+ elif integral_type in ("custom", "interface", "overlap", "cutcell"):
+ # Scaling with custom weight, which includes eventual volume scaling
+ scale = weight
+
+ elif integral_type in ("vertex", "point"):
+ # No need to scale 'integral' over a point
+ scale = 1
+
+ else:
+ error("Unknown integral type {}, don't know how to scale.".format(integral_type))
+
+ return scale
+
+
+def apply_integral_scaling(form):
+ "Multiply integrands by a factor to scale the integral to reference frame."
+ # TODO: Consider adding an in_reference_frame property to Integral
+ # and checking it here and setting it in the returned form
+ if isinstance(form, Form):
+ newintegrals = [apply_integral_scaling(integral)
+ for integral in form.integrals()]
+ return Form(newintegrals)
+
+ elif isinstance(form, Integral):
+ integral = form
+ # Compute and apply integration scaling factor
+ scale = compute_integrand_scaling_factor(integral)
+ newintegrand = integral.integrand() * scale
+ return integral.reconstruct(integrand=newintegrand)
+
+ else:
+ error("Invalid type %s" % (form.__class__.__name__,))
diff --git a/ufl/algorithms/apply_restrictions.py b/ufl/algorithms/apply_restrictions.py
index 5246cfb..34c02c5 100644
--- a/ufl/algorithms/apply_restrictions.py
+++ b/ufl/algorithms/apply_restrictions.py
@@ -1,6 +1,7 @@
+# -*- coding: utf-8 -*-
"""This module contains the apply_restrictions algorithm which propagates restrictions in a form towards the terminals."""
-# Copyright (C) 2008-2014 Martin Sandve Alnes
+# Copyright (C) 2008-2015 Martin Sandve Alnæs
#
# This file is part of UFL.
#
@@ -114,7 +115,7 @@ class RestrictionPropagator(MultiFunction):
def coefficient(self, o):
"Allow coefficients to be unrestricted (apply default if so) if the values are fully continuous across the facet."
- e = o.element()
+ e = o.ufl_element()
d = e.degree()
f = e.family()
# TODO: Move this choice to the element class?
@@ -125,8 +126,8 @@ class RestrictionPropagator(MultiFunction):
return self._require_restriction(o)
def facet_normal(self, o):
- D = o.domain()
- e = D.coordinate_element()
+ D = o.ufl_domain()
+ e = D.ufl_coordinate_element()
f = e.family()
d = e.degree()
gd = D.geometric_dimension()
@@ -142,6 +143,10 @@ class RestrictionPropagator(MultiFunction):
# For other meshes, we require a side to be chosen by the user and respect that
return self._require_restriction(o)
+ # Although the physical normal can be flipped when moving from
+ # + to - in some circumstances, the reference normal cannot.
+ reference_normal = _require_restriction
+
# Defaults for geometric quantities
geometric_cell_quantity = _missing_rule #_require_restriction
geometric_facet_quantity = _missing_rule #_ignore_restriction
@@ -165,6 +170,7 @@ class RestrictionPropagator(MultiFunction):
cell_facet_jacobian = _require_restriction # Depends on cell
cell_facet_jacobian_determinant = _require_restriction # ...
cell_facet_jacobian_inverse = _require_restriction # ...
+ cell_edge_vectors = _require_restriction # ...
cell_normal = _require_restriction # Property of cell
@@ -189,6 +195,7 @@ class RestrictionPropagator(MultiFunction):
def apply_restrictions(expression):
"Propagate restriction nodes to wrap differential terminals directly."
- integral_types = [k for k in integral_type_to_measure_name.keys() if k.startswith("interior_facet")]
+ integral_types = [k for k in integral_type_to_measure_name.keys()
+ if k.startswith("interior_facet")]
rules = RestrictionPropagator()
return map_integrand_dags(rules, expression, only_integral_type=integral_types)
diff --git a/ufl/algorithms/argument_dependencies.py b/ufl/algorithms/argument_dependencies.py
index 9195698..ee1ffce 100644
--- a/ufl/algorithms/argument_dependencies.py
+++ b/ufl/algorithms/argument_dependencies.py
@@ -1,6 +1,7 @@
+# -*- coding: utf-8 -*-
"""Algorithms for analysing argument dependencies in expressions."""
-# Copyright (C) 2008-2014 Martin Sandve Alnes and Anders Logg
+# Copyright (C) 2008-2015 Martin Sandve Alnæs and Anders Logg
#
# This file is part of UFL.
#
diff --git a/ufl/algorithms/change_to_reference.py b/ufl/algorithms/change_to_reference.py
index 472798f..055e6ff 100644
--- a/ufl/algorithms/change_to_reference.py
+++ b/ufl/algorithms/change_to_reference.py
@@ -1,6 +1,7 @@
+# -*- coding: utf-8 -*-
"""Algorithm for replacing gradients in an expression with reference gradients and coordinate mappings."""
-# Copyright (C) 2008-2014 Martin Sandve Alnes
+# Copyright (C) 2008-2015 Martin Sandve Alnæs
#
# This file is part of UFL.
#
@@ -23,43 +24,32 @@ from ufl.log import error, warning
from ufl.assertions import ufl_assert
from ufl.core.multiindex import Index, indices
-from ufl.corealg.multifunction import MultiFunction
+from ufl.corealg.multifunction import MultiFunction, memoized_handler
from ufl.corealg.map_dag import map_expr_dag
-from ufl.classes import (FormArgument, GeometricQuantity,
+from ufl.classes import (Expr, FormArgument, GeometricQuantity,
Terminal, ReferenceGrad, Grad, Restricted, ReferenceValue,
Jacobian, JacobianInverse, JacobianDeterminant,
FacetJacobian, FacetJacobianInverse, FacetJacobianDeterminant,
CellFacetJacobian,
CellEdgeVectors, FacetEdgeVectors,
- FacetNormal, CellNormal,
+ FacetNormal, CellNormal, ReferenceNormal,
CellVolume, FacetArea,
CellOrientation, FacetOrientation, QuadratureWeight,
- Indexed, MultiIndex, FixedIndex)
+ SpatialCoordinate, Indexed, MultiIndex, FixedIndex)
-from ufl.finiteelement import MixedElement
-
-from ufl.constantvalue import as_ufl
-from ufl.tensors import as_tensor, as_vector
-from ufl.operators import sqrt, max_value, min_value
+from ufl.constantvalue import as_ufl, Identity
+from ufl.tensoralgebra import Transposed
+from ufl.tensors import as_tensor, as_vector, as_scalar, ComponentTensor
+from ufl.operators import sqrt, max_value, min_value, sign
from ufl.permutation import compute_indices
-from ufl.algorithms.transformer import ReuseTransformer, apply_transformer
from ufl.compound_expressions import determinant_expr, cross_expr, inverse_expr
+from ufl.finiteelement import FiniteElement, EnrichedElement, VectorElement, MixedElement, OuterProductElement, TensorElement, FacetElement, InteriorElement, BrokenElement, TraceElement
-
-# TODO: Move to ufl.corealg.multifunction?
-def memoized_handler(handler, cachename="_cache"):
- def _memoized_handler(self, o):
- c = getattr(self, cachename)
- r = c.get(o)
- if r is None:
- r = handler(self, o)
- c[o] = r
- return r
- return _memoized_handler
-
-
+from ufl.algorithms.apply_function_pullbacks import apply_function_pullbacks
+from ufl.algorithms.apply_geometry_lowering import apply_geometry_lowering
+from ufl.checks import is_cellwise_constant
"""
# Some notes:
@@ -156,7 +146,7 @@ class NEWChangeToReferenceGrad(MultiFunction):
self._avg = ''
def expr(self, o, *ops):
- return o.reconstruct(*ops)
+ return o._ufl_expr_reconstruct_(*ops)
def terminal(self, o):
return o
@@ -230,7 +220,7 @@ class NEWChangeToReferenceGrad(MultiFunction):
error("Unexpected type {0}.".format(type(t).__name__))
# Some geometry mapping objects we may need multiple times below
- domain = t.domain()
+ domain = t.ufl_domain()
J = Jacobian(domain)
detJ = JacobianDeterminant(domain)
K = JacobianInverse(domain)
@@ -286,9 +276,9 @@ class NEWChangeToReferenceGrad(MultiFunction):
if isinstance(t, FormArgument):
# Find basic subelement and element-local component
- #ec, element, eoffset = t.element().extract_component2(gtc) # FIXME: Translate this correctly
+ #ec, element, eoffset = t.ufl_element().extract_component2(gtc) # FIXME: Translate this correctly
eoffset = 0
- ec, element = t.element().extract_reference_component(gtc)
+ ec, element = t.ufl_element().extract_reference_component(gtc)
# Select mapping M from element, pick row emapping = M[ec,:], or emapping = [] if no mapping
ufl_assert(not isinstance(element, MixedElement),
@@ -381,12 +371,10 @@ class OLDChangeToReferenceGrad(MultiFunction):
return o
def grad(self, o):
- # FIXME: Handle HDiv elements with contravariant piola mapping specially?
- # FIXME: Handle HCurl elements with covariant piola mapping specially?
-
# Peel off the Grads and count them, and get restriction if it's between the grad and the terminal
ngrads = 0
restricted = ''
+ rv = False
while not o._ufl_is_terminal_:
if isinstance(o, Grad):
o, = o.ufl_operands
@@ -394,403 +382,73 @@ class OLDChangeToReferenceGrad(MultiFunction):
elif isinstance(o, Restricted):
restricted = o.side()
o, = o.ufl_operands
+ elif isinstance(o, ReferenceValue):
+ rv = True
+ o, = o.ufl_operands
+ else:
+ error("Invalid type %s" % o._ufl_class_.__name__)
f = o
+ if rv:
+ f = ReferenceValue(f)
# Get domain and create Jacobian inverse object
- domain = f.domain()
+ domain = o.ufl_domain()
Jinv = JacobianInverse(domain)
- # This is an assumption in the below code TODO: Handle grad(grad(.)) for non-affine domains.
- ufl_assert(ngrads == 1 or Jinv.is_cellwise_constant(),
- "Multiple grads for non-affine domains not currently supported in this algorithm.")
+ if is_cellwise_constant(Jinv):
+ # Optimise slightly by turning Grad(Grad(...)) into J^(-T)J^(-T)RefGrad(RefGrad(...))
+ # rather than J^(-T)RefGrad(J^(-T)RefGrad(...))
- # Create some new indices
- ii = indices(f.rank()) # Indices to get to the scalar component of f
- jj = indices(ngrads) # Indices to sum over the local gradient axes with the inverse Jacobian
- kk = indices(ngrads) # Indices for the leftover inverse Jacobian axes
+ # Create some new indices
+ ii = indices(len(f.ufl_shape)) # Indices to get to the scalar component of f
+ jj = indices(ngrads) # Indices to sum over the local gradient axes with the inverse Jacobian
+ kk = indices(ngrads) # Indices for the leftover inverse Jacobian axes
- # Preserve restricted property
- if restricted:
- Jinv = Jinv(restricted)
- f = f(restricted)
-
- # Apply the same number of ReferenceGrad without mappings
- lgrad = f
- for i in range(ngrads):
- lgrad = ReferenceGrad(lgrad)
-
- # Apply mappings with scalar indexing operations (assumes ReferenceGrad(Jinv) is zero)
- jinv_lgrad_f = lgrad[ii+jj]
- for j, k in zip(jj, kk):
- jinv_lgrad_f = Jinv[j, k]*jinv_lgrad_f
-
- # Wrap back in tensor shape, derivative axes at the end
- jinv_lgrad_f = as_tensor(jinv_lgrad_f, ii+kk)
-
- return jinv_lgrad_f
-
- def reference_grad(self, o):
- error("Not expecting reference grad while applying change to reference grad.")
-
- def coefficient_derivative(self, o):
- error("Coefficient derivatives should be expanded before applying change to reference grad.")
+ # Preserve restricted property
+ if restricted:
+ Jinv = Jinv(restricted)
+ f = f(restricted)
+ # Apply the same number of ReferenceGrad without mappings
+ lgrad = f
+ for i in range(ngrads):
+ lgrad = ReferenceGrad(lgrad)
-class ChangeToReferenceGeometry(MultiFunction):
- def __init__(self, physical_coordinates_known, coordinate_coefficient_mapping):
- MultiFunction.__init__(self)
- self.coordinate_coefficient_mapping = coordinate_coefficient_mapping or {}
- self.physical_coordinates_known = physical_coordinates_known
- self._cache = {} # Needed by memoized_handler
-
- expr = MultiFunction.reuse_if_untouched
-
- def terminal(self, o):
- return o
-
- @memoized_handler
- def jacobian(self, o):
- domain = o.domain()
- x = domain.coordinates()
- if x is None:
- r = o
- else:
- x = self.coordinate_coefficient_mapping[x]
- r = ReferenceGrad(x)
- return r
-
- @memoized_handler
- def _future_jacobian(self, o):
- # If we're not using Coefficient to represent the spatial coordinate,
- # we can just as well just return o here too unless we add representation
- # of basis functions and dofs to the ufl layer (which is nice to avoid).
- return o
-
- @memoized_handler
- def jacobian_inverse(self, o):
- domain = o.domain()
- J = self.jacobian(Jacobian(domain))
- return inverse_expr(J)
-
- @memoized_handler
- def jacobian_determinant(self, o):
- domain = o.domain()
- J = self.jacobian(Jacobian(domain))
- return determinant_expr(J)
-
- @memoized_handler
- def facet_jacobian(self, o):
- domain = o.domain()
- J = self.jacobian(Jacobian(domain))
- RFJ = CellFacetJacobian(domain)
- i, j, k = indices(3)
- return as_tensor(J[i, k]*RFJ[k, j], (i, j))
-
- @memoized_handler
- def facet_jacobian_inverse(self, o):
- domain = o.domain()
- FJ = self.facet_jacobian(FacetJacobian(domain))
- return inverse_expr(FJ)
-
- @memoized_handler
- def facet_jacobian_determinant(self, o):
- domain = o.domain()
- FJ = self.facet_jacobian(FacetJacobian(domain))
- return determinant_expr(FJ)
-
- @memoized_handler
- def spatial_coordinate(self, o):
- "Fall through to coordinate field of domain if it exists."
- if self.physical_coordinates_known:
- return o
- else:
- domain = o.domain()
- x = domain.coordinates()
- if x is None:
- return o
- else:
- x = self.coordinate_coefficient_mapping[x]
- return x
-
- @memoized_handler
- def _future_spatial_coordinate(self, o):
- "Fall through to coordinate field of domain if it exists."
- if self.physical_coordinates_known:
- return o
- else:
- # If we're not using Coefficient to represent the spatial coordinate,
- # we can just as well just return o here too unless we add representation
- # of basis functions and dofs to the ufl layer (which is nice to avoid).
- return o
-
- @memoized_handler
- def cell_coordinate(self, o):
- "Compute from physical coordinates if they are known, using the appropriate mappings."
- if self.physical_coordinates_known:
- K = self.jacobian_inverse(JacobianInverse(domain))
- x = self.spatial_coordinate(SpatialCoordinate(domain))
- x0 = CellOrigin(domain)
- i, j = indices(2)
- X = as_tensor(K[i, j] * (x[j] - x0[j]), (i,))
- return X
- else:
- return o
-
- @memoized_handler
- def facet_cell_coordinate(self, o):
- if self.physical_coordinates_known:
- error("Missing computation of facet reference coordinates from physical coordinates via mappings.")
- else:
- return o
-
- @memoized_handler
- def cell_volume(self, o):
- domain = o.domain()
- if not domain.is_piecewise_linear_simplex_domain():
- error("Only know how to compute the cell volume of an affine cell.")
- r = self.jacobian_determinant(JacobianDeterminant(domain))
- r0 = domain.cell().reference_volume()
- return abs(r * r0)
-
- @memoized_handler
- def facet_area(self, o):
- domain = o.domain()
- if not domain.is_piecewise_linear_simplex_domain():
- error("Only know how to compute the facet area of an affine cell.")
- r = self.facet_jacobian_determinant(FacetJacobianDeterminant(domain))
- r0 = domain.cell().reference_facet_volume()
- return abs(r * r0)
-
- @memoized_handler
- def circumradius(self, o):
- domain = o.domain()
- if not domain.is_piecewise_linear_simplex_domain():
- error("Only know how to compute the circumradius of an affine cell.")
- cellname = domain.cell().cellname()
- cellvolume = self.cell_volume(CellVolume(domain))
-
- if cellname == "interval":
- r = 0.5 * cellvolume
-
- elif cellname == "triangle":
- J = self.jacobian(Jacobian(domain))
- trev = CellEdgeVectors(domain)
- num_edges = 3
- i, j, k = indices(3)
- elen = [sqrt((J[i, j]*trev[edge, j])*(J[i, k]*trev[edge, k])) for edge in range(num_edges)]
-
- r = (elen[0] * elen[1] * elen[2]) / (4.0 * cellvolume)
-
- elif cellname == "tetrahedron":
- J = self.jacobian(Jacobian(domain))
- trev = CellEdgeVectors(domain)
- num_edges = 6
- i, j, k = indices(3)
- elen = [sqrt((J[i, j]*trev[edge, j])*(J[i, k]*trev[edge, k])) for edge in range(num_edges)]
-
- # elen[3] = length of edge 3
- # la, lb, lc = lengths of the sides of an intermediate triangle
- la = elen[3] * elen[2]
- lb = elen[4] * elen[1]
- lc = elen[5] * elen[0]
- # p = perimeter
- p = (la + lb + lc)
- # s = semiperimeter
- s = p / 2
- # area of intermediate triangle with Herons formula
- triangle_area = sqrt(s * (s - la) * (s - lb) * (s - lc))
- r = triangle_area / (6.0 * cellvolume)
-
- else:
- error("Unhandled cell type %s." % cellname)
+ # Apply mappings with scalar indexing operations (assumes ReferenceGrad(Jinv) is zero)
+ jinv_lgrad_f = lgrad[ii+jj]
+ for j, k in zip(jj, kk):
+ jinv_lgrad_f = Jinv[j, k]*jinv_lgrad_f
- return r
+ # Wrap back in tensor shape, derivative axes at the end
+ jinv_lgrad_f = as_tensor(jinv_lgrad_f, ii+kk)
- @memoized_handler
- def min_cell_edge_length(self, o):
- domain = o.domain()
- if not domain.is_piecewise_linear_simplex_domain():
- error("Only know how to compute the min_cell_edge_length of an affine cell.")
- cellname = domain.cell().cellname()
-
- J = self.jacobian(Jacobian(domain))
- trev = CellEdgeVectors(domain)
- num_edges = trev.ufl_shape[0]
- i, j, k = indices(3)
- elen = [sqrt((J[i, j]*trev[edge, j])*(J[i, k]*trev[edge, k])) for edge in range(num_edges)]
-
- if cellname == "triangle":
- return min_value(elen[0], min_value(elen[1], elen[2]))
- elif cellname == "tetrahedron":
- min1 = min_value(elen[0], min_value(elen[1], elen[2]))
- min2 = min_value(elen[3], min_value(elen[4], elen[5]))
- return min_value(min1, min2)
- else:
- error("Unhandled cell type %s." % cellname)
-
- @memoized_handler
- def max_cell_edge_length(self, o):
- domain = o.domain()
- if not domain.is_piecewise_linear_simplex_domain():
- error("Only know how to compute the max_cell_edge_length of an affine cell.")
- cellname = domain.cell().cellname()
-
- J = self.jacobian(Jacobian(domain))
- trev = CellEdgeVectors(domain)
- num_edges = trev.ufl_shape[0]
- i, j, k = indices(3)
- elen = [sqrt((J[i, j]*trev[edge, j])*(J[i, k]*trev[edge, k])) for edge in range(num_edges)]
-
- if cellname == "triangle":
- return max_value(elen[0], max_value(elen[1], elen[2]))
- elif cellname == "tetrahedron":
- max1 = max_value(elen[0], max_value(elen[1], elen[2]))
- max2 = max_value(elen[3], max_value(elen[4], elen[5]))
- return max_value(max1, max2)
- else:
- error("Unhandled cell type %s." % cellname)
-
- @memoized_handler
- def min_facet_edge_length(self, o):
- domain = o.domain()
- if not domain.is_piecewise_linear_simplex_domain():
- error("Only know how to compute the min_facet_edge_length of an affine cell.")
- cellname = domain.cell().cellname()
-
- if cellname == "triangle":
- return self.facet_area(FacetArea(domain))
- elif cellname == "tetrahedron":
- J = self.jacobian(Jacobian(domain))
- trev = FacetEdgeVectors(domain)
- num_edges = 3
- i, j, k = indices(3)
- elen = [sqrt((J[i, j]*trev[edge, j])*(J[i, k]*trev[edge, k])) for edge in range(num_edges)]
- return min_value(elen[0], min_value(elen[1], elen[2]))
else:
- error("Unhandled cell type %s." % cellname)
-
- @memoized_handler
- def max_facet_edge_length(self, o):
- domain = o.domain()
- if not domain.is_piecewise_linear_simplex_domain():
- error("Only know how to compute the max_facet_edge_length of an affine cell.")
- cellname = domain.cell().cellname()
-
- if cellname == "triangle":
- return self.facet_area(FacetArea(domain))
- elif cellname == "tetrahedron":
- J = self.jacobian(Jacobian(domain))
- trev = FacetEdgeVectors(domain)
- num_edges = 3
- i, j, k = indices(3)
- elen = [sqrt((J[i, j]*trev[edge, j])*(J[i, k]*trev[edge, k])) for edge in range(num_edges)]
- return max_value(elen[0], max_value(elen[1], elen[2]))
- else:
- error("Unhandled cell type %s." % cellname)
-
- @memoized_handler
- def cell_normal(self, o):
- warning("Untested complicated code for cell normal. Please report if this works correctly or not.")
-
- domain = o.domain()
- gdim = domain.geometric_dimension()
- tdim = domain.topological_dimension()
-
- if tdim == gdim - 1:
- if tdim == 2: # Surface in 3D
- J = self.jacobian(Jacobian(domain))
- cell_normal = cross_expr(J[:, 0], J[:, 1])
- elif tdim == 1: # Line in 2D
- # TODO: Document which normal direction this is
- cell_normal = as_vector((-J[1, 0], J[0, 0]))
- i = Index()
- return cell_normal / sqrt(cell_normal[i]*cell_normal[i])
- elif tdim == gdim:
- return as_vector((0.0,)*tdim + (1.0,))
- else:
- error("What do you mean by cell normal in gdim={0}, tdim={1}?".format(gdim, tdim))
-
- @memoized_handler
- def facet_normal(self, o):
- domain = o.domain()
- gdim = domain.geometric_dimension()
- tdim = domain.topological_dimension()
+ # J^(-T)RefGrad(J^(-T)RefGrad(...))
- if tdim == 3:
- FJ = self.facet_jacobian(FacetJacobian(domain))
+ # Preserve restricted property
+ if restricted:
+ Jinv = Jinv(restricted)
+ f = f(restricted)
- ufl_assert(gdim == 3, "Inconsistent dimensions.")
- ufl_assert(FJ.ufl_shape == (3, 2), "Inconsistent dimensions.")
+ jinv_lgrad_f = f
+ for foo in range(ngrads):
+ ii = indices(len(jinv_lgrad_f.ufl_shape)) # Indices to get to the scalar component of f
+ j, k = indices(2)
- # Compute signed scaling factor
- scale = self.jacobian_determinant(JacobianDeterminant(domain))
+ lgrad = ReferenceGrad(jinv_lgrad_f)
+ jinv_lgrad_f = Jinv[j, k]*lgrad[ii+(j,)]
- # Compute facet normal direction of 3D cell, product of two tangent vectors
- fo = FacetOrientation(domain)
- ndir = (fo * scale) * cross_expr(FJ[:, 0], FJ[:, 1])
+ # Wrap back in tensor shape, derivative axes at the end
+ jinv_lgrad_f = as_tensor(jinv_lgrad_f, ii+(k,))
- # Normalise normal vector
- i = Index()
- n = ndir / sqrt(ndir[i]*ndir[i])
- r = n
-
- elif tdim == 2:
- FJ = self.facet_jacobian(FacetJacobian(domain))
-
- if gdim == 2:
- # 2D facet normal in 2D space
- ufl_assert(FJ.ufl_shape == (2, 1), "Inconsistent dimensions.")
-
- # Compute facet tangent
- tangent = as_vector((FJ[0, 0], FJ[1, 0], 0))
+ return jinv_lgrad_f
- # Compute cell normal
- cell_normal = as_vector((0, 0, 1))
+ def reference_grad(self, o):
+ error("Not expecting reference grad while applying change to reference grad.")
- # Compute signed scaling factor
- scale = self.jacobian_determinant(JacobianDeterminant(domain))
- else:
- # 2D facet normal in 3D space
- ufl_assert(FJ.ufl_shape == (gdim, 1), "Inconsistent dimensions.")
-
- # Compute facet tangent
- tangent = FJ[:, 0]
-
- # Compute cell normal
- cell_normal = self.cell_normal(CellNormal(domain))
-
- # Compute signed scaling factor (input in the manifold case)
- scale = CellOrientation(domain)
-
- ufl_assert(len(tangent) == 3, "Inconsistent dimensions.")
- ufl_assert(len(cell_normal) == 3, "Inconsistent dimensions.")
-
- # Compute normal direction
- cr = cross_expr(tangent, cell_normal)
- if gdim == 2:
- cr = as_vector((cr[0], cr[1]))
- fo = FacetOrientation(domain)
- ndir = (fo * scale) * cr
-
- # Normalise normal vector
- i = Index()
- n = ndir / sqrt(ndir[i]*ndir[i])
- r = n
-
- elif tdim == 1:
- J = self.jacobian(Jacobian(domain)) # dx/dX
- fo = FacetOrientation(domain)
- ndir = fo * J[:, 0]
- if gdim == 1:
- nlen = abs(ndir[0])
- else:
- i = Index()
- nlen = sqrt(ndir[i]*ndir[i])
- n = ndir / nlen
- r = n
+ def coefficient_derivative(self, o):
+ error("Coefficient derivatives should be expanded before applying change to reference grad.")
- ufl_assert(r.ufl_shape == o.ufl_shape, "Inconsistent dimensions (in=%d, out=%d)." % (o.ufl_shape[0], r.ufl_shape[0]))
- return r
def change_to_reference_grad(e):
@@ -805,61 +463,13 @@ def change_to_reference_grad(e):
#mf = NEWChangeToReferenceGrad()
return map_expr_dag(mf, e)
-
-def change_to_reference_geometry(e, physical_coordinates_known, coordinate_coefficient_mapping=None):
- """Change GeometricQuantity objects in expression to the lowest level GeometricQuantity objects.
-
- Assumes the expression is preprocessed or at least that derivatives have been expanded.
-
- @param e:
- An Expr or Form.
- """
- mf = ChangeToReferenceGeometry(physical_coordinates_known, coordinate_coefficient_mapping)
- return map_expr_dag(mf, e)
-
-
-def compute_integrand_scaling_factor(domain, integral_type):
- """Change integrand geometry to the right representations."""
-
- weight = QuadratureWeight(domain)
- tdim = domain.topological_dimension()
-
- if integral_type == "cell":
- scale = abs(JacobianDeterminant(domain)) * weight
-
- elif integral_type.startswith("exterior_facet"):
- if tdim > 1:
- # Scaling integral by facet jacobian determinant and quadrature weight
- scale = FacetJacobianDeterminant(domain) * weight
- else:
- # No need to scale 'integral' over a vertex
- scale = 1
-
- elif integral_type.startswith("interior_facet"):
- if tdim > 1:
- # Scaling integral by facet jacobian determinant from one side and quadrature weight
- scale = FacetJacobianDeterminant(domain)('+') * weight
- else:
- # No need to scale 'integral' over a vertex
- scale = 1
-
- elif integral_type in ("custom", "interface", "overlap", "cutcell"):
- # Scaling with custom weight, which includes eventual volume scaling
- scale = weight
-
- elif integral_type in ("vertex", "point"):
- # No need to scale 'integral' over a point
- scale = 1
-
- else:
- error("Unknown integral type {}, don't know how to scale.".format(integral_type))
-
- return scale
-
+from ufl.algorithms.apply_integral_scaling import compute_integrand_scaling_factor
def change_integrand_geometry_representation(integrand, scale, integral_type):
"""Change integrand geometry to the right representations."""
+ integrand = apply_function_pullbacks(integrand)
+
integrand = change_to_reference_grad(integrand)
integrand = integrand * scale
@@ -868,6 +478,6 @@ def change_integrand_geometry_representation(integrand, scale, integral_type):
physical_coordinates_known = True
else:
physical_coordinates_known = False
- integrand = change_to_reference_geometry(integrand, physical_coordinates_known)
+ integrand = apply_geometry_lowering(integrand, physical_coordinates_known)
return integrand
diff --git a/ufl/algorithms/check_arities.py b/ufl/algorithms/check_arities.py
index b460c47..b9b1ae8 100644
--- a/ufl/algorithms/check_arities.py
+++ b/ufl/algorithms/check_arities.py
@@ -1,12 +1,13 @@
+# -*- coding: utf-8 -*-
from itertools import chain
from ufl.log import UFLException
-from ufl.corealg.traversal import traverse_terminals
+from ufl.corealg.traversal import traverse_unique_terminals
from ufl.corealg.multifunction import MultiFunction
from ufl.corealg.map_dag import map_expr_dag
-from ufl.classes import Argument
+from ufl.classes import Argument, Zero
class ArityMismatch(UFLException):
@@ -28,9 +29,9 @@ class ArityChecker(MultiFunction):
def nonlinear_operator(self, o):
# Cutoff traversal by not having *ops in argument list of this handler.
# Traverse only the terminals under here the fastest way we know of:
- for t in traverse_terminals(o):
+ for t in traverse_unique_terminals(o):
if t._ufl_typecode_ == Argument._ufl_typecode_:
- raise ArityMismatch("Applying nonlinear operator to expression depending on form argument {0}.".format(t))
+ raise ArityMismatch("Applying nonlinear operator {0} to expression depending on form argument {1}.".format(o._ufl_class_.__name__, t))
return self._et
expr = nonlinear_operator
@@ -82,11 +83,30 @@ class ArityChecker(MultiFunction):
# Grad is a linear operator
grad = linear_operator
+ reference_grad = linear_operator
+ reference_value = linear_operator
# Does it make sense to have a Variable(Argument)? I see no problem.
def variable(self, o, f, l):
return f
+ # Conditional is linear on each side of the condition
+ def conditional(self, o, c, a, b):
+ if c:
+ raise ArityMismatch("Condition cannot depend on form arguments ({0}).".format(a))
+ if a and isinstance(o.ufl_operands[2], Zero):
+ # Allow conditional(c, arg, 0)
+ return a
+ elif b and isinstance(o.ufl_operands[1], Zero):
+ # Allow conditional(c, 0, arg)
+ return b
+ elif a == b:
+ # Allow conditional(c, test, test)
+ return a
+ else:
+ # Do not allow e.g. conditional(c, test, trial), conditional(c, test, nonzeroconstant)
+ raise ArityMismatch("Conditional subexpressions with non-matching form arguments {0} vs {1}.".format(a, b))
+
def linear_indexed_type(self, o, a, i):
return a
diff --git a/ufl/algorithms/propagate_restrictions.py b/ufl/algorithms/check_restrictions.py
similarity index 71%
rename from ufl/algorithms/propagate_restrictions.py
rename to ufl/algorithms/check_restrictions.py
index 5eb5c65..a844391 100644
--- a/ufl/algorithms/propagate_restrictions.py
+++ b/ufl/algorithms/check_restrictions.py
@@ -1,6 +1,7 @@
+# -*- coding: utf-8 -*-
"Algorithms related to restrictions."
-# Copyright (C) 2008-2014 Martin Sandve Alnes
+# Copyright (C) 2008-2015 Martin Sandve Alnæs
#
# This file is part of UFL.
#
@@ -41,20 +42,20 @@ class RestrictionChecker(Transformer):
def facet_normal(self, o):
if self.require_restriction:
- ufl_assert(self.current_restriction is not None, "Facet normal must be restricted in interior facet integrals.")
+ ufl_assert(self.current_restriction is not None,
+ "Facet normal must be restricted in interior facet integrals.")
else:
- ufl_assert(self.current_restriction is None, "Restrictions are only allowed for interior facet integrals.")
+ ufl_assert(self.current_restriction is None,
+ "Restrictions are only allowed for interior facet integrals.")
def form_argument(self, o):
if self.require_restriction:
- ufl_assert(self.current_restriction is not None, "Form argument must be restricted in interior facet integrals.")
+ ufl_assert(self.current_restriction is not None,
+ "Form argument must be restricted in interior facet integrals.")
else:
- ufl_assert(self.current_restriction is None, "Restrictions are only allowed for interior facet integrals.")
+ ufl_assert(self.current_restriction is None,
+ "Restrictions are only allowed for interior facet integrals.")
def check_restrictions(expression, require_restriction):
ufl_assert(isinstance(expression, Expr), "Expecting Expr instance.")
return RestrictionChecker(require_restriction).visit(expression)
-
-def propagate_restrictions(expression):
- from ufl.algorithms.apply_restrictions import apply_restrictions
- return apply_restrictions(expression)
diff --git a/ufl/algorithms/checks.py b/ufl/algorithms/checks.py
index 408a742..3ece493 100644
--- a/ufl/algorithms/checks.py
+++ b/ufl/algorithms/checks.py
@@ -1,6 +1,7 @@
+# -*- coding: utf-8 -*-
"""Functions to check the validity of forms."""
-# Copyright (C) 2008-2014 Martin Sandve Alnes
+# Copyright (C) 2008-2015 Martin Sandve Alnæs
#
# This file is part of UFL.
#
@@ -31,8 +32,8 @@ from ufl.integral import Measure
# UFL algorithms
from ufl.algorithms.traversal import iter_expressions
-from ufl.corealg.traversal import traverse_terminals
-from ufl.algorithms.propagate_restrictions import check_restrictions
+from ufl.corealg.traversal import traverse_unique_terminals
+from ufl.algorithms.check_restrictions import check_restrictions
from ufl.measure import integral_type_to_measure_name
def validate_form(form): # TODO: Can we make this return a list of errors instead of raising exception?
@@ -54,14 +55,14 @@ def validate_form(form): # TODO: Can we make this return a list of errors instea
# errors.append("Form is not multilinear in arguments.")
# FIXME DOMAIN: Add check for consistency between domains somehow
- domains = set(t.domain()
+ domains = set(t.ufl_domain()
for e in iter_expressions(form)
- for t in traverse_terminals(e)) - {None}
+ for t in traverse_unique_terminals(e)) - {None}
if not domains:
errors.append("Missing domain definition in form.")
# Check that cell is the same everywhere
- cells = set(dom.cell() for dom in domains) - {None}
+ cells = set(dom.ufl_cell() for dom in domains) - {None}
if not cells:
errors.append("Missing cell definition in form.")
elif len(cells) > 1:
@@ -72,13 +73,13 @@ def validate_form(form): # TODO: Can we make this return a list of errors instea
coefficients = {}
arguments = {}
for e in iter_expressions(form):
- for f in traverse_terminals(e):
+ for f in traverse_unique_terminals(e):
if isinstance(f, Coefficient):
c = f.count()
if c in coefficients:
g = coefficients[c]
- if not f is g:
- errors.append("Found different Coefficients with " + \
+ if f is not g:
+ errors.append("Found different Coefficients with " +
"same count: %s and %s." % (repr(f), repr(g)))
else:
coefficients[c] = f
@@ -88,10 +89,13 @@ def validate_form(form): # TODO: Can we make this return a list of errors instea
p = f.part()
if (n, p) in arguments:
g = arguments[(n, p)]
- if not f is g:
- if n == 0: msg = "TestFunctions"
- elif n == 1: msg = "TrialFunctions"
- else: msg = "Arguments with same number and part"
+ if f is not g:
+ if n == 0:
+ msg = "TestFunctions"
+ elif n == 1:
+ msg = "TrialFunctions"
+ else:
+ msg = "Arguments with same number and part"
msg = "Found different %s: %s and %s." % (msg, repr(f), repr(g))
errors.append(msg)
else:
@@ -100,7 +104,7 @@ def validate_form(form): # TODO: Can we make this return a list of errors instea
# Check that all integrands are scalar
for expression in iter_expressions(form):
if not is_true_ufl_scalar(expression):
- errors.append("Found non-scalar integrand expression:\n%s\n%s" % \
+ errors.append("Found non-scalar integrand expression:\n%s\n%s" %
(str(expression), repr(expression)))
# Check that restrictions are permissible
diff --git a/ufl/algorithms/compute_form_data.py b/ufl/algorithms/compute_form_data.py
index 51c8445..48c9398 100644
--- a/ufl/algorithms/compute_form_data.py
+++ b/ufl/algorithms/compute_form_data.py
@@ -1,8 +1,9 @@
+# -*- coding: utf-8 -*-
"""This module provides the compute_form_data function which form compilers
will typically call prior to code generation to preprocess/simplify a
raw input form given by a user."""
-# Copyright (C) 2008-2014 Martin Sandve Alnes
+# Copyright (C) 2008-2015 Martin Sandve Alnæs
#
# This file is part of UFL.
#
@@ -19,28 +20,32 @@ raw input form given by a user."""
# 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 collections import defaultdict
from itertools import chain
-from time import time
-import ufl
-from ufl.common import lstr, tstr, estr, istr, slice_dict
-from ufl.common import Timer
-from ufl.assertions import ufl_assert
+
from ufl.log import error, warning, info
-from ufl.core.expr import Expr
-from ufl.corealg.traversal import traverse_terminals
-from ufl.form import Form
-from ufl.protocols import id_or_none
-from ufl.geometry import as_domain
-from ufl.classes import GeometricFacetQuantity
-from ufl.algorithms.replace import replace
+from ufl.assertions import ufl_assert
+
+from ufl.classes import GeometricFacetQuantity, Coefficient, Form
+from ufl.corealg.traversal import traverse_unique_terminals
from ufl.algorithms.analysis import extract_coefficients, extract_sub_elements, unique_tuple
-from ufl.algorithms.domain_analysis import build_integral_data, reconstruct_form_from_integral_data
-from ufl.algorithms.formdata import FormData, ExprData
-from ufl.algorithms.ad import expand_derivatives
-from ufl.algorithms.propagate_restrictions import propagate_restrictions
+from ufl.algorithms.formdata import FormData#, ExprData
from ufl.algorithms.formtransformations import compute_form_arities
from ufl.algorithms.check_arities import check_form_arity
+from ufl.algorithms.elementtransformations import reconstruct_element
+
+# These are the main symbolic processing steps:
+from ufl.algorithms.apply_function_pullbacks import apply_function_pullbacks
+from ufl.algorithms.apply_algebra_lowering import apply_algebra_lowering
+from ufl.algorithms.apply_derivatives import apply_derivatives
+from ufl.algorithms.apply_integral_scaling import apply_integral_scaling
+from ufl.algorithms.apply_geometry_lowering import apply_geometry_lowering
+from ufl.algorithms.apply_restrictions import apply_restrictions
+from ufl.algorithms.estimate_degrees import estimate_total_polynomial_degree
+
+# See TODOs at the call sites of these below:
+from ufl.algorithms.domain_analysis import build_integral_data
+from ufl.algorithms.domain_analysis import reconstruct_form_from_integral_data
+from ufl.algorithms.domain_analysis import group_form_integrals
def _auto_select_degree(elements):
@@ -53,11 +58,15 @@ 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 })
+
def _compute_element_mapping(form):
"Compute element mapping for element replacement"
+ # The element mapping is a slightly messy concept with two use cases:
+ # - Expression with missing cell or element TODO: Implement proper Expression handling in UFL and get rid of this
+ # - Constant with missing cell TODO: Fix anything that needs to be worked around to drop this requirement
# Extract all elements and include subelements of mixed elements
- elements = [obj.element() for obj in chain(form.arguments(), form.coefficients())]
+ elements = [obj.ufl_element() for obj in chain(form.arguments(), form.coefficients())]
elements = extract_sub_elements(elements)
# Try to find a common degree for elements
@@ -70,14 +79,15 @@ def _compute_element_mapping(form):
# Flag for whether element needs to be reconstructed
reconstruct = False
- # Set domain/cell
- domain = element.domain()
- if domain is None:
- domains = form.domains()
+ # Set cell
+ cell = element.cell()
+ if cell is None:
+ domains = form.ufl_domains()
ufl_assert(len(domains) == 1,
- "Cannot replace unknown element domain without unique common domain in form.")
+ "Cannot replace unknown element cell without unique common cell in form.")
domain, = domains
- info("Adjusting missing element domain to %s." % (domain,))
+ cell = domain.ufl_cell()
+ info("Adjusting missing element cell to %s." % (cell,))
reconstruct = True
# Set degree
@@ -89,15 +99,16 @@ def _compute_element_mapping(form):
# Reconstruct element and add to map
if reconstruct:
- element_mapping[element] = element.reconstruct(domain=domain, degree=degree)
+ element_mapping[element] = reconstruct_element(element,
+ element.family(), cell, degree)
else:
element_mapping[element] = element
return element_mapping
-def _compute_num_sub_domains(integral_data):
- num_sub_domains = {}
+def _compute_max_subdomain_ids(integral_data):
+ max_subdomain_ids = {}
for itg_data in integral_data:
it = itg_data.integral_type
si = itg_data.subdomain_id
@@ -105,26 +116,35 @@ def _compute_num_sub_domains(integral_data):
newmax = si + 1
else:
newmax = 0
- prevmax = num_sub_domains.get(it, 0)
- num_sub_domains[it] = max(prevmax, newmax)
- return num_sub_domains
+ prevmax = max_subdomain_ids.get(it, 0)
+ max_subdomain_ids[it] = max(prevmax, newmax)
+ return max_subdomain_ids
+
+
+def _compute_form_data_elements(self, arguments, coefficients, domains):
+ self.argument_elements = tuple(f.ufl_element() for f in arguments)
+ self.coefficient_elements = tuple(f.ufl_element() for f in coefficients)
+ self.coordinate_elements = tuple(domain.ufl_coordinate_element() for domain in domains)
+
+ # TODO: Include coordinate elements from argument and coefficient domains as well? Can they differ?
+ # Note: Removed self.elements and self.sub_elements to make sure code that
+ # depends on the selection of argument + coefficient elements blow up,
+ # as opposed to silently almost working, with the introduction of the coordinate elements here.
-def _compute_form_data_elements(self, arguments, coefficients):
- self.argument_elements = tuple(f.element() for f in arguments)
- self.coefficient_elements = tuple(f.element() for f in coefficients)
- self.elements = self.argument_elements + self.coefficient_elements
- self.unique_elements = unique_tuple(self.elements)
- self.sub_elements = extract_sub_elements(self.elements)
- self.unique_sub_elements = unique_tuple(self.sub_elements)
+ all_elements = self.argument_elements + self.coefficient_elements + self.coordinate_elements
+ all_sub_elements = extract_sub_elements(all_elements)
+
+ self.unique_elements = unique_tuple(all_elements)
+ self.unique_sub_elements = unique_tuple(all_sub_elements)
def _check_elements(form_data):
for element in chain(form_data.unique_elements, form_data.unique_sub_elements):
- ufl_assert(element.domain() is not None,
- "Found element with undefined domain: %s" % repr(element))
ufl_assert(element.family() is not None,
"Found element with undefined familty: %s" % repr(element))
+ ufl_assert(element.cell() is not None,
+ "Found element with undefined cell: %s" % repr(element))
def _check_facet_geometry(integral_data):
@@ -133,9 +153,9 @@ def _check_facet_geometry(integral_data):
it = itg_data.integral_type
# Facet geometry is only valid in facet integrals.
# Allowing custom integrals to pass as well, although that's not really strict enough.
- if "facet" not in it and "custom" not in it:
+ if not ("facet" in it or "custom" in it or "interface" in it):
# Not a facet integral
- for expr in traverse_terminals(itg.integrand()):
+ for expr in traverse_unique_terminals(itg.integrand()):
cls = expr._ufl_class_
if issubclass(cls, GeometricFacetQuantity):
error("Integral of type %s cannot contain a %s." % (it, cls.__name__))
@@ -158,20 +178,45 @@ def _build_coefficient_replace_map(coefficients, element_mapping=None):
new_coefficients = []
replace_map = {}
for i, f in enumerate(coefficients):
- old_e = f.element()
+ old_e = f.ufl_element()
new_e = element_mapping.get(old_e, old_e)
- new_f = f.reconstruct(element=new_e, count=i)
+ new_f = Coefficient(new_e, count=i)
new_coefficients.append(new_f)
replace_map[f] = new_f
return new_coefficients, replace_map
-def compute_form_data(form, apply_propagate_restrictions=True):
+def attach_estimated_degrees(form):
+ """Attach estimated polynomial degree to a form's integrals.
+
+ :arg form: The :class:`~.Form` to inspect.
+ :returns: A new Form with estimate degrees attached.
+ """
+ integrals = form.integrals()
+
+ new_integrals = []
+ for integral in integrals:
+ md = {}
+ md.update(integral.metadata())
+ degree = estimate_total_polynomial_degree(integral.integrand())
+ md["estimated_polynomial_degree"] = degree
+ new_integrals.append(integral.reconstruct(metadata=md))
+ return Form(new_integrals)
+
+def compute_form_data(form,
+ # Default arguments configured to behave the way old FFC expects it:
+ do_apply_function_pullbacks=False,
+ do_apply_integral_scaling=False,
+ do_apply_geometry_lowering=False,
+ preserve_geometry_types=(),
+ do_apply_restrictions=True,
+ do_estimate_degrees=True,
+ ):
# TODO: Move this to the constructor instead
self = FormData()
- # Store untouched form for reference.
+ # --- Store untouched form for reference.
# The user of FormData may get original arguments,
# original coefficients, and form signature from this object.
# But be aware that the set of original coefficients are not
@@ -179,37 +224,70 @@ def compute_form_data(form, apply_propagate_restrictions=True):
# See 'reduced_coefficients' below.
self.original_form = form
- # Get rank of form from argument list (assuming not a mixed arity form)
- self.rank = len(form.arguments())
-
- # Extract common geometric dimension (topological is not common!)
- gdims = set(domain.geometric_dimension() for domain in form.domains())
- ufl_assert(len(gdims) == 1,
- "Expecting all integrals in a form to share geometric dimension, got %s." % str(tuple(sorted(gdims))))
- self.geometric_dimension, = gdims
-
- # Build mapping from old incomplete element objects to new well defined elements.
- # This is to support the Expression construct in dolfin which subclasses Coefficient
- # but doesn't provide an element, and the Constant construct that doesn't provide
- # the domain that a Coefficient is supposed to have. A future design iteration in
- # UFL/UFC/FFC/DOLFIN may allow removal of this mapping with the introduction of UFL
- # types for .
- self.element_replace_map = _compute_element_mapping(form)
-
# --- Pass form integrands through some symbolic manipulation
- # Process form the way that is currently expected by FFC
- preprocessed_form = expand_derivatives(form)
-
- if apply_propagate_restrictions:
- preprocessed_form = propagate_restrictions(preprocessed_form)
-
-
- # --- Group and collect data about integrals
- # TODO: Refactor this # TODO: Is form.domains() right here?
- self.integral_data = \
- build_integral_data(preprocessed_form.integrals(), form.domains())
+ # Note: Default behaviour here will process form the way that is currently expected by vanilla FFC
+
+ # Lower abstractions for tensor-algebra types into index notation,
+ # reducing the number of operators later algorithms and form compilers
+ # need to handle
+ form = apply_algebra_lowering(form)
+
+ # Apply differentiation before function pullbacks, because for example
+ # coefficient derivatives are more complicated to derive after coefficients
+ # are rewritten, and in particular for user-defined coefficient relations it just gets too messy
+ form = apply_derivatives(form)
+
+ # --- Group form integrals
+ # TODO: Refactor this, it's rather opaque what this does
+ # TODO: Is self.original_form.ufl_domains() right here?
+ # It will matter when we start including 'num_domains' in ufc form.
+ form = group_form_integrals(form, self.original_form.ufl_domains())
+
+ # Estimate polynomial degree of integrands now, before applying
+ # any pullbacks and geometric lowering. Otherwise quad degrees
+ # blow up horrifically.
+ if do_estimate_degrees:
+ form = attach_estimated_degrees(form)
+
+ if do_apply_function_pullbacks:
+ # Rewrite coefficients and arguments in terms of their reference cell values
+ # with Piola transforms and symmetry transforms injected where needed.
+ # Decision: Not supporting grad(dolfin.Expression) without a Domain.
+ # Current dolfin works if Expression has a cell
+ # but this should be changed to a mesh.
+ form = apply_function_pullbacks(form)
+
+ # Scale integrals to reference cell frames
+ if do_apply_integral_scaling:
+ form = apply_integral_scaling(form)
+
+ # Lower abstractions for geometric quantities into a smaller set of quantities,
+ # allowing the form compiler to deal with a smaller set of types and treating
+ # geometric quantities like any other expressions w.r.t. loop-invariant code motion etc.
+ if do_apply_geometry_lowering:
+ form = apply_geometry_lowering(form, preserve_geometry_types)
+
+ # Apply differentiation again, because the algorithms above can generate
+ # new derivatives or rewrite expressions inside derivatives
+ if do_apply_function_pullbacks or do_apply_geometry_lowering:
+ form = apply_derivatives(form)
+
+ # Neverending story: apply_derivatives introduces new Jinvs, which needs more geometry lowering
+ if do_apply_geometry_lowering:
+ form = apply_geometry_lowering(form, preserve_geometry_types)
+ # Lower derivatives that may have appeared
+ form = apply_derivatives(form)
+
+ # Propagate restrictions to terminals
+ if do_apply_restrictions:
+ form = apply_restrictions(form)
+
+
+ # --- Group integrals into IntegralData objects
+ # Most of the heavy lifting is done above in group_form_integrals.
+ self.integral_data = build_integral_data(form.integrals())
# --- Create replacements for arguments and coefficients
@@ -220,10 +298,6 @@ def compute_form_data(form, apply_propagate_restrictions=True):
# Get all coefficients in integrand
for itg in itg_data.integrals:
itg_coeffs.update(extract_coefficients(itg.integrand()))
- # Add coefficient for integration domain if any
- c = itg_data.domain.coordinates()
- if c is not None:
- itg_coeffs.add(c)
# Store with IntegralData object
itg_data.integral_coefficients = itg_coeffs
@@ -234,7 +308,7 @@ def compute_form_data(form, apply_propagate_restrictions=True):
reduced_coefficients_set.update(itg_data.integral_coefficients)
self.reduced_coefficients = sorted(reduced_coefficients_set, key=lambda c: c.count())
self.num_coefficients = len(self.reduced_coefficients)
- self.original_coefficient_positions = [i for i, c in enumerate(form.coefficients())
+ self.original_coefficient_positions = [i for i, c in enumerate(self.original_form.coefficients())
if c in self.reduced_coefficients]
# Store back into integral data which form coefficients are used by each integral
@@ -242,42 +316,23 @@ def compute_form_data(form, apply_propagate_restrictions=True):
itg_data.enabled_coefficients = [bool(coeff in itg_data.integral_coefficients)
for coeff in self.reduced_coefficients]
- """
- # Build mappings from coefficients, domains and geometric quantities
- # that reside in form to objects with canonical numbering as well as
- # completed elements
-
- coordinate_functions = set(domain.coordinates() for domain in form.domains()) - set((None,))
-
- coordinates_replace_map = {}
- for i, f in enumerate(self.reduced_coefficients):
- if f in coordinate_functions:
- new_f = f.reconstruct(count=i)
- coordinates_replace_map[f] = new_f
-
- domains_replace_map = {}
- for domain in form.domains():
- FIXME
-
- geometry_replace_map = {}
- FIXME
-
- coefficients_replace_map = {}
- for i, f in enumerate(self.reduced_coefficients):
- if f not in coordinate_functions:
- old_e = f.element()
- new_e = self.element_replace_map.get(old_e, old_e)
- new_f = f.reconstruct(element=new_e, count=i)
- coefficients_replace_map[f] = new_f
-
- self.terminals_replace_map = {}
- self.terminals_replace_map.update(coordinates_replace_map)
- self.terminals_replace_map.update(domains_replace_map) # Not currently terminals but soon will be
- self.terminals_replace_map.update(geometry_replace_map)
- self.terminals_replace_map.update(coefficients_replace_map)
-
- renumbered_coefficients = [self.terminals_replace_map[f] for f in self.reduced_coefficients]
- """
+
+ # --- Collect some trivial data
+
+ # Get rank of form from argument list (assuming not a mixed arity form)
+ self.rank = len(self.original_form.arguments())
+
+ # Extract common geometric dimension (topological is not common!)
+ self.geometric_dimension = self.original_form.integrals()[0].ufl_domain().geometric_dimension()
+
+
+ # --- Build mapping from old incomplete element objects to new well defined elements.
+ # This is to support the Expression construct in dolfin which subclasses Coefficient
+ # but doesn't provide an element, and the Constant construct that doesn't provide
+ # the domain that a Coefficient is supposed to have. A future design iteration in
+ # UFL/UFC/FFC/DOLFIN may allow removal of this mapping with the introduction of UFL
+ # types for Expression-like functions that can be evaluated in quadrature points.
+ self.element_replace_map = _compute_element_mapping(self.original_form)
# Mappings from elements and coefficients
# that reside in form to objects with canonical numbering as well as
@@ -287,11 +342,14 @@ def compute_form_data(form, apply_propagate_restrictions=True):
self.function_replace_map = function_replace_map
# --- Store various lists of elements and sub elements (adds members to self)
- _compute_form_data_elements(self, form.arguments(), renumbered_coefficients)
+ _compute_form_data_elements(self,
+ self.original_form.arguments(),
+ renumbered_coefficients,
+ self.original_form.ufl_domains())
# --- Store number of domains for integral types
# TODO: Group this by domain first. For now keep a backwards compatible data structure.
- self.num_sub_domains = _compute_num_sub_domains(self.integral_data)
+ self.max_subdomain_ids = _compute_max_subdomain_ids(self.integral_data)
# --- Checks
@@ -301,9 +359,9 @@ def compute_form_data(form, apply_propagate_restrictions=True):
# TODO: This is a very expensive check... Replace with something faster!
preprocessed_form = reconstruct_form_from_integral_data(self.integral_data)
#_check_form_arity(preprocessed_form)
- check_form_arity(preprocessed_form, form.arguments()) # Currently testing how fast this is
+ check_form_arity(preprocessed_form, self.original_form.arguments()) # Currently testing how fast this is
- # TODO: This is used by unit tests, change the tests!
+ # TODO: This member is used by unit tests, change the tests to remove this!
self.preprocessed_form = preprocessed_form
return self
diff --git a/ufl/algorithms/domain_analysis.py b/ufl/algorithms/domain_analysis.py
index 0597834..1a8ffa5 100644
--- a/ufl/algorithms/domain_analysis.py
+++ b/ufl/algorithms/domain_analysis.py
@@ -1,6 +1,7 @@
+# -*- coding: utf-8 -*-
"""Algorithms for building canonical data structure for integrals over subdomains."""
-# Copyright (C) 2009-2014 Anders Logg and Martin Sandve Alnes
+# Copyright (C) 2009-2015 Anders Logg and Martin Sandve Alnæs
#
# This file is part of UFL.
#
@@ -22,17 +23,13 @@ from six.moves import zip
from six import iteritems
import ufl
-from ufl.utils.sorting import sorted_by_key
from ufl.log import error
from ufl.assertions import ufl_assert
-from ufl.geometry import Domain
from ufl.measure import Measure
from ufl.integral import Integral
from ufl.form import Form
-
-from ufl.sorting import cmp_expr
-from ufl.sorting import sorted_expr
-from ufl.utils.sorting import canonicalize_metadata
+from ufl.sorting import cmp_expr, sorted_expr
+from ufl.utils.sorting import canonicalize_metadata, sorted_by_key, sorted_by_tuple_key
class IntegralData(object):
@@ -45,12 +42,12 @@ class IntegralData(object):
__slots__ = ('domain', 'integral_type', 'subdomain_id', 'integrals', 'metadata',
'integral_coefficients', 'enabled_coefficients')
def __init__(self, domain, integral_type, subdomain_id, integrals, metadata):
- ufl_assert(all(domain.label() == itg.domain().label() for itg in integrals),
- "Domain label mismatch in integral data.")
+ ufl_assert(len(set(itg.ufl_domain() for itg in integrals)) == 1,
+ "Multiple domains mismatch in integral data.")
ufl_assert(all(integral_type == itg.integral_type() for itg in integrals),
- "Domain type mismatch in integral data.")
+ "Integral type mismatch in integral data.")
ufl_assert(all(subdomain_id == itg.subdomain_id() for itg in integrals),
- "Domain id mismatch in integral data.")
+ "Subdomain id mismatch in integral data.")
self.domain = domain
self.integral_type = integral_type
@@ -117,24 +114,20 @@ def group_integrals_by_domain_and_type(integrals, domains):
"""
Input:
integrals: list of Integral objects
- domains: list of Domain objects from the parent Form
+ domains: list of AbstractDomain objects from the parent Form
Output:
integrals_by_domain_and_type: dict: (domain, integral_type) -> list(Integral)
"""
integral_data = []
- domains_by_label = dict((domain.label(), domain) for domain in domains)
-
integrals_by_domain_and_type = defaultdict(list)
for itg in integrals:
- # Canonicalize domain
- domain = itg.domain()
- ufl_assert(domain is not None, "Integrals without a domain is now illegal.")
- domain = domains_by_label.get(domain.label())
- integral_type = itg.integral_type()
+ ufl_assert(itg.ufl_domain() is not None, "Integrals without a domain is now illegal.")
+ key = (itg.ufl_domain(), itg.integral_type())
# Append integral to list of integrals with shared key
- integrals_by_domain_and_type[(domain, integral_type)].append(itg)
+ integrals_by_domain_and_type[key].append(itg)
+
return integrals_by_domain_and_type
def integral_subdomain_ids(integral):
@@ -232,13 +225,50 @@ def accumulate_integrands_with_same_metadata(integrals):
# Sort integrands canonically by integrand first then compiler data
return sorted(by_cdid.values(), key=ExprTupleKey)
-def build_integral_data(integrals, domains):
- integral_data = []
+def build_integral_data(integrals):
+ """Build integral data given a list of integrals.
+
+ :arg integrals: An iterable of :class:`~.Integral` objects.
+ :returns: A tuple of :class:`IntegralData` objects.
+
+ The integrals you pass in here must have been rearranged and
+ gathered (removing the "everywhere" subdomain_id. To do this, you
+ should call :func:`group_form_integrals`.
+ """
+ itgs = defaultdict(list)
+
+ for integral in integrals:
+ domain = integral.ufl_domain()
+ integral_type = integral.integral_type()
+ subdomain_id = integral.subdomain_id()
+ if subdomain_id == "everywhere":
+ raise ValueError("'everywhere' not a valid subdomain id. Did you forget to call group_form_integrals?")
+ # Group for integral data (One integral data object for all
+ # integrals with same domain, itype, subdomain_id (but
+ # possibly different metadata).
+ itgs[(domain, integral_type, subdomain_id)].append(integral)
+
+ # Build list with canonical ordering, iteration over dicts
+ # is not deterministic across python versions
+ integral_datas = []
+ for (d, itype, sid), integrals in sorted_by_tuple_key(itgs):
+ integral_datas.append(IntegralData(d, itype, sid, integrals, {}))
+ return integral_datas
+
+
+def group_form_integrals(form, domains):
+ """Group integrals by domain and type, performing canonical simplification.
+
+ :arg form: the :class:`~.Form` to group the integrals of.
+ :arg domains: an iterable of :class:`~.Domain`\s.
+ :returns: A new :class:`~.Form` with gathered integrands.
+ """
# Group integrals by domain and type
integrals_by_domain_and_type = \
- group_integrals_by_domain_and_type(integrals, domains)
+ group_integrals_by_domain_and_type(form.integrals(), domains)
+ integrals = []
for domain in domains:
for integral_type in ufl.measure.integral_types():
# Get integrals with this domain and type
@@ -258,22 +288,10 @@ def build_integral_data(integrals, domains):
integrands_and_cds = \
accumulate_integrands_with_same_metadata(ss_integrals)
- # Reconstruct integrals with new integrands and the right domain object
- integrals = [Integral(integrand, integral_type, domain, subdomain_id, metadata, None)
- for integrand, metadata in integrands_and_cds]
-
- # Create new metadata dict for each integral data,
- # this is filled in by ffc to associate compiler
- # specific information with this integral data
- metadata = {}
-
- # Finally wrap it all in IntegralData object!
- ida = IntegralData(domain, integral_type, subdomain_id, integrals, {})
-
- # Store integral data objects in list with canonical ordering
- integral_data.append(ida)
-
- return integral_data
+ for integrand, metadata in integrands_and_cds:
+ integrals.append(Integral(integrand, integral_type, domain,
+ subdomain_id, metadata, None))
+ return Form(integrals)
def reconstruct_form_from_integral_data(integral_data):
integrals = []
diff --git a/ufl/algorithms/elementtransformations.py b/ufl/algorithms/elementtransformations.py
index 96ccd3b..ebc5eaa 100644
--- a/ufl/algorithms/elementtransformations.py
+++ b/ufl/algorithms/elementtransformations.py
@@ -1,4 +1,9 @@
-# Copyright (C) 2012 Marie E. Rognes
+# -*- coding: utf-8 -*-
+"""This module provides helper functions to
+ - FFC/DOLFIN adaptive chain,
+ - UFL algorithms taking care of underspecified DOLFIN expressions."""
+
+# Copyright (C) 2012 Marie E. Rognes, 2015 Jan Blechta
#
# This file is part of UFL.
#
@@ -15,44 +20,62 @@
# 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 xrange as range
-from ufl.assertions import ufl_assert
-from ufl.finiteelement import FiniteElement, MixedElement
+from ufl.finiteelement import FiniteElement, VectorElement, TensorElement, \
+ MixedElement, EnrichedElement
+
+__all__ = ['increase_order', 'tear']
+
+def increase_order(element):
+ "Return element of same family, but a polynomial degree higher."
+ return _increase_degree(element, +1)
+
def change_regularity(element, family):
"""
For a given finite element, return the corresponding space
specified by 'family'.
"""
+ return _change_family(element, family)
- n = element.num_sub_elements()
- if n > 0:
- subs = element.sub_elements()
- return MixedElement([change_regularity(subs[i], family)
- for i in range(n)])
- shape = element.value_shape()
- if not shape:
- return FiniteElement(family, element.domain(), element.degree())
- ufl_assert(len(shape) == 1, "TODO: Update this code to handle tensor elements.")
- return MixedElement([FiniteElement(family, element.domain(i), element.degree(i))
- for i in range(shape[0])])
-
-def tear(V):
+def tear(element):
"For a finite element, return the corresponding discontinuous element."
- W = change_regularity(V, "DG")
- return W
+ return change_regularity(element, "DG")
+
+
+def reconstruct_element(element, family, cell, degree):
+ if isinstance(element, FiniteElement):
+ return FiniteElement(family, cell, degree)
+ elif isinstance(element, VectorElement):
+ return VectorElement(family, cell, degree, dim=element.value_shape()[0])
+ elif isinstance(element, TensorElement):
+ return TensorElement(family, cell, degree, shape=element.value_shape())
+ else:
+ error("Element reconstruction is only done to stay compatible with hacks in DOLFIN. Not expecting a %r" % (element,))
-def increase_order(element):
- "Return element of same family, but a polynomial degree higher."
- ufl_assert(len(element.value_shape()) <= 1, "TODO: Update this code to handle tensor elements.")
- n = element.num_sub_elements()
- if n > 0:
- subs = element.sub_elements()
- return MixedElement([increase_order(subs[i]) for i in range(n)])
+def _increase_degree(element, degree_rise):
+ if isinstance(element, (FiniteElement, VectorElement, TensorElement)):
+ return reconstruct_element(element, element.family(), element.cell(),
+ element.degree() + degree_rise)
+ elif isinstance(element, MixedElement):
+ return MixedElement([_increase_degree(e, degree_rise)
+ for e in element.sub_elements()])
+ elif isinstance(element, EnrichedElement):
+ return EnrichedElement([_increase_degree(e, degree_rise)
+ for e in element.sub_elements()])
+ else:
+ error("Element reconstruction is only done to stay compatible with hacks in DOLFIN. Not expecting a %r" % (element,))
- if element.family() == "Real":
- return element
- return FiniteElement(element.family(), element.domain(), element.degree()+1)
+def _change_family(element, family):
+ if isinstance(element, (FiniteElement, VectorElement, TensorElement)):
+ return reconstruct_element(element, family, element.cell(), element.degree())
+ elif isinstance(element, MixedElement):
+ return MixedElement([_change_family(e, family)
+ for e in element.sub_elements()])
+ elif isinstance(element, EnrichedElement):
+ return EnrichedElement([_change_family(e, family)
+ for e in element.sub_elements()])
+ else:
+ error("Element reconstruction is only done to stay compatible with hacks in DOLFIN. Not expecting a %r" % (element,))
diff --git a/ufl/algorithms/estimate_degrees.py b/ufl/algorithms/estimate_degrees.py
index b0901ce..853f6aa 100644
--- a/ufl/algorithms/estimate_degrees.py
+++ b/ufl/algorithms/estimate_degrees.py
@@ -1,6 +1,7 @@
+# -*- coding: utf-8 -*-
"""Algorithms for estimating polynomial degrees of expressions."""
-# Copyright (C) 2008-2014 Martin Sandve Alnes and Anders Logg
+# Copyright (C) 2008-2015 Martin Sandve Alnæs and Anders Logg
#
# This file is part of UFL.
#
@@ -21,17 +22,19 @@
# Modified by Jan Blechta, 2012
from ufl.assertions import ufl_assert
-from ufl.log import warning
+from ufl.log import warning, error
from ufl.form import Form
from ufl.integral import Integral
-from ufl.algorithms.transformer import Transformer
+from ufl.algorithms.multifunction import MultiFunction
+from ufl.corealg.map_dag import map_expr_dags
+from ufl.checks import is_cellwise_constant
-class SumDegreeEstimator(Transformer):
+class SumDegreeEstimator(MultiFunction):
"This algorithm is exact for a few operators and heuristic for many."
def __init__(self, default_degree, element_replace_map):
- Transformer.__init__(self)
+ MultiFunction.__init__(self)
self.default_degree = default_degree
self.element_replace_map = element_replace_map
@@ -41,15 +44,15 @@ class SumDegreeEstimator(Transformer):
def geometric_quantity(self, v):
"Some geometric quantities are cellwise constant. Others are nonpolynomial and thus hard to estimate."
- if v.is_cellwise_constant():
+ if is_cellwise_constant(v):
return 0
else:
# As a heuristic, just returning domain degree to bump up degree somewhat
- return v.domain().coordinate_element().degree()
+ return v.ufl_domain().ufl_coordinate_element().degree()
def spatial_coordinate(self, v):
"A coordinate provides additional degrees depending on coordinate field of domain."
- return v.domain().coordinate_element().degree()
+ return v.ufl_domain().ufl_coordinate_element().degree()
def cell_coordinate(self, v):
"A coordinate provides one additional degree."
@@ -58,12 +61,12 @@ class SumDegreeEstimator(Transformer):
def argument(self, v):
"""A form argument provides a degree depending on the element,
or the default degree if the element has no degree."""
- return v.element().degree() # FIXME: Use component to improve accuracy for mixed elements
+ return v.ufl_element().degree() # FIXME: Use component to improve accuracy for mixed elements
def coefficient(self, v):
"""A form argument provides a degree depending on the element,
or the default degree if the element has no degree."""
- e = v.element()
+ e = v.ufl_element()
e = self.element_replace_map.get(e, e)
d = e.degree() # FIXME: Use component to improve accuracy for mixed elements
if d is None:
@@ -111,6 +114,8 @@ class SumDegreeEstimator(Transformer):
def label(self, v):
return None
# Fall-through, indexing and similar types
+ def reference_value(self, rv, f):
+ return f
def variable(self, v, e, l):
return e
def transposed(self, v, A):
@@ -133,11 +138,14 @@ class SumDegreeEstimator(Transformer):
# TODO: Need a new algorithm which considers direction of derivatives of form arguments
# A spatial derivative reduces the degree with one
grad = _reduce_degree
+ reference_grad = _reduce_degree
# Handling these types although they should not occur... please apply preprocessing before using this algorithm:
nabla_grad = _reduce_degree
div = _reduce_degree
+ reference_div = _reduce_degree
nabla_div = _reduce_degree
curl = _reduce_degree
+ reference_curl = _reduce_degree
def cell_avg(self, v, a):
"Cell average of a function is always cellwise constant."
@@ -272,11 +280,11 @@ def estimate_total_polynomial_degree(e, default_degree=1, element_replace_map={}
de = SumDegreeEstimator(default_degree, element_replace_map)
if isinstance(e, Form):
ufl_assert(e.integrals(), "Got form with no integrals!")
- degrees = [de.visit(integral.integrand()) for integral in e.integrals()]
+ degrees = map_expr_dags(de, [it.integrand() for it in e.integrals()])
elif isinstance(e, Integral):
- degrees = [de.visit(e.integrand())]
+ degrees = map_expr_dags(de, [e.integrand()])
else:
- degrees = [de.visit(e)]
+ degrees = map_expr_dags(de, [e])
degree = max(degrees) if degrees else default_degree
return degree
@@ -289,7 +297,7 @@ def __unused__extract_max_quadrature_element_degree(integral):
elements in integral. Returns None if not found."""
quadrature_elements = [e for e in extract_elements(integral) if "Quadrature" in e.family()]
degrees = [element.degree() for element in quadrature_elements]
- degrees = [q for q in degrees if not q is None]
+ degrees = [q for q in degrees if q is not None]
if not degrees:
return None
max_degree = quadrature_elements[0].degree()
@@ -301,7 +309,7 @@ def __unused__extract_max_quadrature_element_degree(integral):
def __unused__estimate_quadrature_degree(integral):
"Estimate the necessary quadrature order for integral using the sum of argument degrees."
arguments = extract_arguments(integral)
- degrees = [v.element().degree() for v in arguments]
+ degrees = [v.ufl_element().degree() for v in arguments]
if len(arguments) == 0:
return None
if len(arguments) == 1:
diff --git a/ufl/algorithms/expand_compounds.py b/ufl/algorithms/expand_compounds.py
index 04aabf6..70a92ec 100644
--- a/ufl/algorithms/expand_compounds.py
+++ b/ufl/algorithms/expand_compounds.py
@@ -1,7 +1,8 @@
+# -*- coding: utf-8 -*-
"""Algorithm for expanding compound expressions into
equivalent representations using basic operators."""
-# Copyright (C) 2008-2014 Martin Sandve Alnes and Anders Logg
+# Copyright (C) 2008-2015 Martin Sandve Alnæs and Anders Logg
#
# This file is part of UFL.
#
@@ -20,201 +21,7 @@ equivalent representations using basic operators."""
#
# Modified by Anders Logg, 2009-2010
-from ufl.log import error, warning
-from ufl.assertions import ufl_assert
-from ufl.classes import Product, Index, Zero, FormArgument, Grad
-from ufl.core.multiindex import indices
-from ufl.tensors import as_tensor, as_matrix, as_vector
-from ufl.algorithms.transformer import Transformer, ReuseTransformer, apply_transformer
-from ufl.compound_expressions import deviatoric_expr, determinant_expr, cofactor_expr, adj_expr, inverse_expr
+from ufl.algorithms.apply_algebra_lowering import apply_algebra_lowering
-class CompoundExpander(ReuseTransformer):
- "Expands compound expressions to equivalent representations using basic operators."
- def __init__(self):
- ReuseTransformer.__init__(self)
-
- # ------------ Compound tensor operators
-
- def trace(self, o, A):
- i = Index()
- return A[i, i]
-
- def transposed(self, o, A):
- i, j = indices(2)
- return as_tensor(A[i, j], (j, i))
-
- def _square_matrix_shape(self, A):
- sh = A.ufl_shape
- ufl_assert(sh[0] == sh[1], "Expecting square matrix.")
- ufl_assert(sh[0] is not None, "Unknown dimension.")
- return sh
-
- def deviatoric(self, o, A):
- return deviatoric_expr(A)
-
- def skew(self, o, A):
- i, j = indices(2)
- return as_matrix( (A[i, j] - A[j, i]) / 2, (i, j) )
-
- def sym(self, o, A):
- i, j = indices(2)
- return as_matrix( (A[i, j] + A[j, i]) / 2, (i, j) )
-
- def cross(self, o, a, b):
- def c(i, j):
- return Product(a[i], b[j]) - Product(a[j], b[i])
- return as_vector((c(1, 2), c(2, 0), c(0, 1)))
-
- def dot(self, o, a, b):
- ai = indices(a.rank()-1)
- bi = indices(b.rank()-1)
- k = indices(1)
- # Create an IndexSum over a Product
- s = a[ai+k]*b[k+bi]
- return as_tensor(s, ai+bi)
-
- def inner(self, o, a, b):
- ufl_assert(a.rank() == b.rank())
- ii = indices(a.rank())
- # Create multiple IndexSums over a Product
- s = a[ii]*b[ii]
- return s
-
- def outer(self, o, a, b):
- ii = indices(a.rank())
- jj = indices(b.rank())
- # Create a Product with no shared indices
- s = a[ii]*b[jj]
- return as_tensor(s, ii+jj)
-
- def determinant(self, o, A):
- return determinant_expr(A)
-
- def cofactor(self, o, A):
- return cofactor_expr(A)
-
- def inverse(self, o, A):
- return inverse_expr(A)
-
- # ------------ Compound differential operators
-
- def div(self, o, a):
- i = Index()
- return a[..., i].dx(i)
-
- def grad(self, o, a):
- return self.reuse_if_possible(o, a)
-
- def nabla_div(self, o, a):
- i = Index()
- return a[i, ...].dx(i)
-
- def nabla_grad(self, o, a):
- j = Index()
- if a.rank() > 0:
- ii = tuple(indices(a.rank()))
- return as_tensor(a[ii].dx(j), (j,) + ii)
- else:
- return as_tensor(a.dx(j), (j,))
-
- def curl(self, o, a):
- # o = curl a = "[a.dx(1), -a.dx(0)]" if a.ufl_shape == ()
- # o = curl a = "cross(nabla, (a0, a1, 0))[2]" if a.ufl_shape == (2,)
- # o = curl a = "cross(nabla, a)" if a.ufl_shape == (3,)
- def c(i, j):
- return a[j].dx(i) - a[i].dx(j)
- sh = a.ufl_shape
- if sh == ():
- return as_vector((a.dx(1), -a.dx(0)))
- if sh == (2,):
- return c(0, 1)
- if sh == (3,):
- return as_vector((c(1, 2), c(2, 0), c(0, 1)))
- error("Invalid shape %s of curl argument." % (sh,))
-
-
-"""
-FIXME: Make expand_compounds_prediff skip types that we make
-work in expand_derivatives, one by one, and optionally
-use it instead of expand_compounds from expand_derivatives.
-"""
-
-class CompoundExpanderPreDiff(CompoundExpander):
- def __init__(self):
- CompoundExpander.__init__(self)
-
- #inner = Transformer.reuse_if_possible
- #dot = Transformer.reuse_if_possible
-
- def grad(self, o, a):
- return self.reuse_if_possible(o, a)
-
- def nabla_grad(self, o, a):
- r = o.rank()
- ii = indices(r)
- jj = ii[-1:] + ii[:-1]
- return as_tensor(Grad(a)[ii], jj)
-
- def div(self, o, a):
- i = Index()
- return Grad(a)[..., i, i]
-
- def nabla_div(self, o, a):
- i = Index()
- return Grad(a)[i, ..., i]
-
- def curl(self, o, a):
- # o = curl a = "[a.dx(1), -a.dx(0)]" if a.ufl_shape == ()
- # o = curl a = "cross(nabla, (a0, a1, 0))[2]" if a.ufl_shape == (2,)
- # o = curl a = "cross(nabla, a)" if a.ufl_shape == (3,)
- Da = Grad(a)
- def c(i, j):
- #return a[j].dx(i) - a[i].dx(j)
- return Da[j, i] - Da[i, j]
- sh = a.ufl_shape
- if sh == ():
- #return as_vector((a.dx(1), -a.dx(0)))
- return as_vector((Da[1], -Da[0]))
- if sh == (2,):
- return c(0, 1)
- if sh == (3,):
- return as_vector((c(1, 2), c(2, 0), c(0, 1)))
- error("Invalid shape %s of curl argument." % (sh,))
-
-class CompoundExpanderPostDiff(CompoundExpander):
- def __init__(self):
- CompoundExpander.__init__(self)
-
- def nabla_grad(self, o, a, i):
- error("This should not happen.")
-
- def div(self, o, a, i):
- error("This should not happen.")
-
- def nabla_div(self, o, a, i):
- error("This should not happen.")
-
- def curl(self, o, a, i):
- error("This should not happen.")
-
-def expand_compounds1(e):
- """Expand compound objects into basic operators.
- Requires e to have a well defined geometric dimension."""
- return apply_transformer(e, CompoundExpander())
-
-def expand_compounds2(e):
- """Expand compound objects into basic operators.
- Requires e to have a well defined geometric dimension."""
- return expand_compounds_postdiff(expand_compounds_prediff(e))
-
-def expand_compounds_prediff(e):
- """Expand compound objects into basic operators.
- Requires e to have a well defined geometric dimension."""
- return apply_transformer(e, CompoundExpanderPreDiff())
-
-def expand_compounds_postdiff(e):
- """Expand compound objects into basic operators.
- Requires e to have a well defined geometric dimension."""
- return apply_transformer(e, CompoundExpanderPostDiff())
-
-expand_compounds = expand_compounds1
+def expand_compounds(e):
+ return apply_algebra_lowering(e)
diff --git a/ufl/algorithms/expand_indices.py b/ufl/algorithms/expand_indices.py
index eeb069a..581f311 100644
--- a/ufl/algorithms/expand_indices.py
+++ b/ufl/algorithms/expand_indices.py
@@ -1,8 +1,9 @@
+# -*- coding: utf-8 -*-
"""This module defines expression transformation utilities,
for expanding free indices in expressions to explicit fixed
indices only."""
-# Copyright (C) 2008-2014 Martin Sandve Alnes
+# Copyright (C) 2008-2015 Martin Sandve Alnæs
#
# This file is part of UFL.
#
@@ -25,7 +26,7 @@ from six.moves import zip
from six.moves import xrange as range
from ufl.log import error
-from ufl.common import Stack, StackDict
+from ufl.utils.stacks import Stack, StackDict
from ufl.assertions import ufl_assert
from ufl.finiteelement import TensorElement
from ufl.classes import Expr, Terminal, ListTensor, IndexSum, Indexed, FormArgument
@@ -35,7 +36,7 @@ from ufl.constantvalue import Zero
from ufl.core.multiindex import Index, FixedIndex, MultiIndex
from ufl.differentiation import Grad
from ufl.algorithms.transformer import ReuseTransformer, apply_transformer
-from ufl.corealg.traversal import pre_traversal
+from ufl.corealg.traversal import unique_pre_traversal
class IndexExpander(ReuseTransformer):
@@ -63,7 +64,7 @@ class IndexExpander(ReuseTransformer):
if sh == ():
return x
else:
- e = x.element()
+ e = x.ufl_element()
r = len(sh)
# Get component
@@ -81,7 +82,8 @@ class IndexExpander(ReuseTransformer):
ufl_assert(len(x.ufl_shape) == len(self.component()), "Component size mismatch.")
s = set(x.ufl_free_indices) - set(i.count() for i in self._index2value.keys())
- if s: error("Free index set mismatch, these indices have no value assigned: %s." % str(s))
+ if s:
+ error("Free index set mismatch, these indices have no value assigned: %s." % str(s))
# There is no index/shape info in this zero because that is asserted above
return Zero()
@@ -92,7 +94,8 @@ class IndexExpander(ReuseTransformer):
ufl_assert(len(x.ufl_shape) == len(self.component()), "Component size mismatch.")
s = set(x.ufl_free_indices) - set(i.count() for i in self._index2value.keys())
- if s: error("Free index set mismatch, these indices have no value assigned: %s." % str(s))
+ if s:
+ error("Free index set mismatch, these indices have no value assigned: %s." % str(s))
return x._ufl_class_(x.value())
@@ -211,6 +214,6 @@ def purge_list_tensors(expr):
"""Get rid of all ListTensor instances by expanding
expressions to use their components directly.
Will usually increase the size of the expression."""
- if any(isinstance(subexpr, ListTensor) for subexpr in pre_traversal(expr)):
+ if any(isinstance(subexpr, ListTensor) for subexpr in unique_pre_traversal(expr)):
return expand_indices(expr) # TODO: Only expand what's necessary to get rid of list tensors
return expr
diff --git a/ufl/algorithms/formdata.py b/ufl/algorithms/formdata.py
index 7ed827e..d77cb2e 100644
--- a/ufl/algorithms/formdata.py
+++ b/ufl/algorithms/formdata.py
@@ -1,6 +1,7 @@
+# -*- coding: utf-8 -*-
"""FormData class easy for collecting of various data about a form."""
-# Copyright (C) 2008-2014 Martin Sandve Alnes
+# Copyright (C) 2008-2015 Martin Sandve Alnæs
#
# This file is part of UFL.
#
@@ -19,7 +20,7 @@
#
# Modified by Anders Logg, 2008.
-from ufl.common import lstr, tstr, estr
+from ufl.utils.formatting import lstr, tstr, estr
from ufl.assertions import ufl_assert
class FormData(object):
@@ -33,23 +34,24 @@ class FormData(object):
def __str__(self):
"Return formatted summary of form data"
- types = sorted(self.num_sub_domains.keys())
+ types = sorted(self.max_subdomain_ids.keys())
+ geometry = (
+ ("Geometric dimension", self.geometric_dimension),
+ )
subdomains = tuple(("Number of %s subdomains" % integral_type,
- self.num_sub_domains[integral_type]) for integral_type in types)
- return tstr(
- (# Geometry
- ("Geometric dimension", self.geometric_dimension),
- ) + subdomains + (
- # Arguments
- ("Rank", self.rank),
- ("Arguments", lstr(self.original_form.arguments())),
- # Coefficients
- ("Number of coefficients", self.num_coefficients),
- ("Coefficients", lstr(self.reduced_coefficients)),
- # Elements
- ("Unique elements", estr(self.unique_elements)),
- ("Unique sub elements", estr(self.unique_sub_elements)),
- ))
+ self.max_subdomain_ids[integral_type]) for integral_type in types)
+ functions = (
+ # Arguments
+ ("Rank", self.rank),
+ ("Arguments", lstr(self.original_form.arguments())),
+ # Coefficients
+ ("Number of coefficients", self.num_coefficients),
+ ("Coefficients", lstr(self.reduced_coefficients)),
+ # Elements
+ ("Unique elements", estr(self.unique_elements)),
+ ("Unique sub elements", estr(self.unique_sub_elements)),
+ )
+ return tstr(geometry + subdomains + functions)
class ExprData(object):
"""
diff --git a/ufl/algorithms/formfiles.py b/ufl/algorithms/formfiles.py
index 86280c1..21d3d7f 100644
--- a/ufl/algorithms/formfiles.py
+++ b/ufl/algorithms/formfiles.py
@@ -1,6 +1,7 @@
+# -*- coding: utf-8 -*-
"""A collection of utility algorithms for handling UFL files."""
-# Copyright (C) 2008-2014 Martin Sandve Alnes
+# Copyright (C) 2008-2015 Martin Sandve Alnæs
#
# This file is part of UFL.
#
@@ -20,7 +21,8 @@
# Modified by Anders Logg, 2008-2009.
# Modified by Marie E. Rognes, 2011.
-import os, re
+import os
+import re
from ufl.log import error, warning
from ufl.utils.sorting import sorted_by_key
from ufl.assertions import ufl_assert
@@ -41,7 +43,7 @@ class FileData(object):
self.reserved_objects = {}
def __bool__(self):
- return bool(self.elements or self.coefficients or self.forms or self.expressions or\
+ return bool(self.elements or self.coefficients or self.forms or self.expressions or
self.object_names or self.object_by_name or self.reserved_objects)
__nonzero__ = __bool__
@@ -82,14 +84,14 @@ def execute_ufl_code(uflcode, filename):
# Execute code
namespace = {}
try:
- pycode = "from ufl import *\n" + uflcode
+ pycode = "# -*- coding: utf-8 -*-\nfrom ufl import *\n" + uflcode
exec(pycode, namespace)
except:
# Dump python code for debugging if this fails
basename = os.path.splitext(os.path.basename(filename))[0]
basename = "%s_debug" % basename
pyname = "%s.py" % basename
- pycode = "#!/usr/bin/env python\nfrom ufl import *\nset_level(DEBUG)\n" + uflcode
+ pycode = "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\nfrom ufl import *\nset_level(DEBUG)\n" + uflcode
with open(pyname, "w") as f:
f.write(pycode)
warning(infostring % pyname)
diff --git a/ufl/algorithms/formsplitter.py b/ufl/algorithms/formsplitter.py
new file mode 100755
index 0000000..790ef70
--- /dev/null
+++ b/ufl/algorithms/formsplitter.py
@@ -0,0 +1,68 @@
+# -*- coding: utf-8 -*-
+"Extract part of a form in a mixed FunctionSpace."
+
+# Copyright (C) 2016 Chris Richardson and Lawrence Mitchell
+#
+# This file is part of UFL.
+#
+# UFL is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# UFL is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# 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 ufl.corealg.multifunction import MultiFunction
+from ufl.algorithms.map_integrands import map_integrand_dags
+from ufl.constantvalue import Zero
+from ufl.tensors import as_vector
+from ufl.argument import Argument
+from ufl.functionspace import FunctionSpace
+
+class FormSplitter(MultiFunction):
+
+ def split(self, form, ix, iy=0):
+ # Remember which block to extract
+ self.idx = [ix, iy]
+ return map_integrand_dags(self, form)
+
+ def argument(self, obj):
+ Q = obj.ufl_function_space()
+ dom = Q.ufl_domain()
+ sub_elements = obj.ufl_element().sub_elements()
+
+ # If not a mixed element, do nothing
+ if (len(sub_elements) == 0):
+ return obj
+
+ # Split into sub-elements, creating appropriate space for each
+ args = []
+ for i, sub_elem in enumerate(sub_elements):
+ Q_i = FunctionSpace(dom, sub_elem)
+ a = Argument(Q_i, obj.number(), part=obj.part())
+
+ indices =[()]
+ for m in a.ufl_shape:
+ indices = [(k + (j,)) for k in indices for j in range(m)]
+
+ if (i == self.idx[obj.number()]):
+ args += [a[j] for j in indices]
+ else:
+ args += [Zero() for j in indices]
+
+ return as_vector(args)
+
+ def multi_index(self, obj):
+ return obj
+
+ expr = MultiFunction.reuse_if_untouched
+
+def block_split(form, ix, iy=0):
+ fs = FormSplitter()
+ return fs.split(form, ix, iy)
diff --git a/ufl/algorithms/formtransformations.py b/ufl/algorithms/formtransformations.py
index f245107..81caa5b 100644
--- a/ufl/algorithms/formtransformations.py
+++ b/ufl/algorithms/formtransformations.py
@@ -1,7 +1,8 @@
+# -*- coding: utf-8 -*-
"""This module defines utilities for transforming
complete Forms into new related Forms."""
-# Copyright (C) 2008-2014 Martin Sandve Alnes
+# Copyright (C) 2008-2015 Martin Sandve Alnæs
#
# This file is part of UFL.
#
@@ -25,7 +26,7 @@ complete Forms into new related Forms."""
from six import iteritems
from six.moves import xrange as range
-from ufl.common import product
+from ufl.utils.sequences import product
from ufl.log import error, warning, debug
from ufl.assertions import ufl_assert
@@ -410,13 +411,11 @@ def compute_form_action(form, coefficient):
# Pick last argument (will be replaced)
u = arguments[-1]
- e = u.element()
+ fs = u.ufl_function_space()
if coefficient is None:
- coefficient = Coefficient(e)
- else:
- #ufl_assert(coefficient.element() == e, \
- if coefficient.element() != e:
- debug("Computing action of form on a coefficient in a different element space.")
+ coefficient = Coefficient(fs)
+ elif coefficient.ufl_function_space() != fs:
+ debug("Computing action of form on a coefficient in a different function space.")
return replace(form, { u: coefficient })
def compute_energy_norm(form, coefficient):
@@ -436,14 +435,14 @@ def compute_energy_norm(form, coefficient):
ufl_assert(len(arguments) == 2, "Expecting bilinear form.")
v, u = arguments
- e = u.element()
- e2 = v.element()
+ e = u.ufl_function_space()
+ e2 = v.ufl_function_space()
ufl_assert(e == e2, "Expecting equal finite elements for test and trial functions, got '%s' and '%s'." % (str(e), str(e2)))
if coefficient is None:
coefficient = Coefficient(e)
else:
- ufl_assert(coefficient.element() == e, \
- "Trying to compute action of form on a "\
+ ufl_assert(coefficient.ufl_function_space() == e,
+ "Trying to compute action of form on a "
"coefficient in an incompatible element space.")
return replace(form, { u: coefficient, v: coefficient })
@@ -465,8 +464,8 @@ def compute_form_adjoint(form, reordered_arguments=None):
ufl_assert(v.number() < u.number(), "Mistaken assumption in code!")
if reordered_arguments is None:
- reordered_u = u.reconstruct(number=v.number(), part=v.part())
- reordered_v = v.reconstruct(number=u.number(), part=u.part())
+ reordered_u = Argument(u.ufl_function_space(), number=v.number(), part=v.part())
+ reordered_v = Argument(v.ufl_function_space(), number=u.number(), part=u.part())
else:
reordered_u, reordered_v = reordered_arguments
@@ -478,9 +477,9 @@ def compute_form_adjoint(form, reordered_arguments=None):
ufl_assert(reordered_v.part() == u.part(),
"Ordering of new arguments is the same as the old arguments!")
- ufl_assert(reordered_u.element() == u.element(),
+ ufl_assert(reordered_u.ufl_function_space() == u.ufl_function_space(),
"Element mismatch between new and old arguments (trial functions).")
- ufl_assert(reordered_v.element() == v.element(),
+ ufl_assert(reordered_v.ufl_function_space() == v.ufl_function_space(),
"Element mismatch between new and old arguments (test functions).")
return replace(form, {v: reordered_v, u: reordered_u})
diff --git a/ufl/algorithms/forward_ad.py b/ufl/algorithms/forward_ad.py
index 6e190e6..7abf1a0 100644
--- a/ufl/algorithms/forward_ad.py
+++ b/ufl/algorithms/forward_ad.py
@@ -1,6 +1,7 @@
+# -*- coding: utf-8 -*-
"""Forward mode AD implementation."""
-# Copyright (C) 2008-2014 Martin Sandve Alnes
+# Copyright (C) 2008-2015 Martin Sandve Alnæs
#
# This file is part of UFL.
#
@@ -28,12 +29,13 @@ from six.moves import zip
from math import pi
from ufl.log import error, warning, debug
from ufl.assertions import ufl_assert
-from ufl.common import unzip, subdict, lstr
+from ufl.utils.sequences import unzip
+from ufl.utils.dicts import subdict
+from ufl.utils.formatting import lstr
# All classes:
from ufl.core.terminal import Terminal
-from ufl.constantvalue import ConstantValue, Zero, IntValue, Identity,\
- is_true_ufl_scalar, is_ufl_scalar
+from ufl.constantvalue import ConstantValue, Zero, IntValue, Identity, is_true_ufl_scalar, is_ufl_scalar
from ufl.variable import Variable
from ufl.coefficient import Coefficient, FormArgument
from ufl.core.multiindex import MultiIndex, Index, FixedIndex, indices
@@ -41,19 +43,19 @@ from ufl.indexed import Indexed
from ufl.indexsum import IndexSum
from ufl.tensors import ListTensor, ComponentTensor, as_tensor, as_scalar, unit_indexed_tensor, unwrap_list_tensor
from ufl.algebra import Sum, Product, Division, Power, Abs
-from ufl.tensoralgebra import Transposed, Outer, Inner, Dot, Cross, Trace, \
- Determinant, Inverse, Deviatoric, Cofactor
+from ufl.tensoralgebra import Transposed, Outer, Inner, Dot, Cross, Trace, Determinant, Inverse, Deviatoric, Cofactor
from ufl.mathfunctions import MathFunction, Sqrt, Exp, Ln, Cos, Sin, Tan, Acos, Asin, Atan, Atan2, Erf, BesselJ, BesselY, BesselI, BesselK
from ufl.restriction import Restricted, PositiveRestricted, NegativeRestricted
-from ufl.differentiation import Derivative, CoefficientDerivative,\
- VariableDerivative, Grad
+from ufl.differentiation import Derivative, CoefficientDerivative, VariableDerivative, Grad
from ufl.conditional import EQ, NE, LE, GE, LT, GT, Conditional
from ufl.exprcontainers import ExprList, ExprMapping
-from ufl.operators import dot, inner, outer, lt, eq, conditional, sign, \
- sqrt, exp, ln, cos, sin, tan, cosh, sinh, tanh, acos, asin, atan, atan_2, \
- erf, bessel_J, bessel_Y, bessel_I, bessel_K, \
- cell_avg, facet_avg
+from ufl.operators import (dot, inner, outer, lt, eq, conditional, sign,
+ sqrt, exp, ln, cos, sin, tan, cosh, sinh, tanh, acos, asin, atan, atan_2,
+ erf, bessel_J, bessel_Y, bessel_I, bessel_K,
+ cell_avg, facet_avg)
from ufl.algorithms.transformer import Transformer
+from ufl.domain import find_geometric_dimension
+from ufl.checks import is_cellwise_constant
class ForwardAD(Transformer):
@@ -66,7 +68,7 @@ class ForwardAD(Transformer):
"Debugging hook, enable this by renaming to 'visit'."
r = Transformer.visit(self, o)
f, df = r
- if not f is o:
+ if f is not o:
debug("In ForwardAD.visit, didn't get back o:")
debug(" o: %s" % str(o))
debug(" f: %s" % str(f))
@@ -132,7 +134,7 @@ class ForwardAD(Transformer):
if isinstance(Ap, Zero):
op = self._make_zero_diff(o)
else:
- r = Ap.rank() - len(jj)
+ r = len(Ap.ufl_shape) - len(jj)
if r:
ii = indices(r)
op = Indexed(Ap, MultiIndex(jj.indices() + ii))
@@ -442,9 +444,9 @@ class ForwardAD(Transformer):
def binary_condition(self, o, l, r):
o = self.reuse_if_possible(o, l[0], r[0])
#if any(not (isinstance(op[1], Zero) or op[1] is None) for op in (l, r)):
- # warning("Differentiating a conditional with a condition "\
- # "that depends on the differentiation variable."\
- # "Assuming continuity of conditional. The condition "\
+ # warning("Differentiating a conditional with a condition "
+ # "that depends on the differentiation variable."
+ # "Assuming continuity of conditional. The condition "
# "will not be differentiated.")
oprime = None # Shouldn't be used anywhere
return (o, oprime)
@@ -452,9 +454,9 @@ class ForwardAD(Transformer):
def not_condition(self, o, c):
o = self.reuse_if_possible(o, c[0])
#if not (isinstance(c[1], Zero) or c[1] is None):
- # warning("Differentiating a conditional with a condition "\
- # "that depends on the differentiation variable."\
- # "Assuming continuity of conditional. The condition "\
+ # warning("Differentiating a conditional with a condition "
+ # "that depends on the differentiation variable."
+ # "Assuming continuity of conditional. The condition "
# "will not be differentiated.")
oprime = None # Shouldn't be used anywhere
return (o, oprime)
@@ -499,21 +501,21 @@ class GradAD(ForwardAD):
def geometric_quantity(self, o):
"Represent grad(g) as Grad(g)."
# Collapse gradient of cellwise constant function to zero
- if o.is_cellwise_constant():
+ if is_cellwise_constant(o):
return self.terminal(o)
return (o, Grad(o))
def spatial_coordinate(self, o):
"Gradient of x w.r.t. x is Id."
if not hasattr(self, '_Id'):
- gdim = o.geometric_dimension()
+ gdim = find_geometric_dimension(o)
self._Id = Identity(gdim)
return (o, self._Id)
def cell_coordinate(self, o):
"Gradient of X w.r.t. x is K. But I'm not sure if we want to allow this."
error("This has not been validated. Does it make sense to do this here?")
- K = JacobianInverse(o.domain())
+ K = JacobianInverse(o.ufl_domain())
return (o, K)
# TODO: Implement rules for some of these types?
@@ -543,14 +545,14 @@ class GradAD(ForwardAD):
"Represent grad(f) as Grad(f)."
# Collapse gradient of cellwise constant function to zero
# FIXME: Enable this after fixing issue#13
- #if o.is_cellwise_constant():
+ #if is_cellwise_constant(o):
# return zero(...) # TODO: zero annotated with argument
return (o, Grad(o))
def coefficient(self, o):
"Represent grad(f) as Grad(f)."
# Collapse gradient of cellwise constant function to zero
- if o.is_cellwise_constant():
+ if is_cellwise_constant(o):
return self.terminal(o)
return (o, Grad(o))
@@ -559,7 +561,7 @@ class GradAD(ForwardAD):
# TODO: Not sure how to detect that gradient of f is cellwise constant.
# Can we trust element degrees?
- #if o.is_cellwise_constant():
+ #if is_cellwise_constant(o):
# return self.terminal(o)
# TODO: Maybe we can ask "f.has_derivatives_of_order(n)" to check
# if we should make a zero here?
@@ -667,7 +669,7 @@ class CoefficientAD(ForwardAD):
# Make sure we have a tuple to match the self._v tuple
if not isinstance(oprimes, tuple):
oprimes = (oprimes,)
- ufl_assert(len(oprimes) == len(self._v), "Got a tuple of arguments, "+\
+ ufl_assert(len(oprimes) == len(self._v), "Got a tuple of arguments, "+
"expecting a matching tuple of coefficient derivatives.")
# Compute do/dw_j = do/dw_h : v.
@@ -762,7 +764,8 @@ class CoefficientAD(ForwardAD):
# Analyse differentiation variable coefficient
if isinstance(w, FormArgument):
- if not w == o: continue
+ if not w == o:
+ continue
wshape = w.ufl_shape
if isinstance(v, FormArgument):
@@ -788,7 +791,8 @@ class CoefficientAD(ForwardAD):
# Case: d/dt [w[...] + t v[...]]
# Case: d/dt [w[...] + t v]
wval, wcomp = w.ufl_operands
- if not wval == o: continue
+ if not wval == o:
+ continue
assert isinstance(wval, FormArgument)
ufl_assert(all(isinstance(k, FixedIndex) for k in wcomp),
"Expecting only fixed indices in differentiation variable.")
@@ -813,7 +817,7 @@ class CoefficientAD(ForwardAD):
# Make sure we have a tuple to match the self._v tuple
if not isinstance(oprimes, tuple):
oprimes = (oprimes,)
- ufl_assert(len(oprimes) == len(self._v), "Got a tuple of arguments, "+\
+ ufl_assert(len(oprimes) == len(self._v), "Got a tuple of arguments, "+
"expecting a matching tuple of coefficient derivatives.")
# Compute dg/dw_j = dg/dw_h : v.
@@ -879,7 +883,7 @@ def apply_nested_forward_ad(expr):
# Reconstruct if necessary
need_reconstruct = not (preops == postops) # FIXME: Is this efficient? O(n)?
if need_reconstruct:
- expr = expr.reconstruct(*postops)
+ expr = expr._ufl_expr_reconstruct_(*postops)
return expr
elif isinstance(expr, Grad):
# Apply AD recursively to children
@@ -912,7 +916,7 @@ class UnusedADRules(object):
v, vp = v
ufl_assert(isinstance(vp, Zero), "TODO: What happens if vp != 0, i.e. v depends the differentiation variable?")
# Are there any issues with indices here? Not sure, think through it...
- oprime = o.reconstruct(fp, v)
+ oprime = o._ufl_expr_reconstruct_(fp, v)
return (o, oprime)
# --- Tensor algebra (compound types)
@@ -941,7 +945,7 @@ class UnusedADRules(object):
def commute(self, o, a):
"This should work for all single argument operators that commute with d/dw with w scalar."
aprime = a[1]
- return (o, o.reconstruct(aprime))
+ return (o, o._ufl_expr_reconstruct_(aprime))
# FIXME: Not true for derivatives w.r.t. nonscalar variables...
transposed = commute
@@ -955,10 +959,10 @@ class UnusedADRules(object):
curl = commute
def grad(self, o, a):
a, aprime = a
- if aprime.domains(): # TODO: Assuming this is equivalent to 'is_constant', which may not be the case...
- oprime = o.reconstruct(aprime)
- else:
+ if is_cellwise_constant(aprime):
oprime = self._make_zero_diff(o)
+ else:
+ oprime = o._ufl_expr_reconstruct_(aprime)
return (o, oprime)
class UnimplementedADRules(object):
diff --git a/ufl/algorithms/map_integrands.py b/ufl/algorithms/map_integrands.py
index 0a630ec..5dd5440 100644
--- a/ufl/algorithms/map_integrands.py
+++ b/ufl/algorithms/map_integrands.py
@@ -1,6 +1,7 @@
+# -*- coding: utf-8 -*-
"""Basic algorithms for applying functions to subexpressions."""
-# Copyright (C) 2008-2014 Martin Sandve Alnes
+# Copyright (C) 2008-2015 Martin Sandve Alnæs
#
# This file is part of UFL.
#
diff --git a/ufl/algorithms/multifunction.py b/ufl/algorithms/multifunction.py
index e3f25df..63736ea 100644
--- a/ufl/algorithms/multifunction.py
+++ b/ufl/algorithms/multifunction.py
@@ -1,2 +1,3 @@
+# -*- coding: utf-8 -*-
# Moved here to be usable in ufl.* files without depending on ufl.algorithms.*...
from ufl.corealg.multifunction import MultiFunction
diff --git a/ufl/algorithms/pdiffs.py b/ufl/algorithms/pdiffs.py
index 9a5f9c7..b77af30 100644
--- a/ufl/algorithms/pdiffs.py
+++ b/ufl/algorithms/pdiffs.py
@@ -1,7 +1,8 @@
+# -*- coding: utf-8 -*-
"""This module defines partial differentiation rules for
all relevant operands for use with reverse mode AD."""
-# Copyright (C) 2008-2014 Martin Sandve Alnes
+# Copyright (C) 2008-2015 Martin Sandve Alnæs
#
# This file is part of UFL.
#
@@ -32,8 +33,6 @@ class PartialDerivativeComputer(MultiFunction):
"""NB! The main reason for keeping this out of the Expr hierarchy is
to avoid user mistakes in the form of mixups with total derivatives,
and to allow both reverse and forward mode AD."""
- #def __init__(self, spatial_dim):
- #self._spatial_dim = spatial_dim
def __init__(self):
MultiFunction.__init__(self)
@@ -225,19 +224,19 @@ class PartialDerivativeComputer(MultiFunction):
# --- Derivatives
def spatial_derivative(self, f):
- error("Partial derivative of spatial_derivative not implemented, "\
+ error("Partial derivative of spatial_derivative not implemented, "
"when is this called? apply_ad should make sure it isn't called.")
x, i = f.ufl_operands
return (None, None)
def variable_derivative(self, f):
- error("Partial derivative of variable_derivative not implemented, "\
+ error("Partial derivative of variable_derivative not implemented, "
"when is this called? apply_ad should make sure it isn't called.")
x, v = f.ufl_operands
return (None, None)
def coefficient_derivative(self, f):
- error("Partial derivative of coefficient_derivative not implemented, "\
+ error("Partial derivative of coefficient_derivative not implemented, "
"when is this called? apply_ad should make sure it isn't called.")
a, w, v = f.ufl_operands
return (None, None, None)
diff --git a/ufl/algorithms/predicates.py b/ufl/algorithms/predicates.py
index b62ffab..4665e44 100644
--- a/ufl/algorithms/predicates.py
+++ b/ufl/algorithms/predicates.py
@@ -1,6 +1,7 @@
+# -*- coding: utf-8 -*-
"""Functions to check properties of forms and integrals."""
-# Copyright (C) 2008-2014 Martin Sandve Alnes and Anders Logg
+# Copyright (C) 2008-2015 Martin Sandve Alnæs and Anders Logg
#
# This file is part of UFL.
#
@@ -38,8 +39,8 @@ def is_multilinear(form):
if len(nargs) == 1:
debug("This form is linear in %d arguments." % nargs[0])
if len(nargs) > 1:
- warning("This form has more than one argument "\
- "'configuration', it has terms that are linear in %s "\
+ warning("This form has more than one argument "
+ "'configuration', it has terms that are linear in %s "
"arguments respectively." % str(nargs))
except NotMultiLinearException as msg:
diff --git a/ufl/algorithms/renumbering.py b/ufl/algorithms/renumbering.py
index 0f2dedf..df33a8f 100644
--- a/ufl/algorithms/renumbering.py
+++ b/ufl/algorithms/renumbering.py
@@ -1,6 +1,7 @@
+# -*- coding: utf-8 -*-
"Algorithms for renumbering of counted objects, currently variables and indices."
-# Copyright (C) 2008-2014 Martin Sandve Alnes and Anders Logg
+# Copyright (C) 2008-2015 Martin Sandve Alnæs and Anders Logg
#
# This file is part of UFL.
#
@@ -18,7 +19,7 @@
# along with UFL. If not, see <http://www.gnu.org/licenses/>.
from six.moves import zip
-from ufl.common import Stack, StackDict
+from ufl.utils.stacks import Stack, StackDict
from ufl.log import error
from ufl.core.expr import Expr
from ufl.core.multiindex import Index, FixedIndex, MultiIndex
@@ -42,14 +43,18 @@ class VariableRenumberingTransformer(ReuseTransformer):
return v
class IndexRenumberingTransformer(VariableRenumberingTransformer):
-
+ "This is a poorly designed algorithm. It is used in some tests, please do not use for anything else."
def __init__(self):
VariableRenumberingTransformer.__init__(self)
self.index_map = {}
def zero(self, o):
- new_indices = tuple(self.index(Index(count=i) for i in o.ufl_free_indices))
- return o.reconstruct(new_indices)
+ fi = o.ufl_free_indices
+ fid = o.ufl_index_dimensions
+ mapped_fi = tuple(self.index(Index(count=i)) for i in fi)
+ paired_fid = [(mapped_fi[pos], fid[pos]) for pos, a in enumerate(fi)]
+ new_fi, new_fid = zip(*tuple(sorted(paired_fid)))
+ return Zero(o.ufl_shape, new_fi, new_fid)
def index(self, o):
if isinstance(o, FixedIndex):
@@ -63,7 +68,8 @@ class IndexRenumberingTransformer(VariableRenumberingTransformer):
return i
def multi_index(self, o):
- return MultiIndex(tuple(self.index(i) for i in o.indices()))
+ new_indices = tuple(self.index(i) for i in o.indices())
+ return MultiIndex(new_indices)
def renumber_indices(expr):
if isinstance(expr, Expr):
diff --git a/ufl/algorithms/replace.py b/ufl/algorithms/replace.py
index 1dd8381..18c20cf 100644
--- a/ufl/algorithms/replace.py
+++ b/ufl/algorithms/replace.py
@@ -1,6 +1,7 @@
+# -*- coding: utf-8 -*-
"""Algorithm for replacing terminals in an expression."""
-# Copyright (C) 2008-2014 Martin Sandve Alnes and Anders Logg
+# Copyright (C) 2008-2015 Martin Sandve Alnæs and Anders Logg
#
# This file is part of UFL.
#
diff --git a/ufl/algorithms/signature.py b/ufl/algorithms/signature.py
index e9d2a60..bd3a7b5 100644
--- a/ufl/algorithms/signature.py
+++ b/ufl/algorithms/signature.py
@@ -1,6 +1,7 @@
+# -*- coding: utf-8 -*-
"""Signature computation for forms."""
-# Copyright (C) 2012-2014 Martin Sandve Alnes
+# Copyright (C) 2012-2015 Martin Sandve Alnæs
#
# This file is part of UFL.
#
@@ -24,9 +25,9 @@ from ufl.classes import (Terminal, Label,
GeometricQuantity, ConstantValue,
ExprList, ExprMapping)
from ufl.log import error
-from ufl.corealg.traversal import traverse_unique_terminals
-from ufl.common import pre_traversal, sorted_by_count
-from ufl.geometry import join_domains
+from ufl.corealg.traversal import traverse_unique_terminals, pre_traversal
+from ufl.utils.sorting import sorted_by_count
+from ufl.domain import join_domains
from ufl.algorithms.domain_analysis import canonicalize_metadata
def compute_multiindex_hashdata(expr, index_numbering):
@@ -65,16 +66,16 @@ def compute_terminal_hashdata(expressions, renumbering):
data = compute_multiindex_hashdata(expr, index_numbering)
elif isinstance(expr, ConstantValue):
- data = expr.signature_data(renumbering)
+ data = expr._ufl_signature_data_(renumbering)
elif isinstance(expr, Coefficient):
- data = expr.signature_data(renumbering)
+ data = expr._ufl_signature_data_(renumbering)
elif isinstance(expr, Argument):
- data = expr.signature_data(renumbering)
+ data = expr._ufl_signature_data_(renumbering)
elif isinstance(expr, GeometricQuantity):
- data = expr.signature_data(renumbering)
+ data = expr._ufl_signature_data_(renumbering)
elif isinstance(expr, Label):
# Numbering labels as we visit them # TODO: Include in renumbering
@@ -107,52 +108,13 @@ def compute_expression_hashdata(expression, terminal_hashdata):
if expr._ufl_is_terminal_:
data = terminal_hashdata[expr]
else:
- data = expr._ufl_typecode_ # TODO: Use expr.signature_data()? More extensible, but more overhead.
+ data = expr._ufl_typecode_ # TODO: Use expr._ufl_signature_data_()? More extensible, but more overhead.
expression_hashdata.append(data)
# Oneliner: TODO: Benchmark, maybe use a generator?
#expression_hashdata = [(terminal_hashdata[expr] if expr._ufl_is_terminal_ else expr._ufl_typecode_)
# for expr in pre_traversal(expression)]
return expression_hashdata
-def build_domain_numbering(domains):
- # Create canonical numbering of domains for stable signature
- # (ordering defined by __lt__ implementation in Domain class)
- assert None not in domains
-
- # Collect domain keys
- items = []
- for i, domain in enumerate(domains):
- key = (domain.cell(), domain.label())
- items.append((key, i))
-
- # Build domain numbering, not allowing repeated keys
- domain_numbering = {}
- for key, i in items:
- if key in domain_numbering:
- error("Domain key %s occured twice!" % (key,))
- domain_numbering[key] = i
-
- # Build domain numbering extension for None-labeled domains, not allowing ambiguity
- from collections import defaultdict
- domain_numbering2 = defaultdict(list)
- for key, i in items:
- cell, label = key
- key2 = (cell, None)
- domain_numbering2[key2].append(domain_numbering[key])
-
- # Add None-based key only where unambiguous
- for key, i in items:
- cell, label = key
- key2 = (cell, None)
- if len(domain_numbering2[key2]) == 1:
- domain_numbering[key2] = domain_numbering[key]
- else:
- # Two domains occur with same properties but different label,
- # so we cannot decide which one to map None-labeled Domains to.
- pass
-
- return domain_numbering
-
def compute_expression_signature(expr, renumbering): # FIXME: Fix callers
# FIXME: Rewrite in terms of compute_form_signature?
@@ -182,7 +144,7 @@ def compute_form_signature(form, renumbering): # FIXME: Fix callers
integrand_hashdata = compute_expression_hashdata(integral.integrand(),
terminal_hashdata)
- domain_hashdata = integral.domain().signature_data(renumbering)
+ domain_hashdata = integral.ufl_domain()._ufl_signature_data_(renumbering)
# Collect all data about integral that should be reflected in signature,
# including compiler data but not domain data, because compiler data
diff --git a/ufl/algorithms/transformer.py b/ufl/algorithms/transformer.py
index c732663..9dc1df8 100644
--- a/ufl/algorithms/transformer.py
+++ b/ufl/algorithms/transformer.py
@@ -1,9 +1,10 @@
+# -*- coding: utf-8 -*-
"""This module defines the Transformer base class and some
basic specializations to further base other algorithms upon,
as well as some utilities for easier application of such
algorithms."""
-# Copyright (C) 2008-2014 Martin Sandve Alnes and Anders Logg
+# Copyright (C) 2008-2015 Martin Sandve Alnæs and Anders Logg
#
# This file is part of UFL.
#
@@ -129,14 +130,14 @@ class Transformer(object):
if all(a is b for a, b in zip(o.ufl_operands, ops)):
return o
else:
- return o.reconstruct(*ops)
+ return o._ufl_expr_reconstruct_(*ops)
# It's just so slow to compare all operands, avoiding it now
reuse_if_possible = reuse_if_untouched
def always_reconstruct(self, o, *operands):
"Always reconstruct expr."
- return o.reconstruct(*operands)
+ return o._ufl_expr_reconstruct_(*operands)
# Set default behaviour for any Expr
expr = undefined
diff --git a/ufl/algorithms/traversal.py b/ufl/algorithms/traversal.py
index b015215..a81da28 100644
--- a/ufl/algorithms/traversal.py
+++ b/ufl/algorithms/traversal.py
@@ -1,6 +1,7 @@
+# -*- coding: utf-8 -*-
"""This module contains algorithms for traversing expression trees in different ways."""
-# Copyright (C) 2008-2014 Martin Sandve Alnes and Anders Logg
+# Copyright (C) 2008-2015 Martin Sandve Alnæs and Anders Logg
#
# This file is part of UFL.
#
diff --git a/ufl/argument.py b/ufl/argument.py
index 33a6c92..7fac0c5 100644
--- a/ufl/argument.py
+++ b/ufl/argument.py
@@ -1,7 +1,8 @@
+# -*- coding: utf-8 -*-
"""This module defines the class Argument and a number of related
classes (functions), including TestFunction and TrialFunction."""
-# Copyright (C) 2008-2014 Martin Sandve Alnes
+# Copyright (C) 2008-2015 Martin Sandve Alnæs
#
# This file is part of UFL.
#
@@ -22,46 +23,67 @@ classes (functions), including TestFunction and TrialFunction."""
from ufl.log import deprecate
from ufl.assertions import ufl_assert
+from ufl.core.ufl_type import ufl_type
from ufl.core.terminal import Terminal, FormArgument
from ufl.split_functions import split
from ufl.finiteelement import FiniteElementBase
-from ufl.core.ufl_type import ufl_type
+from ufl.domain import default_domain
+from ufl.functionspace import AbstractFunctionSpace, FunctionSpace
+
+
+# Export list for ufl.classes (TODO: not actually classes: drop? these are in ufl.*)
+__all_classes__ = ["TestFunction", "TrialFunction", "TestFunctions", "TrialFunctions"]
+
# --- Class representing an argument (basis function) in a form ---
@ufl_type()
class Argument(FormArgument):
"""UFL value: Representation of an argument to a form."""
- __slots__ = ("_element", "_number", "_part", "_repr")
+ __slots__ = ("_ufl_function_space", "_ufl_shape", "_number", "_part", "_repr")
- def __init__(self, element, number, part=None):
+ def __init__(self, function_space, number, part=None):
FormArgument.__init__(self)
- ufl_assert(isinstance(element, FiniteElementBase),
- "Expecting an element, not %s" % (element,))
+
+ if isinstance(function_space, FiniteElementBase):
+ # For legacy support for .ufl files using cells, we map the cell to The Default Mesh
+ element = function_space
+ domain = default_domain(element.cell())
+ function_space = FunctionSpace(domain, element)
+ elif not isinstance(function_space, AbstractFunctionSpace):
+ error("Expecting a FunctionSpace or FiniteElement.")
+
+ self._ufl_function_space = function_space
+ self._ufl_shape = function_space.ufl_element().value_shape()
+
ufl_assert(isinstance(number, int),
"Expecting an int for number, not %s" % (number,))
ufl_assert(part is None or isinstance(part, int),
"Expecting None or an int for part, not %s" % (part,))
- self._element = element
self._number = number
self._part = part
- self._repr = "Argument(%r, %r, %r)" % (self._element, self._number, self._part)
-
- def reconstruct(self, element=None, number=None, part=None):
- if element is None or (element == self._element): # TODO: Is the == here a workaround for some bug?
- element = self._element
- if number is None:
- number = self._number
- if part is None:
- part = self._part
- if number == self._number and part == self._part and element is self._element:
- return self
- ufl_assert(element.value_shape() == self._element.value_shape(),
- "Cannot reconstruct an Argument with a different value shape.")
- return Argument(element, number, part)
+
+ self._repr = "Argument(%r, %r, %r)" % (self._ufl_function_space, self._number, self._part)
+
+ @property
+ def ufl_shape(self):
+ return self._ufl_shape
+
+ def ufl_function_space(self):
+ "Get the function space of this Argument."
+ return self._ufl_function_space
+
+ def ufl_domain(self):
+ #TODO: deprecate("Argument.ufl_domain() is deprecated, please use .ufl_function_space().ufl_domain() instead.")
+ return self._ufl_function_space.ufl_domain()
+
+ def ufl_element(self):
+ #TODO: deprecate("Argument.ufl_domain() is deprecated, please use .ufl_function_space().ufl_element() instead.")
+ return self._ufl_function_space.ufl_element()
def element(self):
- return self._element
+ deprecate("Argument.element() is deprecated, please use Argument.ufl_element() instead.")
+ return self.ufl_element()
def number(self):
return self._number
@@ -69,15 +91,6 @@ class Argument(FormArgument):
def part(self):
return self._part
- def count(self):
- deprecate("The count of an Argument has been replaced with number() and part().")
- ufl_assert(self.part() is None, "Deprecation transition for count() will not work with parts.")
- return self.number() # I think this will work ok in most cases during the deprecation transition
-
- @property
- def ufl_shape(self):
- return self._element.value_shape()
-
def is_cellwise_constant(self):
"Return whether this expression is spatially constant over each cell."
# TODO: Should in principle do like with Coefficient,
@@ -86,21 +99,15 @@ class Argument(FormArgument):
# When we can annotate zero with arguments, we can change this.
return False
- def domains(self):
+ def ufl_domains(self):
"Return tuple of domains related to this terminal object."
- return self._element.domains()
-
- def signature_data(self, domain_numbering):
- "Signature data for form arguments depend on the global numbering of the form arguments and domains."
- s = self._element.signature_data(domain_numbering=domain_numbering)
- return ("Argument", self._number, self._part) + s
+ #TODO: deprecate("Argument.ufl_domains() is deprecated, please use .ufl_function_space().ufl_domains() instead.")
+ return self._ufl_function_space.ufl_domains()
- def signature_data(self, renumbering):
+ def _ufl_signature_data_(self, renumbering):
"Signature data for form arguments depend on the global numbering of the form arguments and domains."
- edata = self.element().signature_data(renumbering)
- d = self.domain()
- ddata = None if d is None else d.signature_data(renumbering)
- return ("Coefficient", self._number, self._part, edata, ddata)
+ fsdata = self._ufl_function_space._ufl_signature_data_(renumbering)
+ return ("Argument", self._number, self._part, fsdata)
def __str__(self):
number = str(self._number)
@@ -134,31 +141,31 @@ class Argument(FormArgument):
return (type(self) == type(other) and
self._number == other._number and
self._part == other._part and
- self._element == other._element)
+ self._ufl_function_space == other._ufl_function_space)
# --- Helper functions for pretty syntax ---
-def TestFunction(element, part=None):
+def TestFunction(function_space, part=None):
"""UFL value: Create a test function argument to a form."""
- return Argument(element, 0, part)
+ return Argument(function_space, 0, part)
-def TrialFunction(element, part=None):
+def TrialFunction(function_space, part=None):
"""UFL value: Create a trial function argument to a form."""
- return Argument(element, 1, part)
+ return Argument(function_space, 1, part)
# --- Helper functions for creating subfunctions on mixed elements ---
-def Arguments(element, number):
+def Arguments(function_space, number):
"""UFL value: Create an Argument in a mixed space, and return a
tuple with the function components corresponding to the subelements."""
- return split(Argument(element, number))
+ return split(Argument(function_space, number))
-def TestFunctions(element):
+def TestFunctions(function_space):
"""UFL value: Create a TestFunction in a mixed space, and return a
tuple with the function components corresponding to the subelements."""
- return Arguments(element, 0)
+ return Arguments(function_space, 0)
-def TrialFunctions(element):
+def TrialFunctions(function_space):
"""UFL value: Create a TrialFunction in a mixed space, and return a
tuple with the function components corresponding to the subelements."""
- return Arguments(element, 1)
+ return Arguments(function_space, 1)
diff --git a/ufl/assertions.py b/ufl/assertions.py
index 6b3c8d9..b1225c3 100644
--- a/ufl/assertions.py
+++ b/ufl/assertions.py
@@ -1,6 +1,7 @@
+# -*- coding: utf-8 -*-
"""This module provides assertion functions used by the UFL implementation."""
-# Copyright (C) 2008-2014 Martin Sandve Alnes
+# Copyright (C) 2008-2015 Martin Sandve Alnæs
#
# This file is part of UFL.
#
@@ -16,23 +17,29 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with UFL. If not, see <http://www.gnu.org/licenses/>.
-#
-# First added: 2009-01-28
-# Last changed: 2011-06-02
from ufl.log import error
#--- Standardized error messages ---
-expecting_instance = lambda v, c: error("Expecting %s instance, not %s." % (c.__name__, repr(v)))
-expecting_python_scalar = lambda v: error("Expecting Python scalar, not %s." % repr(v))
-expecting_expr = lambda v: error("Expecting Expr instance, not %s." % repr(v))
-expecting_terminal = lambda v: error("Expecting Terminal instance, not %s." % repr(v))
-expecting_true_ufl_scalar = lambda v: error("Expecting UFL scalar expression with no free indices, not %s." % repr(v))
+def expecting_instance(v, c):
+ error("Expecting %s instance, not %s." % (c.__name__, repr(v)))
+
+def expecting_python_scalar(v):
+ error("Expecting Python scalar, not %s." % repr(v))
+
+def expecting_expr(v):
+ error("Expecting Expr instance, not %s." % repr(v))
+
+def expecting_terminal(v):
+ error("Expecting Terminal instance, not %s." % repr(v))
+
+def expecting_true_ufl_scalar(v):
+ error("Expecting UFL scalar expression with no free indices, not %s." % repr(v))
#--- Standardized assertions ---
def ufl_assert(condition, *message):
"Assert that condition is true and otherwise issue an error with given message."
- if not condition: error(*message)
-
+ if not condition:
+ error(*message)
diff --git a/ufl/cell.py b/ufl/cell.py
index 034a1a3..8b5d0f6 100644
--- a/ufl/cell.py
+++ b/ufl/cell.py
@@ -1,6 +1,7 @@
+# -*- coding: utf-8 -*-
"Types for representing a cell."
-# Copyright (C) 2008-2014 Martin Sandve Alnes
+# Copyright (C) 2008-2015 Martin Sandve Alnæs
#
# This file is part of UFL.
#
@@ -22,268 +23,227 @@
# Modified by Marie E. Rognes 2012
# Modified by Andrew T. T. McRae, 2014
-from itertools import chain
-from collections import defaultdict
-
-from ufl.log import warning, error, deprecate
+from ufl.log import warning, error
from ufl.assertions import ufl_assert
-from ufl.common import istr, EmptyDict
+from ufl.utils.formatting import istr
+from ufl.utils.dicts import EmptyDict
from ufl.core.terminal import Terminal
-from ufl.protocols import id_or_none
+from ufl.core.ufl_type import attach_operators_from_hash_data
-# --- Basic cell properties
-# Mapping from cell name to topological dimension
-cellname2dim = {
- "vertex": 0,
- "interval": 1,
- "triangle": 2,
- "tetrahedron": 3,
- "quadrilateral": 2,
- "hexahedron": 3,
- }
+# Export list for ufl.classes
+__all_classes__ = ["AbstractCell", "Cell", "TensorProductCell", "OuterProductCell"]
-def cell2dim(cell):
- "Maps from UFL cell or cell name to topological dimension"
- if isinstance(cell, str):
- # Backwards compatibility
- cellname = cell
- else:
- cellname = cell.cellname()
- if cellname == "OuterProductCell":
- return cell2dim(cell._A) + cell2dim(cell._B)
- else:
- return cellname2dim[cellname]
+# --- The most abstract cell class, base class for other cell types
-# Mapping from cell name to facet name
-_cellname2facetname = {
- "interval": "vertex",
- "triangle": "interval",
- "quadrilateral": "interval",
- "tetrahedron": "triangle",
- "hexahedron": "quadrilateral",
- }
+class AbstractCell(object):
+ "Representation of an abstract finite element cell with only the dimensions known."
+ __slots__ = ("_topological_dimension", "_geometric_dimension")
+ def __init__(self, topological_dimension, geometric_dimension):
+ # Validate dimensions
+ ufl_assert(isinstance(geometric_dimension, int),
+ "Expecting integer geometric dimension, not '%r'" % (geometric_dimension,))
+ ufl_assert(isinstance(topological_dimension, int),
+ "Expecting integer topological dimension, not '%r'" % (topological_dimension,))
+ ufl_assert(topological_dimension <= geometric_dimension,
+ "Topological dimension cannot be larger than geometric dimension.")
-_reference_cell_volume = {
- "vertex": 0.0,
- "interval": 1.0,
- "triangle": 0.5,
- "tetrahedron": 1.0/6.0,
- "quadrilateral": 1.0,
- "hexahedron": 1.0
- }
+ # Store validated dimensions
+ self._topological_dimension = topological_dimension
+ self._geometric_dimension = geometric_dimension
+ def topological_dimension(self):
+ "Return the dimension of the topology of this cell."
+ return self._topological_dimension
+
+ def geometric_dimension(self):
+ "Return the dimension of the space this cell is embedded in."
+ return self._geometric_dimension
+
+ def is_simplex(self):
+ "Return True if this is a simplex cell."
+ raise NotImplementedError("Implement this to allow important checks and optimizations.")
+
+ def has_simplex_facets(self):
+ "Return True if all the facets of this cell are simplex cells."
+ raise NotImplementedError("Implement this to allow important checks and optimizations.")
+
+ def __lt__(self, other):
+ "Define an arbitrarily chosen but fixed sort order for all cells."
+ if not isinstance(other, AbstractCell):
+ return NotImplemented
+ # Sort by gdim first, tdim next, then whatever's left depending on the subclass
+ s = (self.geometric_dimension(), self.topological_dimension())
+ o = (other.geometric_dimension(), other.topological_dimension())
+ if s != o:
+ return s < o
+ return self._ufl_hash_data_() < other._ufl_hash_data_()
+
+
+# --- Basic topological properties of known basic cells
+
+# Mapping from cell name to number of cell entities of each topological dimension
num_cell_entities = {
- "interval": (2, 1),
+ "vertex": (1,),
+ "interval": (2, 1),
"triangle": (3, 3, 1),
"quadrilateral": (4, 4, 1),
"tetrahedron": (4, 6, 4, 1),
"hexahedron": (8, 12, 6, 1),
}
-affine_cells = {"vertex", "interval", "triangle", "tetrahedron"}
+# Mapping from cell name to topological dimension
+cellname2dim = dict((k, len(v)-1) for k,v in num_cell_entities.items())
+
+# Mapping from cell name to facet name
+# Note: This is not generalizable to product elements but it's still in use a couple of places.
+cellname2facetname = {
+ "interval": "vertex",
+ "triangle": "interval",
+ "quadrilateral": "interval",
+ "tetrahedron": "triangle",
+ "hexahedron": "quadrilateral",
+ }
# --- Basic cell representation classes
-class Cell(object):
- "Representation of a finite element cell."
- __slots__ = ("_cellname",
- "_geometric_dimension",
- "_topological_dimension"
- )
- def __init__(self, cellname, geometric_dimension=None, topological_dimension=None):
+ at attach_operators_from_hash_data
+class Cell(AbstractCell):
+ "Representation of a named finite element cell with known structure."
+ __slots__ = ("_cellname",)
+ def __init__(self, cellname, geometric_dimension=None):
"Initialize basic cell description."
+ self._cellname = cellname
+
# The topological dimension is defined by the cell type,
# so the cellname must be among the known ones,
# so we can find the known dimension, unless we have
# a product cell, in which the given dimension is used
- tdim = cellname2dim.get(cellname, topological_dimension)
+ topological_dimension = len(num_cell_entities[cellname]) - 1
# The geometric dimension defaults to equal the topological
- # dimension if undefined
+ # dimension unless overridden for embedded cells
if geometric_dimension is None:
- gdim = tdim
- else:
- gdim = geometric_dimension
+ geometric_dimension = topological_dimension
- # Validate dimensions
- ufl_assert(isinstance(gdim, int),
- "Expecting integer dimension, not '%r'" % (gdim,))
- ufl_assert(isinstance(tdim, int),
- "Expecting integer dimension, not '%r'" % (tdim,))
- ufl_assert(tdim <= gdim,
- "Topological dimension cannot be larger than geometric dimension.")
+ # Initialize and validate dimensions
+ AbstractCell.__init__(self, topological_dimension, geometric_dimension)
- # ... Finally store validated data
- self._cellname = cellname
- self._topological_dimension = tdim
- self._geometric_dimension = gdim
+ # --- Overrides of AbstractCell methods ---
- # --- Fundamental dimensions ---
+ def is_simplex(self):
+ return self.num_vertices() == self.topological_dimension() + 1
- def topological_dimension(self):
- "Return the dimension of the topology of this cell."
- return self._topological_dimension
-
- def geometric_dimension(self):
- "Return the dimension of the space this cell is embedded in."
- return self._geometric_dimension
+ def has_simplex_facets(self):
+ return self.is_simplex() or self.cellname() == "quadrilateral"
- # --- Cell properties ---
+ # --- Specific cell properties ---
def cellname(self):
"Return the cellname of the cell."
return self._cellname
- def num_entities(self, dim=None):
- "The number of cell entities of given topological dimension."
- num = num_cell_entities[self.cellname()]
- if dim is None:
- return num
- else:
- return num[dim]
-
def num_vertices(self):
"The number of cell vertices."
- return self.num_entities(0)
+ return num_cell_entities[self.cellname()][0]
def num_edges(self):
"The number of cell edges."
- return self.num_entities(1)
+ return num_cell_entities[self.cellname()][1]
def num_facets(self):
"The number of cell facets."
tdim = self.topological_dimension()
- return self.num_entities(tdim-1)
-
- def reference_volume(self):
- "The volume of a reference cell of the same type."
- return _reference_cell_volume[self.cellname()]
+ return num_cell_entities[self.cellname()][tdim-1]
# --- Facet properties ---
- # TODO: The concept of a fixed name and number of entities for a facet does not work with product cells.
- # Search for 'facet_cellname' and 'num_facet_' to find usage and figure out another way to handle those places.
-
- # TODO: Maybe return a facet cell instead of all these accessors
- #def facet(self):
- # return Cell(self.facet_cellname(), self.geometric_dimension())
-
- def facet_cellname(self):
- "Return the cellname of the facet of this cell, or None if not available."
- return _cellname2facetname.get(self.cellname())
-
- def num_facet_entities(self, dim):
- "Return the number of cell entities of given topological dimension, or None if not available."
- num = num_cell_entities.get(self.cellname())
- return num[dim] if num else None
-
- def num_facet_vertices(self):
- "The number of cell vertices, or None if not available."
- return self.num_facet_entities(0)
def num_facet_edges(self):
- "The number of facet edges, or None if not available."
- return self.num_facet_entities(1)
-
- def reference_facet_volume(self):
- "The volume of a reference cell of the same type."
- return _reference_cell_volume[self.facet_cellname()]
+ "The number of facet edges."
+ # This is used in geometry.py
+ fn = cellname2facetname[self.cellname()]
+ return num_cell_entities[fn][1]
# --- Special functions for proper object behaviour ---
- def __eq__(self, other):
- if not isinstance(other, Cell):
- return False
- s = (self.geometric_dimension(), self.topological_dimension(), self.cellname())
- o = (other.geometric_dimension(), other.topological_dimension(), other.cellname())
- return s == o
+ def __str__(self):
+ gdim = self.geometric_dimension()
+ tdim = self.topological_dimension()
+ s = self.cellname()
+ if gdim > tdim:
+ s += "%dD" % gdim
+ return s
- def __ne__(self, other):
- return not self == other
+ def __repr__(self):
+ # For standard cells, return name of builtin cell object if possible.
+ # This reduces the size of the repr strings for domains, elements, etc. as well
+ gdim = self.geometric_dimension()
+ tdim = self.topological_dimension()
+ name = self.cellname()
+ if gdim == tdim and name in cellname2dim:
+ return name
+ else:
+ return "Cell(%r, %r)" % (name, gdim)
- def __lt__(self, other):
- if not isinstance(other, Cell):
- return False
- s = (self.geometric_dimension(), self.topological_dimension(), self.cellname())
- o = (other.geometric_dimension(), other.topological_dimension(), other.cellname())
- return s < o
+ def _ufl_hash_data_(self):
+ return (self._geometric_dimension, self._topological_dimension, self._cellname)
- def __hash__(self):
- return hash(repr(self))
+ at attach_operators_from_hash_data
+class TensorProductCell(AbstractCell):
+ __slots__ = ("_cells",)
+ def __init__(self, cells):
+ self._cells = tuple(as_cell(cell) for cell in cells)
- def __str__(self):
- return "<%s cell in %sD>" % (istr(self.cellname()),
- istr(self.geometric_dimension()))
+ gdims = [cell.geometric_dimension() for cell in self._cells]
+ tdims = [cell.topological_dimension() for cell in self._cells]
+ gdim = sum(gdims)
+ tdim = sum(tdims)
- def __repr__(self):
- return "Cell(%r, %r)" % (self.cellname(), self.geometric_dimension())
+ AbstractCell.__init__(self, tdim, gdim)
- def _repr_svg_(self):
- ""
+ def is_simplex(self):
+ "Return True if this is a simplex cell."
+ if len(self._cells) == 1:
+ return self._cells[0].is_simplex()
+ return False
- name = self.cellname()
- m = 200
- if name == "interval":
- points = [(0, 0), (m, 0)]
- elif name == "triangle":
- points = [(0, m), (m, m), (0, 0), (0, m)]
- elif name == "quadrilateral":
- points = [(0, m), (m, m), (m, 0), (0, 0), (0, m)]
- else:
- points = None
-
- svg = '''
- <svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="%s" height="%s">
- <polyline points="%s" style="%s" />
- </svg>
- '''
-
- if points:
- fill = "none"
- stroke = "black"
- strokewidth = 3
-
- width = max(p[0] for p in points) - min(p[0] for p in points)
- height = max(p[1] for p in points) - min(p[1] for p in points)
- width = max(width, strokewidth)
- height = max(height, strokewidth)
- style = "fill:%s; stroke:%s; stroke-width:%s" % (fill, stroke, strokewidth)
- points = " ".join(','.join(map(str, p)) for p in points)
- return svg % (width, height, points, style)
- else:
- return None
+ def has_simplex_facets(self):
+ "Return True if all the facets of this cell are simplex cells."
+ if len(self._cells) == 1:
+ return self._cells[0].has_simplex_facets()
+ return False
-class ProductCell(Cell):
- __slots__ = ("_cells",)
- def __init__(self, *cells):
- cells = tuple(as_cell(cell) for cell in cells)
- gdim = sum(cell.geometric_dimension() for cell in cells)
- tdim = sum(cell.topological_dimension() for cell in cells)
- Cell.__init__(self, "product", gdim, tdim)
- self._cells = tuple(cells)
+ def num_vertices(self):
+ "The number of cell vertices."
+ return product(c.num_vertices() for c in self._cells)
+
+ def num_edges(self):
+ "The number of cell edges."
+ error("Not defined for TensorProductCell.")
+
+ def num_facets(self):
+ "The number of cell facets."
+ return sum(c.num_facets() for c in self._cells if c.topological_dimension() > 0)
def sub_cells(self):
"Return list of cell factors."
return self._cells
- def __eq__(self, other):
- if not isinstance(other, ProductCell):
- return False
- return self._cells == other._cells
-
- def __lt__(self, other):
- if not isinstance(other, ProductCell):
- return False
- return self._cells < other._cells
+ def __str__(self):
+ return repr(self)
def __repr__(self):
- return "ProductCell(*%r)" % (self._cells,)
+ return "TensorProductCell(%s)" % ", ".join(repr(c) for c in self._cells)
+
+ def _ufl_hash_data_(self):
+ return tuple(c._ufl_hash_data_() for c in self._cells)
-class OuterProductCell(Cell):
+ at attach_operators_from_hash_data
+class OuterProductCell(AbstractCell): # TODO: Remove this and use TensorProductCell instead
"""Representation of a cell formed as the Cartesian product of
two existing cells"""
__slots__ = ("_A", "_B", "facet_horiz", "facet_vert")
@@ -306,7 +266,8 @@ class OuterProductCell(Cell):
raise TypeError("gdim must be an integer")
if gdim < gdim_temp:
raise ValueError("gdim must be at least %d" % gdim_temp)
- Cell.__init__(self, "OuterProductCell", gdim, tdim)
+
+ AbstractCell.__init__(self, tdim, gdim)
# facets for extruded cells
if B.cellname() == "interval":
@@ -320,48 +281,70 @@ class OuterProductCell(Cell):
# Don't know how to extrude this
self.facet_vert = None
- def num_entities(self, dim):
- "The number of cell entities of given topological dimension."
- # Return None unless asked for the number of vertices / volumes
- templist = [None,] * (self.topological_dimension() + 1)
- templist[0] = self._A.num_vertices() * self._B.num_vertices()
- templist[-1] = 1
- return templist[dim]
-
- def reference_volume(self):
- "The volume of a reference cell of the same type."
- return _reference_cell_volume[self._A.cellname()] * _reference_cell_volume[self._B.cellname()]
-
- def __eq__(self, other):
- if not isinstance(other, OuterProductCell):
- return False
- # This is quite subtle: my intuition says that the OPCs of
- # Cell("triangle") with Cell("interval"), and
- # Cell("triangle", 3) with Cell("interval")
- # are essentially the same: triangular prisms with gdim = tdim = 3.
- # For safety, though, we will only compare equal if the
- # subcells are *identical*, including immersion.
- return (self._A, self._B) == (other._A, other._B) and self.geometric_dimension() == other.geometric_dimension()
+ def is_simplex(self):
+ "Return True if this is a simplex cell."
+ return False
- def __lt__(self, other):
- if not isinstance(other, OuterProductCell):
- return NotImplemented
- return (self._A, self._B) < (other._A, other._B)
+ def has_simplex_facets(self):
+ "Return True if all the facets of this cell are simplex cells."
+ # Actually sometimes true
+ return False
+
+ def num_vertices(self):
+ "The number of cell vertices."
+ return self._A.num_vertices() * self._B.num_vertices()
+
+ def num_edges(self):
+ "The number of cell edges."
+ error("Not defined for OuterProductCell.")
+
+ def num_facets(self):
+ "The number of cell facets."
+ return self._A.num_facets() + self._B.num_facets()
def __repr__(self):
return "OuterProductCell(*%r)" % list([self._A, self._B])
+ def _ufl_hash_data_(self):
+ return tuple(c._ufl_hash_data_() for c in (self._A, self._B))
+
# --- Utility conversion functions
+# Mapping from topological dimension to reference cell name for simplices
+_simplex_dim2cellname = {
+ 0: "vertex",
+ 1: "interval",
+ 2: "triangle",
+ 3: "tetrahedron",
+ }
+
+# Mapping from topological dimension to reference cell name for hypercubes
+_hypercube_dim2cellname = {
+ 0: "vertex",
+ 1: "interval",
+ 2: "quadrilateral",
+ 3: "hexahedron",
+ }
+
+def simplex(topological_dimension, geometric_dimension=None):
+ "Return a simplex cell of given dimension."
+ return Cell(_simplex_dim2cellname[topological_dimension], geometric_dimension)
+
+def hypercube(topological_dimension, geometric_dimension=None):
+ "Return a hypercube cell of given dimension."
+ return Cell(_hypercube_dim2cellname[topological_dimension], geometric_dimension)
+
def as_cell(cell):
- """Convert any valid object to a Cell (in particular, cellname string),
- or return cell if it is already a Cell."""
- if isinstance(cell, Cell):
+ """Convert any valid object to a Cell or return cell if it is already a Cell.
+
+ Allows an already valid cell, a known cellname string, or a tuple of cells for a product cell.
+ """
+ if isinstance(cell, AbstractCell):
return cell
- elif hasattr(cell, "ufl_cell"):
- return cell.ufl_cell()
elif isinstance(cell, str):
return Cell(cell)
+ elif isinstance(cell, tuple):
+ return TensorProductCell(cell)
else:
error("Invalid cell %s." % cell)
diff --git a/ufl/checks.py b/ufl/checks.py
index 635c5d6..e40cc5e 100644
--- a/ufl/checks.py
+++ b/ufl/checks.py
@@ -1,6 +1,7 @@
+# -*- coding: utf-8 -*-
"""Utility functions for checking properties of expressions."""
-# Copyright (C) 2013-2014 Martin Sandve Alnes
+# Copyright (C) 2013-2015 Martin Sandve Alnæs
#
# This file is part of UFL.
#
@@ -37,21 +38,33 @@ def is_true_ufl_scalar(expression):
return isinstance(expression, Expr) and \
not (expression.ufl_shape or expression.ufl_free_indices)
+def is_cellwise_constant(expr):
+ "Return whether expression is constant over a single cell."
+ # TODO: Implement more accurately considering e.g. derivatives?
+ return all(t.is_cellwise_constant() for t in traverse_unique_terminals(expr))
+
def is_globally_constant(expr):
"""Check if an expression is globally constant, which
includes spatially independent constant coefficients that
are not known before assembly time."""
-
# TODO: This does not consider gradients of coefficients, so false negatives are possible.
- from ufl.argument import Argument
- from ufl.coefficient import Coefficient
-
+ #from ufl.argument import Argument
+ #from ufl.coefficient import Coefficient
+ from ufl.geometry import GeometricQuantity
+ from ufl.core.terminal import FormArgument
for e in traverse_unique_terminals(expr):
- if isinstance(e, Argument):
- return False
- if isinstance(e, Coefficient) and e.element().family() != "Real":
- return False
- if not e.is_cellwise_constant():
+ # Return False if any single terminal is not constant
+ if e._ufl_is_literal_:
+ # Accept literals first, they are the most common terminals
+ continue
+ elif isinstance(e, FormArgument):
+ # Accept only Real valued Arguments and Coefficients
+ if e.ufl_element().family() == "Real":
+ continue
+ else:
+ return False
+ elif isinstance(e, GeometricQuantity):
+ # Reject all geometric quantities, they all vary over cells
return False
# All terminals passed constant check
@@ -61,6 +74,6 @@ def is_scalar_constant_expression(expr):
"""Check if an expression is a globally constant scalar expression."""
if is_python_scalar(expr):
return True
- if expr.ufl_shape != ():
+ if expr.ufl_shape:
return False
return is_globally_constant(expr)
diff --git a/ufl/classes.py b/ufl/classes.py
index 3f6d783..c63587d 100644
--- a/ufl/classes.py
+++ b/ufl/classes.py
@@ -1,10 +1,11 @@
+# -*- coding: utf-8 -*-
"""This file is useful for external code like tests and form compilers,
since it enables the syntax "from ufl.classes import CellFacetooBar" for getting
implementation details not exposed through the default ufl namespace.
It also contains functionality used by algorithms for dealing with groups
of classes, and for mapping types to different handler functions."""
-# Copyright (C) 2008-2014 Martin Sandve Alnes
+# Copyright (C) 2008-2015 Martin Sandve Alnæs
#
# This file is part of UFL.
#
@@ -96,49 +97,44 @@ def populate_namespace_with_expr_classes(namespace):
__all__ += populate_namespace_with_expr_classes(locals())
-# Domain types
-from ufl.cell import Cell, ProductCell, OuterProductCell
-from ufl.domain import Domain, ProductDomain
-__all__ += [
- "Cell", "ProductCell", "OuterProductCell",
- "Domain", "ProductDomain",
- ]
+#
+# Semi-automated imports of non-expr classes:
+#
-# Elements
-from ufl.finiteelement import (
- FiniteElementBase,
- FiniteElement,
- MixedElement, VectorElement, TensorElement,
- EnrichedElement, RestrictedElement,
- TensorProductElement, OuterProductElement, OuterProductVectorElement)
+def populate_namespace_with_module_classes(mod, loc):
+ """Export the classes that submodules list in __all_classes__."""
+ names = mod.__all_classes__
+ for name in names:
+ loc[name] = getattr(mod, name)
+ return names
-__all__ += [
- "FiniteElementBase",
- "FiniteElement",
- "MixedElement", "VectorElement", "TensorElement",
- "EnrichedElement", "RestrictedElement",
- "TensorProductElement", "OuterProductElement", "OuterProductVectorElement",
- ]
+import ufl.cell
+__all__ += populate_namespace_with_module_classes(ufl.cell, locals())
-# Other non-Expr types
-from ufl.argument import TestFunction, TrialFunction, TestFunctions, TrialFunctions
-from ufl.core.multiindex import IndexBase, FixedIndex, Index
+import ufl.finiteelement
+__all__ += populate_namespace_with_module_classes(ufl.finiteelement, locals())
-__all__ += [
- "TestFunction", "TrialFunction", "TestFunctions", "TrialFunctions",
- "IndexBase", "FixedIndex", "Index",
- ]
+import ufl.domain
+__all__ += populate_namespace_with_module_classes(ufl.domain, locals())
-# Higher level abstractions
-from ufl.measure import Measure, MeasureSum, MeasureProduct
-from ufl.integral import Integral
-from ufl.form import Form
-from ufl.equation import Equation
+import ufl.functionspace
+__all__ += populate_namespace_with_module_classes(ufl.functionspace, locals())
-__all__ += [
- "Measure", "MeasureSum", "MeasureProduct",
- "Integral",
- "Form",
- "Equation",
- ]
+import ufl.core.multiindex
+__all__ += populate_namespace_with_module_classes(ufl.core.multiindex, locals())
+
+import ufl.argument
+__all__ += populate_namespace_with_module_classes(ufl.argument, locals())
+
+import ufl.measure
+__all__ += populate_namespace_with_module_classes(ufl.measure, locals())
+
+import ufl.integral
+__all__ += populate_namespace_with_module_classes(ufl.integral, locals())
+
+import ufl.form
+__all__ += populate_namespace_with_module_classes(ufl.form, locals())
+
+import ufl.equation
+__all__ += populate_namespace_with_module_classes(ufl.equation, locals())
diff --git a/ufl/coefficient.py b/ufl/coefficient.py
index d178dd7..0356f49 100644
--- a/ufl/coefficient.py
+++ b/ufl/coefficient.py
@@ -1,7 +1,8 @@
+# -*- coding: utf-8 -*-
"""This module defines the Coefficient class and a number
of related classes, including Constant."""
-# Copyright (C) 2008-2014 Martin Sandve Alnes
+# Copyright (C) 2008-2015 Martin Sandve Alnæs
#
# This file is part of UFL.
#
@@ -20,13 +21,15 @@ of related classes, including Constant."""
#
# Modified by Anders Logg, 2008-2009.
-from ufl.log import warning
+from ufl.log import deprecate
from ufl.assertions import ufl_assert
+from ufl.core.ufl_type import ufl_type
from ufl.core.terminal import Terminal, FormArgument
from ufl.finiteelement import FiniteElementBase, FiniteElement, VectorElement, TensorElement
+from ufl.domain import as_domain, default_domain
+from ufl.functionspace import AbstractFunctionSpace, FunctionSpace
from ufl.split_functions import split
-from ufl.common import counted_init
-from ufl.core.ufl_type import ufl_type
+from ufl.utils.counted import counted_init
# --- The Coefficient class represents a coefficient in a form ---
@@ -35,64 +38,63 @@ class Coefficient(FormArgument):
"""UFL form argument type: Representation of a form coefficient."""
# Slots are disabled here because they cause trouble in PyDOLFIN multiple inheritance pattern:
- #__slots__ = ("_count", "_element", "_repr", "_gradient", "_derivatives")
+ #__slots__ = ("_count", "_ufl_function_space", "_repr", "_ufl_shape")
_ufl_noslots_ = True
_globalcount = 0
- def __init__(self, element, count=None):
+ def __init__(self, function_space, count=None):
FormArgument.__init__(self)
counted_init(self, count, Coefficient)
- ufl_assert(isinstance(element, FiniteElementBase),
- "Expecting a FiniteElementBase instance.")
- self._element = element
- self._repr = None
+ if isinstance(function_space, FiniteElementBase):
+ # For legacy support for .ufl files using cells, we map the cell to The Default Mesh
+ element = function_space
+ domain = default_domain(element.cell())
+ function_space = FunctionSpace(domain, element)
+ elif not isinstance(function_space, AbstractFunctionSpace):
+ error("Expecting a FunctionSpace or FiniteElement.")
- def count(self):
- return self._count
+ self._ufl_function_space = function_space
+ self._ufl_shape = function_space.ufl_element().value_shape()
- def reconstruct(self, element=None, count=None):
- # This code is shared with the FooConstant classes
- if element is None or element == self._element:
- element = self._element
- if count is None or count == self._count:
- count = self._count
- if count is self._count and element is self._element:
- return self
- ufl_assert(isinstance(element, FiniteElementBase),
- "Expecting an element, not %s" % element)
- ufl_assert(isinstance(count, int),
- "Expecting an int, not %s" % count)
- ufl_assert(element.value_shape() == self._element.value_shape(),
- "Cannot reconstruct a Coefficient with a different value shape.")
- return self._reconstruct(element, count)
-
- def _reconstruct(self, element, count):
- # This code is class specific
- return Coefficient(element, count)
+ self._repr = "Coefficient(%r, %r)" % (self._ufl_function_space, self._count)
- def element(self):
- return self._element
+ def count(self):
+ return self._count
@property
def ufl_shape(self):
- return self._element.value_shape()
+ return self._ufl_shape
+
+ def ufl_function_space(self):
+ "Get the function space of this coefficient."
+ return self._ufl_function_space
+
+ def ufl_domain(self):
+ "Shortcut to get the domain of the function space of this coefficient."
+ return self._ufl_function_space.ufl_domain()
+
+ def ufl_element(self):
+ "Shortcut to get the finite element of the function space of this coefficient."
+ return self._ufl_function_space.ufl_element()
+
+ def element(self):
+ 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._element.is_cellwise_constant()
+ return self.ufl_element().is_cellwise_constant()
- def domains(self):
+ def ufl_domains(self):
"Return tuple of domains related to this terminal object."
- return self._element.domains()
+ return self._ufl_function_space.ufl_domains()
- def signature_data(self, renumbering):
+ def _ufl_signature_data_(self, renumbering):
"Signature data for form arguments depend on the global numbering of the form arguments and domains."
count = renumbering[self]
- edata = self.element().signature_data(renumbering)
- d = self.domain()
- ddata = None if d is None else d.signature_data(renumbering)
- return ("Coefficient", count, edata, ddata)
+ fsdata = self._ufl_function_space._ufl_signature_data_(renumbering)
+ return ("Coefficient", count, fsdata)
def __str__(self):
count = str(self._count)
@@ -102,8 +104,6 @@ class Coefficient(FormArgument):
return "w_{%s}" % count
def __repr__(self):
- if self._repr is None:
- self._repr = "Coefficient(%r, %r)" % (self._element, self._count)
return self._repr
def __eq__(self, other):
@@ -112,28 +112,34 @@ class Coefficient(FormArgument):
if self is other:
return True
return (self._count == other._count and
- self._element == other._element)
+ self._ufl_function_space == other._ufl_function_space)
# --- Helper functions for defining constant coefficients without specifying element ---
def Constant(domain, count=None):
"""UFL value: Represents a globally constant scalar valued coefficient."""
- e = FiniteElement("Real", domain, 0)
- return Coefficient(e, count=count)
+ domain = as_domain(domain)
+ element = FiniteElement("Real", domain.ufl_cell(), 0)
+ fs = FunctionSpace(domain, element)
+ return Coefficient(fs, count=count)
def VectorConstant(domain, dim=None, count=None):
"""UFL value: Represents a globally constant vector valued coefficient."""
- e = VectorElement("Real", domain, 0, dim)
- return Coefficient(e, count=count)
+ domain = as_domain(domain)
+ element = VectorElement("Real", domain.ufl_cell(), 0, dim)
+ fs = FunctionSpace(domain, element)
+ return Coefficient(fs, count=count)
def TensorConstant(domain, shape=None, symmetry=None, count=None):
"""UFL value: Represents a globally constant tensor valued coefficient."""
- e = TensorElement("Real", domain, 0, shape=shape, symmetry=symmetry)
- return Coefficient(e, count=count)
+ domain = as_domain(domain)
+ element = TensorElement("Real", domain.ufl_cell(), 0, shape=shape, symmetry=symmetry)
+ fs = FunctionSpace(domain, element)
+ return Coefficient(fs, count=count)
# --- Helper functions for subfunctions on mixed elements ---
-def Coefficients(element):
+def Coefficients(function_space):
"""UFL value: Create a Coefficient in a mixed space, and return a
tuple with the function components corresponding to the subelements."""
- return split(Coefficient(element))
+ return split(Coefficient(function_space))
diff --git a/ufl/common.py b/ufl/common.py
deleted file mode 100644
index 15fbbe9..0000000
--- a/ufl/common.py
+++ /dev/null
@@ -1,36 +0,0 @@
-"This module contains a collection of common utilities."
-
-# Copyright (C) 2008-2014 Martin Sandve Alnes and Anders Logg
-#
-# This file is part of UFL.
-#
-# UFL is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Lesser General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# UFL is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with UFL. If not, see <http://www.gnu.org/licenses/>.
-#
-# Modified by Kristian Oelgaard, 2009
-
-# TODO: These things used to reside here, if we import from ufl.utils instead where applicable we can remove common.py
-
-from ufl.utils.indexflattening import shape_to_strides, unflatten_index, flatten_multiindex
-from ufl.utils.sequences import product, unzip, xor, or_tuples, and_tuples, iter_tree, recursive_chain
-from ufl.corealg.traversal import (pre_traversal, unique_pre_traversal,
- unique_pre_traversal, unique_post_traversal,
- post_traversal, unique_post_traversal)
-from ufl.utils.formatting import lstr, estr, istr, sstr, tstr, dstr, camel2underscore
-from ufl.utils.dicts import split_dict, slice_dict, mergedicts, mergedicts2, subdict, dict_sum, EmptyDictType, EmptyDict
-from ufl.utils.counted import counted_init, ExampleCounted
-from ufl.utils.timer import Timer
-from ufl.utils.stacks import Stack, StackDict
-from ufl.utils.ufltypedicts import UFLTypeDict, UFLTypeDefaultDict
-from ufl.utils.sorting import topological_sorting, sorted_by_count, sorted_by_key
-from ufl.utils.system import get_status_output, openpdf, pdflatex, write_file
diff --git a/ufl/compound_expressions.py b/ufl/compound_expressions.py
index 0ec2fb6..00b5512 100644
--- a/ufl/compound_expressions.py
+++ b/ufl/compound_expressions.py
@@ -1,6 +1,7 @@
+# -*- coding: utf-8 -*-
"""Functions implementing compound expressions as equivalent representations using basic operators."""
-# Copyright (C) 2008-2014 Martin Sandve Alnes and Anders Logg
+# Copyright (C) 2008-2015 Martin Sandve Alnæs and Anders Logg
#
# This file is part of UFL.
#
@@ -21,7 +22,7 @@
from ufl.log import error, warning
from ufl.assertions import ufl_assert
-from ufl.core.multiindex import indices
+from ufl.core.multiindex import indices, Index
from ufl.tensors import as_tensor, as_matrix, as_vector
from ufl.operators import sqrt
@@ -35,7 +36,6 @@ from ufl.operators import sqrt
# would be a major improvement to UFL and the form compiler toolchain.
# It could easily be a moderate to major undertaking to get rid of though.
-
def cross_expr(a, b):
assert len(a) == 3
assert len(b) == 3
@@ -43,15 +43,29 @@ def cross_expr(a, b):
return a[i]*b[j] - a[j]*b[i]
return as_vector((c(1, 2), c(2, 0), c(0, 1)))
-
-def pseudo_determinant_expr(A):
+def generic_pseudo_determinant_expr(A):
"""Compute the pseudo-determinant of A: sqrt(det(A.T*A))."""
i, j, k = indices(3)
ATA = as_tensor(A[k, i]*A[k, j], (i, j))
return sqrt(determinant_expr(ATA))
+def pseudo_determinant_expr(A):
+ """Compute the pseudo-determinant of A."""
+ m, n = A.ufl_shape
+ if n == 1:
+ # Special case 1xm for simpler expression
+ i = Index()
+ return sqrt(A[i,0]*A[i,0])
+ elif n == 2 and m == 3:
+ # Special case 2x3 for simpler expression
+ c = cross_expr(A[:,0], A[:,1])
+ i = Index()
+ return sqrt(c[i]*c[i])
+ else:
+ # Generic formulation based on A.T*A
+ return generic_pseudo_determinant_expr(A)
-def pseudo_inverse_expr(A):
+def generic_pseudo_inverse_expr(A):
"""Compute the Penrose-Moore pseudo-inverse of A: (A.T*A)^-1 * A.T."""
i, j, k = indices(3)
ATA = as_tensor(A[k, i]*A[k, j], (i, j))
@@ -59,9 +73,20 @@ def pseudo_inverse_expr(A):
q, r, s = indices(3)
return as_tensor(ATAinv[r, q] * A[s, q], (r, s))
+def pseudo_inverse_expr(A):
+ """Compute the Penrose-Moore pseudo-inverse of A: (A.T*A)^-1 * A.T."""
+ m, n = A.ufl_shape
+ if n == 1:
+ # Simpler special case for 1d
+ i, j, k = indices(3)
+ return as_tensor(A[i,j], (j,i)) / (A[k,0]*A[k,0])
+ else:
+ # Generic formulation
+ return generic_pseudo_inverse_expr(A)
+
def determinant_expr(A):
- "Compute the determinant of A."
+ "Compute the (pseudo-)determinant of A."
sh = A.ufl_shape
if sh == ():
return A
@@ -84,11 +109,24 @@ def _det_2x2(B, i, j, k, l):
def determinant_expr_2x2(B):
return _det_2x2(B, 0, 1, 0, 1)
-def determinant_expr_3x3(A):
+def old_determinant_expr_3x3(A):
return (A[0, 0]*_det_2x2(A, 1, 2, 1, 2) +
A[0, 1]*_det_2x2(A, 1, 2, 2, 0) +
A[0, 2]*_det_2x2(A, 1, 2, 0, 1))
+def determinant_expr_3x3(A):
+ return codeterminant_expr_nxn(A, [0,1,2], [0,1,2])
+
+def codeterminant_expr_nxn(A, rows, cols):
+ if len(rows) == 2:
+ return _det_2x2(A, rows[0], rows[1], cols[0], cols[1])
+ codet = 0.0
+ r = rows[0]
+ subrows = rows[1:]
+ for i, c in enumerate(cols):
+ subcols = cols[i+1:] + cols[:i]
+ codet += A[r, c] * codeterminant_expr_nxn(A, subrows, subcols)
+ return codet
def inverse_expr(A):
"Compute the inverse of A."
diff --git a/ufl/conditional.py b/ufl/conditional.py
index a45e651..faffbb5 100644
--- a/ufl/conditional.py
+++ b/ufl/conditional.py
@@ -1,6 +1,7 @@
+# -*- coding: utf-8 -*-
"""This module defines classes for conditional expressions."""
-# Copyright (C) 2008-2014 Martin Sandve Alnes
+# Copyright (C) 2008-2015 Martin Sandve Alnæs
#
# This file is part of UFL.
#
@@ -24,7 +25,7 @@ from ufl.constantvalue import as_ufl
from ufl.precedence import parstr
from ufl.exprequals import expr_equals
from ufl.checks import is_true_ufl_scalar
-from ufl.common import EmptyDict
+from ufl.utils.dicts import EmptyDict
from ufl.core.ufl_type import ufl_type
#--- Condition classes ---
diff --git a/ufl/constantvalue.py b/ufl/constantvalue.py
index f090329..fa931bf 100644
--- a/ufl/constantvalue.py
+++ b/ufl/constantvalue.py
@@ -1,6 +1,7 @@
+# -*- coding: utf-8 -*-
"This module defines classes representing constant values."
-# Copyright (C) 2008-2014 Martin Sandve Alnes
+# Copyright (C) 2008-2015 Martin Sandve Alnæs
#
# This file is part of UFL.
#
@@ -28,7 +29,7 @@ from ufl.assertions import ufl_assert, expecting_python_scalar
from ufl.core.expr import Expr
from ufl.core.terminal import Terminal
from ufl.core.multiindex import Index, FixedIndex
-from ufl.common import EmptyDict
+from ufl.utils.dicts import EmptyDict
from ufl.core.ufl_type import ufl_type
#--- Helper functions imported here for compatibility---
@@ -56,7 +57,7 @@ class ConstantValue(Terminal):
"Return whether this expression is spatially constant over each cell."
return True
- def domains(self):
+ def ufl_domains(self):
"Return tuple of domains related to this terminal object."
return ()
@@ -71,11 +72,6 @@ class ConstantValue(Terminal):
# self._name = name
# self.ufl_shape = shape
#
-# def reconstruct(self, name=None):
-# if name is None:
-# name = self._name
-# return AbstractSymbol(name, self.ufl_shape)
-#
# def __str__(self):
# return "<Abstract symbol named '%s' with shape %s>" % (self._name, self.ufl_shape)
#
@@ -144,23 +140,6 @@ class Zero(ConstantValue):
self.ufl_free_indices = free_indices
self.ufl_index_dimensions = index_dimensions
- def free_indices(self):
- "Intermediate helper property getter to transition from .free_indices() to .ufl_free_indices."
- return tuple(Index(count=i) for i in self.ufl_free_indices)
-
- def index_dimensions(self):
- "Intermediate helper property getter to transition from .index_dimensions() to .ufl_index_dimensions."
- return { Index(count=i): d for i, d in zip(self.ufl_free_indices, self.ufl_index_dimensions) }
-
- def reconstruct(self, free_indices=None):
- if not free_indices:
- return self
- ufl_assert(len(free_indices) == len(self.ufl_free_indices),
- "Size mismatch between old and new indices.")
- fid = self.ufl_index_dimensions
- new_fi, new_fid = zip(*tuple(sorted((free_indices[pos], fid[pos]) for pos, a in enumerate(self.ufl_free_indices))))
- return Zero(self.ufl_shape, new_fi, new_fid)
-
def evaluate(self, x, mapping, component, index_values):
return 0.0
@@ -402,5 +381,5 @@ def as_ufl(expression):
return FloatValue(expression)
if isinstance(expression, int):
return IntValue(expression)
- error(("Invalid type conversion: %s can not be converted to any UFL type.\n"+\
+ error(("Invalid type conversion: %s can not be converted to any UFL type.\n"+
"The representation of the object is:\n%r") % (type(expression), expression))
diff --git a/ufl/core/compute_expr_hash.py b/ufl/core/compute_expr_hash.py
index 2621227..7ff6af2 100644
--- a/ufl/core/compute_expr_hash.py
+++ b/ufl/core/compute_expr_hash.py
@@ -1,10 +1,11 @@
+# -*- coding: utf-8 -*-
"""Non-recursive traversal based hash computation algorithm.
Fast iteration over nodes in an Expr DAG to compute
memoized hashes for all unique nodes.
"""
-# Copyright (C) 2015 Martin Sandve Alnes
+# Copyright (C) 2015 Martin Sandve Alnæs
#
# This file is part of UFL.
#
diff --git a/ufl/core/expr.py b/ufl/core/expr.py
index c314d8a..0e28b72 100644
--- a/ufl/core/expr.py
+++ b/ufl/core/expr.py
@@ -1,3 +1,4 @@
+# -*- coding: utf-8 -*-
"""This module defines the Expr class, the superclass
for all expression tree node types in UFL.
@@ -8,7 +9,7 @@ as well as the transpose "A.T" and spatial derivative "a.dx(i)".
This is to avoid circular dependencies between Expr and its subclasses.
"""
-# Copyright (C) 2008-2015 Martin Sandve Alnes
+# Copyright (C) 2008-2015 Martin Sandve Alnæs
#
# This file is part of UFL.
#
@@ -29,8 +30,7 @@ This is to avoid circular dependencies between Expr and its subclasses.
from six.moves import xrange as range
-from ufl.log import warning, error
-
+from ufl.log import warning, error, deprecate
#--- The base object for all UFL expression tree nodes ---
@@ -184,7 +184,7 @@ class Expr(object):
# --- All subclasses must define these object attributes ---
- # FIXME: Enable checks in ufl_type
+ # Each subclass of Expr is checked to have these properties in ufl_type
_ufl_required_properties_ = (
# A tuple of operands, all of them Expr instances.
"ufl_operands",
@@ -199,28 +199,32 @@ class Expr(object):
"ufl_index_dimensions",
)
+ # Each subclass of Expr is checked to have these methods in ufl_type
# FIXME: Add more and enable all
_ufl_required_methods_ = (
# To compute the hash on demand, this method is called.
"_ufl_compute_hash_",
- # The == operator must be implemented to compare for identical representation, used by set() and dict().
+ # The data returned from this method is used to compute the signature of a form
+ "_ufl_signature_data_",
+
+ # The == operator must be implemented to compare for identical representation,
+ # used by set() and dict(). The __hash__ operator is added by ufl_type.
"__eq__",
# To reconstruct an object of the same type with operands or properties changed.
- "reconstruct",
+ "_ufl_expr_reconstruct_", # Implemented in Operator and Terminal so this should never fail
- # Return whether this expression is spatially constant over each cell.
- "domains",
- "is_cellwise_constant",
- #"cell",
- #"domain",
- #"geometric_dimension",
+ "ufl_domains",
+ #"ufl_cell",
+ #"ufl_domain",
#"__str__",
#"__repr__",
- #"signature_data",
- #"__repr__",
+
+ # TODO: Add checks for methods/properties of terminals only?
+ # Required for terminals:
+ #"is_cellwise_constant", # TODO: Rename to ufl_is_cellwise_constant?
)
@@ -285,61 +289,34 @@ class Expr(object):
#--- Functions for reconstructing expression ---
- # All subclasses must implement reconstruct
- def reconstruct(self, *operands):
+ def _ufl_expr_reconstruct_(self, *operands):
"Return a new object of the same type with new operands."
- raise NotImplementedError(self.__class__.reconstruct)
-
- #--- Functions for expression tree traversal ---
-
- # All subclasses must implement operands
- def operands(self):
- "Return a sequence with all subtree nodes in expression tree."
- raise NotImplementedError(self.__class__.operands)
+ raise NotImplementedError(self.__class__._ufl_expr_reconstruct_)
#--- Functions for geometric properties of expression ---
- # All subclasses must implement domains if it is known
- def domains(self):
- # TODO: Is it better to use an external traversal algorithm for this?
- from ufl.geometry import extract_domains
+ def ufl_domains(self): # TODO: Deprecate this and use extract_domains(expr)
+ "Return all domains this expression is defined on."
+ from ufl.domain import extract_domains
return extract_domains(self)
- # All subclasses must implement domain if it is known
- def domain(self):
+ def ufl_domain(self): # TODO: Deprecate this and use extract_unique_domain(expr)
"Return the single unique domain this expression is defined on or throw an error."
- domains = self.domains()
- if len(domains) == 1:
- domain, = domains
- return domain
- elif domains:
- error("Found multiple domains, cannot return just one.")
- else:
- return None
-
- # All subclasses must implement cell if it is known
- def cell(self): # TODO: Deprecate this
- "Return the cell this expression is defined on."
- domain = self.domain()
- return domain.cell() if domain is not None else None
-
- # This function was introduced to clarify and
- # eventually reduce direct dependencies on cells.
- def geometric_dimension(self):
- "Return the geometric dimension this expression lives in."
- # TODO: Deprecate this, and use external analysis algorithm?
- for domain in self.domains():
- return domain.geometric_dimension()
- error("Cannot get geometric dimension from an expression with no domains!")
+ from ufl.domain import extract_unique_domain
+ return extract_unique_domain(self)
- def is_cellwise_constant(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."
- raise NotImplementedError(self.__class__.is_cellwise_constant)
+ 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):
"""Evaluate expression at given coordinate with given values for terminals."""
+ #from ufl.corealg.evaluate import evaluate_expr # TODO: Implement in corealg.eval module
+ #return evaluate_expr(self, ...) # ... then deprecate and remove expr.evaluate()
error("Symbolic evaluation of %s not available." % self._ufl_class_.__name__)
def _ufl_evaluate_scalar_(self):
@@ -380,33 +357,12 @@ class Expr(object):
## Fail gracefully if no valid type conversion found
#raise TypeError("Cannot convert a {0.__class__.__name__} to UFL type.".format(value))
- #--- Functions for shape and index handling ---
-
- def shape(self):
- "Return the tensor shape of the expression."
- return self.ufl_shape
-
- def rank(self):
- "Return the tensor rank of the expression."
- return len(self.ufl_shape)
-
- # All subclasses that can have indices must implement free_indices
- def free_indices(self):
- "Return a tuple with the free indices (unassigned) of the expression."
- raise NotImplementedError(self.__class__.free_indices)
-
- # All subclasses must implement index_dimensions
- def index_dimensions(self):
- """Return a dict with the free or repeated indices in the expression
- as keys and the dimensions of those indices as values."""
- raise NotImplementedError(self.__class__.index_dimensions)
-
#--- Special functions for string representations ---
- # All subclasses must implement signature_data
- def signature_data(self):
+ # All subclasses must implement _ufl_signature_data_
+ def _ufl_signature_data_(self, renumbering):
"Return data that uniquely identifies form compiler relevant aspects of this object."
- raise NotImplementedError(self.__class__.signature_data)
+ raise NotImplementedError(self.__class__._ufl_signature_data_)
# All subclasses must implement __repr__
def __repr__(self):
@@ -459,6 +415,62 @@ class Expr(object):
return round(float(self), n)
+ #--- Deprecated functions
+
+ def reconstruct(self, *operands):
+ "Return a new object of the same type with new operands."
+ 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
+ deprecate("Expr.geometric_dimension() is deprecated, please use find_geometric_dimension(expr) instead.")
+ return find_geometric_dimension(self)
+
+ def domains(self):
+ deprecate("Expr.domains() is deprecated, please use .ufl_domains() instead.")
+ return self.ufl_domains()
+
+ def cell(self):
+ 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):
+ deprecate("Expr.domain() is deprecated, please use .ufl_domain() instead.")
+ return self.ufl_domain()
+
+ def operands(self):
+ 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."
+ 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."
+ deprecate("Expr.rank() is deprecated," +
+ " please use len(expr.ufl_shape) instead.")
+ return len(self.ufl_shape)
+
+ def free_indices(self):
+ 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):
+ 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
Expr._ufl_class_ = Expr
Expr._ufl_all_handler_names_.add(Expr)
diff --git a/ufl/core/multiindex.py b/ufl/core/multiindex.py
index ad85495..62860d9 100644
--- a/ufl/core/multiindex.py
+++ b/ufl/core/multiindex.py
@@ -1,6 +1,7 @@
+# -*- coding: utf-8 -*-
"""This module defines the single index types and some internal index utilities."""
-# Copyright (C) 2008-2014 Martin Sandve Alnes and Anders Logg
+# Copyright (C) 2008-2015 Martin Sandve Alnæs and Anders Logg
#
# This file is part of UFL.
#
@@ -21,11 +22,15 @@ from six.moves import xrange as range
from ufl.log import error
from ufl.assertions import ufl_assert
-from ufl.common import counted_init
+from ufl.utils.counted import counted_init
from ufl.core.ufl_type import ufl_type
from ufl.core.terminal import Terminal
+# Export list for ufl.classes
+__all_classes__ = ["IndexBase", "FixedIndex", "Index"]
+
+
class IndexBase(object):
__slots__ = ()
def __init__(self):
@@ -176,10 +181,9 @@ class MultiIndex(Terminal):
error("Multiindex has no free indices (it is not a tensor expression).")
def is_cellwise_constant(self):
- error("Asking if a Multiindex is cellwise constant makes no sense (it is not a tensor expression).")
- #return True # Could also just return True, after all it doesn't change with the cell
+ return True
- def domains(self):
+ def ufl_domains(self):
"Return tuple of domains related to this terminal object."
return ()
diff --git a/ufl/core/operator.py b/ufl/core/operator.py
index 5b730f6..92ec39c 100644
--- a/ufl/core/operator.py
+++ b/ufl/core/operator.py
@@ -1,6 +1,7 @@
+# -*- coding: utf-8 -*-
"Base class for all operators, i.e. non-terminal expr types."
-# Copyright (C) 2008-2014 Martin Sandve Alnes
+# Copyright (C) 2008-2015 Martin Sandve Alnæs
#
# This file is part of UFL.
#
@@ -24,7 +25,6 @@ from six import iteritems
from ufl.log import error
from ufl.core.expr import Expr
from ufl.core.ufl_type import ufl_type
-from ufl.core.multiindex import Index
#--- Base class for operator objects ---
@@ -42,30 +42,13 @@ class Operator(Expr):
if operands is not None:
self.ufl_operands = operands
- def reconstruct(self, *operands):
+ def _ufl_expr_reconstruct_(self, *operands):
"Return a new object of the same type with new operands."
return self._ufl_class_(*operands)
- def signature_data(self):
+ def _ufl_signature_data_(self):
return self._ufl_typecode_
def _ufl_compute_hash_(self):
"Compute a hash code for this expression. Used by sets and dicts."
return hash((self._ufl_typecode_,) + tuple(hash(o) for o in self.ufl_operands))
-
- def is_cellwise_constant(self):
- "Return whether this expression is spatially constant over each cell."
- return all(o.is_cellwise_constant() for o in self.ufl_operands)
-
- # --- Transitional property getters ---
-
- def operands(self):
- return self.ufl_operands
-
- def free_indices(self):
- "Intermediate helper property getter to transition from .free_indices() to .ufl_free_indices."
- return tuple(Index(count=i) for i in self.ufl_free_indices)
-
- def index_dimensions(self):
- "Intermediate helper property getter to transition from .index_dimensions() to .ufl_index_dimensions."
- return { Index(count=i): d for i, d in zip(self.ufl_free_indices, self.ufl_index_dimensions) }
diff --git a/ufl/core/terminal.py b/ufl/core/terminal.py
index 8d4c0a2..6a55e2c 100644
--- a/ufl/core/terminal.py
+++ b/ufl/core/terminal.py
@@ -1,7 +1,8 @@
+# -*- coding: utf-8 -*-
"""This module defines the Terminal class, the superclass
for all types that are terminal nodes in the expression trees."""
-# Copyright (C) 2008-2014 Martin Sandve Alnes
+# Copyright (C) 2008-2015 Martin Sandve Alnæs
#
# This file is part of UFL.
#
@@ -22,8 +23,6 @@ for all types that are terminal nodes in the expression trees."""
from ufl.log import error, warning
from ufl.assertions import ufl_assert
-from ufl.common import EmptyDict
-from ufl.common import counted_init
from ufl.core.expr import Expr
from ufl.core.ufl_type import ufl_type
@@ -37,29 +36,17 @@ class Terminal(Expr):
def __init__(self):
Expr.__init__(self)
- def reconstruct(self, *operands):
+ def _ufl_expr_reconstruct_(self, *operands):
"Return self."
if operands:
- error("Got call to reconstruct in a terminal with non-empty operands.")
+ error("Terminal has no operands.")
return self
ufl_operands = ()
ufl_free_indices = ()
ufl_index_dimensions = ()
- def operands(self):
- "A Terminal object never has operands."
- return ()
-
- def free_indices(self):
- "A Terminal object never has free indices."
- return ()
-
- def index_dimensions(self):
- "A Terminal object never has free indices."
- return EmptyDict
-
- def domains(self):
+ def ufl_domains(self):
"Return tuple of domains related to this terminal object."
raise NotImplementedError("Missing implementation of domains().")
@@ -100,7 +87,7 @@ class Terminal(Expr):
f = f[c]
return f
- def signature_data(self, renumbering):
+ def _ufl_signature_data_(self, renumbering):
"Default signature data for of terminals just return the repr string."
return repr(self)
diff --git a/ufl/core/ufl_id.py b/ufl/core/ufl_id.py
new file mode 100644
index 0000000..8bb7b3b
--- /dev/null
+++ b/ufl/core/ufl_id.py
@@ -0,0 +1,62 @@
+# -*- coding: utf-8 -*-
+"Utilites for types with a globally counted unique id attached to each object."
+
+# Copyright (C) 2008-2015 Martin Sandve Alnæs
+#
+# This file is part of UFL.
+#
+# UFL is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# UFL is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with UFL. If not, see <http://www.gnu.org/licenses/>.
+
+def attach_ufl_id(cls):
+ """Equip class with .ufl_id() and handle bookkeeping.
+
+ Usage:
+
+ # 1) Apply to class
+ @attach_ufl_id
+ class MyClass(object):
+ # 2) If __slots__ is defined, include "_ufl_id" attribute
+ __slots__ = ("_ufl_id",)
+ # 3) Add keyword argument to constructor
+ def __init__(self, *args, ufl_id=None):
+ # 4) Call self._init_ufl_id with ufl_id and assign to ._ufl_id attribute
+ self._ufl_id = self._init_ufl_id(ufl_id)
+
+ Result:
+
+ MyClass().ufl_id() returns unique value for each constructed object.
+
+ """
+
+ def _get_ufl_id(self):
+ "Return the ufl_id of this object."
+ return self._ufl_id
+
+ def _init_ufl_id(cls):
+ "Initialize new ufl_id for the object under construction."
+ # Bind cls with closure here
+ def init_ufl_id(self, ufl_id):
+ if ufl_id is None:
+ ufl_id = cls._ufl_global_id
+ cls._ufl_global_id = max(ufl_id, cls._ufl_global_id) + 1
+ return ufl_id
+ return init_ufl_id
+
+ # Modify class:
+ if hasattr(cls, "__slots__"):
+ assert "_ufl_id" in cls.__slots__
+ cls._ufl_global_id = 0
+ cls.ufl_id = _get_ufl_id
+ cls._init_ufl_id = _init_ufl_id(cls)
+ return cls
diff --git a/ufl/core/ufl_type.py b/ufl/core/ufl_type.py
index 7848287..b39919a 100644
--- a/ufl/core/ufl_type.py
+++ b/ufl/core/ufl_type.py
@@ -1,13 +1,40 @@
+# -*- coding: utf-8 -*-
from ufl.core.expr import Expr
from ufl.core.compute_expr_hash import compute_expr_hash
-from ufl.common import camel2underscore, EmptyDict
+from ufl.utils.formatting import camel2underscore
+from ufl.utils.dicts import EmptyDict
# Make UFL type coercion available under the as_ufl name
#as_ufl = Expr._ufl_coerce_
+def attach_operators_from_hash_data(cls):
+ """Class decorator to attach __hash__, __eq__ and __ne__ implementations.
+
+ These are implemented in terms of a ._ufl_hash_data() method on the class,
+ which should return a tuple or hashable and comparable data.
+ """
+ assert hasattr(cls, "_ufl_hash_data_")
+
+ def __hash__(self):
+ "__hash__ implementation attached in attach_operators_from_hash_data"
+ return hash(self._ufl_hash_data_())
+ cls.__hash__ = __hash__
+
+ def __eq__(self, other):
+ "__eq__ implementation attached in attach_operators_from_hash_data"
+ return type(self) == type(other) and self._ufl_hash_data_() == other._ufl_hash_data_()
+ cls.__eq__ = __eq__
+
+ def __ne__(self, other):
+ "__ne__ implementation attached in attach_operators_from_hash_data"
+ return type(self) != type(other) or self._ufl_hash_data_() != other._ufl_hash_data_()
+ cls.__ne__ = __ne__
+
+ return cls
+
def get_base_attr(cls, name):
"Return first non-None attribute of given name among base classes."
for base in cls.mro():
@@ -142,50 +169,26 @@ def attach_implementations_of_indexing_interface(cls, inherit_shape_from_operand
# Scalar or index-free? Then we can simplify the implementation of tensor
# properties by attaching them here.
if cls._ufl_is_scalar_:
- # New interface
cls.ufl_shape = ()
- # Legacy interface
- def _scalar_shape(self):
- return ()
- cls.shape = _scalar_shape
if cls._ufl_is_scalar_ or cls._ufl_is_index_free_:
- # New interface
cls.ufl_free_indices = ()
cls.ufl_index_dimensions = ()
- # Legacy interface
- def _empty_free_indices(self):
- return ()
- def _empty_index_dimensions(self):
- return EmptyDict
- cls.free_indices = _empty_free_indices
- cls.index_dimensions = _empty_index_dimensions
# Automate direct inheriting of shape and indices from one of the operands.
# This simplifies refactoring because a lot of types do this.
if inherit_shape_from_operand is not None:
def _inherited_ufl_shape(self):
return self.ufl_operands[inherit_shape_from_operand].ufl_shape
- # New interface
cls.ufl_shape = property(_inherited_ufl_shape)
- # Legacy interface
- cls.shape = _inherited_ufl_shape
if inherit_indices_from_operand is not None:
- # New interface
def _inherited_ufl_free_indices(self):
return self.ufl_operands[inherit_indices_from_operand].ufl_free_indices
def _inherited_ufl_index_dimensions(self):
return self.ufl_operands[inherit_indices_from_operand].ufl_index_dimensions
cls.ufl_free_indices = property(_inherited_ufl_free_indices)
cls.ufl_index_dimensions = property(_inherited_ufl_index_dimensions)
- # Legacy interface
- def _inherited_legacy_free_indices(self):
- return self.ufl_operands[inherit_indices_from_operand].free_indices()
- def _inherited_legacy_index_dimensions(self):
- return self.ufl_operands[inherit_indices_from_operand].index_dimensions()
- cls.free_indices = _inherited_legacy_free_indices
- cls.index_dimensions = _inherited_legacy_index_dimensions
def update_global_expr_attributes(cls):
"Update global Expr attributes, mainly by adding cls to global collections of ufl types."
diff --git a/ufl/corealg/map_dag.py b/ufl/corealg/map_dag.py
index d0506ed..0931c77 100644
--- a/ufl/corealg/map_dag.py
+++ b/ufl/corealg/map_dag.py
@@ -1,6 +1,7 @@
+# -*- coding: utf-8 -*-
"""Basic algorithms for applying functions to subexpressions."""
-# Copyright (C) 2014 Martin Sandve Alnes
+# Copyright (C) 2014-2016 Martin Sandve Alnæs
#
# This file is part of UFL.
#
@@ -18,9 +19,10 @@
# along with UFL. If not, see <http://www.gnu.org/licenses/>.
from ufl.core.expr import Expr
-from ufl.corealg.traversal import post_traversal, cutoff_post_traversal
+from ufl.corealg.traversal import unique_post_traversal, cutoff_unique_post_traversal
from ufl.corealg.multifunction import MultiFunction
+
def map_expr_dag(function, expression, compress=True):
"""Apply a function to each subexpression node in expression dag.
@@ -30,54 +32,70 @@ def map_expr_dag(function, expression, compress=True):
Returns the result of the final function call.
"""
+ result, = map_expr_dags(function, [expression], compress=compress)
+ return result
+
+
+def map_expr_dags(function, expressions, compress=True):
+ """Apply a function to each subexpression node in expression dag.
+
+ If compress is True (default), the output object from
+ the function is cached in a dict and reused such that the
+ resulting expression dag does not contain duplicate objects.
+
+ Returns a list with the result of the final function call for each expression.
+ """
# Temporary data structures
- vcache = {}
- rcache = {}
- results = []
+ vcache = {} # expr -> r = function(expr,...), cache of intermediate results
+ rcache = {} # r -> r, cache of result objects for memory reuse
# Build mapping typecode:bool, for which types to skip the subtree of
if isinstance(function, MultiFunction):
cutoff_types = function._is_cutoff_type
+ handlers = function._handlers # Optimization
else:
# Regular function: no skipping supported
cutoff_types = [False]*Expr._ufl_num_typecodes_
+ handlers = [function]*Expr._ufl_num_typecodes_
+
+ # Create visited set here to share between traversal calls
+ visited = set()
# Pick faster traversal algorithm if we have no cutoffs
if any(cutoff_types):
- traversal = lambda expression: cutoff_post_traversal(expression, cutoff_types)
+ def traversal(expression):
+ return cutoff_unique_post_traversal(expression, cutoff_types, visited)
else:
- traversal = lambda expression: post_traversal(expression)
-
- # Iterate over all subexpression nodes, child before parent
- for v in traversal(expression):
-
- # Check if v is in vcache (to be able to skip transformations)
- i = vcache.get(v)
-
- # Cache hit: skip transformation
- if i is not None:
- continue
-
- # Cache miss: Get transformed operands, then apply transformation
- if cutoff_types[v._ufl_typecode_]:
- r = function(v)
- else:
- rops = [results[vcache[u]] for u in v.ufl_operands]
- r = function(v, *rops)
-
- # Optionally check if r is in rcache (to be able to keep representation of result compact)
- i = rcache.get(r) if compress else None
-
- if i is None:
- # Cache miss: Assign result index and store in results list
- i = len(results)
- results.append(r)
- # Store in rcache
+ def traversal(expression):
+ return unique_post_traversal(expression, visited)
+
+ for expression in expressions:
+ # Iterate over all subexpression nodes, child before parent
+ for v in traversal(expression):
+ # Skip transformations on cache hit
+ if v in vcache:
+ continue
+
+ # Cache miss: Get transformed operands, then apply transformation
+ if cutoff_types[v._ufl_typecode_]:
+ r = handlers[v._ufl_typecode_](v)
+ else:
+ r = handlers[v._ufl_typecode_](v, *[vcache[u] for u in v.ufl_operands])
+
+ # Optionally check if r is in rcache, a memory optimization
+ # to be able to keep representation of result compact
if compress:
- rcache[r] = i
-
- # Store in vcache
- vcache[v] = i
-
- return results[i]
+ r2 = rcache.get(r)
+ if r2 is None:
+ # Cache miss: store in rcache
+ rcache[r] = r
+ else:
+ # Cache hit: Use previously computed object r2,
+ # allowing r to be garbage collected as soon as possible
+ r = r2
+
+ # Store result in cache
+ vcache[v] = r
+
+ return [vcache[expression] for expression in expressions]
diff --git a/ufl/corealg/multifunction.py b/ufl/corealg/multifunction.py
index 4fc9ffb..3f34557 100644
--- a/ufl/corealg/multifunction.py
+++ b/ufl/corealg/multifunction.py
@@ -1,6 +1,7 @@
+# -*- coding: utf-8 -*-
"""Base class for multifunctions with UFL Expr type dispatch."""
-# Copyright (C) 2008-2014 Martin Sandve Alnes
+# Copyright (C) 2008-2015 Martin Sandve Alnæs
#
# This file is part of UFL.
#
@@ -26,6 +27,17 @@ def get_num_args(function):
insp = getargspec(function)
return len(insp[0]) + int(insp[1] is not None)
+def memoized_handler(handler):
+ "Function decorator to memoize MultiFunction handlers."
+ def _memoized_handler(self, o):
+ c = getattr(self, "_memoized_handler_cache")
+ r = c.get(o)
+ if r is None:
+ r = handler(self, o)
+ c[o] = r
+ return r
+ return _memoized_handler
+
class MultiFunction(object):
"""Base class for collections of nonrecursive expression node handlers.
@@ -67,6 +79,9 @@ class MultiFunction(object):
self._handlers = [getattr(self, name) for name in cache_data]
self._is_cutoff_type = [get_num_args(h) == 2 for h in self._handlers]
+ # Create cache for memoized_handler
+ self._memoized_handler_cache = {}
+
def __call__(self, o, *args):
"Delegate to handler function based on typecode of first argument."
return self._handlers[o._ufl_typecode_](o, *args)
@@ -87,7 +102,7 @@ class MultiFunction(object):
if all(a is b for a, b in zip(o.ufl_operands, ops)):
return o
else:
- return o.reconstruct(*ops)
+ return o._ufl_expr_reconstruct_(*ops)
# Set default behaviour for any Expr as undefined
expr = undefined
diff --git a/ufl/corealg/traversal.py b/ufl/corealg/traversal.py
index 5880286..fd74d10 100644
--- a/ufl/corealg/traversal.py
+++ b/ufl/corealg/traversal.py
@@ -1,10 +1,11 @@
+# -*- coding: utf-8 -*-
"""Various expression traversal utilities.
The algorithms here are non-recursive, which is faster than recursion
by a factor 10 or so because of the function call overhead.
"""
-# Copyright (C) 2008-2014 Martin Sandve Alnes
+# Copyright (C) 2008-2016 Martin Sandve Alnæs
#
# This file is part of UFL.
#
@@ -96,7 +97,8 @@ def unique_pre_traversal(expr, visited=None):
stack = [None]*_recursion_limit_
stack[0] = expr
stacksize = 1
- visited = visited or set()
+ if visited is None:
+ visited = set()
while stacksize > 0:
stacksize -= 1
expr = stack[stacksize]
@@ -115,7 +117,8 @@ def unique_post_traversal(expr, visited=None):
stack = [None]*_recursion_limit_
stack[0] = (expr, list(expr.ufl_operands))
stacksize = 1
- visited = visited or set()
+ if visited is None:
+ visited = set()
while stacksize > 0:
expr, ops = stack[stacksize - 1]
for i, o in enumerate(ops):
@@ -130,6 +133,29 @@ def unique_post_traversal(expr, visited=None):
stacksize -= 1
+def cutoff_unique_post_traversal(expr, cutofftypes, visited=None):
+ """Yields o for each node o in expr, child before parent.
+
+ Never visits a node twice."""
+ stack = [None]*_recursion_limit_
+ stack[0] = (expr, () if cutofftypes[expr._ufl_typecode_] else list(expr.ufl_operands))
+ stacksize = 1
+ if visited is None:
+ visited = set()
+ while stacksize > 0:
+ expr, ops = stack[stacksize - 1]
+ for i, o in enumerate(ops):
+ if o is not None and o not in visited:
+ stack[stacksize] = (o, () if cutofftypes[o._ufl_typecode_] else list(o.ufl_operands))
+ stacksize += 1
+ ops[i] = None
+ break
+ else:
+ yield expr
+ visited.add(expr)
+ stacksize -= 1
+
+
def traverse_terminals(expr):
"Iterate over all terminal objects in expression, including duplicates."
stack = [None]*_recursion_limit_
@@ -146,12 +172,13 @@ def traverse_terminals(expr):
stacksize += 1
-def traverse_unique_terminals(expr):
+def traverse_unique_terminals(expr, visited=None):
"Iterate over all terminal objects in expression, not including duplicates."
stack = [None]*_recursion_limit_
stack[0] = expr
stacksize = 1
- visited = set()
+ if visited is None:
+ visited = set()
while stacksize > 0:
stacksize -= 1
expr = stack[stacksize]
diff --git a/ufl/differentiation.py b/ufl/differentiation.py
index 88d308c..07387d5 100644
--- a/ufl/differentiation.py
+++ b/ufl/differentiation.py
@@ -1,6 +1,7 @@
+# -*- coding: utf-8 -*-
"Differential operators."
-# Copyright (C) 2008-2014 Martin Sandve Alnes
+# Copyright (C) 2008-2015 Martin Sandve Alnæs
#
# This file is part of UFL.
#
@@ -21,18 +22,22 @@
from ufl.log import warning, error
from ufl.assertions import ufl_assert
-from ufl.common import subdict, mergedicts, EmptyDict
+from ufl.utils.dicts import subdict, mergedicts, EmptyDict
+
from ufl.core.expr import Expr
from ufl.core.terminal import Terminal
from ufl.core.operator import Operator
+from ufl.core.ufl_type import ufl_type
+from ufl.core.multiindex import Index, FixedIndex, MultiIndex
+
from ufl.exprcontainers import ExprList, ExprMapping
from ufl.constantvalue import Zero
from ufl.coefficient import Coefficient
-from ufl.core.multiindex import Index, FixedIndex, MultiIndex
from ufl.indexed import Indexed
from ufl.variable import Variable
from ufl.precedence import parstr
-from ufl.core.ufl_type import ufl_type
+from ufl.domain import find_geometric_dimension
+from ufl.checks import is_cellwise_constant
#--- Basic differentiation objects ---
@@ -121,19 +126,18 @@ class Grad(CompoundDerivative):
def __new__(cls, f):
# Return zero if expression is trivially constant
- if f.is_cellwise_constant():
- dim = f.geometric_dimension()
+ if is_cellwise_constant(f):
+ dim = find_geometric_dimension(f)
return Zero(f.ufl_shape + (dim,), f.ufl_free_indices, f.ufl_index_dimensions)
-
return CompoundDerivative.__new__(cls)
def __init__(self, f):
CompoundDerivative.__init__(self, (f,))
- self._dim = f.geometric_dimension()
+ self._dim = find_geometric_dimension(f)
- def reconstruct(self, op):
+ def _ufl_expr_reconstruct_(self, op):
"Return a new object of the same type with new operands."
- if op.is_cellwise_constant():
+ if is_cellwise_constant(op):
ufl_assert(op.ufl_shape == self.ufl_operands[0].ufl_shape,
"Operand shape mismatch in Grad reconstruct.")
ufl_assert(self.ufl_operands[0].ufl_free_indices == op.ufl_free_indices,
@@ -169,20 +173,18 @@ class ReferenceGrad(CompoundDerivative):
def __new__(cls, f):
# Return zero if expression is trivially constant
- if f.is_cellwise_constant():
- dim = f.domain().topological_dimension()
+ if is_cellwise_constant(f):
+ dim = f.ufl_domain().topological_dimension()
return Zero(f.ufl_shape + (dim,), f.ufl_free_indices, f.ufl_index_dimensions)
return CompoundDerivative.__new__(cls)
def __init__(self, f):
CompoundDerivative.__init__(self, (f,))
- domain = f.domain()
- dim = domain.topological_dimension()
- self._dim = dim
+ self._dim = f.ufl_domain().topological_dimension()
- def reconstruct(self, op):
+ def _ufl_expr_reconstruct_(self, op):
"Return a new object of the same type with new operands."
- if op.is_cellwise_constant():
+ if is_cellwise_constant(op):
ufl_assert(op.ufl_shape == self.ufl_operands[0].ufl_shape,
"Operand shape mismatch in ReferenceGrad reconstruct.")
ufl_assert(self.ufl_operands[0].ufl_free_indices == op.ufl_free_indices,
@@ -218,7 +220,7 @@ class Div(CompoundDerivative):
"Free indices in the divergence argument is not allowed.")
# Return zero if expression is trivially constant
- if f.is_cellwise_constant():
+ if is_cellwise_constant(f):
return Zero(f.ufl_shape[:-1]) # No free indices asserted above
return CompoundDerivative.__new__(cls)
@@ -248,7 +250,7 @@ class ReferenceDiv(CompoundDerivative):
"Free indices in the divergence argument is not allowed.")
# Return zero if expression is trivially constant
- if f.is_cellwise_constant():
+ if is_cellwise_constant(f):
return Zero(f.ufl_shape[:-1]) # No free indices asserted above
return CompoundDerivative.__new__(cls)
@@ -272,18 +274,18 @@ class NablaGrad(CompoundDerivative):
def __new__(cls, f):
# Return zero if expression is trivially constant
- if f.is_cellwise_constant():
- dim = f.geometric_dimension()
+ if is_cellwise_constant(f):
+ dim = find_geometric_dimension(f)
return Zero((dim,) + f.ufl_shape, f.ufl_free_indices, f.ufl_index_dimensions)
return CompoundDerivative.__new__(cls)
def __init__(self, f):
CompoundDerivative.__init__(self, (f,))
- self._dim = f.geometric_dimension()
+ self._dim = find_geometric_dimension(f)
- def reconstruct(self, op):
+ def _ufl_expr_reconstruct_(self, op):
"Return a new object of the same type with new operands."
- if op.is_cellwise_constant():
+ if is_cellwise_constant(op):
ufl_assert(op.ufl_shape == self.ufl_operands[0].ufl_shape,
"Operand shape mismatch in NablaGrad reconstruct.")
ufl_assert(self.ufl_operands[0].ufl_free_indices == op.ufl_free_indices,
@@ -311,7 +313,7 @@ class NablaDiv(CompoundDerivative):
"Free indices in the divergence argument is not allowed.")
# Return zero if expression is trivially constant
- if f.is_cellwise_constant():
+ if is_cellwise_constant(f):
return Zero(f.ufl_shape[1:]) # No free indices asserted above
return CompoundDerivative.__new__(cls)
@@ -342,7 +344,7 @@ class Curl(CompoundDerivative):
"Free indices in the curl argument is not allowed.")
# Return zero if expression is trivially constant
- if f.is_cellwise_constant():
+ if is_cellwise_constant(f):
sh = { (): (2,), (2,): (), (3,): (3,) }[sh]
return Zero(sh) # No free indices asserted above
return CompoundDerivative.__new__(cls)
@@ -373,7 +375,7 @@ class ReferenceCurl(CompoundDerivative):
"Free indices in the curl argument is not allowed.")
# Return zero if expression is trivially constant
- if f.is_cellwise_constant():
+ if is_cellwise_constant(f):
sh = { (): (2,), (2,): (), (3,): (3,) }[sh]
return Zero(sh) # No free indices asserted above
return CompoundDerivative.__new__(cls)
diff --git a/ufl/domain.py b/ufl/domain.py
index 46ec44e..2012570 100644
--- a/ufl/domain.py
+++ b/ufl/domain.py
@@ -1,6 +1,7 @@
+# -*- coding: utf-8 -*-
"Types for representing a geometric domain."
-# Copyright (C) 2008-2014 Martin Sandve Alnes
+# Copyright (C) 2008-2015 Martin Sandve Alnæs
#
# This file is part of UFL.
#
@@ -24,416 +25,345 @@
from collections import defaultdict
from six import iteritems
+from ufl.core.terminal import Terminal
+from ufl.core.ufl_type import attach_operators_from_hash_data
+from ufl.core.ufl_id import attach_ufl_id
from ufl.corealg.traversal import traverse_unique_terminals
from ufl.log import warning, error, deprecate
from ufl.assertions import ufl_assert
-from ufl.common import istr, EmptyDict
-from ufl.core.terminal import Terminal
+from ufl.utils.formatting import istr
+from ufl.utils.dicts import EmptyDict
from ufl.protocols import id_or_none
-from ufl.cell import as_cell, affine_cells, Cell, ProductCell
+from ufl.cell import as_cell, AbstractCell, Cell, TensorProductCell
-class Domain(object):
- """Symbolic representation of a geometrical domain.
+# Export list for ufl.classes
+__all_classes__ = ["AbstractDomain", "Mesh", "MeshView", "TensorProductMesh"]
- Used in the definition of geometric terminal expressions,
- finite element spaces, and integration measures.
- Takes a single positional argument which is either the
- cell of the underlying mesh
+class AbstractDomain(object):
+ """Symbolic representation of a geometric domain with only a geometric and topological dimension."""
+ def __init__(self, topological_dimension, geometric_dimension):
+ # Validate dimensions
+ ufl_assert(isinstance(geometric_dimension, int),
+ "Expecting integer geometric dimension, not '%r'" % (geometric_dimension,))
+ ufl_assert(isinstance(topological_dimension, int),
+ "Expecting integer topological dimension, not '%r'" % (topological_dimension,))
+ ufl_assert(topological_dimension <= geometric_dimension,
+ "Topological dimension cannot be larger than geometric dimension.")
- D = Domain(triangle)
+ # Store validated dimensions
+ self._topological_dimension = topological_dimension
+ self._geometric_dimension = geometric_dimension
- or the coordinate field which is a vector valued Coefficient.
+ def geometric_dimension(self):
+ "Return the dimension of the space this domain is embedded in."
+ return self._geometric_dimension
- P2 = VectorElement("CG", D, 2)
- x = Coefficient(P2)
- E = Domain(x)
+ def topological_dimension(self):
+ "Return the dimension of the topology of this domain."
+ return self._topological_dimension
- With the cell variant of the constructor, an optional
- label can be passed to distinguish two domains from each
- other.
- Da = Domain(cell, label="a")
- Db = Domain(cell, label="b")
+# TODO: Would it be useful to have a domain representing R^d? E.g. for Expression.
+#class EuclideanSpace(AbstractDomain):
+# def __init__(self, geometric_dimension):
+# AbstractDomain.__init__(self, geometric_dimension, geometric_dimension)
- an optional data argument can also be passed, for integration
- with problem solver environments (e.g. dolfin), this is typically
- the underlying mesh.
- Da = Domain(cell, label="a", data=mesha)
- Db = Domain(cell, label="b", data=meshb)
+ at attach_operators_from_hash_data
+ at attach_ufl_id
+class Mesh(AbstractDomain):
+ """Symbolic representation of a mesh."""
+ def __init__(self, coordinate_element, ufl_id=None, cargo=None):
+ self._ufl_id = self._init_ufl_id(ufl_id)
- """
- __slots__ = (
- "_geometric_dimension",
- "_topological_dimension",
- "_cell",
- "_coordinates",
- "_label",
- "_data",
- )
- def __init__(self, *args, **kwargs):
- # Parse positional argument, either a Cell or a Coefficient
- ufl_assert(len(args) == 1, "Only one positional argument accepted. See Domain docstring.")
- arg, = args
-
- # To avoid circular dependencies...
+ # Store reference to object that will not be used by UFL
+ self._ufl_cargo = cargo
+ if cargo is not None and cargo.ufl_id() != self._ufl_id:
+ error("Expecting cargo object (e.g. dolfin.Mesh) to have the same ufl_id.")
+
+ # No longer accepting coordinates provided as a Coefficient
from ufl.coefficient import Coefficient
+ if isinstance(coordinate_element, Coefficient):
+ error("Expecting a coordinate element in the ufl.Mesh construct.")
- if isinstance(arg, Cell):
- # Allow keyword arguments for label or data
- self._coordinates = None
- self._cell = arg
- self._label = kwargs.pop("label", None)
- self._data = kwargs.pop("data", None)
-
- elif isinstance(arg, Coefficient):
- # Disallow additional label and data, get from underlying 'flat domain'
- self._coordinates = arg
- flat_domain = arg.domain()
- self._cell = flat_domain.cell()
- self._label = flat_domain.label()
- self._data = flat_domain.data()
-
- # Get geometric dimension from self._coordinates shape
- gdim, = self._coordinates.ufl_shape
- if gdim != self._cell.geometric_dimension():
- warning("Using geometric dimension from coordinates!")
- self._cell = Cell(self._cell.cellname(), gdim)
- #ufl_assert(self._coordinates.ufl_shape == (self._cell.geometric_dimension(),),
- # "Shape of coordinates %s does not match geometric dimension %d of cell." %\
- # (self._coordinates.ufl_shape, self._cell.geometric_dimension()))
- else:
- ufl_error("Invalid first argument to Domain.")
-
- # Now we should have a Cell or something went wrong
- ufl_assert(isinstance(self._cell, Cell), "Failed to construct a Cell from input arguments.")
- self._geometric_dimension = self._cell.geometric_dimension()
- self._topological_dimension = self._cell.topological_dimension()
-
- # Sanity checks
- ufl_assert(isinstance(self._geometric_dimension, int),
- "Expecting integer geometric dimension.")
- ufl_assert(isinstance(self._topological_dimension, int),
- "Expecting integer topological dimension.")
- ufl_assert(self._topological_dimension <= self._geometric_dimension,
- "Topological dimension cannot be greater than geometric dimension.")
- ufl_assert(self._topological_dimension >= 0,
- "Topological dimension must be non-negative.")
-
- if self._coordinates is not None:
- ufl_assert(isinstance(self._coordinates, Coefficient),
- "Expecting None or Coefficient for coordinates.")
- ufl_assert(self._coordinates.domain().coordinates() is None,
- "Coordinates must be defined on a domain without coordinates of its own.")
- ufl_assert(self._label is None or isinstance(self._label, str),
- "Expecting None or str for label.")
- ufl_assert(self._data is None or hasattr(self._data, "ufl_id"),
- "Expecting data object to implement ufl_id().")
-
- # Check that we didn't get any arguments that we havent interpreted
- ufl_assert(not kwargs, "Got unused keyword arguments %s" % ', '.join(sorted(kwargs)))
-
- def reconstruct(self, cell=None, coordinates=None, label=None, data=None):
- "Create a new Domain object with possibly changed label or data."
- if coordinates is None:
- if cell is None:
- cell = self.cell()
- if label is None:
- label = self.label()
- if data is None:
- data = self.data()
- return Domain(cell, label=label, data=data)
- else:
- ufl_assert(all((cell is None, label is None, data is None)),
- "No other arguments allowed with coordinates.")
- return Domain(coordinates)
+ # Accept a cell in place of an element for brevity Mesh(triangle)
+ if isinstance(coordinate_element, AbstractCell):
+ from ufl.finiteelement import VectorElement
+ cell = coordinate_element
+ coordinate_element = VectorElement("Lagrange", cell, 1, dim=cell.geometric_dimension())
- def geometric_dimension(self):
- "Return the dimension of the space this domain is embedded in."
- return self._geometric_dimension
+ # Store coordinate element
+ self._ufl_coordinate_element = coordinate_element
- def topological_dimension(self):
- "Return the dimension of the topology of this domain."
- return self._topological_dimension
+ # Derive dimensions from element
+ gdim, = coordinate_element.value_shape()
+ tdim = coordinate_element.cell().topological_dimension()
+ AbstractDomain.__init__(self, tdim, gdim)
+ def ufl_cargo(self):
+ "Return carried object that will not be used by UFL."
+ return self._ufl_cargo
+
+ def ufl_coordinate_element(self):
+ return self._ufl_coordinate_element
+
+ def ufl_cell(self):
+ return self._ufl_coordinate_element.cell()
+
+ def is_piecewise_linear_simplex_domain(self):
+ return (self._ufl_coordinate_element.degree() == 1) and self.ufl_cell().is_simplex()
+
+ def __repr__(self):
+ return "Mesh(%r, %r)" % (self._ufl_coordinate_element, self._ufl_id)
+
+ def __str__(self):
+ return "<Mesh #%s with coordinates parameterized by %s>" % (self._ufl_id, self._ufl_coordinate_element)
+
+ def _ufl_hash_data_(self):
+ return (self._ufl_id, self._ufl_coordinate_element)
+
+ def _ufl_signature_data_(self, renumbering):
+ return ("Mesh", renumbering[self], self._ufl_coordinate_element)
+
+ # NB! Dropped __lt__ here, don't want users to write 'mesh1 < mesh2'.
+ def _ufl_sort_key_(self):
+ typespecific = (self._ufl_id, self._ufl_coordinate_element)
+ return (self.geometric_dimension(), self.topological_dimension(), "Mesh", typespecific)
+
+ # Deprecations inherited from Domain
def cell(self):
- "Return the cell this domain is defined in terms of."
- return self._cell
+ deprecate("Mesh.cell() is deprecated, please use .ufl_cell() instead.")
+ return self.ufl_cell()
def coordinates(self):
- "Return the coordinate vector field this domain is defined in terms of."
- return self._coordinates
-
- def coordinate_element(self):
- "Return the finite element of the coordinate vector field of this domain."
- x = self.coordinates()
- if x is None:
- from ufl import VectorElement
- return VectorElement("Lagrange", self, 1)
- else:
- return x.element()
+ 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.")
+
+
+ at attach_operators_from_hash_data
+ at attach_ufl_id
+class MeshView(AbstractDomain):
+ """Symbolic representation of a mesh."""
+ def __init__(self, mesh, topological_dimension, ufl_id=None):
+ self._ufl_id = self._init_ufl_id(ufl_id)
+
+ # Store mesh
+ self._ufl_mesh = mesh
- def label(self):
- "Return the label identifying this domain. None means no label has been set."
- return self._label
+ # Derive dimensions from element
+ gdim, = coordinate_element.value_shape()
+ tdim = coordinate_element.cell().topological_dimension()
+ AbstractDomain.__init__(self, tdim, gdim)
+
+ def ufl_mesh(self):
+ return self._ufl_mesh
+
+ def ufl_cell(self):
+ return self._ufl_mesh.ufl_cell()
def is_piecewise_linear_simplex_domain(self):
- return (self.coordinate_element().degree() == 1) and (self.cell().cellname() in affine_cells)
-
- def data(self):
- "Return attached data object."
- return self._data
-
- def signature_data(self, renumbering):
- "Signature data of domain depend on the global domain numbering."
- count = renumbering[self]
- cdata = self.cell()
- x = self.coordinates()
- xdata = (None if x is None else x.signature_data(renumbering))
- return (count, cdata, xdata)
-
- def hash_data(self):
- # Including only id of data here.
- # If this is a problem in pydolfin, the user will just have
- # to create explicit Domain objects to avoid problems.
- # NB! This data is used in both __hash__ and __eq__.
- return (self._label,
- self._cell,
- self._coordinates, # None or a Coefficient
- id_or_none(self._data))
-
- def __hash__(self):
- return hash(self.hash_data())
-
- def __eq__(self, other):
- return type(self) == type(other) and self.hash_data() == other.hash_data()
-
- def __lt__(self, other):
- if type(self) != type(other):
- return NotImplemented
- return self.hash_data() < other.hash_data()
+ return self._ufl_mesh.is_piecewise_linear_simplex_domain()
+
+ def __repr__(self):
+ return "MeshView(%r, %r, %r)" % (self._ufl_mesh, self.topological_dimension(), self._ufl_id)
def __str__(self):
- if self._coordinates is None:
- c = ""
- else:
- c = " and coordinates %r" % self._coordinates
- s = (self._cell, self._label, c)
- return "<Domain built from %s with label %s%s>" % s
+ return "<MeshView #%s of dimension %d over mesh %s>" % (self._ufl_id, self.topological_dimension(), self._ufl_mesh)
+
+ def _ufl_hash_data_(self):
+ return (self._ufl_id,) + self._ufl_mesh._ufl_hash_data_()
+
+ def _ufl_signature_data_(self, renumbering):
+ return ("MeshView", renumbering[self], self._ufl_mesh._ufl_signature_data_(renumbering))
+
+ # NB! Dropped __lt__ here, don't want users to write 'mesh1 < mesh2'.
+ def _ufl_sort_key_(self):
+ typespecific = (self._ufl_id, self._ufl_mesh)
+ return (self.geometric_dimension(), self.topological_dimension(), "MeshView", typespecific)
+
- def reconstruction_signature(self):
- """Format as string for evaluation as Python object.
+ at attach_operators_from_hash_data
+ at attach_ufl_id
+class TensorProductMesh(AbstractDomain):
+ """Symbolic representation of a mesh."""
+ def __init__(self, meshes, ufl_id=None):
+ self._ufl_id = self._init_ufl_id(ufl_id)
- For use with cross language frameworks, stored in generated code
- and evaluated later in Python to reconstruct this object.
+ # TODO: Error checking of meshes
+ self._ufl_meshes = meshes
- This differs from repr in that it does not include domain
- label and data or coordinates, which must be reconstructed
- or supplied by other means.
- """
- s = (self._cell,)
- return "Domain(%r)" % s
+ # TODO: Is this what we want to do?
+ # Build cell from mesh cells
+ self._ufl_cell = TensorProductCell([mesh.ufl_cell() for mesh in meshes])
+
+ # TODO: Is this what we want to do?
+ # Build coordinate element from mesh coordinate elements
+ self._ufl_coordinate_element = TensorProductElement([mesh.ufl_coordinate_element() for mesh in meshes])
+
+ # Derive dimensions from meshes
+ gdim = sum(mesh.geometric_dimension() for mesh in meshes)
+ tdim = sum(mesh.topological_dimension() for mesh in meshes)
+
+ AbstractDomain.__init__(self, tdim, gdim)
+
+ def ufl_coordinate_element(self):
+ return self._ufl_coordinate_element
+
+ def ufl_cell(self):
+ return self._ufl_cell
+
+ def is_piecewise_linear_simplex_domain(self):
+ return False # TODO: Any cases this is True
def __repr__(self):
- if self._coordinates is None:
- d = None if self._data is None else "<data with id %s>" % id_or_none(self._data)
- s = (self._cell, self._label, d)
- return "Domain(%r, label=%r, data=%r)" % s
- else:
- s = (self._coordinates,)
- return "Domain(%r)" % s
-
-class OverlapDomain(Domain):
- """WARNING: This is work in progress, design is in no way completed."""
- __slots__ = ("_child_domains",)
- def __init__(self, domain1, domain2, label=None, data=None):
- # Check domain compatibility
- ufl_assert(domain1.cell() == domain2.cell(),
- "Cell mismatch in overlap domain.")
- ufl_assert(domain1.geometric_dimension() == domain2.geometric_dimension(),
- "Dimension mismatch in overlap domain.")
- ufl_assert(domain1.topological_dimension() == domain2.topological_dimension(),
- "Dimension mismatch in overlap domain.")
-
- # Initialize parent class
- Domain.__init__(self, domain1.cell(), label=label, data=data)
-
- # Save child domains for later
- self._child_domains = (domain1, domain2)
-
- def child_domains(self):
- return self._child_domains
-
-class IntersectionDomain(Domain):
- """WARNING: This is work in progress, design is in no way completed."""
- __slots__ = ("_child_domains",)
- def __init__(self, domain1, domain2, label=None, data=None):
- # Check domain compatibility
- ufl_assert(domain1.cell() == domain2.cell(),
- "Cell mismatch in overlap domain.")
- ufl_assert(domain1.geometric_dimension() == domain2.geometric_dimension(),
- "Dimension mismatch in overlap domain.")
- ufl_assert(domain1.topological_dimension() == domain2.topological_dimension(),
- "Dimension mismatch in overlap domain.")
-
- # Get the right properties of this domain
- gdim = domain1.geometric_dimension()
- tdim = domain1.topological_dimension()-1
- cell = Cell(domain1.cell().facet_cellname(), gdim)
- ufl_assert(cell.topological_dimension() == tdim)
-
- # Initialize parent class
- Domain.__init__(self, cell, gdim, tdim, label=label, data=data)
-
- # Save child domains for later
- self._child_domains = (domain1, domain2)
-
- def child_domains(self):
- return self._child_domains
-
-class ProductDomain(Domain):
- """WARNING: This is work in progress, design is in no way completed."""
- __slots__ = ("_child_domains",)
- def __init__(self, domains, data=None):
- # Get the right properties of this domain
- gdim = sum(domain.geometric_dimension() for domain in domains)
- tdim = sum(domain.topological_dimension() for domain in domains)
- cell = ProductCell(*[domain.cell() for domain in domains])
- label = "product_of_%s" % "_".join(str(domain.label()) for domain in domains)
-
- # Initialize parent class
- Domain.__init__(self, cell, gdim, tdim, label=label, data=data)
-
- # Save child domains for later
- self._child_domains = tuple(domains)
-
- def child_domains(self):
- return self._child_domains
+ return "TensorProductMesh(%r, %r)" % (self._ufl_meshes, self._ufl_id)
+
+ def __str__(self):
+ return "<TensorProductMesh #%s with meshes %s>" % (self._ufl_id, self._ufl_meshes)
+
+ def _ufl_hash_data_(self):
+ return (self._ufl_id,) + tuple(mesh._ufl_hash_data_() for mesh in self._ufl_meshes)
+
+ def _ufl_signature_data_(self, renumbering):
+ return ("TensorProductMesh",) + tuple(mesh._ufl_signature_data_(renumbering) for mesh in self._ufl_meshes)
+
+ # NB! Dropped __lt__ here, don't want users to write 'mesh1 < mesh2'.
+ def _ufl_sort_key_(self):
+ typespecific = (self._ufl_id, tuple(mesh._ufl_sort_key_() for mesh in self._ufl_meshes))
+ return (self.geometric_dimension(), self.topological_dimension(), "TensorProductMesh", typespecific)
+
# --- Utility conversion functions
+def affine_mesh(cell, ufl_id=None):
+ "Create a Mesh over a given cell type with an affine geometric parameterization."
+ from ufl.finiteelement import VectorElement
+ cell = as_cell(cell)
+ gdim = cell.geometric_dimension()
+ degree = 1
+ coordinate_element = VectorElement("Lagrange", cell, degree, dim=gdim)
+ return Mesh(coordinate_element, ufl_id=ufl_id)
+
+_default_domains = {}
+def default_domain(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)
+ if domain is None:
+ # Create one and only one affine Mesh with
+ # a negative ufl_id to avoid id collision
+ ufl_id = -(len(_default_domains)+1)
+ domain = affine_mesh(cell, ufl_id=ufl_id)
+ _default_domains[cell] = domain
+ return domain
def as_domain(domain):
- """Convert any valid object to a Domain (in particular, cell or cellname string),
- or return domain if it is already a Domain."""
- if isinstance(domain, Domain):
+ """Convert any valid object to an AbstractDomain type."""
+ if isinstance(domain, AbstractDomain):
+ # Modern .ufl files and dolfin behaviour
return domain
elif hasattr(domain, "ufl_domain"):
+ # If we get a dolfin.Mesh, it can provide us a corresponding ufl.Mesh.
+ # This would be unnecessary if dolfin.Mesh could subclass ufl.Mesh.
return domain.ufl_domain()
else:
- return Domain(as_cell(domain))
-
-def join_subdomain_data(subdomain_datas): # FIXME: Remove? Think it's unused now.
- newdata = {}
- for data in subdomain_datas:
- for k, v in iteritems(data):
- nv = newdata.get(k)
- if nv is None:
- # New item, just add it
- newdata[k] = v
- elif v is not None:
- id1 = id_or_none(nv)
- id2 = id_or_none(v)
- if id1 != id2:
- error("Found multiple data objects with key %s." % k)
- return newdata
-
-def check_domain_compatibility(domains):
- # Validate that the domains are the same except for possibly the data
- labels = set(domain.label() for domain in domains)
- ufl_assert(len(labels) == 1 or (len(labels) == 2 and None in labels),
- "Got incompatible domain labels %s in check_domain_compatibility." % (labels,))
-
- all_cellnames = [dom.cell().cellname() for dom in domains]
- if len(set(all_cellnames)) != 1:
- error("Cellname mismatch between domains with same label.")
-
- all_coordinates = set(dom.coordinates() for dom in domains) - set((None,))
- if len(all_coordinates) > 1:
- error("Coordinates mismatch between domains with same label.")
+ # Legacy .ufl files
+ # TODO: Make this conversion in the relevant constructors closer to the user interface?
+ # TODO: Make this configurable to be an error from the dolfin side?
+ cell = as_cell(domain)
+ return default_domain(cell)
+ #else:
+ # error("Invalid domain %s" % (domain,))
+
+def sort_domains(domains):
+ "Sort domains in a canonical ordering."
+ return tuple(sorted(domains, key=lambda domain: domain._ufl_sort_key_()))
def join_domains(domains):
- """Take a list of Domains and return a list 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 label are compatible,
- and allows data to be None or
+ Checks that domains with the same id are compatible.
"""
- # Ignore Nones in input domains
- domains = [domain for domain in domains if domain is not None]
+ # Use hashing to join domains, ignore None
+ domains = set(domains) - set((None,))
+ if not domains:
+ return ()
- # Build lists of domain objects with same label
- label2domlist = defaultdict(list)
+ # Check geometric dimension compatibility
+ gdims = set()
for domain in domains:
- label2domlist[domain.label()].append(domain)
-
- # Extract None list from this dict, map to label but only if only one exists
- if None in label2domlist:
- none_domains = {}
- if len(label2domlist) == 1:
- pass
- elif len(label2domlist) == 2:
- none_domains = label2domlist[None]
- del label2domlist[None]
- key, = list(label2domlist.keys())
- label2domlist[key].extend(none_domains)
- else:
- error("Ambiguous mapping of domains with label None to multiple domains with different labels.")
- else:
- none_domains = {}
-
- # Join domain data to get a list with only one domain for each label
- newdomains = []
- for label in sorted(label2domlist.keys()):
- domlist = label2domlist[label]
- if len(domlist) == 1:
- dom, = domlist
+ gdims.add(domain.geometric_dimension())
+ if len(gdims) != 1:
+ error("Found domains with different geometric dimensions.")
+ gdim, = gdims
+
+ # Split into legacy and modern style domains
+ legacy_domains = []
+ modern_domains = []
+ for domain in domains:
+ if isinstance(domain, Mesh) and domain.ufl_id() < 0:
+ assert domain.ufl_cargo() is None
+ legacy_domains.append(domain)
else:
- # Validate that the domains are the same except for possibly the data
- check_domain_compatibility(domlist)
-
- # Pick first non-None data object
- for dom in domlist:
- newdata = dom.data()
- if newdata is not None:
- break
- cell = dom.cell()
- gdim = dom.geometric_dimension()
- tdim = dom.topological_dimension()
-
- # Validate that data ids match if present
- if newdata is not None:
- data_ids = [id_or_none(dom.data()) for dom in domlist]
- data_ids = set(i for i in data_ids if i is not None)
- if len(data_ids) > 1:
- error("Found data objects with different ids in domains with same label.")
-
- # Pick first non-None coordinates object
- for dom in domlist:
- newcoordinates = dom.coordinates()
- if newcoordinates is not None:
- ufl_assert(newcoordinates.domain().coordinates() is None,
- "A coordinate domain cannot have coordinates.")
- break
-
- # Validate that coordinates match if present
- if newcoordinates is not None:
- all_coordinates = [dom.coordinates() for dom in domlist]
- all_coordinates = set(c for c in all_coordinates if c is not None)
- if len(all_coordinates) > 1:
- error("Found different coordinates in domains with same label.")
-
- # Construct a new domain object with fully completed data
- if newcoordinates is not None:
- dom = Domain(newcoordinates)
- else:
- dom = Domain(cell, label=label, data=newdata)
- newdomains.append(dom)
- return tuple(newdomains)
+ modern_domains.append(domain)
+
+ # Handle legacy domains checking
+ if legacy_domains:
+ if modern_domains:
+ error("Found both a new-style domain and a legacy default domain.\n"
+ "These should not be used interchangeably. To find the legacy\n"
+ "domain, note that it is automatically created from a cell so\n"
+ "look for constructors taking a cell.")
+ return tuple(legacy_domains)
+
+ # Handle modern domains checking (assuming correct by construction)
+ return tuple(modern_domains)
+
+
+# TODO: Move these to an analysis module?
def extract_domains(expr):
+ "Return all domains expression is defined on."
domainlist = []
for t in traverse_unique_terminals(expr):
- domainlist.extend(t.domains())
+ domainlist.extend(t.ufl_domains())
return sorted(join_domains(domainlist))
+
+def extract_unique_domain(expr):
+ "Return the single unique domain expression is defined on or throw an error."
+ domains = extract_domains(expr)
+ if len(domains) == 1:
+ return domains[0]
+ elif domains:
+ error("Found multiple domains, cannot return just one.")
+ else:
+ #error("Found no domains.")
+ return None
+
+def find_geometric_dimension(expr):
+ "Find the geometric dimension of an expression."
+ gdims = set()
+ for t in traverse_unique_terminals(expr):
+ if hasattr(t, "ufl_domain"):
+ domain = t.ufl_domain()
+ if domain is not None:
+ gdims.add(domain.geometric_dimension())
+ if hasattr(t, "ufl_element"):
+ element = t.ufl_element()
+ if element is not None:
+ cell = element.cell()
+ if cell is not None:
+ gdims.add(cell.geometric_dimension())
+ if len(gdims) != 1:
+ error("Cannot determine geometric dimension from expression.")
+ gdim, = gdims
+ return gdim
diff --git a/ufl/equation.py b/ufl/equation.py
index 072bd13..4c6df8b 100644
--- a/ufl/equation.py
+++ b/ufl/equation.py
@@ -1,6 +1,7 @@
+# -*- coding: utf-8 -*-
"The Equation class, used to express equations like a == L."
-# Copyright (C) 2012-2014 Anders Logg and Martin Sandve Alnes
+# Copyright (C) 2012-2015 Anders Logg and Martin Sandve Alnæs
#
# This file is part of UFL.
#
@@ -17,7 +18,10 @@
# You should have received a copy of the GNU Lesser General Public License
# along with UFL. If not, see <http://www.gnu.org/licenses/>.
-class Equation:
+# Export list for ufl.classes
+__all_classes__ = ["Equation"]
+
+class Equation(object):
"""This class is used to represent equations expressed by the "=="
operator. Examples include a == L and F == 0 where a, L and F are
Form objects."""
@@ -28,7 +32,14 @@ class Equation:
self.rhs = rhs
def __bool__(self):
- "Evaluate bool(lhs_form == rhs_form)."
+ """Evaluate bool(lhs_form == rhs_form).
+
+ This will not trigger when setting 'equation = a == L',
+ but when e.g. running 'if equation:'.
+ """
+ # NB!: pep8 will say you should use isinstance here, but we do
+ # actually want to compare the exact types in this case.
+ # Not equal if types are not identical (i.e. not accepting subclasses)
if type(self.lhs) != type(self.rhs):
return False
# Try to delegate to equals function
diff --git a/ufl/exprcontainers.py b/ufl/exprcontainers.py
index 8ab21fb..654978c 100644
--- a/ufl/exprcontainers.py
+++ b/ufl/exprcontainers.py
@@ -1,6 +1,7 @@
+# -*- coding: utf-8 -*-
"""This module defines special types for representing mapping of expressions to expressions."""
-# Copyright (C) 2014 Martin Sandve Alnes
+# Copyright (C) 2014 Martin Sandve Alnæs
#
# This file is part of UFL.
#
@@ -19,7 +20,7 @@
from ufl.log import error, warning
from ufl.assertions import ufl_assert
-from ufl.common import EmptyDict
+from ufl.utils.dicts import EmptyDict
from ufl.core.expr import Expr
from ufl.core.operator import Operator
from ufl.core.ufl_type import ufl_type
@@ -80,10 +81,10 @@ class ExprMapping(Operator):
if not all(isinstance(e, Expr) for e in operands):
error("Expecting Expr in ExprMapping.")
- def domains(self):
+ def ufl_domains(self):
# Because this type can act like a terminal if it has no operands, we need to override some recursive operations
if self.ufl_operands:
- return Operator.domains()
+ return Operator.ufl_domains()
else:
return []
diff --git a/ufl/exprequals.py b/ufl/exprequals.py
index 8eefa79..34c50f8 100644
--- a/ufl/exprequals.py
+++ b/ufl/exprequals.py
@@ -1,3 +1,4 @@
+# -*- coding: utf-8 -*-
from collections import defaultdict
from six.moves import zip
@@ -6,7 +7,7 @@ from ufl.core.expr import Expr
from ufl.log import error
from ufl.core.operator import Operator
from ufl.core.terminal import Terminal
-from ufl.common import pre_traversal
+from ufl.corealg.traversal import pre_traversal
hash_total = defaultdict(int)
diff --git a/ufl/exproperators.py b/ufl/exproperators.py
index 9d4e776..99a1234 100644
--- a/ufl/exproperators.py
+++ b/ufl/exproperators.py
@@ -1,8 +1,9 @@
+# -*- coding: utf-8 -*-
"""This module attaches special functions to Expr.
This way we avoid circular dependencies between e.g.
Sum and its superclass Expr."""
-# Copyright (C) 2008-2014 Martin Sandve Alnes
+# Copyright (C) 2008-2015 Martin Sandve Alnæs
#
# This file is part of UFL.
#
@@ -23,7 +24,8 @@ from itertools import chain
from ufl.log import error
from ufl.assertions import ufl_assert
-from ufl.common import mergedicts, subdict, StackDict
+from ufl.utils.dicts import mergedicts, subdict
+from ufl.utils.stacks import StackDict
from ufl.core.expr import Expr
from ufl.core.operator import Operator
from ufl.constantvalue import Zero, as_ufl
@@ -58,6 +60,7 @@ def _gt(left, right):
"UFL operator: A boolean expresion (left > right) for use with conditional."
return GT(left, right)
+
# '==' needs to implement comparison of expression representations for use in
# hashmaps (dict and set), but the others can be overloaded in the language.
# It is possible that we can overload eq as well, but we'll need to fix some
@@ -79,9 +82,19 @@ Expr.__gt__ = _gt
Expr.__le__ = _le
Expr.__ge__ = _ge
-#Expr.__and__ = And
-#Expr.__or__ = Or
-#Expr.__xor__ = Xor
+# Python operators 'and'/'or' cannot be overloaded, and bitwise
+# operators &/| don't have the right precedence levels
+#Expr.__and__ = _and
+#Expr.__or__ = _or
+
+def _as_tensor(self, indices):
+ "UFL operator: A^indices := as_tensor(A, indices)."
+ if not isinstance(indices, tuple):
+ error("Expecting a tuple of Index objects to A^indices := as_tensor(A, indices).")
+ if not all(isinstance(i, Index) for i in indices):
+ error("Expecting a tuple of Index objects to A^indices := as_tensor(A, indices).")
+ return as_tensor(self, indices)
+Expr.__xor__ = _as_tensor
#--- Helper functions for product handling ---
@@ -119,7 +132,7 @@ def _mult(a, b):
#v[i]*M[i,:]
# Apply product to scalar components
- ti = indices(b.rank())
+ ti = indices(len(b.ufl_shape))
p = Product(a, b[ti])
elif r1 == 2 and r2 in (1, 2): # Matrix-matrix or matrix-vector
@@ -131,8 +144,8 @@ def _mult(a, b):
return Zero(shape, fi, fid)
# Return dot product in index notation
- ai = indices(a.rank() - 1)
- bi = indices(b.rank() - 1)
+ ai = indices(len(a.ufl_shape) - 1)
+ bi = indices(len(b.ufl_shape) - 1)
k = indices(1)
p = a[ai + k] * b[k + bi]
@@ -180,6 +193,9 @@ Expr.__add__ = _add
def _radd(self, o):
if not isinstance(o, _valid_types):
return NotImplemented
+ if isinstance(o, int) and o == 0:
+ # Allow adding scalar int 0 as a no-op, even for shaped self, needed for sum([a,b])
+ return self
return Sum(o, self)
Expr.__radd__ = _radd
@@ -411,6 +427,9 @@ Expr.__getitem__ = _getitem
def _dx(self, *ii):
"Return the partial derivative with respect to spatial variable number i."
d = self
+ # Unwrap ii to allow .dx(i,j) and .dx((i,j))
+ if len(ii) == 1 and isinstance(ii[0], tuple):
+ ii = ii[0]
# Apply all derivatives
for i in ii:
d = Grad(d)
diff --git a/ufl/finiteelement/__init__.py b/ufl/finiteelement/__init__.py
index 7330aae..2b65f9a 100644
--- a/ufl/finiteelement/__init__.py
+++ b/ufl/finiteelement/__init__.py
@@ -1,6 +1,7 @@
+# -*- coding: utf-8 -*-
"This module defines the UFL finite element classes."
-# Copyright (C) 2008-2014 Martin Sandve Alnes
+# Copyright (C) 2008-2015 Martin Sandve Alnæs
#
# This file is part of UFL.
#
@@ -31,9 +32,27 @@ from ufl.finiteelement.enrichedelement import EnrichedElement
from ufl.finiteelement.restrictedelement import RestrictedElement
from ufl.finiteelement.tensorproductelement import TensorProductElement
from ufl.finiteelement.outerproductelement import OuterProductElement
-from ufl.finiteelement.outerproductelement import OuterProductVectorElement
-from ufl.finiteelement.hdivcurl import HDiv, HCurl
+from ufl.finiteelement.hdivcurl import HDivElement, HCurlElement
from ufl.finiteelement.brokenelement import BrokenElement
from ufl.finiteelement.traceelement import TraceElement
from ufl.finiteelement.facetelement import FacetElement
from ufl.finiteelement.interiorelement import InteriorElement
+
+# Export list for ufl.classes
+__all_classes__ = [
+ "FiniteElementBase",
+ "FiniteElement",
+ "MixedElement",
+ "VectorElement",
+ "TensorElement",
+ "EnrichedElement",
+ "RestrictedElement",
+ "TensorProductElement",
+ "OuterProductElement",
+ "HDivElement",
+ "HCurlElement",
+ "BrokenElement",
+ "TraceElement",
+ "FacetElement",
+ "InteriorElement",
+ ]
diff --git a/ufl/finiteelement/brokenelement.py b/ufl/finiteelement/brokenelement.py
index 0234a29..b0fbc8d 100644
--- a/ufl/finiteelement/brokenelement.py
+++ b/ufl/finiteelement/brokenelement.py
@@ -1,3 +1,4 @@
+# -*- coding: utf-8 -*-
# Copyright (C) 2014 Andrew T. T. McRae
#
# This file is part of UFL.
@@ -22,32 +23,19 @@ class BrokenElement(FiniteElementBase):
"""The discontinuous version of an existing Finite Element space"""
def __init__(self, element):
self._element = element
- self._repr = "BrokenElement(%s)" % str(element._repr)
+ self._repr = "BrokenElement(%r)" % (element,)
family = "BrokenElement"
- domain = element.domain()
+ cell = element.cell()
degree = element.degree()
quad_scheme = element.quadrature_scheme()
value_shape = element.value_shape()
reference_value_shape = element.reference_value_shape()
- FiniteElementBase.__init__(self, family, domain, degree,
+ FiniteElementBase.__init__(self, family, cell, degree,
quad_scheme, value_shape, reference_value_shape)
- def reconstruct(self, **kwargs):
- """Construct a new BrokenElement object with some properties
- replaced with new values."""
- domain = kwargs.get("domain", self.domain())
- ele = self._element.reconstruct(domain=domain)
- return BrokenElement(ele)
-
- def reconstruction_signature(self):
- return "BrokenElement(%s)" % self._element.reconstruction_signature()
-
- def signature_data(self, renumbering):
- data = ("BrokenElement", self._element.signature_data(renumbering),
- ("no domain" if self._domain is None else self._domain
- .signature_data(renumbering)))
- return data
+ def mapping(self):
+ return self._element.mapping()
def __str__(self):
return "BrokenElement(%s)" % str(self._element)
diff --git a/ufl/finiteelement/elementlist.py b/ufl/finiteelement/elementlist.py
index b55c23f..950ff8c 100644
--- a/ufl/finiteelement/elementlist.py
+++ b/ufl/finiteelement/elementlist.py
@@ -1,8 +1,9 @@
+# -*- coding: utf-8 -*-
"""This module provides an extensive list of predefined finite element
families. Users or more likely, form compilers, may register new
elements by calling the function register_element."""
-# Copyright (C) 2008-2014 Martin Sandve Alnes and Anders Logg
+# Copyright (C) 2008-2015 Martin Sandve Alnæs and Anders Logg
#
# This file is part of UFL.
#
@@ -26,7 +27,8 @@ from __future__ import print_function
from ufl.assertions import ufl_assert
from ufl.sobolevspace import L2, H1, H2, HDiv, HCurl, HEin
-from ufl.common import istr
+from ufl.utils.formatting import istr
+from ufl.cell import Cell
# List of valid elements
ufl_elements = {}
@@ -226,7 +228,10 @@ def canonical_element_description(family, cell, order, form_degree):
if cell is not None:
tdim = cell.topological_dimension()
gdim = cell.geometric_dimension()
- cellname = cell.cellname()
+ if isinstance(cell, Cell):
+ cellname = cell.cellname()
+ else:
+ cellname = None
else:
tdim = None
gdim = None
@@ -258,14 +263,14 @@ def canonical_element_description(family, cell, order, form_degree):
# Validate order if specified
if order is not None:
ufl_assert(krange is not None,
- 'Order "%s" invalid for "%s" finite element, '\
+ 'Order "%s" invalid for "%s" finite element, '
'should be None.' % (order, family))
kmin, kmax = krange
ufl_assert(kmin is None or order >= kmin,
- 'Order "%s" invalid for "%s" finite element.' %\
+ 'Order "%s" invalid for "%s" finite element.' %
(order, family))
ufl_assert(kmax is None or order <= kmax,
- 'Order "%s" invalid for "%s" finite element.' %\
+ 'Order "%s" invalid for "%s" finite element.' %
(istr(order), family))
# Override sobolev_space for piecewise constants (TODO: necessary?)
@@ -279,7 +284,7 @@ def canonical_element_description(family, cell, order, form_degree):
value_shape = (gdim, gdim)
elif value_rank == 1:
# Vector valued fundamental elements in HDiv and HCurl have a shape
- ufl_assert(gdim != None and tdim != None,
+ ufl_assert(gdim is not None and tdim is not None,
"Cannot infer shape of element without topological and geometric dimensions.")
reference_value_shape = (tdim,)
value_shape = (gdim,)
diff --git a/ufl/finiteelement/enrichedelement.py b/ufl/finiteelement/enrichedelement.py
index 72bf663..d4ddb46 100644
--- a/ufl/finiteelement/enrichedelement.py
+++ b/ufl/finiteelement/enrichedelement.py
@@ -1,6 +1,7 @@
+# -*- coding: utf-8 -*-
"This module defines the UFL finite element classes."
-# Copyright (C) 2008-2014 Martin Sandve Alnes
+# Copyright (C) 2008-2015 Martin Sandve Alnæs
#
# This file is part of UFL.
#
@@ -22,8 +23,6 @@
from six.moves import zip
from ufl.assertions import ufl_assert
-from ufl.permutation import compute_indices
-from ufl.common import product, istr, EmptyDict
from ufl.log import info_blue, warning, warning_blue, error
from ufl.finiteelement.finiteelementbase import FiniteElementBase
@@ -36,9 +35,9 @@ class EnrichedElement(FiniteElementBase):
def __init__(self, *elements):
self._elements = elements
- domain = elements[0].domain()
- ufl_assert(all(e.domain() == domain for e in elements),
- "Domain mismatch for sub elements of enriched element.")
+ cell = elements[0].cell()
+ ufl_assert(all(e.cell() == cell for e in elements),
+ "Cell mismatch for sub elements of enriched element.")
if isinstance(elements[0].degree(), int):
degrees = { e.degree() for e in elements } - { None }
@@ -50,7 +49,7 @@ class EnrichedElement(FiniteElementBase):
quad_schemes = [e.quadrature_scheme() for e in elements]
quad_schemes = [qs for qs in quad_schemes if qs is not None]
quad_scheme = quad_schemes[0] if quad_schemes else None
- ufl_assert(all(qs == quad_scheme for qs in quad_schemes),\
+ ufl_assert(all(qs == quad_scheme for qs in quad_schemes),
"Quadrature scheme mismatch.")
value_shape = elements[0].value_shape()
@@ -66,30 +65,11 @@ class EnrichedElement(FiniteElementBase):
# "Element mapping mismatch.")
# Initialize element data
- FiniteElementBase.__init__(self, "EnrichedElement", domain, degree,
+ FiniteElementBase.__init__(self, "EnrichedElement", cell, degree,
quad_scheme, value_shape, reference_value_shape)
# Cache repr string
- self._repr = "EnrichedElement(*%r)" % ([repr(e) for e in self._elements],)
-
- def reconstruction_signature(self):
- """Format as string for evaluation as Python object.
-
- For use with cross language frameworks, stored in generated code
- and evaluated later in Python to reconstruct this object.
-
- This differs from repr in that it does not include domain
- label and data, which must be reconstructed or supplied by other means.
- """
- return "EnrichedElement(%s)" % (', '.join(e.reconstruction_signature() for e in self._elements),)
-
- def reconstruct(self, **kwargs):
- """Construct a new EnrichedElement object with some properties
- replaced with new values."""
- elements = [e.reconstruct(**kwargs) for e in self._elements]
- if all(a == b for (a, b) in zip(elements, self._elements)):
- return self
- return EnrichedElement(*elements)
+ self._repr = "EnrichedElement(%s)" % ", ".join(repr(e) for e in self._elements)
def is_cellwise_constant(self):
"""Return whether the basis functions of this
@@ -106,8 +86,3 @@ class EnrichedElement(FiniteElementBase):
def shortstr(self):
"Format as string for pretty printing."
return "<%s>" % " + ".join(e.shortstr() for e in self._elements)
-
- def signature_data(self, renumbering):
- data = ("EnrichedElement",
- tuple(e.signature_data(renumbering) for e in self._elements))
- return data
diff --git a/ufl/finiteelement/facetelement.py b/ufl/finiteelement/facetelement.py
index adb5420..cfc9d5a 100644
--- a/ufl/finiteelement/facetelement.py
+++ b/ufl/finiteelement/facetelement.py
@@ -1,3 +1,4 @@
+# -*- coding: utf-8 -*-
# Copyright (C) 2014 Andrew T. T. McRae
#
# This file is part of UFL.
@@ -23,32 +24,19 @@ class FacetElement(FiniteElementBase):
associated with the interior have been discarded"""
def __init__(self, element):
self._element = element
- self._repr = "FacetElement(%r)" % element
+ self._repr = "FacetElement(%r)" % (element,)
family = "FacetElement"
- domain = element.domain()
+ cell = element.cell()
degree = element.degree()
quad_scheme = element.quadrature_scheme()
value_shape = element.value_shape()
reference_value_shape = element.reference_value_shape()
- FiniteElementBase.__init__(self, family, domain, degree,
+ FiniteElementBase.__init__(self, family, cell, degree,
quad_scheme, value_shape, reference_value_shape)
- def reconstruct(self, **kwargs):
- """Construct a new FacetElement object with some properties
- replaced with new values."""
- domain = kwargs.get("domain", self.domain())
- ele = self._element.reconstruct(domain=domain)
- return FacetElement(ele)
-
- def reconstruction_signature(self):
- return "FacetElement(%s)" % self._element.reconstruction_signature()
-
- def signature_data(self, renumbering):
- data = ("FacetElement", self._element.signature_data(renumbering),
- ("no domain" if self._domain is None else self._domain
- .signature_data(renumbering)))
- return data
+ def mapping(self):
+ return self._element.mapping()
def __str__(self):
return "FacetElement(%s)" % str(self._element)
diff --git a/ufl/finiteelement/finiteelement.py b/ufl/finiteelement/finiteelement.py
index 2664e77..8dfccee 100644
--- a/ufl/finiteelement/finiteelement.py
+++ b/ufl/finiteelement/finiteelement.py
@@ -1,6 +1,7 @@
+# -*- coding: utf-8 -*-
"This module defines the UFL finite element classes."
-# Copyright (C) 2008-2014 Martin Sandve Alnes
+# Copyright (C) 2008-2015 Martin Sandve Alnæs
#
# This file is part of UFL.
#
@@ -22,9 +23,8 @@
# Modified by Anders Logg 2014
from ufl.assertions import ufl_assert
-from ufl.permutation import compute_indices
-from ufl.common import product, istr, EmptyDict
-from ufl.geometry import as_domain, as_cell
+from ufl.utils.formatting import istr
+from ufl.cell import as_cell
from ufl.log import info_blue, warning, warning_blue, error
from ufl.finiteelement.elementlist import canonical_element_description
@@ -39,7 +39,7 @@ class FiniteElement(FiniteElementBase):
)
def __init__(self,
family,
- domain=None,
+ cell=None,
degree=None,
form_degree=None,
quad_scheme=None):
@@ -48,8 +48,8 @@ class FiniteElement(FiniteElementBase):
*Arguments*
family (string)
The finite element family
- domain
- The geometric domain
+ cell
+ The geometric cell
degree (int)
The polynomial degree (optional)
form_degree (int)
@@ -58,12 +58,9 @@ class FiniteElement(FiniteElementBase):
quad_scheme
The quadrature scheme (optional)
"""
- if domain is None:
- cell = None
- else:
- domain = as_domain(domain)
- cell = domain.cell()
- ufl_assert(cell is not None, "Missing cell in given domain.")
+ # Note: Unfortunately, dolfin sometimes passes None for cell. Until this is fixed, allow it:
+ if cell is not None:
+ cell = as_cell(cell)
family, short_name, degree, value_shape, reference_value_shape, sobolev_space, mapping = \
canonical_element_description(family, cell, degree, form_degree)
@@ -74,12 +71,13 @@ class FiniteElement(FiniteElementBase):
self._short_name = short_name
# Initialize element data
- FiniteElementBase.__init__(self, family, domain, degree,
+ FiniteElementBase.__init__(self, family, cell, degree,
quad_scheme, value_shape, reference_value_shape)
# Cache repr string
- self._repr = "FiniteElement(%r, %r, %r, quad_scheme=%r)" % (
- self.family(), self.domain(), self.degree(), self.quadrature_scheme())
+ qs = self.quadrature_scheme()
+ quad_str = "" if qs is None else ", quad_scheme=%r" % (qs,)
+ self._repr = "FiniteElement(%r, %r, %r%s)" % (self.family(), self.cell(), self.degree(), quad_str)
assert '"' not in self._repr
def mapping(self):
@@ -88,40 +86,12 @@ class FiniteElement(FiniteElementBase):
def sobolev_space(self):
return self._sobolev_space
- def reconstruction_signature(self):
- """Format as string for evaluation as Python object.
-
- For use with cross language frameworks, stored in generated code
- and evaluated later in Python to reconstruct this object.
-
- This differs from repr in that it does not include domain
- label and data, which must be reconstructed or supplied by other means.
- """
- return "FiniteElement(%r, %s, %r, %r)" % (
- self.family(), self.domain().reconstruction_signature(), self.degree(), self.quadrature_scheme())
-
- def signature_data(self, renumbering):
- data = ("FiniteElement", self._family, self._degree,
- self._value_shape, self._reference_value_shape,
- self._quad_scheme,
- ("no domain" if self._domain is None else self._domain.signature_data(renumbering)))
- return data
-
- def reconstruct(self, **kwargs):
- """Construct a new FiniteElement object with some properties
- replaced with new values."""
- kwargs["family"] = kwargs.get("family", self.family())
- kwargs["domain"] = kwargs.get("domain", self.domain())
- kwargs["degree"] = kwargs.get("degree", self.degree())
- kwargs["quad_scheme"] = kwargs.get("quad_scheme", self.quadrature_scheme())
- return FiniteElement(**kwargs)
-
def __str__(self):
"Format as string for pretty printing."
qs = self.quadrature_scheme()
qs = "" if qs is None else "(%s)" % qs
- return "<%s%s%s on a %s>" % (self._short_name, istr(self.degree()),\
- qs, self.domain())
+ return "<%s%s%s on a %s>" % (self._short_name, istr(self.degree()),
+ qs, self.cell())
def shortstr(self):
"Format as string for pretty printing."
diff --git a/ufl/finiteelement/finiteelementbase.py b/ufl/finiteelement/finiteelementbase.py
index 8d10528..c8f9294 100644
--- a/ufl/finiteelement/finiteelementbase.py
+++ b/ufl/finiteelement/finiteelementbase.py
@@ -1,6 +1,7 @@
+# -*- coding: utf-8 -*-
"This module defines the UFL finite element classes."
-# Copyright (C) 2008-2014 Martin Sandve Alnes
+# Copyright (C) 2008-2015 Martin Sandve Alnæs
#
# This file is part of UFL.
#
@@ -23,15 +24,16 @@
from six.moves import zip
from ufl.assertions import ufl_assert
from ufl.permutation import compute_indices
-from ufl.common import product, istr, EmptyDict
-from ufl.geometry import Cell, as_cell, as_domain, Domain
+from ufl.utils.sequences import product
+from ufl.utils.formatting import istr
+from ufl.utils.dicts import EmptyDict
from ufl.log import info_blue, warning, warning_blue, error
-
+from ufl.cell import AbstractCell, as_cell
class FiniteElementBase(object):
"Base class for all finite elements"
__slots__ = ("_family",
- "_cell", "_domain",
+ "_cell",
"_degree",
"_form_degree",
"_quad_scheme",
@@ -39,64 +41,46 @@ class FiniteElementBase(object):
"_reference_value_shape",
"_repr",
"__weakref__")
-
- def __init__(self, family, domain, degree, quad_scheme, value_shape, reference_value_shape):
+ # TODO: Not all these should be in the base class! In particular family, degree, and quad_scheme do not belong here.
+ def __init__(self, family, cell, degree, quad_scheme, value_shape, reference_value_shape):
"Initialize basic finite element data"
ufl_assert(isinstance(family, str), "Invalid family type.")
ufl_assert(isinstance(degree, (int, tuple)) or degree is None, "Invalid degree type.")
ufl_assert(isinstance(value_shape, tuple), "Invalid value_shape type.")
ufl_assert(isinstance(reference_value_shape, tuple), "Invalid reference_value_shape type.")
- # TODO: Support multiple domains for composite mesh mixed elements
- if domain is None:
- self._domain = None
- self._cell = None
- else:
- self._domain = as_domain(domain)
- self._cell = self._domain.cell()
- ufl_assert(isinstance(self._domain, Domain), "Invalid domain type.")
- ufl_assert(isinstance(self._cell, Cell), "Invalid cell type.")
+ if cell is not None:
+ cell = as_cell(cell)
+ ufl_assert(isinstance(cell, AbstractCell), "Invalid cell type.")
self._family = family
+ self._cell = cell
self._degree = degree
self._value_shape = value_shape
self._reference_value_shape = reference_value_shape
self._quad_scheme = quad_scheme
+
def __repr__(self):
"Format as string for evaluation as Python object."
return self._repr
- def reconstruction_signature(self):
- """Format as string for evaluation as Python object.
-
- For use with cross language frameworks, stored in generated code
- and evaluated later in Python to reconstruct this object.
-
- This differs from repr in that it does not include domain
- label and data, which must be reconstructed or supplied by other means.
- """
- raise NotImplementedError("Class %s must implement FiniteElementBase.reconstruction_signature" % (type(self).__name__,))
-
- def signature_data(self, renumbering):
- data = ("FiniteElementBase", self._family, self._degree,
- self._value_shape, self._reference_value_shape,
- self._quad_scheme,
- ("no domain" if self._domain is None else self._domain.signature_data(renumbering)))
- return data
+ def _ufl_hash_data_(self):
+ return repr(self)
def __hash__(self):
"Compute hash code for insertion in hashmaps."
- return hash(repr(self))
+ return hash(self._ufl_hash_data_())
def __eq__(self, other):
"Compute element equality for insertion in hashmaps."
- return type(self) == type(other) and repr(self) == repr(other)
+ return type(self) == type(other) and self._ufl_hash_data_() == other._ufl_hash_data_()
def __lt__(self, other):
"Compare elements by repr, to give a natural stable sorting."
return repr(self) < repr(other)
+
def family(self): # FIXME: Undefined for base?
"Return finite element family"
return self._family
@@ -117,24 +101,6 @@ class FiniteElementBase(object):
"Return cell of finite element"
return self._cell
- def domain(self, component=None): # TODO: Deprecate this
- "Return the domain on which this element is defined."
- domains = self.domains(component)
- n = len(domains)
- if n == 0:
- return None
- elif n == 1:
- return domains[0]
- else:
- error("Cannot return the requested single domain, as this element has multiple domains.")
-
- def domains(self, component=None):
- "Return the domain on which this element is defined."
- if self._domain is None:
- return ()
- else:
- return (self._domain,)
-
def is_cellwise_constant(self, component=None):
"""Return whether the basis functions of this
element is spatially constant over each cell."""
@@ -148,6 +114,14 @@ class FiniteElementBase(object):
"Return the shape of the value space on the reference cell."
return self._reference_value_shape
+ def value_size(self):
+ "Return the integer product of the value shape."
+ return product(self.value_shape())
+
+ def reference_value_size(self):
+ "Return the integer product of the reference value shape."
+ return product(self.reference_value_shape())
+
def symmetry(self): # FIXME: different approach
"""Return the symmetry dict, which is a mapping c0 -> c1
meaning that component c0 is represented by component c1."""
@@ -158,7 +132,7 @@ class FiniteElementBase(object):
sh = self.value_shape()
r = len(sh)
if not (len(i) == r and all(j < k for (j, k) in zip(i, sh))):
- error(("Illegal component index '%r' (value rank %d)" + \
+ error(("Illegal component index '%r' (value rank %d)" +
"for element (value rank %d).") % (i, len(i), r))
def extract_subelement_component(self, i):
@@ -182,7 +156,7 @@ class FiniteElementBase(object):
sh = self.value_shape()
r = len(sh)
if not (len(i) == r and all(j < k for (j, k) in zip(i, sh))):
- error(("Illegal component index '%r' (value rank %d)" + \
+ error(("Illegal component index '%r' (value rank %d)" +
"for element (value rank %d).") % (i, len(i), r))
def extract_subelement_reference_component(self, i):
@@ -213,7 +187,7 @@ class FiniteElementBase(object):
"Add two elements, creating an enriched element"
ufl_assert(isinstance(other, FiniteElementBase),
"Can't add element and %s." % other.__class__)
- warning_blue("WARNING: Creating an EnrichedElement,\n " +\
+ warning_blue("WARNING: Creating an EnrichedElement,\n " +
"if you intended to create a MixedElement use '*' instead of '+'.")
from ufl.finiteelement import EnrichedElement
return EnrichedElement(self, other)
@@ -227,13 +201,7 @@ class FiniteElementBase(object):
def __getitem__(self, index):
"Restrict finite element to a subdomain, subcomponent or topology (cell)."
- # NOTE: RestrictedElement will not be used to represent restriction
- # to subdomains, as that is represented by the element having
- # a domain property that is a Region.
- # NOTE: Implementing restriction to subdomains with [] should not be
- # done, as V[1] is ambiguously similar to both indexing expressions
- # and obtaining a subdomain, such as myexpr[1] and mydomain[1].
- if isinstance(index, Cell) or index == "facet":
+ if index in ("facet", "interior"):
from ufl.finiteelement import RestrictedElement
return RestrictedElement(self, index)
return NotImplemented
diff --git a/ufl/finiteelement/hdivcurl.py b/ufl/finiteelement/hdivcurl.py
index 9db045a..a6c50cb 100644
--- a/ufl/finiteelement/hdivcurl.py
+++ b/ufl/finiteelement/hdivcurl.py
@@ -1,4 +1,5 @@
-# Copyright (C) 2008-2014 Andrew T. T. McRae
+# -*- coding: utf-8 -*-
+# Copyright (C) 2008-2015 Andrew T. T. McRae
#
# This file is part of UFL.
#
@@ -19,91 +20,63 @@ from ufl.finiteelement.outerproductelement import OuterProductElement
from ufl.finiteelement.finiteelementbase import FiniteElementBase
-class HDiv(OuterProductElement):
+class HDivElement(OuterProductElement):
"""A div-conforming version of an outer product element, assuming
this makes mathematical sense."""
__slots__ = ("_element")
def __init__(self, element):
self._element = element
- self._repr = "HDiv(%s)" % str(element._repr)
+ self._repr = "HDivElement(%r)" % (element,)
+ self._mapping = "contravariant Piola"
family = "OuterProductElement"
- domain = element.domain()
+ cell = element.cell()
degree = element.degree()
quad_scheme = element.quadrature_scheme()
value_shape = (element.cell().geometric_dimension(),)
- reference_value_shape = (element.cell().topological_dimension(),) # TODO: Is this right?
+ reference_value_shape = (element.cell().topological_dimension(),)
+
# Skipping OuterProductElement constructor! Bad code smell, refactor to avoid this non-inheritance somehow.
- FiniteElementBase.__init__(self, family, domain, degree,
+ FiniteElementBase.__init__(self, family, cell, degree,
quad_scheme, value_shape, reference_value_shape)
- def reconstruct(self, **kwargs):
- """Construct a new HDiv object with some properties
- replaced with new values."""
- domain = kwargs.get("domain", self.domain())
- ele = self._element.reconstruct(domain=domain)
- return HDiv(ele)
-
- def reconstruction_signature(self):
- return "HDiv(%s)" % self._element.reconstruction_signature()
-
- def signature_data(self, renumbering):
- data = ("HDiv", self._element.signature_data(renumbering),
- ("no domain" if self._domain is None else self._domain
- .signature_data(renumbering)))
- return data
-
def __str__(self):
- return "HDiv(%s)" % str(self._element)
+ return "HDivElement(%s)" % str(self._element)
def shortstr(self):
- return "HDiv(%s)" % str(self._element.shortstr())
+ return "HDivElement(%s)" % str(self._element.shortstr())
def __repr__(self):
return self._repr
-class HCurl(OuterProductElement):
+class HCurlElement(OuterProductElement):
"""A curl-conforming version of an outer product element, assuming
this makes mathematical sense."""
__slots__ = ("_element")
def __init__(self, element):
self._element = element
- self._repr = "HCurl(%s)" % str(element._repr)
+ self._repr = "HCurlElement(%r)" % (element,)
+ self._mapping = "covariant Piola"
family = "OuterProductElement"
- domain = element.domain()
+ cell = element.cell()
degree = element.degree()
quad_scheme = element.quadrature_scheme()
- value_shape = (element.cell().geometric_dimension(),)
- reference_value_shape = (element.cell().topological_dimension(),) # TODO: Is this right?
+ cell = element.cell()
+ value_shape = (cell.geometric_dimension(),)
+ reference_value_shape = (cell.topological_dimension(),) # TODO: Is this right?
# Skipping OuterProductElement constructor! Bad code smell, refactor to avoid this non-inheritance somehow.
- FiniteElementBase.__init__(self, family, domain, degree,
+ FiniteElementBase.__init__(self, family, cell, degree,
quad_scheme, value_shape, reference_value_shape)
- def reconstruct(self, **kwargs):
- """Construct a new HCurl object with some properties
- replaced with new values."""
- domain = kwargs.get("domain", self.domain())
- ele = self._element.reconstruct(domain=domain)
- return HCurl(ele)
-
- def reconstruction_signature(self):
- return "HCurl(%s)" % self._element.reconstruction_signature()
-
- def signature_data(self, renumbering):
- data = ("HCurl", self._element.signature_data(renumbering),
- ("no domain" if self._domain is None else self._domain
- .signature_data(renumbering)))
- return data
-
def __str__(self):
- return "HCurl(%s)" % str(self._element)
+ return "HCurlElement(%s)" % str(self._element)
def shortstr(self):
- return "HCurl(%s)" % str(self._element.shortstr())
+ return "HCurlElement(%s)" % str(self._element.shortstr())
def __repr__(self):
return self._repr
diff --git a/ufl/finiteelement/interiorelement.py b/ufl/finiteelement/interiorelement.py
index f7ad399..bcc0d57 100644
--- a/ufl/finiteelement/interiorelement.py
+++ b/ufl/finiteelement/interiorelement.py
@@ -1,3 +1,4 @@
+# -*- coding: utf-8 -*-
# Copyright (C) 2014 Andrew T. T. McRae
#
# This file is part of UFL.
@@ -23,32 +24,19 @@ class InteriorElement(FiniteElementBase):
associated with the interior have been kept"""
def __init__(self, element):
self._element = element
- self._repr = "InteriorElement(%r)" % element
+ self._repr = "InteriorElement(%r)" % (element,)
family = "InteriorElement"
- domain = element.domain()
+ cell = element.cell()
degree = element.degree()
quad_scheme = element.quadrature_scheme()
value_shape = element.value_shape()
reference_value_shape = element.reference_value_shape()
- FiniteElementBase.__init__(self, family, domain, degree,
+ FiniteElementBase.__init__(self, family, cell, degree,
quad_scheme, value_shape, reference_value_shape)
- def reconstruct(self, **kwargs):
- """Construct a new InteriorElement object with some properties
- replaced with new values."""
- domain = kwargs.get("domain", self.domain())
- ele = self._element.reconstruct(domain=domain)
- return InteriorElement(ele)
-
- def reconstruction_signature(self):
- return "InteriorElement(%s)" % self._element.reconstruction_signature()
-
- def signature_data(self, renumbering):
- data = ("InteriorElement", self._element.signature_data(renumbering),
- ("no domain" if self._domain is None else self._domain
- .signature_data(renumbering)))
- return data
+ def mapping(self):
+ return self._element.mapping()
def __str__(self):
return "InteriorElement(%s)" % str(self._element)
diff --git a/ufl/finiteelement/mixedelement.py b/ufl/finiteelement/mixedelement.py
index 247a56a..edba470 100644
--- a/ufl/finiteelement/mixedelement.py
+++ b/ufl/finiteelement/mixedelement.py
@@ -1,6 +1,7 @@
+# -*- coding: utf-8 -*-
"This module defines the UFL finite element classes."
-# Copyright (C) 2008-2014 Martin Sandve Alnes
+# Copyright (C) 2008-2015 Martin Sandve Alnæs
#
# This file is part of UFL.
#
@@ -29,9 +30,11 @@ from six.moves import xrange as range
from ufl.assertions import ufl_assert
from ufl.permutation import compute_indices
-from ufl.common import product, istr, EmptyDict
+from ufl.utils.sequences import product
+from ufl.utils.formatting import istr
+from ufl.utils.dicts import EmptyDict
from ufl.utils.indexflattening import flatten_multiindex, unflatten_index, shape_to_strides
-from ufl.geometry import as_domain
+from ufl.cell import as_cell
from ufl.log import info_blue, warning, warning_blue, error
from ufl.finiteelement.finiteelementbase import FiniteElementBase
@@ -40,11 +43,14 @@ from ufl.finiteelement.finiteelement import FiniteElement
class MixedElement(FiniteElementBase):
"A finite element composed of a nested hierarchy of mixed or simple elements"
- __slots__ = ("_sub_elements", "_domains")
+ __slots__ = ("_sub_elements", "_cells")
def __init__(self, *elements, **kwargs):
"Create mixed finite element from given list of elements"
+ if type(self) is MixedElement:
+ ufl_assert(not kwargs, "Not expecting keyword arguments to MixedElement constructor.")
+
# Un-nest arguments if we get a single argument with a list of elements
if len(elements) == 1 and isinstance(elements[0], (tuple, list)):
elements = elements[0]
@@ -53,81 +59,50 @@ class MixedElement(FiniteElementBase):
for e in elements]
self._sub_elements = elements
- # Pick the first domain, for now all should be equal
- domains = tuple(sorted(set(chain(*[element.domains() for element in elements]))))
- self._domains = domains
- if domains:
- # Base class currently only handles one domain, this is work in progress
- domain = domains[0]
-
- # Check that domains have same geometric dimension
- gdim = domain.geometric_dimension()
- ufl_assert(all(dom.geometric_dimension() == gdim for dom in domains),
- "Sub elements must live in the same geometric dimension.")
- # Require that all elements are defined on the same domain
- # TODO: allow mixed elements on different domains,
- # or add a CompositeMixedElement class for that
- ufl_assert(all(dom == domain for dom in domains),
- "Sub elements must live on the same domain (for now).")
+ # Pick the first cell, for now all should be equal
+ cells = tuple(sorted(set(element.cell() for element in elements) - set([None])))
+ self._cells = cells
+ if cells:
+ cell = cells[0]
+ # Require that all elements are defined on the same cell
+ ufl_assert(all(c == cell for c in cells[1:]),
+ "Sub elements must live on the same cell.")
else:
- domain = None
+ cell = None
# Check that all elements use the same quadrature scheme
# TODO: We can allow the scheme not to be defined.
quad_scheme = elements[0].quadrature_scheme()
- ufl_assert(all(e.quadrature_scheme() == quad_scheme for e in elements),\
+ ufl_assert(all(e.quadrature_scheme() == quad_scheme for e in elements),
"Quadrature scheme mismatch for sub elements of mixed element.")
- # Compute value shape
+ # Compute value sizes in global and reference configurations
value_size_sum = sum(product(s.value_shape()) for s in self._sub_elements)
- # Default value dimension: Treated simply as all subelement
- # values unpacked in a vector.
+ reference_value_size_sum = sum(product(s.reference_value_shape()) for s in self._sub_elements)
+
+ # Default value shape: Treated simply as all subelement values unpacked in a vector.
value_shape = kwargs.get('value_shape', (value_size_sum,))
- # Validate value_shape
+
+ # Default reference value shape: Treated simply as all subelement reference values unpacked in a vector.
+ reference_value_shape = kwargs.get('reference_value_shape', (reference_value_size_sum,))
+
+ # Validate value_shape (deliberately not for subclasses VectorElement and TensorElement)
if type(self) is MixedElement:
# This is not valid for tensor elements with symmetries,
# assume subclasses deal with their own validation
ufl_assert(product(value_shape) == value_size_sum,
- "Provided value_shape doesn't match the total "\
+ "Provided value_shape doesn't match the total "
"value size of all subelements.")
- # Always use a flat reference value shape
- reference_value_shape = (sum(product(s.reference_value_shape()) for s in self._sub_elements),)
-
# Initialize element data
degrees = { e.degree() for e in self._sub_elements } - { None }
degree = max(degrees) if degrees else None
- FiniteElementBase.__init__(self, "Mixed", domain, degree, quad_scheme,
+ FiniteElementBase.__init__(self, "Mixed", cell, degree, quad_scheme,
value_shape, reference_value_shape)
# Cache repr string
- self._repr = "MixedElement(*%r, **{'value_shape': %r })" %\
- (self._sub_elements, self._value_shape)
-
- def reconstruction_signature(self):
- """Format as string for evaluation as Python object.
-
- For use with cross language frameworks, stored in generated code
- and evaluated later in Python to reconstruct this object.
-
- This differs from repr in that it does not include domain
- label and data, which must be reconstructed or supplied by other means.
- """
- return "MixedElement(%s, **{'value_shape': %r })" % \
- (', '.join(e.reconstruction_signature() for e in self._sub_elements), self._value_shape)
-
- def reconstruct(self, **kwargs):
- """Construct a new MixedElement object with some
- properties replaced with new values."""
- elements = [e.reconstruct(**kwargs) for e in self._sub_elements]
- # Value shape cannot be changed, or at
- # least we have no valid use case for it.
- # Reconstructing an expression with a reconstructed
- # coefficient with a different value shape would
- # be way into undefined behaviour territory...
- ufl_assert("value_shape" not in kwargs,
- "Cannot change value_shape in reconstruct.")
- return self.reconstruct_from_elements(*elements)
+ if type(self) is MixedElement:
+ self._repr = "MixedElement(%s)" % ", ".join(repr(e) for e in self._sub_elements)
def reconstruct_from_elements(self, *elements):
"Reconstruct a mixed element from new subelements."
@@ -136,7 +111,7 @@ class MixedElement(FiniteElementBase):
ufl_assert(all(a.value_shape() == b.value_shape()
for (a, b) in zip(elements, self._sub_elements)),
"Expecting new elements to have same value shape as old ones.")
- return MixedElement(*elements, value_shape=self.value_shape())
+ return MixedElement(*elements)
def symmetry(self):
"""Return the symmetry dict, which is a mapping c0 -> c1
@@ -161,7 +136,10 @@ class MixedElement(FiniteElementBase):
return sm or EmptyDict
def mapping(self):
- error("The mapping of a mixed element is not defined. Inspect subelements instead.")
+ if all(e.mapping() == "identity" for e in self._sub_elements):
+ return "identity"
+ else:
+ return "undefined"
def num_sub_elements(self):
"Return number of sub elements."
@@ -251,16 +229,6 @@ class MixedElement(FiniteElementBase):
i, e = self.extract_component(component)
return e.is_cellwise_constant()
- def domains(self, component=None):
- "Return the domain(s) on which this element is defined."
- if component is None:
- # Return all unique domains
- return self._domains
- else:
- # Return the domains of subelement
- i, e = self.extract_component(component)
- return e.domains()
-
def degree(self, component=None):
"Return polynomial degree of finite element"
if component is None:
@@ -269,11 +237,6 @@ class MixedElement(FiniteElementBase):
i, e = self.extract_component(component)
return e.degree()
- def signature_data(self, renumbering):
- data = ("MixedElement", self._value_shape,
- tuple(e.signature_data(renumbering) for e in self._sub_elements))
- return data
-
def __str__(self):
"Format as string for pretty printing."
tmp = ", ".join(str(element) for element in self._sub_elements)
@@ -288,95 +251,66 @@ class MixedElement(FiniteElementBase):
class VectorElement(MixedElement):
"A special case of a mixed finite element where all elements are equal"
- def __init__(self, family, domain, degree, dim=None,
+ def __init__(self, family, cell=None, degree=None, dim=None,
form_degree=None, quad_scheme=None):
"""
Create vector element (repeated mixed element)
*Arguments*
family (string)
- The finite element family
- domain
- The geometric domain
+ The finite element family (or an existing FiniteElement)
+ cell
+ The geometric cell, ignored if family is a FiniteElement
degree (int)
- The polynomial degree
+ The polynomial degree, ignored if family is a FiniteElement
dim (int)
The value dimension of the element (optional)
form_degree (int)
The form degree (FEEC notation, used when field is
- viewed as k-form)
+ viewed as k-form), ignored if family is a FiniteElement
quad_scheme
- The quadrature scheme (optional)
+ The quadrature scheme (optional), ignored if family is a FiniteElement
"""
- if domain is not None:
- domain = as_domain(domain)
+
+ if isinstance(family, FiniteElementBase):
+ sub_element = family
+ cell = sub_element.cell()
+ else:
+ if cell is not None:
+ cell = as_cell(cell)
+ # Create sub element
+ sub_element = FiniteElement(family, cell, degree,
+ form_degree=form_degree,
+ quad_scheme=quad_scheme)
# Set default size if not specified
if dim is None:
- ufl_assert(domain is not None,
- "Cannot infer vector dimension without a domain.")
- dim = domain.geometric_dimension()
-
- # Create mixed element from list of finite elements
- sub_element = FiniteElement(family, domain, degree,
- form_degree=form_degree,
- quad_scheme=quad_scheme)
- sub_elements = [sub_element]*dim
+ ufl_assert(cell is not None,
+ "Cannot infer vector dimension without a cell.")
+ dim = cell.geometric_dimension()
- # Get common family name (checked in FiniteElement.__init__)
- family = sub_element.family()
+ # Create list of sub elements for mixed element constructor
+ sub_elements = [sub_element]*dim
- # Compute value shape
- shape = (dim,)
- value_shape = shape + sub_element.value_shape()
+ # Compute value shapes
+ value_shape = (dim,) + sub_element.value_shape()
+ reference_value_shape = (dim,) + sub_element.reference_value_shape()
# Initialize element data
- MixedElement.__init__(self, sub_elements, value_shape=value_shape)
- self._family = family
- self._degree = degree
+ MixedElement.__init__(self, sub_elements, value_shape=value_shape, reference_value_shape=reference_value_shape)
+ # FIXME: Storing this here is strange, isn't that handled by subclass?
+ self._family = sub_element.family()
+ self._degree = sub_element.degree()
self._sub_element = sub_element
self._form_degree = form_degree # Storing for signature_data, not sure if it's needed
# Cache repr string
- self._repr = "VectorElement(%r, %r, %r, dim=%d, quad_scheme=%r)" % \
- (self._family, self.domain(), self._degree,
- len(self._sub_elements), self._quad_scheme)
-
- def mapping(self):
- return self._sub_element.mapping()
-
- def signature_data(self, renumbering):
- data = ("VectorElement", self._family, self._degree, len(self._sub_elements), self._quad_scheme, self._form_degree,
- ("no domain" if self._domain is None else self._domain.signature_data(renumbering)))
- return data
-
- def reconstruction_signature(self):
- """Format as string for evaluation as Python object.
-
- For use with cross language frameworks, stored in generated code
- and evaluated later in Python to reconstruct this object.
-
- This differs from repr in that it does not include domain
- label and data, which must be reconstructed or supplied by other means.
- """
- return "VectorElement(%r, %s, %r, %d, %r)" % (
- self._family, self.domain().reconstruction_signature(), self._degree,
- len(self._sub_elements), self._quad_scheme)
-
- def reconstruct(self, **kwargs):
- kwargs["family"] = kwargs.get("family", self.family())
- kwargs["domain"] = kwargs.get("domain", self.domain())
- kwargs["degree"] = kwargs.get("degree", self.degree())
- ufl_assert("dim" not in kwargs, "Cannot change dim in reconstruct.")
- kwargs["dim"] = len(self._sub_elements)
- kwargs["quad_scheme"] = kwargs.get("quad_scheme", self.quadrature_scheme())
- return VectorElement(**kwargs)
+ self._repr = "VectorElement(%r, dim=%d)" % (sub_element, len(self._sub_elements))
def __str__(self):
"Format as string for pretty printing."
- return "<%s vector element of degree %s on a %s: %d x %s>" % \
- (self.family(), istr(self.degree()), self.domain(),
- len(self._sub_elements), self._sub_element)
+ return ("<vector element with %d components of %s>" %
+ (len(self._sub_elements), self._sub_element))
def shortstr(self):
"Format as string for pretty printing."
@@ -385,46 +319,65 @@ class VectorElement(MixedElement):
class TensorElement(MixedElement):
"A special case of a mixed finite element where all elements are equal"
- __slots__ = ("_sub_element", "_shape", "_symmetry", "_sub_element_mapping",)
- def __init__(self, family, domain, degree, shape=None,
+ __slots__ = ("_sub_element", "_shape", "_symmetry",
+ "_sub_element_mapping", "_flattened_sub_element_mapping",
+ "_mapping")
+ def __init__(self, family, cell=None, degree=None, shape=None,
symmetry=None, quad_scheme=None):
- "Create tensor element (repeated mixed element with optional symmetries)"
- if domain is not None:
- domain = as_domain(domain)
+ """Create tensor element (repeated mixed element with optional symmetries).
+
+ :arg family: The family string, or an existing FiniteElement.
+ :arg cell: The geometric cell (ignored if family is a FiniteElement).
+ :arg degree: The polynomial degree (ignored if family is a FiniteElement).
+ :arg shape: The shape of the element (defaults to a square
+ tensor given by the geometric dimension of the cell).
+ :arg symmetry: Optional symmetries.
+ :arg quad_scheme: Optional quadrature scheme (ignored if
+ family is a FiniteElement)."""
+
+ if isinstance(family, FiniteElementBase):
+ sub_element = family
+ cell = sub_element.cell()
+ else:
+ if cell is not None:
+ cell = as_cell(cell)
+ # Create scalar sub element
+ sub_element = FiniteElement(family, cell, degree, quad_scheme)
+
+ ufl_assert(sub_element.value_shape() == (),
+ "Expecting only scalar valued subelement for TensorElement.")
# Set default shape if not specified
if shape is None:
- ufl_assert(domain is not None,
- "Cannot infer vector dimension without a domain.")
- dim = domain.geometric_dimension()
+ ufl_assert(cell is not None,
+ "Cannot infer tensor shape without a cell.")
+ dim = cell.geometric_dimension()
shape = (dim, dim)
- # Construct default symmetry for matrix elements
- if symmetry == True:
+ if symmetry is None:
+ symmetry = EmptyDict
+ elif symmetry is True:
+ # Construct default symmetry dict for matrix elements
ufl_assert(len(shape) == 2 and shape[0] == shape[1],
"Cannot set automatic symmetry for non-square tensor.")
symmetry = dict( ((i, j), (j, i)) for i in range(shape[0])
for j in range(shape[1]) if i > j )
+ else:
+ ufl_assert(isinstance(symmetry, dict), "Expecting symmetry to be None (unset), True, or dict.")
# Validate indices in symmetry dict
- if isinstance(symmetry, dict):
- for i, j in iteritems(symmetry):
- ufl_assert(len(i) == len(j),
- "Non-matching length of symmetry index tuples.")
- for k in range(len(i)):
- ufl_assert(i[k] >= 0 and j[k] >= 0 and
- i[k] < shape[k] and j[k] < shape[k],
- "Symmetry dimensions out of bounds.")
- else:
- ufl_assert(symmetry is None, "Expecting symmetry to be None (unset), True, or dict.")
- symmetry = EmptyDict
+ for i, j in iteritems(symmetry):
+ ufl_assert(len(i) == len(j),
+ "Non-matching length of symmetry index tuples.")
+ for k in range(len(i)):
+ ufl_assert(i[k] >= 0 and j[k] >= 0 and
+ i[k] < shape[k] and j[k] < shape[k],
+ "Symmetry dimensions out of bounds.")
# Compute all index combinations for given shape
indices = compute_indices(shape)
- # Compute sub elements and mapping from indices
- # to sub elements, accounting for symmetry
- sub_element = FiniteElement(family, domain, degree, quad_scheme)
+ # Compute mapping from indices to sub element number, accounting for symmetry
sub_elements = []
sub_element_mapping = {}
for index in indices:
@@ -437,62 +390,42 @@ class TensorElement(MixedElement):
for index in indices:
if index in symmetry:
sub_element_mapping[index] = sub_element_mapping[symmetry[index]]
-
- # Get common family name (checked in FiniteElement.__init__)
- family = sub_element.family()
+ flattened_sub_element_mapping = [sub_element_mapping[index] for i, index in enumerate(indices)]
# Compute value shape
- value_shape = shape + sub_element.value_shape()
+ value_shape = shape
+
+ # Compute reference value shape based on symmetries
+ if symmetry:
+ # Flatten and subtract symmetries
+ reference_value_shape = (product(shape)-len(symmetry),)
+ self._mapping = "symmetries"
+ else:
+ # Do not flatten if there are no symmetries
+ reference_value_shape = shape
+ self._mapping = "identity"
# Initialize element data
- MixedElement.__init__(self, sub_elements, value_shape=value_shape)
- self._family = family
- self._degree = degree
+ MixedElement.__init__(self, sub_elements, value_shape=value_shape, reference_value_shape=reference_value_shape)
+ self._family = sub_element.family()
+ self._degree = sub_element.degree()
self._sub_element = sub_element
self._shape = shape
self._symmetry = symmetry
self._sub_element_mapping = sub_element_mapping
+ self._flattened_sub_element_mapping = flattened_sub_element_mapping
# Cache repr string
- self._repr = "TensorElement(%r, %r, %r, shape=%r, symmetry=%r, quad_scheme=%r)" % \
- (self._family, self.domain(), self._degree, self._shape,
- self._symmetry, self._quad_scheme)
+ self._repr = "TensorElement(%r, shape=%r, symmetry=%r)" % (sub_element, self._shape, self._symmetry)
def mapping(self):
- return self._sub_element.mapping()
-
- def signature_data(self, renumbering):
- data = ("TensorElement", self._family, self._degree, self._shape, repr(self._symmetry), self._quad_scheme,
- ("no domain" if self._domain is None else self._domain.signature_data(renumbering)))
- return data
-
- def reconstruction_signature(self):
- """Format as string for evaluation as Python object.
-
- For use with cross language frameworks, stored in generated code
- and evaluated later in Python to reconstruct this object.
-
- This differs from repr in that it does not include domain
- label and data, which must be reconstructed or supplied by other means.
- """
- return "TensorElement(%r, %s, %r, %r, %r, %r)" % (
- self._family, self.domain().reconstruction_signature(), self._degree,
- self._shape, self._symmetry, self._quad_scheme)
-
- def reconstruct(self, **kwargs):
- kwargs["family"] = kwargs.get("family", self.family())
- kwargs["domain"] = kwargs.get("domain", self.domain())
- kwargs["degree"] = kwargs.get("degree", self.degree())
-
- ufl_assert("shape" not in kwargs, "Cannot change shape in reconstruct.")
- kwargs["shape"] = self.value_shape() # Must use same shape as self!
-
- # Not sure about symmetry, but no use case I can see
- ufl_assert("symmetry" not in kwargs, "Cannot change symmetry in reconstruct.")
- kwargs["symmetry"] = self.symmetry()
+ if self._symmetry:
+ return "symmetries"
+ else:
+ return "identity"
- kwargs["quad_scheme"] = kwargs.get("quad_scheme", self.quadrature_scheme())
- return TensorElement(**kwargs)
+ def flattened_sub_element_mapping(self):
+ return self._flattened_sub_element_mapping
def extract_subelement_component(self, i):
"""Extract direct subelement index and subelement relative
@@ -522,8 +455,8 @@ class TensorElement(MixedElement):
sym = " with symmetries (%s)" % tmp
else:
sym = ""
- return "<%s tensor element of degree %s and shape %s on a %s%s>" % \
- (self.family(), istr(self.degree()), self.value_shape(), self.domain(), sym)
+ return ("<tensor element with shape %s of %s%s>" %
+ (self.value_shape(), self._sub_element, sym))
def shortstr(self):
"Format as string for pretty printing."
diff --git a/ufl/finiteelement/outerproductelement.py b/ufl/finiteelement/outerproductelement.py
index 706de40..2d8014c 100644
--- a/ufl/finiteelement/outerproductelement.py
+++ b/ufl/finiteelement/outerproductelement.py
@@ -1,6 +1,7 @@
+# -*- coding: utf-8 -*-
"This module defines the UFL finite element classes."
-# Copyright (C) 2008-2014 Martin Sandve Alnes and Andrew T. T. McRae
+# Copyright (C) 2008-2015 Martin Sandve Alnæs and Andrew T. T. McRae
#
# This file is part of UFL.
#
@@ -22,8 +23,7 @@
# Modified by Lawrence Mitchell 2014
from ufl.assertions import ufl_assert
-from ufl.cell import OuterProductCell
-from ufl.domain import as_domain
+from ufl.cell import OuterProductCell, as_cell
from ufl.finiteelement.mixedelement import MixedElement
from ufl.finiteelement.finiteelementbase import FiniteElementBase
@@ -36,66 +36,41 @@ class OuterProductElement(FiniteElementBase):
Given bases :math:`{\phi_A, \phi_B}` for :math:`A, B`,
:math:`{\phi_A * \phi_B}` forms a basis for :math:`V`.
"""
- __slots__ = ("_A", "_B")
+ __slots__ = ("_A", "_B", "_mapping")
- def __init__(self, A, B, domain=None, form_degree=None,
- quad_scheme=None):
+ def __init__(self, A, B, cell=None):
"Create OuterProductElement from a given pair of elements."
self._A = A
self._B = B
family = "OuterProductElement"
- if domain is None:
+ if cell is None:
# Define cell as the product of sub-cells
cell = OuterProductCell(A.cell(), B.cell())
- domain = as_domain(cell)
else:
- domain = as_domain(domain)
- cell = domain.cell()
- ufl_assert(cell is not None, "Missing cell in given domain.")
+ cell = as_cell(cell)
+
+ self._repr = "OuterProductElement(%r, %r, %r)" % (self._A, self._B, cell)
- self._repr = "OuterProductElement(*%r, %r)" % (list([self._A, self._B]),
- domain)
# Define polynomial degree as a tuple of sub-degrees
degree = (A.degree(), B.degree())
# match FIAT implementation
- if len(A.value_shape()) == 0 and len(B.value_shape()) == 0:
- value_shape = ()
- reference_value_shape = ()
- elif len(A.value_shape()) == 1 and len(B.value_shape()) == 0:
- value_shape = (A.value_shape()[0],)
- reference_value_shape = (A.reference_value_shape()[0],) # TODO: Is this right?
- elif len(A.value_shape()) == 0 and len(B.value_shape()) == 1:
- value_shape = (B.value_shape()[0],)
- reference_value_shape = (B.reference_value_shape()[0],) # TODO: Is this right?
+ value_shape = A.value_shape() + B.value_shape()
+ reference_value_shape = A.reference_value_shape() + B.reference_value_shape()
+ ufl_assert(len(value_shape) <= 1, "Product of vector-valued elements not supported")
+ ufl_assert(len(reference_value_shape) <= 1, "Product of vector-valued elements not supported")
+
+ if A.mapping() == "identity" and B.mapping() == "identity":
+ self._mapping = "identity"
else:
- raise Exception("Product of vector-valued elements not supported")
+ self._mapping = "undefined"
- FiniteElementBase.__init__(self, family, domain, degree,
- quad_scheme, value_shape, reference_value_shape)
+ FiniteElementBase.__init__(self, family, cell, degree,
+ None, value_shape, reference_value_shape)
def mapping(self):
- error("TODO: The mapping of an outer product element is not implemented.")
-
- def reconstruct(self, **kwargs):
- """Construct a new OuterProductElement with some properties
- replaced with new values."""
- domain = kwargs.get("domain", self.domain())
- return OuterProductElement(self._A, self._B, domain=domain)
-
- def reconstruction_signature(self):
- """Format as string for evaluation as Python object.
-
- For use with cross language frameworks, stored in generated code
- and evaluated later in Python to reconstruct this object.
-
- This differs from repr in that it does not include domain
- label and data, which must be reconstructed or supplied by other means.
- """
- return "OuterProductElement(%r, %r, %s, %r)" % (
- self._A, self._B, self.domain().reconstruction_signature(),
- self._quad_scheme)
+ return self._mapping
def __str__(self):
"Pretty-print."
@@ -106,88 +81,3 @@ class OuterProductElement(FiniteElementBase):
"Short pretty-print."
return "OuterProductElement(%s)" \
% str([self._A.shortstr(), self._B.shortstr()])
-
- def signature_data(self, renumbering):
- data = ("OuterProductElement",
- self._A,
- self._B,
- self._quad_scheme,
- ("no domain" if self._domain is None else self._domain.signature_data(renumbering)))
- return data
-
-
-class OuterProductVectorElement(MixedElement):
- """A special case of a mixed finite element where all
- elements are equal OuterProductElements"""
- __slots__ = ("_sub_element")
-
- def __init__(self, A, B, domain=None, dim=None,
- form_degree=None, quad_scheme=None):
- if domain is not None:
- domain = as_domain(domain)
-
- sub_element = OuterProductElement(A, B, domain=domain)
- dim = dim or sub_element.cell().geometric_dimension()
- sub_elements = [sub_element]*dim
-
- # Get common family name (checked in FiniteElement.__init__)
- family = sub_element.family()
-
- # Compute value shape
- shape = (dim,)
- value_shape = shape + sub_element.value_shape()
-
- # Initialize element data
- MixedElement.__init__(self, sub_elements, value_shape=value_shape)
- self._family = family
- self._degree = A.degree(), B.degree()
-
- self._sub_element = sub_element
- # Cache repr string
- self._repr = "OuterProductVectorElement(%r, %r, dim=%d)" % \
- (self._sub_element, self.domain(), len(self._sub_elements))
-
- @property
- def _A(self):
- return self._sub_element._A
-
- @property
- def _B(self):
- return self._sub_element._B
-
- def signature_data(self, renumbering):
- data = ("OuterProductVectorElement", self._A, self._B,
- len(self._sub_elements), self._quad_scheme,
- ("no domain" if self._domain is None else
- self._domain.signature_data(renumbering)))
- return data
-
- def reconstruct(self, **kwargs):
- """Construct a new OuterProductVectorElement with some properties
- replaced with new values."""
- domain = kwargs.get("domain", self.domain())
- dim = kwargs.get("dim", self.num_sub_elements())
- return OuterProductVectorElement(self._A, self._B,
- domain=domain, dim=dim)
-
- def reconstruction_signature(self):
- """Format as string for evaluation as Python object.
-
- For use with cross language frameworks, stored in generated code
- and evaluated later in Python to reconstruct this object.
-
- This differs from repr in that it does not include domain
- label and data, which must be reconstructed or supplied by other means.
- """
- return "OuterProductVectorElement(%r, %s, %d, %r)" % (
- self._sub_element, self.domain().reconstruction_signature(),
- len(self._sub_elements), self._quad_scheme)
-
- def __str__(self):
- "Format as string for pretty printing."
- return "<Outer product vector element: %r x %r>" % \
- (self._sub_element, self.num_sub_elements())
-
- def shortstr(self):
- "Format as string for pretty printing."
- return "OPVector"
diff --git a/ufl/finiteelement/restrictedelement.py b/ufl/finiteelement/restrictedelement.py
index 34264fd..2774c1e 100644
--- a/ufl/finiteelement/restrictedelement.py
+++ b/ufl/finiteelement/restrictedelement.py
@@ -1,6 +1,7 @@
+# -*- coding: utf-8 -*-
"This module defines the UFL finite element classes."
-# Copyright (C) 2008-2014 Martin Sandve Alnes
+# Copyright (C) 2008-2015 Martin Sandve Alnæs
#
# This file is part of UFL.
#
@@ -21,77 +22,57 @@
# Modified by Marie E. Rognes 2010, 2012
from ufl.assertions import ufl_assert
-from ufl.permutation import compute_indices
-from ufl.common import product, istr, EmptyDict
-from ufl.geometry import Cell, as_cell
-from ufl.log import info_blue, warning, warning_blue, error
+from ufl.cell import Cell, as_cell
+from ufl.log import info_blue, warning, warning_blue, error, deprecate
from ufl.finiteelement.finiteelementbase import FiniteElementBase
+valid_restriction_domains = ("interior", "facet", "face", "edge", "vertex")
+
class RestrictedElement(FiniteElementBase):
"Represents the restriction of a finite element to a type of cell entity."
- def __init__(self, element, cell_restriction):
+ def __init__(self, element, restriction_domain):
ufl_assert(isinstance(element, FiniteElementBase),
"Expecting a finite element instance.")
- ufl_assert(isinstance(cell_restriction, Cell) or cell_restriction == "facet",
- "Expecting a Cell instance, or the string 'facet'.")
+ ufl_assert(restriction_domain in valid_restriction_domains,
+ "Expecting one of the strings %r." % (valid_restriction_domains,))
- FiniteElementBase.__init__(self, "RestrictedElement", element.domain(),
+ FiniteElementBase.__init__(self, "RestrictedElement", element.cell(),
element.degree(), element.quadrature_scheme(), element.value_shape(), element.reference_value_shape())
- self._element = element
-
- if isinstance(cell_restriction, Cell):
- # Just attach cell_restriction if it is a Cell
- self._cell_restriction = cell_restriction
- elif cell_restriction == "facet":
- # Create a cell
- cell = element.cell()
- self._cell_restriction = Cell(cell.facet_cellname(),
- geometric_dimension=cell.geometric_dimension())
-
- self._repr = "RestrictedElement(%r, %r)" % (self._element, self._cell_restriction)
-
- def reconstruction_signature(self):
- """Format as string for evaluation as Python object.
- For use with cross language frameworks, stored in generated code
- and evaluated later in Python to reconstruct this object.
+ self._element = element
- This differs from repr in that it does not include domain
- label and data, which must be reconstructed or supplied by other means.
- """
- return "RestrictedElement(%s, %r)" % (self._element.reconstruction_signature(), self._cell_restriction)
+ self._restriction_domain = restriction_domain
- def reconstruct(self, **kwargs):
- """Construct a new RestrictedElement object with
- some properties replaced with new values."""
- element = self._element.reconstruct(**kwargs)
- cell_restriction = kwargs.get("cell_restriction", self.cell_restriction())
- return RestrictedElement(element=element, cell_restriction=cell_restriction)
+ self._repr = "RestrictedElement(%r, %r)" % (self._element, self._restriction_domain)
def is_cellwise_constant(self):
"""Return whether the basis functions of this
element is spatially constant over each cell."""
return self._element.is_cellwise_constant()
- def element(self):
+ def sub_element(self):
"Return the element which is restricted."
return self._element
+ def element(self):
+ deprecate("RestrictedElement.element() is deprecated, please use .sub_element() instead.")
+ return self.sub_element()
+
def mapping(self):
return self._element.mapping()
- def cell_restriction(self):
+ def restriction_domain(self):
"Return the domain onto which the element is restricted."
- return self._cell_restriction
+ return self._restriction_domain
def __str__(self):
"Format as string for pretty printing."
- return "<%s>|_{%s}" % (self._element, self._cell_restriction)
+ return "<%s>|_{%s}" % (self._element, self._restriction_domain)
def shortstr(self):
"Format as string for pretty printing."
- return "<%s>|_{%s}" % (self._element.shortstr(), self._cell_restriction)
+ return "<%s>|_{%s}" % (self._element.shortstr(), self._restriction_domain)
def symmetry(self):
"""Return the symmetry dict, which is a mapping c0 -> c1
@@ -119,8 +100,3 @@ class RestrictedElement(FiniteElementBase):
# w.r.t. different sub_elements meanings.
"Return list of restricted sub elements."
return (self._element,)
-
- def signature_data(self, renumbering):
- data = ("RestrictedElement", self._element.signature_data(renumbering),
- repr(self._cell_restriction)) # Note: I'm pretty sure repr is safe here but that may change
- return data
diff --git a/ufl/finiteelement/tensorproductelement.py b/ufl/finiteelement/tensorproductelement.py
index 22e9dc7..4d15b24 100644
--- a/ufl/finiteelement/tensorproductelement.py
+++ b/ufl/finiteelement/tensorproductelement.py
@@ -1,6 +1,7 @@
+# -*- coding: utf-8 -*-
"This module defines the UFL finite element classes."
-# Copyright (C) 2008-2014 Martin Sandve Alnes
+# Copyright (C) 2008-2015 Martin Sandve Alnæs
#
# This file is part of UFL.
#
@@ -21,9 +22,7 @@
# Modified by Marie E. Rognes 2010, 2012
from ufl.assertions import ufl_assert
-from ufl.permutation import compute_indices
-from ufl.common import product, istr, EmptyDict
-from ufl.geometry import as_domain, as_cell, ProductCell, ProductDomain
+from ufl.cell import as_cell, TensorProductCell
from ufl.log import info_blue, warning, warning_blue, error
from ufl.finiteelement.finiteelementbase import FiniteElementBase
@@ -39,9 +38,11 @@ class TensorProductElement(FiniteElementBase):
"""
__slots__ = ("_sub_elements",)
- def __init__(self, *elements):
+ def __init__(self, elements):
"Create TensorProductElement from a given list of elements."
+ warning("The TensorProductElement is work in progress and the design may change at any moment without notice.")
+
self._sub_elements = elements
ufl_assert(len(self._sub_elements) > 0,
"Cannot create TensorProductElement from empty list.")
@@ -49,8 +50,8 @@ class TensorProductElement(FiniteElementBase):
family = "TensorProductElement"
- # Define domain as the product of each elements domain
- domain = ProductDomain([e.domain() for e in self._sub_elements])
+ # Define cell as the product of each elements cell
+ cell = TensorProductCell([e.cell() for e in self._sub_elements])
# Define polynomial degree as the maximal of each subelement
degrees = { e.degree() for e in self._sub_elements } - { None }
@@ -68,22 +69,14 @@ class TensorProductElement(FiniteElementBase):
for e in self._sub_elements),
"All subelements in must have same value shape")
- FiniteElementBase.__init__(self, family, domain, degree,
+ FiniteElementBase.__init__(self, family, cell, degree,
quad_scheme, value_shape, reference_value_shape)
- def reconstruction_signature(self):
- """Format as string for evaluation as Python object.
-
- For use with cross language frameworks, stored in generated code
- and evaluated later in Python to reconstruct this object.
-
- This differs from repr in that it does not include domain
- label and data, which must be reconstructed or supplied by other means.
- """
- return "TensorProductElement(%s)" % (', '.join(e.reconstruction_signature() for e in self._sub_elements),)
-
def mapping(self):
- error("The mapping of a mixed element is not defined. Inspect subelements instead.")
+ if all(e.mapping() == "identity" for e in self._sub_elements):
+ return "identity"
+ else:
+ return "undefined"
def num_sub_elements(self):
"Return number of subelements."
@@ -102,8 +95,3 @@ class TensorProductElement(FiniteElementBase):
"Short pretty-print."
return "TensorProductElement(%s)" \
% str([e.shortstr() for e in self.sub_elements()])
-
- def signature_data(self, renumbering):
- data = ("TensorProductElement",
- tuple(e.signature_data(renumbering) for e in self._sub_elements))
- return data
diff --git a/ufl/finiteelement/traceelement.py b/ufl/finiteelement/traceelement.py
index bf4cc29..26bd238 100644
--- a/ufl/finiteelement/traceelement.py
+++ b/ufl/finiteelement/traceelement.py
@@ -1,3 +1,4 @@
+# -*- coding: utf-8 -*-
# Copyright (C) 2014 Andrew T. T. McRae
#
# This file is part of UFL.
@@ -17,39 +18,26 @@
from ufl.finiteelement.finiteelementbase import FiniteElementBase
-
class TraceElement(FiniteElementBase):
"""A finite element space: the trace of a given hdiv element.
This is effectively a scalar-valued restriction which is
non-zero only on cell facets."""
+
def __init__(self, element):
self._element = element
- self._repr = "TraceElement(%s)" % str(element._repr)
+ self._repr = "TraceElement(%s)" % repr(element)
family = "TraceElement"
- domain = element.domain()
+ cell = element.cell()
degree = element.degree()
quad_scheme = element.quadrature_scheme()
value_shape = ()
reference_value_shape = ()
- FiniteElementBase.__init__(self, family, domain, degree,
+ FiniteElementBase.__init__(self, family, cell, degree,
quad_scheme, value_shape, reference_value_shape)
- def reconstruct(self, **kwargs):
- """Construct a new TraceElement object with some properties
- replaced with new values."""
- domain = kwargs.get("domain", self.domain())
- ele = self._element.reconstruct(domain=domain)
- return TraceElement(ele)
-
- def reconstruction_signature(self):
- return "TraceElement(%s)" % self._element.reconstruction_signature()
-
- def signature_data(self, renumbering):
- data = ("TraceElement", self._element.signature_data(renumbering),
- ("no domain" if self._domain is None else self._domain
- .signature_data(renumbering)))
- return data
+ def mapping(self):
+ return "identity"
def __str__(self):
return "TraceElement(%s)" % str(self._element)
diff --git a/ufl/form.py b/ufl/form.py
index 5fb6cc5..301799c 100644
--- a/ufl/form.py
+++ b/ufl/form.py
@@ -1,6 +1,7 @@
+# -*- coding: utf-8 -*-
"The Form class."
-# Copyright (C) 2008-2014 Martin Sandve Alnes
+# Copyright (C) 2008-2015 Martin Sandve Alnæs
#
# This file is part of UFL.
#
@@ -31,6 +32,10 @@ from ufl.equation import Equation
from ufl.core.expr import Expr
from ufl.constantvalue import Zero
from ufl.protocols import id_or_none
+from ufl.coefficient import Coefficient
+
+# Export list for ufl.classes
+__all_classes__ = ["Form"]
# --- The Form class, representing a complete variational form or functional ---
@@ -41,8 +46,9 @@ def _sorted_integrals(integrals):
# Group integrals in multilevel dict by keys [domain][integral_type][subdomain_id]
integrals_dict = defaultdict(lambda:defaultdict(lambda:defaultdict(list)))
for integral in integrals:
- d = integral.domain()
- ufl_assert(d is not None, "An Integral without a Domain is now illegal.")
+ d = integral.ufl_domain()
+ if d is None:
+ error("Each integral in a form must have a uniquely defined integration domain.")
it = integral.integral_type()
si = integral.subdomain_id()
integrals_dict[d][it][si] += [integral]
@@ -130,6 +136,10 @@ class Form(object):
return self.integrals() == ()
def domains(self):
+ 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.
NB! This does not include domains of coefficients defined on other meshes.
@@ -141,15 +151,18 @@ class Form(object):
return self._integration_domains
def cell(self):
- "Return the single cell this form is defined on, fails if multiple cells are found."
- domains = self.domains()
- ufl_assert(all(domain.cell() == domains[0].cell() for domain in domains),
- "Calling Form.domain() is only valid if all integrals share domain.")
- # Need to support missing domain to allow
- # assemble(Constant(1)*dx, mesh=mesh) in dolfin
- return domains[0].cell() if domains else None
+ deprecate("Form.cell() is deprecated, please use .ufl_cell() instead.")
+ return self.ufl_cell()
def domain(self):
+ 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 self.ufl_domain().ufl_cell()
+
+ def ufl_domain(self):
"""Return the single geometric integration domain occuring in the form.
Fails if multiple domains are found.
@@ -157,12 +170,20 @@ class Form(object):
NB! This does not include domains of coefficients defined on other
meshes, look at form data for that additional information.
"""
- domains = self.domains()
+ # Collect all domains
+ domains = self.ufl_domains()
+ # Check that all are equal TODO: don't return more than one if all are equal?
ufl_assert(all(domain == domains[0] for domain in domains),
- "Calling Form.domain() is only valid if all integrals share domain.")
- # Need to support missing domain to allow
- # assemble(Constant(1)*dx, mesh=mesh) in dolfin
- return domains[0] if domains else None
+ "Calling Form.ufl_domain() is only valid if all integrals share domain.")
+ # Return the one and only domain
+ return domains[0]
+
+ def geometric_dimension(self):
+ "Return the geometric dimension shared by all domains and functions in this form."
+ gdims = tuple(set(domain.geometric_dimension() for domain in self.ufl_domains()))
+ ufl_assert(len(gdims) == 1,
+ "Expecting all domains and functions in a form to share geometric dimension, got %s." % str(tuple(sorted(gdims))))
+ return gdims[0]
def domain_numbering(self):
"Return a contiguous numbering of domains in a mapping { domain: number }."
@@ -215,7 +236,7 @@ class Form(object):
return self._hash
def __eq__(self, other):
- """Delayed evaluation of the __eq__ operator!
+ """Delayed evaluation of the == operator!
Just 'lhs_form == rhs_form' gives an Equation,
while 'bool(lhs_form == rhs_form)' delegates
@@ -223,6 +244,10 @@ class Form(object):
"""
return Equation(self, other)
+ def __ne__(self, other):
+ "Immediate evaluation of the != operator (as opposed to the == operator)."
+ return not self.equals(other)
+
def equals(self, other):
"Evaluate 'bool(lhs_form == rhs_form)'."
if type(other) != Form:
@@ -258,6 +283,10 @@ class Form(object):
"Subtract other form from this one."
return self + (-other)
+ def __rsub__(self, other):
+ "Subtract this form from other."
+ return other + (-self)
+
def __neg__(self):
"""Negate all integrals in form.
@@ -305,18 +334,19 @@ class Form(object):
# --- Analysis functions, precomputation and caching of various quantities ---
def _analyze_domains(self):
- # TODO: join_domains function needs work, later when dolfin integration of a Domain or ufl.Mesh class is finished.
- from ufl.geometry import join_domains
+ from ufl.domain import join_domains, sort_domains
+
+ # Collect unique integration domains
+ integration_domains = join_domains([itg.ufl_domain() for itg in self._integrals])
- # Collect integration domains and make canonical list of them
- integration_domains = join_domains([itg.domain() for itg in self._integrals])
- self._integration_domains = tuple(sorted(integration_domains, key=lambda x: x.label()))
+ # Make canonically ordered list of the domains
+ self._integration_domains = sort_domains(integration_domains)
# TODO: Not including domains from coefficients and arguments here, may need that later
self._domain_numbering = dict((d, i) for i, d in enumerate(self._integration_domains))
def _analyze_subdomain_data(self):
- integration_domains = self.domains()
+ integration_domains = self.ufl_domains()
integrals = self.integrals()
# Make clear data structures to collect subdomain data in
@@ -326,7 +356,7 @@ class Form(object):
for integral in integrals:
# Get integral properties
- domain = integral.domain()
+ domain = integral.ufl_domain()
it = integral.integral_type()
sd = integral.subdomain_data()
@@ -343,19 +373,6 @@ class Form(object):
from ufl.algorithms.analysis import extract_arguments_and_coefficients
arguments, coefficients = extract_arguments_and_coefficients(self)
- # Include coordinate coefficients from integration domains
- domains = self.domains()
- coordinates = [c for c in (domain.coordinates() for domain in domains) if c is not None]
- coefficients.extend(coordinates)
-
- # TODO: Not including domains from coefficients and arguments here. Will we need that later?
- # I believe argument domains must be among integration domains in each integral, anything else is not well defined.
- # Furthermore if a coefficient domain differ from the integration domain, it will
- # currently be interpolated to the same element on the integration domain in dolfin.
- # Therefore their domain should not be included here.
- # In the future we may generate code for quadrature point evaluation of these instead,
- # and then the coefficient domains are still of no value in the code generation process.
-
# Define canonical numbering of arguments and coefficients
self._arguments = tuple(sorted(set(arguments), key=lambda x: x.number()))
self._coefficients = tuple(sorted(set(coefficients), key=lambda x: x.count()))
@@ -372,7 +389,7 @@ class Form(object):
# Add domains of coefficients, these may include domains not among integration domains
k = len(dn)
for c in cn:
- d = c.domain()
+ d = c.ufl_domain()
if d is not None and d not in renumbering:
renumbering[d] = k
k += 1
@@ -383,6 +400,7 @@ class Form(object):
from ufl.algorithms.signature import compute_form_signature
self._signature = compute_form_signature(self, self._compute_renumbering())
+
def as_form(form):
"Convert to form if not a form, otherwise return form."
if not isinstance(form, Form):
@@ -390,7 +408,6 @@ def as_form(form):
return 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.
@@ -398,7 +415,7 @@ def replace_integral_domains(form, common_domain): # TODO: Move elsewhere
This is to support ill formed forms with no domain specified,
some times occuring in pydolfin, e.g. assemble(1*dx, mesh=mesh).
"""
- domains = form.domains()
+ domains = form.ufl_domains()
if common_domain is not None:
gdim = common_domain.geometric_dimension()
tdim = common_domain.topological_dimension()
@@ -406,11 +423,12 @@ def replace_integral_domains(form, common_domain): # TODO: Move elsewhere
tdim == domain.topological_dimension())
for domain in domains),
"Common domain does not share dimensions with form domains.")
+
reconstruct = False
integrals = []
for itg in form.integrals():
- domain = itg.domain()
- if domain is None or domain.label() != common_domain.label():
+ domain = itg.ufl_domain()
+ if domain != common_domain:
itg = itg.reconstruct(domain=common_domain)
reconstruct = True
integrals.append(itg)
diff --git a/ufl/formatting/graph.py b/ufl/formatting/graph.py
index a0a71d2..4096769 100644
--- a/ufl/formatting/graph.py
+++ b/ufl/formatting/graph.py
@@ -1,6 +1,7 @@
+# -*- coding: utf-8 -*-
"""Algorithms for working with linearized computational graphs."""
-# Copyright (C) 2008-2014 Martin Sandve Alnes
+# Copyright (C) 2008-2015 Martin Sandve Alnæs
#
# This file is part of UFL.
#
@@ -22,7 +23,7 @@ from six.moves import xrange as range
from six.moves import map
from heapq import heapify, heappop, heappush
-from ufl.corealg.traversal import pre_traversal
+from ufl.corealg.traversal import unique_pre_traversal
from ufl.corealg.multifunction import MultiFunction
# O(n) = O(|V|) = O(|E|), since |E| < c|V| for a fairly small c.
@@ -50,7 +51,7 @@ def build_graph(expr): # O(n)
E = []
handled = {}
#for v in post_traversal(expr):
- for v in reversed(list(pre_traversal(expr))):
+ for v in reversed(list(unique_pre_traversal(expr))):
i = handled.get(v)
if i is None:
i = len(V)
diff --git a/ufl/formatting/latextools.py b/ufl/formatting/latextools.py
index f0dedcc..40991c0 100644
--- a/ufl/formatting/latextools.py
+++ b/ufl/formatting/latextools.py
@@ -1,6 +1,7 @@
+# -*- coding: utf-8 -*-
"This module defines basic utilities for stitching together LaTeX documents."
-# Copyright (C) 2008-2014 Martin Sandve Alnes
+# Copyright (C) 2008-2015 Martin Sandve Alnæs
#
# This file is part of UFL.
#
diff --git a/ufl/formatting/printing.py b/ufl/formatting/printing.py
index f5726d5..1b27057 100644
--- a/ufl/formatting/printing.py
+++ b/ufl/formatting/printing.py
@@ -1,7 +1,8 @@
+# -*- coding: utf-8 -*-
"""A collection of utility algorithms for printing
of UFL objects, mostly intended for debugging purposes."""
-# Copyright (C) 2008-2014 Martin Sandve Alnes
+# Copyright (C) 2008-2015 Martin Sandve Alnæs
#
# This file is part of UFL.
#
@@ -37,7 +38,7 @@ def integral_info(integral):
s += " Type:\n"
s += " %s\n" % integral.integral_type()
s += " Domain:\n"
- s += " %r\n" % integral.domain()
+ s += " %r\n" % integral.ufl_domain()
s += " Domain id:\n"
s += " %r\n" % integral.subdomain_id()
s += " Domain data:\n"
diff --git a/ufl/formatting/ufl2dot.py b/ufl/formatting/ufl2dot.py
index 1fa7087..82e23c5 100644
--- a/ufl/formatting/ufl2dot.py
+++ b/ufl/formatting/ufl2dot.py
@@ -1,8 +1,9 @@
+# -*- coding: utf-8 -*-
"""A collection of utility algorithms for printing
of UFL objects in the DOT graph visualization language,
mostly intended for debugging purposers."""
-# Copyright (C) 2008-2014 Martin Sandve Alnes
+# Copyright (C) 2008-2015 Martin Sandve Alnæs
#
# This file is part of UFL.
#
@@ -121,7 +122,7 @@ class CompactLabeller(ReprLabeller):
return "diff"
# Make this class like the ones above to use fancy math symbol labels
-class2label = { \
+class2label = {
"IndexSum": "∑",
"Sum": "∑",
"Product": "∏",
diff --git a/ufl/formatting/ufl2latex.py b/ufl/formatting/ufl2latex.py
index 723e271..b2799b9 100644
--- a/ufl/formatting/ufl2latex.py
+++ b/ufl/formatting/ufl2latex.py
@@ -1,8 +1,9 @@
+# -*- coding: utf-8 -*-
"""This module defines expression transformation utilities,
either converting UFL expressions to new UFL expressions or
converting UFL expressions to other representations."""
-# Copyright (C) 2008-2014 Martin Sandve Alnes
+# Copyright (C) 2008-2015 Martin Sandve Alnæs
#
# This file is part of UFL.
#
@@ -27,7 +28,7 @@ from itertools import chain
import ufl
from ufl.log import error, warning
from ufl.assertions import ufl_assert
-from ufl.common import write_file, pdflatex, openpdf
+from ufl.utils.system import write_file, pdflatex, openpdf
from ufl.permutation import compute_indices
# All classes:
@@ -45,12 +46,12 @@ from ufl.conditional import EQ, NE, LE, GE, LT, GT, Conditional
from ufl.form import Form
from ufl.integral import Measure
from ufl.classes import terminal_classes
-from ufl.geometry import Domain
# Other algorithms:
from ufl.algorithms.compute_form_data import compute_form_data
from ufl.algorithms.formfiles import load_forms
from ufl.algorithms.transformer import Transformer
+from ufl.corealg.traversal import unique_post_traversal
from ufl.formatting.graph import build_graph, partition, extract_outgoing_vertex_connections
from ufl.formatting.latextools import align, document, verbatim
@@ -67,10 +68,10 @@ def _extract_variables(a):
handled = set()
variables = []
for e in iter_expressions(a):
- for o in post_traversal(e):
+ for o in unique_post_traversal(e):
if isinstance(o, Variable):
expr, label = o.ufl_operands
- if not label in handled:
+ if label not in handled:
variables.append(o)
handled.add(label)
return variables
@@ -427,9 +428,9 @@ def form2latex(form, formdata):
# Define elements
lines = []
for i, f in enumerate(formdata.original_arguments):
- lines.append(r"\mathcal{P}_{%d} = \{%s\} " % (i, element2latex(f.element())))
+ lines.append(r"\mathcal{P}_{%d} = \{%s\} " % (i, element2latex(f.ufl_element())))
for i, f in enumerate(formdata.original_coefficients):
- lines.append(r"\mathcal{Q}_{%d} = \{%s\} " % (i, element2latex(f.element())))
+ lines.append(r"\mathcal{Q}_{%d} = \{%s\} " % (i, element2latex(f.ufl_element())))
if lines:
sections.append(("Finite elements", align(lines)))
@@ -463,7 +464,7 @@ def form2latex(form, formdata):
variables = _extract_variables(itg.integrand())
for v in variables:
l = v._label
- if not l in handled_variables:
+ if l not in handled_variables:
handled_variables.add(l)
exprlatex = expression2latex(v._expression, formdata.argument_names, formdata.coefficient_names)
lines.append(("s_{%d}" % l._count, "= %s" % exprlatex))
@@ -487,9 +488,8 @@ def form2latex(form, formdata):
integral_type = itg.integral_type()
dstr = domain_strings[integral_type]
- domain = itg.domain()
- label = domain.label()
- # TODO: Use domain label!
+ domain = itg.ufl_domain()
+ # TODO: Render domain description
subdomain_id = itg.subdomain_id()
if isinstance(subdomain_id, int):
diff --git a/ufl/formoperators.py b/ufl/formoperators.py
index 8e0a298..150ad95 100644
--- a/ufl/formoperators.py
+++ b/ufl/formoperators.py
@@ -1,6 +1,7 @@
+# -*- coding: utf-8 -*-
"Various high level ways to transform a complete Form into a new Form."
-# Copyright (C) 2008-2014 Martin Sandve Alnes
+# Copyright (C) 2008-2015 Martin Sandve Alnæs
#
# This file is part of UFL.
#
@@ -39,6 +40,7 @@ from ufl.indexed import Indexed
from ufl.core.multiindex import FixedIndex, MultiIndex
from ufl.tensors import as_tensor, ListTensor
from ufl.sorting import sorted_expr
+from ufl.functionspace import FunctionSpace
# An exception to the rule that ufl.* does not depend on ufl.algorithms.* ...
from ufl.algorithms import compute_form_adjoint, \
@@ -48,11 +50,25 @@ from ufl.algorithms import compute_form_adjoint, \
compute_form_rhs, \
compute_form_functional, \
expand_derivatives, \
- extract_arguments
+ extract_arguments, \
+ FormSplitter
# Part of the external interface
from ufl.algorithms import replace
+def block_split(form, ix, iy=0):
+ """UFL form operator:
+ Given a linear or bilinear form on a mixed space,
+ extract the block correspoinding to the indices ix, iy.
+
+ Example:
+
+ a = inner(grad(u), grad(v))*dx + div(u)*q*dx + div(v)*p*dx
+ a = block_split(a, 0, 0) -> inner(grad(u), grad(v))*dx
+ """
+ fs = FormSplitter()
+ return fs.split(form, ix, iy)
+
def lhs(form):
"""UFL form operator:
Given a combined bilinear and linear form,
@@ -154,7 +170,7 @@ def _handle_derivative_arguments(form, coefficient, argument):
if isinstance(form, Form):
form_arguments = form.arguments()
else:
- # To handler derivative(expression), which is at least used in tests. Remove?
+ # To handle derivative(expression), which is at least used in tests. Remove?
form_arguments = extract_arguments(form)
numbers = sorted(set(arg.number() for arg in form_arguments))
@@ -166,13 +182,17 @@ def _handle_derivative_arguments(form, coefficient, argument):
part = None
# Create argument and split it if in a mixed space
- elements = [c.element() for c in coefficients]
- if len(elements) > 1:
- elm = MixedElement(*elements)
- arguments = split(Argument(elm, number, part))
+ function_spaces = [c.ufl_function_space() for c in coefficients]
+ domains = [fs.ufl_domain() for fs in function_spaces]
+ elements = [fs.ufl_element() for fs in function_spaces]
+ if len(function_spaces) == 1:
+ arguments = (Argument(function_spaces[0], number, part),)
else:
- elm, = elements
- arguments = (Argument(elm, number, part),)
+ # Create in mixed space over assumed (for now) same domain
+ assert all(fs.ufl_domain() == domains[0] for fs in function_spaces)
+ elm = MixedElement(*elements)
+ fs = FunctionSpace(domains[0], elm)
+ arguments = split(Argument(fs, number, part))
else:
# Wrap single argument in tuple for uniform treatment below
if isinstance(argument, (list, tuple)):
diff --git a/ufl/functionspace.py b/ufl/functionspace.py
new file mode 100644
index 0000000..64b2cde
--- /dev/null
+++ b/ufl/functionspace.py
@@ -0,0 +1,133 @@
+# -*- coding: utf-8 -*-
+"Types for representing function spaces."
+
+# Copyright (C) 2015-2015 Martin Sandve Alnæs
+#
+# This file is part of UFL.
+#
+# UFL is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# UFL is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# 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 ufl.core.terminal import Terminal
+#from ufl.corealg.traversal import traverse_unique_terminals
+#from ufl.utils.formatting import istr
+#from ufl.utils.dicts import EmptyDict
+#from ufl.log import warning, error, deprecate
+#from ufl.assertions import ufl_assert
+#from ufl.protocols import id_or_none
+#from ufl.cell import as_cell, AbstractCell, Cell, ProductCell
+
+from ufl.core.ufl_type import attach_operators_from_hash_data
+from ufl.domain import join_domains
+from ufl.finiteelement import MixedElement
+
+# Export list for ufl.classes
+__all_classes__ = [
+ "AbstractFunctionSpace",
+ "FunctionSpace",
+ "MixedFunctionSpace",
+ "TensorProductFunctionSpace",
+ ]
+
+class AbstractFunctionSpace(object):
+ def ufl_sub_spaces(self):
+ raise NotImplementedError("Missing implementation of IFunctionSpace.ufl_sub_spaces in %s." % self.__class__.__name__)
+
+ at attach_operators_from_hash_data
+class FunctionSpace(AbstractFunctionSpace):
+ def __init__(self, domain, element):
+ AbstractFunctionSpace.__init__(self)
+ self._ufl_domain = domain
+ self._ufl_element = element
+
+ def ufl_sub_spaces(self):
+ return ()
+
+ def ufl_domain(self):
+ return self._ufl_domain
+
+ def ufl_element(self):
+ return self._ufl_element
+
+ def ufl_domains(self):
+ return (self.ufl_domain(),)
+
+ def _ufl_hash_data_(self):
+ edata = repr(self.ufl_element())
+ domain = self.ufl_domain()
+ ddata = None if domain is None else domain._ufl_hash_data_()
+ return ("FunctionSpace", ddata, edata)
+
+ def _ufl_signature_data_(self, renumbering):
+ edata = repr(self.ufl_element())
+ domain = self.ufl_domain()
+ ddata = None if domain is None else domain._ufl_signature_data_(renumbering)
+ return ("FunctionSpace", ddata, edata)
+
+ def __repr__(self):
+ return "FunctionSpace(%r, %r)" % (self._ufl_domain, self._ufl_element)
+
+ at attach_operators_from_hash_data
+class MixedFunctionSpace(AbstractFunctionSpace):
+ def __init__(self, *function_spaces):
+ AbstractFunctionSpace.__init__(self)
+ self._ufl_function_spaces = function_spaces
+ self._ufl_element = MixedElement(*[fs.ufl_element() for fs in function_spaces])
+
+ def ufl_sub_spaces(self):
+ return self._ufl_function_spaces
+
+ def ufl_element(self):
+ return self._ufl_element
+
+ def ufl_domains(self):
+ domainlist = []
+ for s in self._ufl_function_spaces:
+ domainlist.extend(s.ufl_domains())
+ return join_domains(domainlist)
+
+ def ufl_domain(self):
+ domains = self.ufl_domains()
+ if len(domains) == 1:
+ return domains[0]
+ elif domains:
+ error("Found multiple domains, cannot return just one.")
+ else:
+ return None
+
+ def _ufl_hash_data_(self):
+ return ("MixedFunctionSpace",) + tuple(V._ufl_hash_data_() for V in self.ufl_sub_spaces())
+
+ def _ufl_signature_data_(self, renumbering):
+ return ("MixedFunctionSpace",) + tuple(V._ufl_signature_data_(renumbering) for V in self.ufl_sub_spaces())
+
+ def __repr__(self):
+ return "MixedFunctionSpace(*%r)" % (self._ufl_function_spaces,)
+
+ at attach_operators_from_hash_data
+class TensorProductFunctionSpace(AbstractFunctionSpace):
+ def __init__(self, *function_spaces):
+ AbstractFunctionSpace.__init__(self)
+ self._ufl_function_spaces = function_spaces
+
+ def ufl_sub_spaces(self):
+ return self._ufl_function_spaces
+
+ def _ufl_hash_data_(self):
+ return ("TensorProductFunctionSpace",) + tuple(V._ufl_hash_data_() for V in self.ufl_sub_spaces())
+
+ def _ufl_signature_data_(self, renumbering):
+ return ("TensorProductFunctionSpace",) + tuple(V._ufl_signature_data_(renumbering) for V in self.ufl_sub_spaces())
+
+ def __repr__(self):
+ return "TensorProductFunctionSpace(*%r)" % (self._ufl_function_spaces,)
diff --git a/ufl/geometry.py b/ufl/geometry.py
index 838fb79..d9ed91c 100644
--- a/ufl/geometry.py
+++ b/ufl/geometry.py
@@ -1,6 +1,7 @@
+# -*- coding: utf-8 -*-
"Types for representing symbolic expressions for geometric quantities."
-# Copyright (C) 2008-2014 Martin Sandve Alnes
+# Copyright (C) 2008-2015 Martin Sandve Alnæs
#
# This file is part of UFL.
#
@@ -21,15 +22,11 @@
# Modified by Kristian B. Oelgaard, 2009
# Modified by Marie E. Rognes 2012
-from collections import defaultdict
from ufl.log import warning, error, deprecate
from ufl.assertions import ufl_assert
-from ufl.common import istr, EmptyDict
-from ufl.core.terminal import Terminal
-from ufl.protocols import id_or_none
-from ufl.cell import as_cell, affine_cells, Cell, ProductCell
-from ufl.domain import as_domain, Domain, extract_domains, join_domains, ProductDomain
from ufl.core.ufl_type import ufl_type
+from ufl.core.terminal import Terminal
+from ufl.domain import as_domain
"""
@@ -83,7 +80,7 @@ x = Jxf Xf + x0f
Inverse relations:
X = K * (x - x0)
- CellCoordinate = JacobianInverse * (SpatialCoordinate - CellOrigio)
+ CellCoordinate = JacobianInverse * (SpatialCoordinate - CellOrigin)
Xf = FK * (x - x0f)
FacetCoordinate = FacetJacobianInverse * (SpatialCoordinate - FacetOrigin)
@@ -103,7 +100,7 @@ class GeometricQuantity(Terminal):
Terminal.__init__(self)
self._domain = as_domain(domain)
- def domains(self):
+ def ufl_domains(self):
return (self._domain,)
def is_cellwise_constant(self):
@@ -114,9 +111,9 @@ class GeometricQuantity(Terminal):
# NB! Geometric quantities are scalar by default. Override if needed.
ufl_shape = ()
- def signature_data(self, renumbering):
+ def _ufl_signature_data_(self, renumbering):
"Signature data of geometric quantities depend on the domain numbering."
- return (self._ufl_class_.__name__,) + self._domain.signature_data(renumbering)
+ return (self._ufl_class_.__name__,) + self._domain._ufl_signature_data_(renumbering)
def __str__(self):
return self._ufl_class_.name
@@ -125,7 +122,7 @@ class GeometricQuantity(Terminal):
return "%s(%r)" % (self._ufl_class_.__name__, self._domain)
def _ufl_compute_hash_(self):
- return hash((type(self).__name__,) + self._domain.hash_data())
+ return hash((type(self).__name__,) + self._domain._ufl_hash_data_())
def __eq__(self, other):
return isinstance(other, self._ufl_class_) and other._domain == self._domain
@@ -356,7 +353,7 @@ class CellEdgeVectors(GeometricCellQuantity):
@property
def ufl_shape(self):
- cell = self.domain().cell()
+ cell = self.ufl_domain().ufl_cell()
ne = cell.num_edges()
t = cell.topological_dimension()
return (ne, t)
@@ -379,7 +376,7 @@ class FacetEdgeVectors(GeometricFacetQuantity):
@property
def ufl_shape(self):
- cell = self.domain().cell()
+ cell = self.ufl_domain().ufl_cell()
nfe = cell.num_facet_edges()
t = cell.topological_dimension()
return (nfe, t)
@@ -510,13 +507,11 @@ class FacetNormal(GeometricFacetQuantity):
def is_cellwise_constant(self):
"Return whether this expression is spatially constant over each cell."
- # TODO: For product cells, this depends on which facet. Seems like too much work to fix right now.
- # Only true for a piecewise linear coordinate field with simplex _facets_
- is_piecewise_linear = self._domain.coordinate_element().degree() == 1
- has_simplex_facets = (self._domain.cell().facet_cellname() in affine_cells)
- return is_piecewise_linear and has_simplex_facets
+ # For product cells, this is only true for some but not all facets. Seems like too much work to fix right now.
+ # Only true for a piecewise linear coordinate field with simplex _facets_.
+ is_piecewise_linear = self._domain.ufl_coordinate_element().degree() == 1
+ return is_piecewise_linear and self._domain.ufl_cell().has_simplex_facets()
-# TODO: Should it be CellNormals? For interval in 3D we have two!
@ufl_type()
class CellNormal(GeometricCellQuantity):
"""UFL geometry representation: The upwards pointing normal vector of the current manifold cell."""
@@ -526,9 +521,22 @@ class CellNormal(GeometricCellQuantity):
@property
def ufl_shape(self):
g = self._domain.geometric_dimension()
+ #t = self._domain.topological_dimension()
+ #return (g-t,g) # TODO: Should it be CellNormals? For interval in 3D we have two!
return (g,)
-# TODO: Implement in change_to_reference_geometry and enable
+ at ufl_type()
+class ReferenceNormal(GeometricFacetQuantity):
+ """UFL geometry representation: The outwards pointing normal vector of the current facet on the reference cell"""
+ __slots__ = ()
+ name = "reference_normal"
+
+ @property
+ def ufl_shape(self):
+ t = self._domain.topological_dimension()
+ return (t,)
+
+# TODO: Implement in apply_geometry_lowering and enable
#@ufl_type()
#class FacetTangents(GeometricFacetQuantity):
# """UFL geometry representation: The tangent vectors of the current facet."""
@@ -548,13 +556,12 @@ class CellNormal(GeometricCellQuantity):
#
# def is_cellwise_constant(self): # NB! Copied from FacetNormal
# "Return whether this expression is spatially constant over each cell."
-# # TODO: For product cells, this depends on which facet. Seems like too much work to fix right now.
-# # Only true for a piecewise linear coordinate field with simplex _facets_
-# is_piecewise_linear = self._domain.coordinate_element().degree() == 1
-# has_simplex_facets = (self._domain.cell().facet_cellname() in affine_cells)
-# return is_piecewise_linear and has_simplex_facets
+# # For product cells, this is only true for some but not all facets. Seems like too much work to fix right now.
+# # Only true for a piecewise linear coordinate field with simplex _facets_.
+# is_piecewise_linear = self._domain.ufl_coordinate_element().degree() == 1
+# return is_piecewise_linear and self._domain.ufl_cell().has_simplex_facets()
-# TODO: Implement in change_to_reference_geometry and enable
+# TODO: Implement in apply_geometry_lowering and enable
#@ufl_type()
#class CellTangents(GeometricCellQuantity):
# """UFL geometry representation: The tangent vectors of the current manifold cell."""
@@ -600,6 +607,18 @@ class CellNormal(GeometricCellQuantity):
# TODO: Clean up this set of types? Document!
@ufl_type()
+class ReferenceCellVolume(GeometricCellQuantity):
+ """UFL geometry representation: The volume of the reference cell."""
+ __slots__ = ()
+ name = "reference_cell_volume"
+
+ at ufl_type()
+class ReferenceFacetVolume(GeometricFacetQuantity):
+ """UFL geometry representation: The volume of the reference cell of the current facet."""
+ __slots__ = ()
+ name = "reference_facet_volume"
+
+ at ufl_type()
class CellVolume(GeometricCellQuantity):
"""UFL geometry representation: The volume of the cell."""
__slots__ = ()
@@ -684,32 +703,3 @@ class QuadratureWeight(GeometricQuantity):
"Return whether this expression is spatially constant over each cell."
# The weight usually varies with the quadrature points
return False
-
-
-# --- Attach deprecated cell properties
-
-# TODO: Remove this deprecated part after a release or two.
-
-def _deprecated_dim(self):
- """The dimension of the cell.
-
- Only valid if the geometric and topological dimensions are the same.
- """
- deprecate("cell.d is deprecated, please use one of cell.topological_dimension(), cell.geometric_dimension(), domain.topological_dimension() or domain.geometric_dimension() instead.")
- ufl_assert(self.topological_dimension() == self.geometric_dimension(),
- "Cell.d is undefined when geometric and topological dimensions are not the same.")
- return self.geometric_dimension()
-
-def _deprecated_geometric_quantity(name, cls):
- def f(self):
- "UFL geometry value. Deprecated, please use the constructor types instead."
- deprecate("cell.%s is deprecated, please use %s(domain) instead" % (name, cls.__name__))
- return cls(as_domain(self))
- return f
-
-Cell.d = property(_deprecated_dim)
-Cell.x = property(_deprecated_geometric_quantity("x", SpatialCoordinate))
-Cell.n = property(_deprecated_geometric_quantity("n", FacetNormal))
-Cell.volume = property(_deprecated_geometric_quantity("volume", CellVolume))
-Cell.circumradius = property(_deprecated_geometric_quantity("circumradius", Circumradius))
-Cell.facet_area = property(_deprecated_geometric_quantity("facet_area", FacetArea))
diff --git a/ufl/index_combination_utils.py b/ufl/index_combination_utils.py
index 30c1285..f878b36 100644
--- a/ufl/index_combination_utils.py
+++ b/ufl/index_combination_utils.py
@@ -1,6 +1,7 @@
+# -*- coding: utf-8 -*-
"Utilities for analysing and manipulating free index tuples"
-# Copyright (C) 2008-2014 Martin Sandve Alnes
+# Copyright (C) 2008-2015 Martin Sandve Alnæs
#
# This file is part of UFL.
#
diff --git a/ufl/indexed.py b/ufl/indexed.py
index 73674ef..bc685b8 100644
--- a/ufl/indexed.py
+++ b/ufl/indexed.py
@@ -1,6 +1,7 @@
+# -*- coding: utf-8 -*-
"""This module defines the Indexed class."""
-# Copyright (C) 2008-2014 Martin Sandve Alnes
+# Copyright (C) 2008-2015 Martin Sandve Alnæs
#
# This file is part of UFL.
#
@@ -24,7 +25,7 @@ from ufl.core.operator import Operator
from ufl.core.multiindex import Index, FixedIndex, MultiIndex, as_multi_index
from ufl.index_combination_utils import unique_sorted_indices, merge_unique_indices
from ufl.precedence import parstr
-from ufl.common import EmptyDict
+from ufl.utils.dicts import EmptyDict
from ufl.core.ufl_type import ufl_type
#--- Indexed expression ---
@@ -48,9 +49,9 @@ class Indexed(Operator):
# Error checking
if len(shape) != len(multiindex):
- error("Invalid number of indices (%d) for tensor "\
- "expression of rank %d:\n\t%r\n"\
- % (len(multiindex), expression.rank(), expression))
+ error("Invalid number of indices (%d) for tensor "
+ "expression of rank %d:\n\t%r\n"
+ % (len(multiindex), len(expression.ufl_shape), expression))
if any(int(di) >= int(si) for si, di in zip(shape, multiindex) if isinstance(di, FixedIndex)):
error("Fixed index out of range!")
@@ -80,10 +81,6 @@ class Indexed(Operator):
ufl_shape = ()
- def is_cellwise_constant(self):
- "Return whether this expression is spatially constant over each cell."
- return self.ufl_operands[0].is_cellwise_constant()
-
def evaluate(self, x, mapping, component, index_values, derivatives=()):
A, ii = self.ufl_operands
component = ii.evaluate(x, mapping, None, index_values)
diff --git a/ufl/indexing.py b/ufl/indexing.py
index 30da4f1..53c6e9b 100644
--- a/ufl/indexing.py
+++ b/ufl/indexing.py
@@ -1,6 +1,7 @@
+# -*- coding: utf-8 -*-
"""This module defines the single index types and some internal index utilities."""
-# Copyright (C) 2008-2014 Martin Sandve Alnes and Anders Logg
+# Copyright (C) 2008-2015 Martin Sandve Alnæs and Anders Logg
#
# This file is part of UFL.
#
diff --git a/ufl/indexsum.py b/ufl/indexsum.py
index e0cc2b5..678c249 100644
--- a/ufl/indexsum.py
+++ b/ufl/indexsum.py
@@ -1,6 +1,7 @@
+# -*- coding: utf-8 -*-
"""This module defines the IndexSum class."""
-# Copyright (C) 2008-2014 Martin Sandve Alnes
+# Copyright (C) 2008-2015 Martin Sandve Alnæs
#
# This file is part of UFL.
#
@@ -25,7 +26,7 @@ from ufl.core.expr import Expr
from ufl.core.operator import Operator
from ufl.core.multiindex import Index, MultiIndex, as_multi_index
from ufl.precedence import parstr
-from ufl.common import EmptyDict
+from ufl.utils.dicts import EmptyDict
from ufl.core.ufl_type import ufl_type
from ufl.constantvalue import Zero
@@ -77,10 +78,6 @@ class IndexSum(Operator):
def ufl_shape(self):
return self.ufl_operands[0].ufl_shape
- def is_cellwise_constant(self):
- "Return whether this expression is spatially constant over each cell."
- return self.ufl_operands[0].is_cellwise_constant()
-
def evaluate(self, x, mapping, component, index_values):
i, = self.ufl_operands[1]
tmp = 0
diff --git a/ufl/integral.py b/ufl/integral.py
index 60bf7c4..e8b0e30 100644
--- a/ufl/integral.py
+++ b/ufl/integral.py
@@ -1,6 +1,7 @@
+# -*- coding: utf-8 -*-
"""The Integral class."""
-# Copyright (C) 2008-2014 Martin Sandve Alnes
+# Copyright (C) 2008-2015 Martin Sandve Alnæs
#
# This file is part of UFL.
#
@@ -20,7 +21,7 @@
# Modified by Anders Logg, 2008-2009
import ufl
-from ufl.log import error, warning
+from ufl.log import error, warning, deprecate
from ufl.assertions import ufl_assert
from ufl.core.expr import Expr
from ufl.checks import (is_true_ufl_scalar, is_python_scalar, is_globally_constant,
@@ -28,11 +29,14 @@ from ufl.checks import (is_true_ufl_scalar, is_python_scalar, is_globally_consta
from ufl.measure import Measure
from ufl.protocols import id_or_none
+# Export list for ufl.classes
+__all_classes__ = ["Integral"]
+
class Integral(object):
"An integral over a single domain."
__slots__ = ("_integrand",
"_integral_type",
- "_domain",
+ "_ufl_domain",
"_subdomain_id",
"_metadata",
"_subdomain_data",
@@ -42,7 +46,7 @@ class Integral(object):
"Expecting integrand to be an Expr instance.")
self._integrand = integrand
self._integral_type = integral_type
- self._domain = domain
+ self._ufl_domain = domain
self._subdomain_id = subdomain_id
self._metadata = metadata
self._subdomain_data = subdomain_data
@@ -62,7 +66,7 @@ class Integral(object):
if integral_type is None:
integral_type = self.integral_type()
if domain is None:
- domain = self.domain()
+ domain = self.ufl_domain()
if subdomain_id is None:
subdomain_id = self.subdomain_id()
if metadata is None:
@@ -80,8 +84,12 @@ class Integral(object):
return self._integral_type
def domain(self):
+ 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._domain
+ return self._ufl_domain
def subdomain_id(self):
"Return the subdomain id of this integral."
@@ -112,17 +120,17 @@ class Integral(object):
def __str__(self):
fmt = "{ %s } * %s(%s[%s], %s)"
mname = ufl.measure.integral_type_to_measure_name[self._integral_type]
- s = fmt % (self._integrand, mname, self._domain, self._subdomain_id, self._metadata)
+ s = fmt % (self._integrand, mname, self._ufl_domain, self._subdomain_id, self._metadata)
return s
def __repr__(self):
return "Integral(%r, %r, %r, %r, %r, %r)" % (
- self._integrand, self._integral_type, self._domain, self._subdomain_id, self._metadata, self._subdomain_data)
+ self._integrand, self._integral_type, self._ufl_domain, self._subdomain_id, self._metadata, self._subdomain_data)
def __eq__(self, other):
return (isinstance(other, Integral)
and self._integral_type == other._integral_type
- and self._domain == other._domain
+ and self._ufl_domain == other._ufl_domain
and self._subdomain_id == other._subdomain_id
and self._integrand == other._integrand
and self._metadata == other._metadata
@@ -131,8 +139,9 @@ class Integral(object):
def __hash__(self):
# 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._domain), self._subdomain_id,
+ hashdata = (hash(self._integrand),
+ self._integral_type,
+ hash(self._ufl_domain),
+ self._subdomain_id,
id_or_none(self._subdomain_data))
return hash(hashdata)
-
diff --git a/ufl/log.py b/ufl/log.py
index eeafbe2..0d29a5e 100644
--- a/ufl/log.py
+++ b/ufl/log.py
@@ -1,7 +1,8 @@
+# -*- coding: utf-8 -*-
"""This module provides functions used by the UFL implementation to
output messages. These may be redirected by the user of UFL."""
-# Copyright (C) 2005-2014 Anders Logg and Martin Sandve Alnaes
+# Copyright (C) 2005-2015 Anders Logg and Martin Sandve Alnaes
#
# This file is part of UFL.
#
@@ -44,10 +45,16 @@ def emit(self, record):
self.stream.write(format_string % message)
self.flush()
-# Colors
-RED = "\033[1;37;31m%s\033[0m"
-BLUE = "\033[1;37;34m%s\033[0m"
-GREEN = "\033[1;37;32m%s\033[0m"
+# Colors if the terminal supports it (disabled e.g. when piped to file)
+import sys
+if sys.stdout.isatty() and sys.stderr.isatty():
+ RED = "\033[1;37;31m%s\033[0m"
+ BLUE = "\033[1;37;34m%s\033[0m"
+ GREEN = "\033[1;37;32m%s\033[0m"
+else:
+ RED = "%s"
+ BLUE = "%s"
+ GREEN = "%s"
# Logger class
class Logger:
diff --git a/ufl/mathfunctions.py b/ufl/mathfunctions.py
index 6df7aaf..be1fdda 100644
--- a/ufl/mathfunctions.py
+++ b/ufl/mathfunctions.py
@@ -1,6 +1,7 @@
+# -*- coding: utf-8 -*-
"""This module provides basic mathematical functions."""
-# Copyright (C) 2008-2014 Martin Sandve Alnes
+# Copyright (C) 2008-2015 Martin Sandve Alnæs
#
# This file is part of UFL.
#
@@ -25,7 +26,7 @@ from ufl.log import warning, error
from ufl.assertions import ufl_assert
from ufl.core.operator import Operator
from ufl.constantvalue import is_true_ufl_scalar, ScalarValue, Zero, FloatValue, IntValue, as_ufl
-from ufl.common import EmptyDict
+from ufl.utils.dicts import EmptyDict
from ufl.core.ufl_type import ufl_type
"""
diff --git a/ufl/measure.py b/ufl/measure.py
index 5a70485..3c0f629 100644
--- a/ufl/measure.py
+++ b/ufl/measure.py
@@ -1,6 +1,7 @@
+# -*- coding: utf-8 -*-
"""The Measure class."""
-# Copyright (C) 2008-2014 Martin Sandve Alnes
+# Copyright (C) 2008-2015 Martin Sandve Alnæs
#
# This file is part of UFL.
#
@@ -17,19 +18,23 @@
# You should have received a copy of the GNU Lesser General Public License
# along with UFL. If not, see <http://www.gnu.org/licenses/>.
#
-# Modified by Anders Logg 2008-2014
+# Modified by Anders Logg 2008-2015
from ufl.assertions import ufl_assert
-from ufl.log import error, warning
+from ufl.log import error, warning, deprecate
from ufl.core.expr import Expr
-from ufl.geometry import Domain, as_domain
from ufl.checks import is_true_ufl_scalar
from ufl.constantvalue import as_ufl
-from ufl.common import EmptyDict
-
+from ufl.utils.dicts import EmptyDict
+from ufl.domain import as_domain, AbstractDomain, extract_domains
from ufl.protocols import id_or_none, metadata_equal, metadata_hashdata
-# TODO: Design a class DomainType(name, shortname, codim, num_cells, ...)?
+
+# Export list for ufl.classes
+__all_classes__ = ["Measure", "MeasureSum", "MeasureProduct"]
+
+
+# TODO: Design a class IntegralType(name, shortname, codim, num_cells, ...)?
# TODO: Improve descriptions below:
# Enumeration of valid domain types
@@ -48,26 +53,27 @@ _integral_types = [
#("point", "dP"), # TODO: Use this over arbitrary points inside cells?
# === Integration over custom domains
- ("custom", "dc"), # Over custom user-defined domains (run-time quadrature points)
- ("overlap", "dO"), # Over a cell fragment overlapping with two or more cells (run-time quadrature points)
- ("interface", "dI"), # Over facet fragment overlapping with two or more cells (run-time quadrature points)
- ("cutcell", "dC"), # Over a cell with some part cut away (run-time quadrature points)
+ ("custom", "dc"), # Over custom user-defined domains (run-time quadrature points)
+ ("cutcell", "dC"), # Over a cell with some part cut away (run-time quadrature points)
+ ("interface", "dI"), # Over a facet fragment overlapping with two or more cells (run-time quadrature points)
+ ("overlap", "dO"), # Over a cell fragment overlapping with two or more cells (run-time quadrature points)
# === Firedrake specific hacks on the way out:
# TODO: Remove these, firedrake can use metadata instead and create the measure objects in firedrake:
("exterior_facet_bottom", "ds_b"), # Over bottom facets on extruded mesh
- ("exterior_facet_top", "ds_t"), # Over top facets on extruded mesh
- ("exterior_facet_vert", "ds_v"), # Over side facets of an extruded mesh
- ("interior_facet_horiz", "dS_h"), # Over horizontal facets of an extruded mesh
- ("interior_facet_vert", "dS_v"), # Over vertical facets of an extruded mesh
+ ("exterior_facet_top", "ds_t"), # Over top facets on extruded mesh
+ ("exterior_facet_vert", "ds_v"), # Over side facets of an extruded mesh
+ ("interior_facet_horiz", "dS_h"), # Over horizontal facets of an extruded mesh
+ ("interior_facet_vert", "dS_v"), # Over vertical facets of an extruded mesh
]
integral_type_to_measure_name = dict((l, s) for l, s in _integral_types)
measure_name_to_integral_type = dict((s, l) for l, s in _integral_types)
+custom_integral_types = ("custom", "cutcell", "interface", "overlap")
def register_integral_type(integral_type, measure_name):
global integral_type_to_measure_name, measure_name_to_integral_type
ufl_assert(measure_name == integral_type_to_measure_name.get(integral_type, measure_name),
- "Domain type already added with different measure name!")
+ "Integral type already added with different measure name!")
ufl_assert(integral_type == measure_name_to_integral_type.get(measure_name, integral_type),
"Measure name already used for another domain type!")
integral_type_to_measure_name[integral_type] = measure_name
@@ -116,28 +122,27 @@ class Measure(object):
or short form "dx", etc.
domain:
- a Domain object (includes cell, dims, label, domain data)
+ an AbstractDomain object (most often a Mesh)
subdomain_id:
either string "everywhere",
a single subdomain id int,
or tuple of ints
- metadata
+ metadata:
dict, with additional compiler-specific parameters
affecting how code is generated, including parameters
for optimization or debugging of generated code.
- subdomain_data
+ subdomain_data:
object representing data to interpret subdomain_id with.
"""
# Map short name to long name and require a valid one
self._integral_type = as_integral_type(integral_type)
- # Check that we either have a proper Domain or none
+ # Check that we either have a proper AbstractDomain or none
self._domain = None if domain is None else as_domain(domain)
- ufl_assert(self._domain is None or isinstance(self._domain, Domain),
- "Invalid domain.")
+ ufl_assert(self._domain is None or isinstance(self._domain, AbstractDomain), "Invalid domain.")
# Store subdomain data
self._subdomain_data = subdomain_data
@@ -166,6 +171,10 @@ class Measure(object):
return self._integral_type
def domain(self):
+ 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.
This may be None or a Domain object.
@@ -202,7 +211,7 @@ class Measure(object):
if subdomain_id is None:
subdomain_id = self.subdomain_id()
if domain is None:
- domain = self.domain()
+ domain = self.ufl_domain()
if metadata is None:
metadata = self.metadata()
if subdomain_data is None:
@@ -228,7 +237,7 @@ class Measure(object):
# Let syntax dx(domain) or dx(domain, metadata) mean integral over entire domain.
# To do this we need to hijack the first argument:
- if subdomain_id is not None and (isinstance(subdomain_id, Domain) or hasattr(subdomain_id, 'ufl_domain')):
+ if subdomain_id is not None and (isinstance(subdomain_id, AbstractDomain) or hasattr(subdomain_id, 'ufl_domain')):
ufl_assert(domain is None, "Ambiguous: setting domain both as keyword argument and first argument.")
subdomain_id, domain = "everywhere", as_domain(subdomain_id)
@@ -249,21 +258,14 @@ class Measure(object):
def __getitem__(self, data):
"""This operator supports legacy syntax in python dolfin programs.
- The implementation makes assumptions on the type of data,
- namely that it is a dolfin MeshFunction with a member mesh()
- which returns a dolfin Mesh.
-
- The intention is to deprecase and remove this operator at
- some later point. Please attach your domain data to a Domain
- object instead of using the ds[data] syntax.
-
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).
"""
- return self.reconstruct(subdomain_data=data)
+ deprecate("Notation dx[meshfunction] is deprecated. Please use dx(subdomain_data=meshfunction) instead.")
+ return self(subdomain_data=data)
def __str__(self):
global integral_type_to_measure_name
@@ -302,8 +304,11 @@ class Measure(object):
def __hash__(self):
"Return a hash value for this Measure."
- hashdata = (self._integral_type, self._subdomain_id, hash(self._domain),
- metadata_hashdata(self._metadata), id_or_none(self._subdomain_data))
+ hashdata = (self._integral_type,
+ self._subdomain_id,
+ hash(self._domain),
+ metadata_hashdata(self._metadata),
+ id_or_none(self._subdomain_data))
return hash(hashdata)
def __eq__(self, other):
@@ -371,7 +376,7 @@ class Measure(object):
if not is_true_ufl_scalar(integrand):
msg = ("Can only integrate scalar expressions. The integrand is a " +
"tensor expression with value rank %d and free indices %r.")
- error(msg % (integrand.rank(), integrand.ufl_free_indices))
+ error(msg % (len(integrand.ufl_shape), integrand.ufl_free_indices))
# If we have a tuple of domain ids, delegate composition to Integral.__add__:
subdomain_id = self.subdomain_id()
@@ -384,30 +389,15 @@ class Measure(object):
"Expecting integer or string domain id.")
# If we don't have an integration domain, try to find one in integrand
- domain = self.domain()
+ domain = self.ufl_domain()
if domain is None:
- domains = integrand.domains()
+ domains = extract_domains(integrand)
if len(domains) == 1:
domain, = domains
+ elif len(domains) == 0:
+ error("This integral is missing an integration domain.")
else:
- # TODO: Should this be an error? For now we need to support
- # assemble(1*dx, mesh=mesh) in dolfin for compatibility.
- # Maybe we can add a deprecation warning?
- #deprecation_warning("Integrals over undefined domains are deprecated.")
- domain = None
-
- # FIXME: Fix getitem so we can support this as well:
- # (probably need to store subdomain_data with Form or Integral?)
- # Suggestion to store canonically in Form:
- # integral.subdomain_data() = value
- # form.subdomain_data()[label][key] = value
- # all(domain.data() == {} for domain in form.domains())
- # Then getitem data follows the data flow:
- # dxs = dx[gd]; dxs._subdomain_data is gd
- # dxs0 = dxs(0); dxs0._subdomain_data is gd
- # M = 1*dxs0; M.integrals()[0].subdomain_data() is gd
- # ; M.subdomain_data()[None][dxs.integral_type()] is gd
- #assemble(1*dx[cells] + 1*ds[bnd], mesh=mesh)
+ error("Multiple domains found, making the choice of integration domain ambiguous.")
# Otherwise create and return a one-integral form
integral = Integral(integrand=integrand,
diff --git a/ufl/objects.py b/ufl/objects.py
index d5cd806..dca0184 100644
--- a/ufl/objects.py
+++ b/ufl/objects.py
@@ -1,6 +1,7 @@
+# -*- coding: utf-8 -*-
"Utility objects for pretty syntax in user code."
-# Copyright (C) 2008-2014 Martin Sandve Alnes
+# Copyright (C) 2008-2015 Martin Sandve Alnæs
#
# This file is part of UFL.
#
@@ -34,7 +35,10 @@ for integral_type, measure_name in integral_type_to_measure_name.items():
globals()[measure_name] = Measure(integral_type)
ds_tb = ds_b + ds_t # TODO: Firedrake hack, remove later
-# Cell types
+# Default measure dX including both uncut and cut cells
+dX = dx + dC
+
+# Create objects for builtin known cell types
vertex = Cell("vertex", 0)
interval = Cell("interval", 1)
triangle = Cell("triangle", 2)
@@ -44,3 +48,5 @@ hexahedron = Cell("hexahedron", 3)
# Facet is just a dummy declaration for RestrictedElement
facet = "facet"
+
+#__all__ = [] # FIXME:
diff --git a/ufl/operators.py b/ufl/operators.py
index 84722bc..53b51b0 100644
--- a/ufl/operators.py
+++ b/ufl/operators.py
@@ -1,9 +1,10 @@
+# -*- coding: utf-8 -*-
"""This module extends the form language with free function operators,
which are either already available as member functions on UFL objects
or defined as compound operators involving basic operations on the UFL
objects."""
-# Copyright (C) 2008-2014 Martin Sandve Alnes and Anders Logg
+# Copyright (C) 2008-2015 Martin Sandve Alnæs and Anders Logg
#
# This file is part of UFL.
#
@@ -44,7 +45,9 @@ from ufl.mathfunctions import Sqrt, Exp, Ln, Erf,\
from ufl.restriction import CellAvg, FacetAvg
from ufl.core.multiindex import indices
from ufl.indexed import Indexed
-from ufl.geometry import SpatialCoordinate
+from ufl.geometry import SpatialCoordinate, FacetNormal
+from ufl.checks import is_globally_constant, is_cellwise_constant
+from ufl.domain import extract_domains
#--- Basic operators ---
@@ -130,10 +133,10 @@ def inner(a, b):
return Inner(a, b)
# TODO: Something like this would be useful in some cases,
-# but should inner just support a.rank() != b.rank() instead?
+# but should inner just support len(a.ufl_shape) != len(b.ufl_shape) instead?
def _partial_inner(a, b):
"UFL operator: Take the partial inner product of a and b."
- ar, br = a.rank(), b.rank()
+ ar, br = len(a.ufl_shape), len(b.ufl_shape)
n = min(ar, br)
return contraction(a, list(range(n-ar, n-ar+n)), b, list(range(n)))
@@ -144,7 +147,7 @@ def dot(a, b):
if a.ufl_shape == () and b.ufl_shape == ():
return a*b
return Dot(a, b)
- #return contraction(a, (a.rank()-1,), b, (b.rank()-1,))
+ #return contraction(a, (len(a.ufl_shape)-1,), b, (len(b.ufl_shape)-1,))
def contraction(a, a_axes, b, b_axes):
"UFL operator: Take the contraction of a and b over given axes."
@@ -152,8 +155,8 @@ def contraction(a, a_axes, b, b_axes):
ufl_assert(len(ai) == len(bi), "Contraction must be over the same number of axes.")
ash = a.ufl_shape
bsh = b.ufl_shape
- aii = indices(a.rank())
- bii = indices(b.rank())
+ aii = indices(len(a.ufl_shape))
+ bii = indices(len(b.ufl_shape))
cii = indices(len(ai))
shape = [None]*len(ai)
for i, j in enumerate(ai):
@@ -164,7 +167,7 @@ def contraction(a, a_axes, b, b_axes):
ufl_assert(shape[i] == bsh[j], "Shape mismatch in contraction.")
s = a[aii]*b[bii]
cii = set(cii)
- ii = tuple(i for i in (aii + bii) if not i in cii)
+ ii = tuple(i for i in (aii + bii) if i not in cii)
return as_tensor(s, ii)
def perp(v):
@@ -214,7 +217,7 @@ def diag(A):
# TODO: Make a compound type or two for this operator
# Get and check dimensions
- r = A.rank()
+ r = len(A.ufl_shape)
if r == 1:
n, = A.ufl_shape
elif r == 2:
@@ -239,7 +242,7 @@ def diag_vector(A):
# TODO: Make a compound type for this operator
# Get and check dimensions
- ufl_assert(A.rank() == 2, "Expecting rank 2 tensor.")
+ ufl_assert(len(A.ufl_shape) == 2, "Expecting rank 2 tensor.")
m, n = A.ufl_shape
ufl_assert(m == n, "Can only take diagonal of square tensors.")
@@ -278,10 +281,9 @@ def Dn(f):
"""UFL operator: Take the directional derivative of f in the
facet normal direction, Dn(f) := dot(grad(f), n)."""
f = as_ufl(f)
- if f.is_cellwise_constant():
+ if is_cellwise_constant(f):
return Zero(f.ufl_shape, f.ufl_free_indices, f.ufl_index_dimensions)
- from ufl.geometry import FacetNormal
- return dot(grad(f), FacetNormal(f.domain()))
+ return dot(grad(f), FacetNormal(f.ufl_domain()))
def diff(f, v):
"""UFL operator: Take the derivative of f with respect to the variable v.
@@ -385,21 +387,21 @@ rot = curl
def jump(v, n=None):
"UFL operator: Take the jump of v across a facet."
v = as_ufl(v)
- is_not_constant = len(v.domains()) > 0 # FIXME: Not quite right...
- if is_not_constant:
+ is_constant = len(extract_domains(v)) > 0
+ if is_constant:
if n is None:
return v('+') - v('-')
- r = v.rank()
+ r = len(v.ufl_shape)
if r == 0:
return v('+')*n('+') + v('-')*n('-')
else:
return dot(v('+'), n('+')) + dot(v('-'), n('-'))
else:
- warning("Returning zero from jump of expression without a domain. This may be erroneous.")
- # FIXME: Is this right? If v has no cell, it doesn't depend on
+ warning("Returning zero from jump of expression without a domain. This may be erroneous if a dolfin.Expression is involved.")
+ # FIXME: Is this right? If v has no domain, it doesn't depend on
# anything spatially variable or any form arguments, and thus
# the jump is zero. In other words, I'm assuming that
- # "not v.domains()" is equivalent with "v is a constant".
+ # "v has no geometric domains" is equivalent with "v is a spatial constant".
# Update: This is NOT true for jump(Expression("x[0]")) from dolfin.
return Zero(v.ufl_shape, v.ufl_free_indices, v.ufl_index_dimensions)
@@ -610,7 +612,7 @@ def exterior_derivative(f):
if len(indices) > 1:
raise NotImplementedError
index = int(indices[0])
- element = expression.element()
+ element = expression.ufl_element()
element = element.extract_component(index)[1]
elif isinstance(f, ListTensor):
f0 = f.ufl_operands[0]
@@ -618,17 +620,17 @@ def exterior_derivative(f):
if len(f0indices) > 1:
raise NotImplementedError
index = int(f0indices[0])
- element = f0expr.element()
+ element = f0expr.ufl_element()
element = element.extract_component(index)[1]
else:
try:
- element = f.element()
+ element = f.ufl_element()
except:
error("Unable to determine element from %s" % f)
- # Extract the family and the
+ # Extract the family and the geometric dimension
family = element.family()
- gdim = element.domain().geometric_dimension()
+ gdim = element.cell().geometric_dimension()
# L^2 elements:
if "Disc" in family:
diff --git a/ufl/permutation.py b/ufl/permutation.py
index 5250b01..5e4d545 100644
--- a/ufl/permutation.py
+++ b/ufl/permutation.py
@@ -1,7 +1,8 @@
+# -*- coding: utf-8 -*-
"""This module provides utility functions for computing permutations
and generating index lists."""
-# Copyright (C) 2008-2014 Anders Logg and Kent-Andre Mardal
+# Copyright (C) 2008-2015 Anders Logg and Kent-Andre Mardal
#
# This file is part of UFL.
#
@@ -18,7 +19,7 @@ and generating index lists."""
# You should have received a copy of the GNU Lesser General Public License
# along with UFL. If not, see <http://www.gnu.org/licenses/>.
#
-# Modified by Martin Alnes 2009-2014
+# Modified by Martin Alnæs 2009-2015
from six.moves import xrange as range
@@ -69,7 +70,7 @@ def compute_permutations(k, n, skip = None):
if skip is None:
skip = []
if k == 1:
- return [(i,) for i in range(n) if not i in skip]
+ return [(i,) for i in range(n) if i not in skip]
pp = compute_permutations(k - 1, n, skip)
permutations = []
for i in range(n):
diff --git a/ufl/precedence.py b/ufl/precedence.py
index bfba04d..e10492d 100644
--- a/ufl/precedence.py
+++ b/ufl/precedence.py
@@ -1,6 +1,7 @@
+# -*- coding: utf-8 -*-
"Precedence handling."
-# Copyright (C) 2008-2014 Martin Sandve Alnes
+# Copyright (C) 2008-2015 Martin Sandve Alnæs
#
# This file is part of UFL.
#
diff --git a/ufl/protocols.py b/ufl/protocols.py
index 4ff0155..d26f519 100644
--- a/ufl/protocols.py
+++ b/ufl/protocols.py
@@ -1,10 +1,27 @@
+# -*- coding: utf-8 -*-
+# Copyright (C) 2008-2015 Martin Sandve Alnæs
+#
+# This file is part of UFL.
+#
+# UFL is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# UFL is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with UFL. If not, see <http://www.gnu.org/licenses/>.
def id_or_none(obj):
"""Returns None if the object is None, obj.ufl_id() if available, or id(obj) if not.
This allows external libraries to implement an alternative
to id(obj) in the ufl_id() function, such that ufl can identify
- objects as the same without knowing about their types.
+ objects as the same without knowing about their types.
"""
if obj is None:
return None
@@ -20,4 +37,3 @@ def metadata_equal(a, b):
def metadata_hashdata(md):
return tuple(sorted((k, id(v)) for k, v in list(md.items())))
-
diff --git a/ufl/referencevalue.py b/ufl/referencevalue.py
index e7e44c7..beb304f 100644
--- a/ufl/referencevalue.py
+++ b/ufl/referencevalue.py
@@ -1,6 +1,7 @@
+# -*- coding: utf-8 -*-
"Representation of the reference value of a function."
-# Copyright (C) 2008-2014 Martin Sandve Alnes
+# Copyright (C) 2008-2015 Martin Sandve Alnæs
#
# This file is part of UFL.
#
@@ -37,11 +38,7 @@ class ReferenceValue(Operator):
@property
def ufl_shape(self):
- return self.ufl_operands[0].element().reference_value_shape()
-
- def reconstruct(self, op):
- "Return a new object of the same type with new operands."
- return self._ufl_class_(op)
+ return self.ufl_operands[0].ufl_element().reference_value_shape()
def evaluate(self, x, mapping, component, index_values, derivatives=()):
"Get child from mapping and return the component asked for."
diff --git a/ufl/restriction.py b/ufl/restriction.py
index f227e4c..89a9df8 100644
--- a/ufl/restriction.py
+++ b/ufl/restriction.py
@@ -1,6 +1,7 @@
+# -*- coding: utf-8 -*-
"""Restriction operations."""
-# Copyright (C) 2008-2015 Martin Sandve Alnes
+# Copyright (C) 2008-2015 Martin Sandve Alnæs
#
# This file is part of UFL.
#
diff --git a/ufl/sobolevspace.py b/ufl/sobolevspace.py
index de1ccee..1f4352e 100644
--- a/ufl/sobolevspace.py
+++ b/ufl/sobolevspace.py
@@ -1,3 +1,4 @@
+# -*- coding: utf-8 -*-
"""This module defines a symbolic heirarchy of Sobolev spaces to enable
symbolic reasoning about the spaces in which finite elements lie."""
@@ -23,7 +24,6 @@ symbolic reasoning about the spaces in which finite elements lie."""
# Modified by Martin Alnaes 2014
# Modified by Lizao Li 2015
-
class SobolevSpace(object):
"""Symbolic representation of a Sobolev space. This implements a
subset of the methods of a Python set so that finite elements and
@@ -57,6 +57,9 @@ class SobolevSpace(object):
def __eq__(self, other):
return isinstance(other, SobolevSpace) and self.name == other.name
+ def __ne__(self, other):
+ return not self == other
+
def __hash__(self):
return hash(("SobolevSpace", self.name))
@@ -91,6 +94,16 @@ class SobolevSpace(object):
of." """
return (self == other) or (self in other.parents)
+ def __call__(self, element):
+ """Syntax shortcut to create a HDivElement or HCurlElement."""
+ if self.name == "HDiv":
+ from ufl.finiteelement import HDivElement
+ return HDivElement(element)
+ elif self.name == "HCurl":
+ from ufl.finiteelement import HCurlElement
+ return HCurlElement(element)
+ raise NotImplementedError("SobolevSpace has no call operator (only the specific HDiv and HCurl instances).")
+
L2 = SobolevSpace("L2")
HDiv = SobolevSpace("HDiv", [L2])
HCurl = SobolevSpace("HCurl", [L2])
diff --git a/ufl/sorting.py b/ufl/sorting.py
index 689f1b8..42c9a79 100644
--- a/ufl/sorting.py
+++ b/ufl/sorting.py
@@ -1,7 +1,8 @@
+# -*- coding: utf-8 -*-
"""This module contains a sorting rule for expr objects that
is more robust w.r.t. argument numbering than using repr."""
-# Copyright (C) 2008-2014 Martin Sandve Alnes
+# Copyright (C) 2008-2015 Martin Sandve Alnæs
#
# This file is part of UFL.
#
@@ -20,9 +21,6 @@ is more robust w.r.t. argument numbering than using repr."""
#
# Modified by Anders Logg, 2009-2010.
# Modified by Johan Hake, 2010.
-#
-# First added: 2008-11-26
-# Last changed: 2013-01-02
from six.moves import zip
@@ -32,7 +30,6 @@ from ufl.argument import Argument
from ufl.coefficient import Coefficient
from ufl.core.multiindex import Index, FixedIndex, MultiIndex
from ufl.variable import Label
-from ufl.geometry import GeometricQuantity
def _cmp3(a, b):
@@ -236,6 +233,3 @@ def sorted_expr_sum(seq):
for e in seq2[1:]:
s = s + e
return s
-
-
-from ufl.common import topological_sorting # FIXME: Remove this, update whoever uses it in ufl and ffc etc.
diff --git a/ufl/split_functions.py b/ufl/split_functions.py
index 11635de..55c81db 100644
--- a/ufl/split_functions.py
+++ b/ufl/split_functions.py
@@ -1,6 +1,7 @@
+# -*- coding: utf-8 -*-
"Algorithm for splitting a Coefficient or Argument into subfunctions."
-# Copyright (C) 2008-2014 Martin Sandve Alnes
+# Copyright (C) 2008-2015 Martin Sandve Alnæs
#
# This file is part of UFL.
#
@@ -23,7 +24,8 @@ from six.moves import xrange as range
from ufl.log import error
from ufl.assertions import ufl_assert
-from ufl.common import product, EmptyDict
+from ufl.utils.sequences import product
+from ufl.utils.dicts import EmptyDict
from ufl.finiteelement import MixedElement, TensorElement
from ufl.tensors import as_vector, as_matrix, as_tensor
@@ -32,7 +34,7 @@ def split(v):
"""UFL operator: If v is a Coefficient or Argument in a mixed space, returns
a tuple with the function components corresponding to the subelements."""
# Special case: simple element, just return function in a tuple
- element = v.element()
+ element = v.ufl_element()
if not isinstance(element, MixedElement):
return (v,)
@@ -92,12 +94,12 @@ def split(v):
c = s.get(c, c)
i, j = c
# Extract component c of this subvalue from global tensor v
- if v.rank() == 1:
+ if len(v.ufl_shape) == 1:
# Mapping into a flattened vector
k = offset + i*shape[1] + j
component = v[k]
#print "k, offset, i, j, shape, component", k, offset, i, j, shape, component
- elif v.rank() == 2:
+ elif len(v.ufl_shape) == 2:
# Mapping into a concatenated tensor (is this a figment of my imagination?)
error("Not implemented.")
row_offset, col_offset = 0, 0 # TODO
diff --git a/ufl/tensoralgebra.py b/ufl/tensoralgebra.py
index cb47ce2..05a93e2 100644
--- a/ufl/tensoralgebra.py
+++ b/ufl/tensoralgebra.py
@@ -1,6 +1,7 @@
+# -*- coding: utf-8 -*-
"""Compound tensor algebra operations."""
-# Copyright (C) 2008-2014 Martin Sandve Alnes
+# Copyright (C) 2008-2015 Martin Sandve Alnæs
#
# This file is part of UFL.
#
@@ -104,7 +105,7 @@ class Transposed(CompoundTensorOperator):
def __init__(self, A):
CompoundTensorOperator.__init__(self, (A,))
- ufl_assert(A.rank() == 2, "Transposed is only defined for rank 2 tensors.")
+ ufl_assert(len(A.ufl_shape) == 2, "Transposed is only defined for rank 2 tensors.")
@property
def ufl_shape(self):
@@ -196,8 +197,8 @@ class Dot(CompoundTensorOperator):
# Checks
ufl_assert((ar >= 1 and br >= 1) or scalar,
- "Dot product requires non-scalar arguments, "\
- "got arguments with ranks %d and %d." % \
+ "Dot product requires non-scalar arguments, "
+ "got arguments with ranks %d and %d." %
(ar, br))
ufl_assert(scalar or ash[-1] == bsh[0], "Dimension mismatch in dot product.")
@@ -266,7 +267,7 @@ class Trace(CompoundTensorOperator):
def __new__(cls, A):
# Checks
- ufl_assert(A.rank() == 2, "Trace of tensor with rank != 2 is undefined.")
+ ufl_assert(len(A.ufl_shape) == 2, "Trace of tensor with rank != 2 is undefined.")
# Simplification
if isinstance(A, Zero):
diff --git a/ufl/tensors.py b/ufl/tensors.py
index 4a4a115..d189af1 100644
--- a/ufl/tensors.py
+++ b/ufl/tensors.py
@@ -1,6 +1,7 @@
+# -*- coding: utf-8 -*-
"""Classes used to group scalar expressions into expressions with rank > 0."""
-# Copyright (C) 2008-2014 Martin Sandve Alnes
+# Copyright (C) 2008-2015 Martin Sandve Alnæs
#
# This file is part of UFL.
#
@@ -21,15 +22,15 @@ from six.moves import zip
from six.moves import xrange as range
from ufl.log import warning, error
-from ufl.common import subdict, EmptyDict
+from ufl.utils.dicts import subdict, EmptyDict
from ufl.assertions import ufl_assert
+from ufl.core.ufl_type import ufl_type
from ufl.core.expr import Expr
from ufl.core.operator import Operator
from ufl.constantvalue import as_ufl, Zero
from ufl.core.multiindex import Index, FixedIndex, MultiIndex, indices
from ufl.indexed import Indexed
from ufl.index_combination_utils import remove_indices
-from ufl.core.ufl_type import ufl_type
# --- Classes representing tensors of UFL expressions ---
@@ -69,20 +70,16 @@ class ListTensor(Operator):
# Checks
indexset = set(self.ufl_operands[0].ufl_free_indices)
- ufl_assert(all(not (indexset ^ set(e.ufl_free_indices)) for e in self.ufl_operands),\
+ ufl_assert(all(not (indexset ^ set(e.ufl_free_indices)) for e in self.ufl_operands),
"Can't combine subtensor expressions with different sets of free indices.")
@property
def ufl_shape(self):
return (len(self.ufl_operands),) + self.ufl_operands[0].ufl_shape
- def is_cellwise_constant(self):
- "Return whether this expression is spatially constant over each cell."
- return all(e.is_cellwise_constant() for e in self.ufl_operands)
-
def evaluate(self, x, mapping, component, index_values, derivatives=()):
ufl_assert(len(component) == len(self.ufl_shape),
- "Can only evaluate scalars, expecting a component "\
+ "Can only evaluate scalars, expecting a component "
"tuple of length %d, not %s." % (len(self.ufl_shape), component))
a = self.ufl_operands[component[0]]
component = component[1:]
@@ -158,18 +155,14 @@ class ComponentTensor(Operator):
self.ufl_index_dimensions = fid
self.ufl_shape = sh
- def is_cellwise_constant(self):
- "Return whether this expression is spatially constant over each cell."
- return self.ufl_operands[0].is_cellwise_constant()
-
- def reconstruct(self, expressions, indices):
+ def _ufl_expr_reconstruct_(self, expressions, indices):
# Special case for simplification as_tensor(A[ii], ii) -> A
if isinstance(expressions, Indexed):
A, ii = expressions.ufl_operands
if indices == ii:
#print "RETURNING", A, "FROM", expressions, indices, "SELF IS", self
return A
- return Operator.reconstruct(self, expressions, indices)
+ return Operator._ufl_expr_reconstruct_(self, expressions, indices)
def indices(self):
return self.ufl_operands[1]
@@ -281,7 +274,7 @@ def as_matrix(expressions, indices = None):
if indices is None:
# Allow as_matrix(as_matrix(A)) in user code
if isinstance(expressions, Expr):
- ufl_assert(expressions.rank() == 2, "Expecting rank 2 tensor.")
+ ufl_assert(len(expressions.ufl_shape) == 2, "Expecting rank 2 tensor.")
return expressions
# To avoid importing numpy unneeded, it's quite slow...
@@ -303,7 +296,7 @@ def as_vector(expressions, index = None):
if index is None:
# Allow as_vector(as_vector(v)) in user code
if isinstance(expressions, Expr):
- ufl_assert(expressions.rank() == 1, "Expecting rank 1 tensor.")
+ ufl_assert(len(expressions.ufl_shape) == 1, "Expecting rank 1 tensor.")
return expressions
# To avoid importing numpy unneeded, it's quite slow...
@@ -326,7 +319,7 @@ def as_scalar(expression):
(a,b) = (A[indices], indices)
such that a is always a scalar valued expression."""
- ii = indices(expression.rank())
+ ii = indices(len(expression.ufl_shape))
if ii:
expression = expression[ii]
return expression, ii
@@ -338,7 +331,7 @@ def as_scalars(*expressions):
(a,b) = ([A[0][indices], ..., A[-1][indices]], indices)
such that a is always a list of scalar valued expressions."""
- ii = indices(expressions[0].rank())
+ ii = indices(len(expressions[0].ufl_shape))
if ii:
expressions = [expression[ii] for expression in expressions]
return expressions, ii
diff --git a/ufl/utils/counted.py b/ufl/utils/counted.py
index 25eaaad..3f7b8c9 100644
--- a/ufl/utils/counted.py
+++ b/ufl/utils/counted.py
@@ -1,6 +1,7 @@
+# -*- coding: utf-8 -*-
"Utilites for types with a global unique counter attached to each object."
-# Copyright (C) 2008-2014 Martin Sandve Alnes
+# Copyright (C) 2008-2015 Martin Sandve Alnæs
#
# This file is part of UFL.
#
diff --git a/ufl/utils/derivativetuples.py b/ufl/utils/derivativetuples.py
index c1c8384..8a3d4f9 100644
--- a/ufl/utils/derivativetuples.py
+++ b/ufl/utils/derivativetuples.py
@@ -1,6 +1,7 @@
+# -*- coding: utf-8 -*-
"This module contains a collection of utilities for representing partial derivatives as integer tuples."
-# Copyright (C) 2013-2014 Martin Sandve Alnes and Anders Logg
+# Copyright (C) 2013-2015 Martin Sandve Alnæs and Anders Logg
#
# This file is part of UFL.
#
diff --git a/ufl/utils/dicts.py b/ufl/utils/dicts.py
index 0c1cc6f..5c07105 100644
--- a/ufl/utils/dicts.py
+++ b/ufl/utils/dicts.py
@@ -1,6 +1,7 @@
+# -*- coding: utf-8 -*-
"Various dict manipulation utilities."
-# Copyright (C) 2008-2014 Martin Sandve Alnes
+# Copyright (C) 2008-2015 Martin Sandve Alnæs
#
# This file is part of UFL.
#
diff --git a/ufl/utils/formatting.py b/ufl/utils/formatting.py
index 46a1819..9812731 100644
--- a/ufl/utils/formatting.py
+++ b/ufl/utils/formatting.py
@@ -1,6 +1,7 @@
+# -*- coding: utf-8 -*-
"Various string formatting utilities."
-# Copyright (C) 2008-2014 Martin Sandve Alnes and Anders Logg
+# Copyright (C) 2008-2015 Martin Sandve Alnæs and Anders Logg
#
# This file is part of UFL.
#
diff --git a/ufl/utils/indexflattening.py b/ufl/utils/indexflattening.py
index 2c6f738..728aea1 100644
--- a/ufl/utils/indexflattening.py
+++ b/ufl/utils/indexflattening.py
@@ -1,6 +1,7 @@
+# -*- coding: utf-8 -*-
"This module contains a collection of utilities for mapping between multiindices and a flattened index space."
-# Copyright (C) 2008-2014 Martin Sandve Alnes
+# Copyright (C) 2008-2015 Martin Sandve Alnæs
#
# This file is part of UFL.
#
diff --git a/ufl/utils/sequences.py b/ufl/utils/sequences.py
index 18a0dd3..707b533 100644
--- a/ufl/utils/sequences.py
+++ b/ufl/utils/sequences.py
@@ -1,6 +1,7 @@
+# -*- coding: utf-8 -*-
"Various sequence manipulation utilities."
-# Copyright (C) 2008-2014 Martin Sandve Alnes
+# Copyright (C) 2008-2015 Martin Sandve Alnæs
#
# This file is part of UFL.
#
diff --git a/ufl/utils/sorting.py b/ufl/utils/sorting.py
index b6a3a7a..f7b9a46 100644
--- a/ufl/utils/sorting.py
+++ b/ufl/utils/sorting.py
@@ -1,6 +1,7 @@
+# -*- coding: utf-8 -*-
"Utilites for sorting."
-# Copyright (C) 2008-2014 Johan Hake
+# Copyright (C) 2008-2016 Johan Hake
#
# This file is part of UFL.
#
@@ -55,14 +56,31 @@ def topological_sorting(nodes, edges):
return L
+
def sorted_by_count(seq):
"Sort a sequence by the item.count()."
return sorted(seq, key=lambda x: x.count())
+
+def sorted_by_ufl_id(seq):
+ "Sort a sequence by the item.ufl_id()."
+ return sorted(seq, key=lambda x: x.ufl_id())
+
+
def sorted_by_key(mapping):
"Sort dict items by key, allowing different key types."
# Python3 doesn't allow comparing builtins of different type, therefore the typename trick here
- return sorted(iteritems(mapping), key=lambda x: (type(x[0]).__name__, x[0]))
+ def _key(x):
+ return (type(x[0]).__name__, x[0])
+ 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):
diff --git a/ufl/utils/stacks.py b/ufl/utils/stacks.py
index 88b9d35..b677712 100644
--- a/ufl/utils/stacks.py
+++ b/ufl/utils/stacks.py
@@ -1,6 +1,7 @@
+# -*- coding: utf-8 -*-
"Various utility data structures."
-# Copyright (C) 2008-2014 Martin Sandve Alnes
+# Copyright (C) 2008-2015 Martin Sandve Alnæs
#
# This file is part of UFL.
#
diff --git a/ufl/utils/system.py b/ufl/utils/system.py
index 93d5de1..0ead587 100644
--- a/ufl/utils/system.py
+++ b/ufl/utils/system.py
@@ -1,6 +1,7 @@
+# -*- coding: utf-8 -*-
"Various utilities accessing system io."
-# Copyright (C) 2008-2014 Martin Sandve Alnes and Johannes Ring
+# Copyright (C) 2008-2015 Martin Sandve Alnæs and Johannes Ring
#
# This file is part of UFL.
#
diff --git a/ufl/utils/timer.py b/ufl/utils/timer.py
index 68373d8..e454074 100644
--- a/ufl/utils/timer.py
+++ b/ufl/utils/timer.py
@@ -1,6 +1,7 @@
+# -*- coding: utf-8 -*-
"Timer utilites."
-# Copyright (C) 2008-2014 Martin Sandve Alnes and Anders Logg
+# Copyright (C) 2008-2015 Martin Sandve Alnæs and Anders Logg
#
# This file is part of UFL.
#
diff --git a/ufl/utils/ufltypedicts.py b/ufl/utils/ufltypedicts.py
index aead9b9..2bbf2b3 100644
--- a/ufl/utils/ufltypedicts.py
+++ b/ufl/utils/ufltypedicts.py
@@ -1,6 +1,7 @@
+# -*- coding: utf-8 -*-
"Various utility data structures."
-# Copyright (C) 2008-2014 Martin Sandve Alnes
+# Copyright (C) 2008-2015 Martin Sandve Alnæs
#
# This file is part of UFL.
#
diff --git a/ufl/variable.py b/ufl/variable.py
index ffb14f3..f390525 100644
--- a/ufl/variable.py
+++ b/ufl/variable.py
@@ -1,7 +1,8 @@
+# -*- coding: utf-8 -*-
"""Defines the Variable and Label classes, used to label
expressions as variables for differentiation."""
-# Copyright (C) 2008-2014 Martin Sandve Alnes
+# Copyright (C) 2008-2015 Martin Sandve Alnæs
#
# This file is part of UFL.
#
@@ -18,7 +19,7 @@ expressions as variables for differentiation."""
# 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 ufl.common import counted_init
+from ufl.utils.counted import counted_init
from ufl.log import error
from ufl.assertions import ufl_assert
from ufl.core.expr import Expr
@@ -60,10 +61,9 @@ class Label(Terminal):
error("Label has no free indices (it is not a tensor expression).")
def is_cellwise_constant(self):
- error("Asking if a Label is cellwise constant makes no sense (it is not a tensor expression).")
- #return True # Could also just return True, after all it doesn't change with the cell
+ return True
- def domains(self):
+ def ufl_domains(self):
"Return tuple of domains related to this terminal object."
return ()
@@ -96,11 +96,8 @@ class Variable(Operator):
Operator.__init__(self, (expression, label))
- def domains(self):
- return self.ufl_operands[0].domains()
-
- def is_cellwise_constant(self):
- return self.ufl_operands[0].is_cellwise_constant()
+ def ufl_domains(self):
+ return self.ufl_operands[0].ufl_domains()
def evaluate(self, x, mapping, component, index_values):
a = self.ufl_operands[0].evaluate(x, mapping, component, index_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