From d74c243c8151c0c09062911e626ed1935193e5a2 Mon Sep 17 00:00:00 2001 From: "Garth N. Wells" Date: Fri, 12 Feb 2016 11:47:47 +0000 Subject: [PATCH 1/8] Use setuptools if available, and add numpy and six as dependencies. --- setup.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index e4f17ed6b..b55409783 100755 --- a/setup.py +++ b/setup.py @@ -1,7 +1,12 @@ #!/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 @@ -59,6 +64,7 @@ "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"), From b04b75c2a5df68576f42333a4c856535ad228214 Mon Sep 17 00:00:00 2001 From: Lawrence Mitchell Date: Tue, 19 Jan 2016 18:34:04 +0000 Subject: [PATCH 2/8] Split form grouping out of build_integral_data Introduce new function group_form_integrals which returns a new Form with canonically grouped integrals. This allows attaching estimated polynomial degrees to canonically grouped integrals before geometric lowering is applied in compute_form_data. --- ufl/algorithms/compute_form_data.py | 42 ++++++++++++++++++--- ufl/algorithms/domain_analysis.py | 58 ++++++++++++++++++++--------- 2 files changed, 76 insertions(+), 24 deletions(-) diff --git a/ufl/algorithms/compute_form_data.py b/ufl/algorithms/compute_form_data.py index d3bebb8a1..2aecd333f 100644 --- a/ufl/algorithms/compute_form_data.py +++ b/ufl/algorithms/compute_form_data.py @@ -25,7 +25,7 @@ from ufl.log import error, warning, info from ufl.assertions import ufl_assert -from ufl.classes import GeometricFacetQuantity, Coefficient +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.formdata import FormData#, ExprData @@ -40,10 +40,12 @@ 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): @@ -184,6 +186,23 @@ def _build_coefficient_replace_map(coefficients, element_mapping=None): return new_coefficients, replace_map +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, @@ -191,6 +210,7 @@ def compute_form_data(form, do_apply_geometry_lowering=False, preserve_geometry_types=(), do_apply_restrictions=True, + do_estimate_degrees=True, ): # TODO: Move this to the constructor instead @@ -219,6 +239,18 @@ def compute_form_data(form, # 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. @@ -252,11 +284,9 @@ def compute_form_data(form, form = apply_restrictions(form) - # --- Group and collect data about 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. - self.integral_data = build_integral_data(form.integrals(), self.original_form.ufl_domains()) + # --- 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 diff --git a/ufl/algorithms/domain_analysis.py b/ufl/algorithms/domain_analysis.py index d7da57fe7..cebf40120 100644 --- a/ufl/algorithms/domain_analysis.py +++ b/ufl/algorithms/domain_analysis.py @@ -228,13 +228,47 @@ 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) + + return tuple(IntegralData(d, itype, sid, integrals, {}) for + (d, itype, sid), integrals in itgs.items()) + + +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. + """ + integrals = form.integrals() # Group integrals by domain and type integrals_by_domain_and_type = \ group_integrals_by_domain_and_type(integrals, domains) + integrals = [] for domain in domains: for integral_type in ufl.measure.integral_types(): # Get integrals with this domain and type @@ -254,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 = [] From 5c2d6e89bd1eed57bd9ef64e7389d7f6f4bfcf6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Sandve=20Aln=C3=A6s?= Date: Fri, 5 Feb 2016 13:34:40 +0100 Subject: [PATCH 3/8] Avoid use of same variable name twice in function. --- ufl/algorithms/domain_analysis.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/ufl/algorithms/domain_analysis.py b/ufl/algorithms/domain_analysis.py index cebf40120..9375155ce 100644 --- a/ufl/algorithms/domain_analysis.py +++ b/ufl/algorithms/domain_analysis.py @@ -228,6 +228,7 @@ 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): """Build integral data given a list of integrals. @@ -254,7 +255,6 @@ def build_integral_data(integrals): return tuple(IntegralData(d, itype, sid, integrals, {}) for (d, itype, sid), integrals in itgs.items()) - def group_form_integrals(form, domains): """Group integrals by domain and type, performing canonical simplification. @@ -262,11 +262,9 @@ def group_form_integrals(form, domains): :arg domains: an iterable of :class:`~.Domain`\s. :returns: A new :class:`~.Form` with gathered integrands. """ - integrals = form.integrals() - # 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: From 9335ca48ada1a9d9c3fc97b1d9fdbfb41ae2f472 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Sandve=20Aln=C3=A6s?= Date: Fri, 5 Feb 2016 13:57:34 +0100 Subject: [PATCH 4/8] Sort integral_datas. --- ufl/algorithms/domain_analysis.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/ufl/algorithms/domain_analysis.py b/ufl/algorithms/domain_analysis.py index 9375155ce..52d2a080b 100644 --- a/ufl/algorithms/domain_analysis.py +++ b/ufl/algorithms/domain_analysis.py @@ -252,8 +252,13 @@ def build_integral_data(integrals): # possibly different metadata). itgs[(domain, integral_type, subdomain_id)].append(integral) - return tuple(IntegralData(d, itype, sid, integrals, {}) for - (d, itype, sid), integrals in itgs.items()) + # Build list with canonical ordering, iteration over dicts + # is not deterministic across python versions + integral_datas = [] + for (d, itype, sid), integrals in sorted(itgs.items(), key=lambda x: x[0]): + 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. From 1cb06286f2d092e16affbe1ca277794cfb9ce1bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Sandve=20Aln=C3=A6s?= Date: Mon, 15 Feb 2016 09:10:57 +0100 Subject: [PATCH 5/8] Use sorted_by_keys() which works around new py3 sorting behaviour. --- ufl/algorithms/domain_analysis.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ufl/algorithms/domain_analysis.py b/ufl/algorithms/domain_analysis.py index 52d2a080b..190fb4b8b 100644 --- a/ufl/algorithms/domain_analysis.py +++ b/ufl/algorithms/domain_analysis.py @@ -255,7 +255,7 @@ def build_integral_data(integrals): # Build list with canonical ordering, iteration over dicts # is not deterministic across python versions integral_datas = [] - for (d, itype, sid), integrals in sorted(itgs.items(), key=lambda x: x[0]): + for (d, itype, sid), integrals in sorted_by_key(itgs): integral_datas.append(IntegralData(d, itype, sid, integrals, {})) return integral_datas From 70652b0d29ac6d9f11b17bf3fef885271baf2895 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Sandve=20Aln=C3=A6s?= Date: Mon, 15 Feb 2016 16:02:52 +0100 Subject: [PATCH 6/8] Add sorted_by_tuple_key utility to work around py3 sorting issue. --- ufl/algorithms/domain_analysis.py | 7 ++----- ufl/utils/sorting.py | 17 +++++++++++++++-- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/ufl/algorithms/domain_analysis.py b/ufl/algorithms/domain_analysis.py index 190fb4b8b..be6bb320f 100644 --- a/ufl/algorithms/domain_analysis.py +++ b/ufl/algorithms/domain_analysis.py @@ -23,15 +23,12 @@ 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.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.sorting import cmp_expr, sorted_expr, sorted_by_key, sorted_by_tuple_key from ufl.utils.sorting import canonicalize_metadata @@ -255,7 +252,7 @@ def build_integral_data(integrals): # Build list with canonical ordering, iteration over dicts # is not deterministic across python versions integral_datas = [] - for (d, itype, sid), integrals in sorted_by_key(itgs): + for (d, itype, sid), integrals in sorted_by_tuple_key(itgs): integral_datas.append(IntegralData(d, itype, sid, integrals, {})) return integral_datas diff --git a/ufl/utils/sorting.py b/ufl/utils/sorting.py index eaa1b968f..f7b9a469a 100644 --- a/ufl/utils/sorting.py +++ b/ufl/utils/sorting.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- "Utilites for sorting." -# Copyright (C) 2008-2015 Johan Hake +# Copyright (C) 2008-2016 Johan Hake # # This file is part of UFL. # @@ -56,18 +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): From 7b55c256ab558a7535cbb2721c9db5a7004ad773 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Sandve=20Aln=C3=A6s?= Date: Mon, 15 Feb 2016 16:07:27 +0100 Subject: [PATCH 7/8] Fix import bug. --- ufl/algorithms/domain_analysis.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ufl/algorithms/domain_analysis.py b/ufl/algorithms/domain_analysis.py index be6bb320f..1a8ffa500 100644 --- a/ufl/algorithms/domain_analysis.py +++ b/ufl/algorithms/domain_analysis.py @@ -28,8 +28,8 @@ from ufl.measure import Measure from ufl.integral import Integral from ufl.form import Form -from ufl.sorting import cmp_expr, sorted_expr, sorted_by_key, sorted_by_tuple_key -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): From 761b5fd1486c9cefb231cb6fba2abfb70700f1ef Mon Sep 17 00:00:00 2001 From: "Garth N. Wells" Date: Tue, 23 Feb 2016 10:04:35 +0000 Subject: [PATCH 8/8] Add instruction on installing UFL. --- doc/sphinx/source/index.rst | 63 ++++++++++++++++++++++++++----------- 1 file changed, 45 insertions(+), 18 deletions(-) diff --git a/doc/sphinx/source/index.rst b/doc/sphinx/source/index.rst index 2ad218d27..9e577342d 100644 --- a/doc/sphinx/source/index.rst +++ b/doc/sphinx/source/index.rst @@ -24,32 +24,58 @@ Compiler (FFC) and in combination with the problem solving environment DOLFIN. -Preliminaries -============= Installation ------------- +============ -Ubuntu package -^^^^^^^^^^^^^^ +Debian/Ubuntu packages +---------------------- -UFL may be installed directly from source, but the Debian (Ubuntu) -package ``python-ufl`` is also available for UFL, as for other FEniCS -components. +Debian/Ubuntu +^^^^^^^^^^^^^ -Manual from source -^^^^^^^^^^^^^^^^^^ +A Debian/Ubuntu package ``python-ufl`` is available for UFL: -To retrieve the latest development version of UFL:: + sudo apt-get install python-ufl - git clone https://bitbucket.org/fenics-project/ufl -To install 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@googlegroups.com. @@ -61,23 +87,24 @@ 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@googlegroups.com +questions and to make feature requests, use +fenics-dev@googlegroups.com. Bugs can be registered at https://bitbucket.org/fenics-project/ufl/issues. -Manual and API Reference + +Manual and API reference ======================== .. toctree:: :titlesonly: User Manual - API Reference + API Reference Releases - * :ref:`modindex` * :ref:`genindex` * :ref:`search`