[ufl] 01/03: New upstream version 2017.1.0
Johannes Ring
johannr-guest at moszumanska.debian.org
Wed May 10 10:42:17 UTC 2017
This is an automated email from the git hooks/post-receive script.
johannr-guest pushed a commit to branch experimental
in repository ufl.
commit 4844d838c3315cbd0cfc7f9111a0688740bc44c9
Author: Johannes Ring <johannr at simula.no>
Date: Wed May 10 12:26:35 2017 +0200
New upstream version 2017.1.0
---
ChangeLog | 275 ----------------------
ChangeLog.rst | 379 ++++++++++++++++++++++++++++++
MANIFEST.in | 8 +
README.rst | 2 +-
doc/sphinx/source/conf.py | 18 +-
doc/sphinx/source/releases.rst | 2 +
doc/sphinx/source/releases/next.rst | 20 --
doc/sphinx/source/releases/v2016.2.0.rst | 67 ++++++
doc/sphinx/source/releases/v2017.1.0.rst | 19 ++
setup.cfg | 2 +-
setup.py | 7 +-
test/test_sobolevspace.py | 159 ++++++++++++-
test/test_tensoralgebra.py | 12 +-
ufl/__init__.py | 9 +-
ufl/algorithms/compute_form_data.py | 5 +-
ufl/algorithms/elementtransformations.py | 36 +--
ufl/constantvalue.py | 6 +-
ufl/exproperators.py | 33 +++
ufl/finiteelement/__init__.py | 2 +
ufl/finiteelement/brokenelement.py | 3 +
ufl/finiteelement/elementlist.py | 1 +
ufl/finiteelement/enrichedelement.py | 74 +++++-
ufl/finiteelement/facetelement.py | 40 +---
ufl/finiteelement/finiteelement.py | 107 ++++++---
ufl/finiteelement/finiteelementbase.py | 1 -
ufl/finiteelement/hdivcurl.py | 15 ++
ufl/finiteelement/interiorelement.py | 40 +---
ufl/finiteelement/mixedelement.py | 12 +-
ufl/finiteelement/restrictedelement.py | 4 +
ufl/finiteelement/tensorproductelement.py | 20 ++
ufl/functionspace.py | 13 +
ufl/log.py | 4 +-
ufl/operators.py | 2 +
ufl/sobolevspace.py | 118 ++++++++--
ufl/utils/sorting.py | 2 +-
35 files changed, 1024 insertions(+), 493 deletions(-)
diff --git a/ChangeLog b/ChangeLog
deleted file mode 100644
index 83209cb..0000000
--- a/ChangeLog
+++ /dev/null
@@ -1,275 +0,0 @@
-2016.2.0 [2016-11-30]
- - Add call operator syntax to Form to replace arguments and
- coefficients. This makes it easier to e.g. express the norm
- defined by a bilinear form as a functional. Example usage:
- # Equivalent to replace(a, {u: f, v: f})
- M = a(f, f)
- # Equivalent to replace(a, {f:1})
- c = a(coefficients={f:1})
- - Add call operator syntax to Form to replace arguments and coefficients:
- a(f, g) == replace(a, {u: f, v: g})
- a(coefficients={f:1}) == replace(a, {f:1})
- - Add @ operator to Form: form @ f == action(form, f) (python 3.5+ only)
- - Reduce noise in Mesh str such that print(form) gets more short and readable
- - Fix repeated split(function) for arbitrary nested elements
- - EnrichedElement: Remove +/* warning
- In the distant past, A + B => MixedElement([A, B]). The change that
- A + B => EnrichedElement([A, B]) was made in d622c74 (22 March 2010).
- A warning was introduced in fcbc5ff (26 March 2010) that the meaning of
- "+" had changed, and that users wanting a MixedElement should use "*"
- instead. People have, presumably, been seeing this warning for 6 1/2
- years by now, so it's probably safe to remove.
- - Rework TensorProductElement implementation, replaces OuterProductElement
- - Rework TensorProductCell implementation, replaces OuterProductCell
- - Remove OuterProductVectorElement and OuterProductTensorElement
- - Add FacetElement and InteriorElement
- - Add Hellan-Herrmann-Johnson element
- - Add support for double covariant and contravariant mappings in mixed elements
- - Support discontinuous Taylor elements on all simplices
- - Some more performance improvements
- - Minor bugfixes
- - Improve Python 3 support
- - More permissive in integer types accepted some places
- - Make ufl pass almost all flake8 tests
- - Add bitbucket pipelines testing
- - Improve documentation
-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
- - 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
-1.5.0 [2015-01-12]
- - Require Python 2.7
- - Python 3 support
- - Change to py.test
- - Rewrite parts of expression representation core, providing
- significant optimizations in speed and memory use, as well
- as a more elaborate type metadata system for internal use
- - Use expr.ufl_shape instead of ufl.shape()
- - Use expr.ufl_indices instead of ufl.indices(),
- returns tuple of free index ids, not Index objects
- - Use expr.ufl_index_dimensions instead of ufl.index_dimensions(),
- returns tuple of dimensions ordered corresponding to expr.ufl_indices, not a dict
- - Rewrite core algorithms for expression traversal
- - Add new core algorithms map_expr_dag(), map_integrand_dag(),
- similar to python map() but applying a callable MultiFunction
- recursively to each Expr node, without Python recursion
- - Highly recommend rewriting algorithms based on Transformer using
- map_expr_dag and MultiFunction, avoiding Python recursion overhead
- - Rewrite core algorithms apply_derivatives, apply_restrictions
- - Form signature is now computed without applying derivatives first,
- introducing smaller overhead on jit cache hits
- - Use form.signature() to compute form signature
- - Use form.arguments() instead of extract_arguments(form)
- - Use form.coefficients() instead of extract_coefficients(form)
- - Small improvement to str and latex output of expressions
- - Allow diff(expr, coefficient) without wrapping coefficient in variable
- - Add keywords to measures: dx(..., degree=3, rule="canonical")
- - Introduce notation from the Periodic Table of the Finite Elements
- - Introduce notation for FEEC families of elements: P-, P, Q-, S
- - Experimental support for high-order geometric domains
- - Algorithms for symbolic rewriting of geometric quantities (used by uflacs)
- - Remove the *Constant* classes, using Coefficient with a Real element instead
- - Add types for MinValue and MaxValue
- - Disable automatic rewriting a+a->2*a, a*a->a**2, a/a->1, these are
- costly and the compiler should handle them instead
- - Fix signature stability w.r.t. metadata dicts
- - Minor bugfixes, removal of unused code and cleanups
-1.4.0 [2014-06-02]
- - New integral type custom_integral (*dc)
- - Add analysis of which coefficients each integral actually uses to optimize assembly
- - Improved svg rendering of cells and sobolevspaces in ipython notebook
- - Add sobolev spaces, use notation "element in HCurl" (HCurl, HDiv, H1, H2, L2)
- - Improved error checking of facet geometry in non-facet integrals
- - Improved restriction handling, restricting continuous coefficients and constants is now optional
- - Introduce notation from the Periodic Table of the Finite Elements (draft)
- - Remove alias "Q" for quadrature element, use "Quadrature"
- - New derivative type ReferenceGrad
- - New discontinuous RT element
- - New geometry types Jacobian, JacobianInverse, JacobianDeterminant
- - New geometry types FacetJacobian, FacetJacobianInverse, FacetJacobianDeterminant
- - New geometry types CellFacetJacobian, CellFacetJacobianInverse, CellFacetJacobianDeterminant
- - New geometry types FacetOrigin, CellOrigin
- - New geometry types CellCoordinate, FacetCoordinate
- - New geometry types CellNormal, CellOrientation, QuadratureWeight
- - Argument (and TestFunction, TrialFunction) now use absolute numbering f.number() instead of relative f.count()
- - New syntax: integrand*dx(domain)
- - New syntax: integrand*dx(1, domain=domain)
- - New syntax: integrand*dx(1, subdomain_data=domain_data)
- - Using domain instead of cell in many places.
- - Deprecated notation 'cell.n', 'cell.x' etc.
- - Recommended new notation: FacetNormal(domain)
- - Experimental: Argument (and TestFunction, TrialFunction) now can have a specified part index for representing block systems
- - Experimental: Domains can now be created with a Coefficient providing coordinates: Domain(Coefficient(VectorElement("CG", domain, 2)))
- - Experimental: New concept Domain: domain = Domain(triangle, geometric_dimension=3, label="MyDomain")
- - Various general optimizations
- - Various minor bugfixes
- - Various docstring improvements
-1.3.0 [2014-01-07]
- - Add cell_avg and facet_avg operators, can be applied to a Coefficient or Argument or restrictions thereof
- - Fix bug in cofactor: now it is transposed the correct way.
- - Add cell.min_facet_edge_length
- - Add cell.max_facet_edge_length
- - Simplify 0^f -> 0 if f is a non-negative scalar value
- - Add atan2 function
- - Allow form+0 -> form
-1.2.0 [2013-03-24]
- - NB! Using shapes such as (1,) and (1,1) instead of () for 1D tensor quantities I, x, grad(f)
- - Add cell.facet_diameter
- - Add new concept Domain
- - Add new concept Region, which is the union of numbered subdomains
- - Add integration over regions (which may be overlapping by sharing subdomains)
- - Add integration over everywhere
- - Add functions cosh, sinh, tanh, Max, Min
- - Generalize jump(v,n) for rank(v) > 2
- - Fix some minor bugs
-1.1.0 [2013-01-07]
- - Add support for pickling of expressions (thanks to Graham Markall)
- - Add shorthand notation A**2 == inner(A, A), special cased for power 2.
- - Add support for measure sum notation f*(dx(0) + dx(3)) == f*dx(0) + f*dx(3)
- - Supporting code for bugfix in PyDOLFIN when comparing test/trial functions
- - Remove support for tuple form notation as this was ambiguous
- - Bugfix in quadrature degree estimation, never returning <0 now
- - Remove use of cmp to accomodate removal from python 3
-1.1-alpha-prerelease [2012-11-18]
-(Not released, snapshot archived with submission of UFL journal paper)
- - Support adding 0 to forms, allowing sum([a])
- - Major memory savings and optimizations.
- - Some bugfixes.
- - Add perp operator.
- - Support nested tuple syntax like MixedElement((U,V),W)
- - Allow outer(a, b, c, ...) by recursive application from left.
- - Add simplification f/f -> 1
- - Add operators <,>,<=,>= in place of lt,gt,le,ge
-1.0.0 [2011-12-07]
- - No changes since rc1.
-1.0-rc1 [2011-11-22]
- - Added tests covering snippets from UFL chapter in FEniCS book
- - Added more unit tests
- - Added operators diag and diag_vector
- - Added geometric quantities cell.surface_area and cell.facet_area
- - Fixed rtruediv bug
- - Fixed bug with derivatives of elements of type Real with unspecified cell
-1.0-beta3 [2011-10-26]
- - Added nabla_grad and nabla_div operators
- - Added error function erf(x)
- - Added bessel functions of first and second kind, normal and modified,
- bessel_J(nu, x), bessel_Y(nu, x), bessel_I(nu, x), bessel_K(nu, x)
- - Extended derivative() to allow indexed coefficient(s) as differentiation variable
- - Made *Constant use the 'Real' space instead of 'DG0'
- - Bugfix in adjoint where test and trial functions were in different spaces
- - Bugfix in replace where the argument to a grad was replaced with 0
- - Bugfix in reconstruction of tensor elements
- - Some other minor bugfixes
-1.0-beta2 [2011-08-11]
- - Support c*form where c depends on a coefficient in a Real space
-1.0-beta [2011-07-08]
- - Add script ufl-version
- - Added syntax for associating an arbitrary domain data object with a measure:
- dss = ds[boundaries]; M = f*dss(1) + g*dss(2)
- - Added new operators elem_mult, elem_div, elem_pow and elem_op for
- elementwise application of scalar operators to tensors of equal shape
- - Added condition operators And(lhs,rhs) and Or(lhs,rhs) and Not(cond)
- - Fixed support for symmetries in subelements of a mixed element
- - Add support for specifying derivatives of coefficients to derivative()
-0.9.1 [2011-05-16]
- - Remove set_foo functions in finite element classes
- - Change license from GPL v3 or later to LGPL v3 or later
- - Change behavior of preprocess(), form.compute_form_data(), form_data.preprocessed_form
- - Allowing grad, div, inner, dot, det, inverse on scalars
- - Simplify Identity(1) -> IntValue(1) automatically
- - Added Levi-Cevita symbol: e = PermutationSymbol(3); e[i,j,k]
- - Fix bug with future division behaviour (ufl does not support floor division)
- - Add subdomain member variables to form class
- - Allow action on forms of arbitrary rank
-0.9.0 [2011-02-23]
- - Allow jump(Sigma, n) for matrix-valued expression Sigma
- - Bug fix in scalar curl operator
- - Bug fix in deviatoric operator
-0.5.4 [2010-09-01]
- - Bug fixes in PartExtracter
- - Do not import x for coordinate
- - Add Circumradius to Cell (Cell.circumradius)
- - Add CellVolume to Cell (Cell.volume)
-0.5.3 [2010-07-01]
- - Rename ElementRestriction --> RestrictedElement
- - Experimental import of x from tetrahedron
- - Make lhs/rhs work for resrictions
- - Redefine operator + for FiniteElements and replace + by *
- - Rename ElementUnion -> EnrichedElement
- - Add support for tan() and inverse trigonometric functions
-0.5.2 [2010-02-15]
- - Attach form data to preprocessed form, accessible by form.form_data()
-0.5.1 [2010-02-03]
- - Fix bug in propagate_restriction
-0.5.0 [2010-02-01]
- - Several interface changes in FormData class
- - Introduce call preprocess(form) to be called at beginning of compilation
- - Rename BasisFunction --> Argument
- - Rename Function --> Coefficient
-0.4.1 [2009-12-04]
- - Redefine grad().T --> grad()
- - New meaning of estimate_max_polynomial_degree
- - New function estimate_total_polynomial_degree
- - Allow degree = None and cell = None for elements
-0.4.0 [2009-09-23]
- - Extensions for ElementRestriction (restrict FiniteElement to Cell)
- - Bug fix for lhs/rhs with list tensor types
- - Add new log function set_prefix
- - Add new log function log(level, message)
- - Added macro cell integral *dE
- - Added mechanism to add additional integral types
- - Added LiftingOperator and LiftingFunction
- - Added ElementRestriction
-0.3.0 [2009-05-28]
- - Some critical bugfixes, in particular in differentiation.
- - Added form operators "system" and "sensitivity_rhs".
- - diff can take form as argument, applies to all integrands.
- - Rudimentary precedence handling for better
- use of parentheses in str(expression).
- - Added script ufl2py, mainly for debugging purposes.
- - Crude implementation of estimate_max_polynomial_degree
- for quadrature degree estimation.
- - Improved manual.
-0.2.0 [2009-04-07]
- - Initial release of UFL.
-0.1.0 [...]
- - Unreleased development versions of UFL.
diff --git a/ChangeLog.rst b/ChangeLog.rst
new file mode 100644
index 0000000..caa2be5
--- /dev/null
+++ b/ChangeLog.rst
@@ -0,0 +1,379 @@
+Changelog
+=========
+
+2017.1.0 (2017-05-09)
+---------------------
+
+- Add the ``DirectionalSobolevSpace`` subclass of ``SobolevSpace``. This
+ allows one to use spaces where elements have varying continuity in
+ different spatial directions.
+- Add ``sobolev_space`` methods for ``HDiv`` and ``HCurl`` finite
+ elements.
+- Add ``sobolev_space`` methods for ``TensorProductElement`` and
+ ``EnrichedElement``. The smallest shared Sobolev space will be
+ returned for enriched elements. For the tensor product elements, a
+ ``DirectionalSobolevSpace`` is returned depending on the order of the
+ spaces associated with the component elements.
+
+2016.2.0 (2016-11-30)
+---------------------
+
+- Add call operator syntax to ``Form`` to replace arguments and
+ coefficients. This makes it easier to e.g. express the norm
+ defined by a bilinear form as a functional. Example usage::
+
+ # Equivalent to replace(a, {u: f, v: f})
+ M = a(f, f)
+ # Equivalent to replace(a, {f:1})
+ c = a(coefficients={f:1})
+- Add call operator syntax to ``Form`` to replace arguments and
+ coefficients::
+
+ a(f, g) == replace(a, {u: f, v: g})
+ a(coefficients={f:1}) == replace(a, {f:1})
+- Add ``@`` operator to ``Form``: ``form @ f == action(form, f)``
+ (python 3.5+ only)
+- Reduce noise in Mesh str such that ``print(form)`` gets more short and
+ readable
+- Fix repeated ``split(function)`` for arbitrary nested elements
+- EnrichedElement: Remove ``+/*`` warning
+
+ In the distant past, ``A + B => MixedElement([A, B])``. The change
+ that ``A + B => EnrichedElement([A, B])`` was made in ``d622c74`` (22
+ March 2010). A warning was introduced in ``fcbc5ff`` (26 March 2010)
+ that the meaning of ``+`` had changed, and that users wanting a
+ ``MixedElement`` should use ``*`` instead. People have, presumably,
+ been seeing this warning for 6 1/2 years by now, so it's probably safe
+ to remove.
+- Rework ``TensorProductElement`` implementation, replaces
+ ``OuterProductElement``
+- Rework ``TensorProductCell`` implementation, replaces
+ ``OuterProductCell``
+- Remove ``OuterProductVectorElement`` and ``OuterProductTensorElement``
+- Add ``FacetElement`` and ``InteriorElement``
+- Add ``Hellan-Herrmann-Johnson`` element
+- Add support for double covariant and contravariant mappings in mixed
+ elements
+- Support discontinuous Taylor elements on all simplices
+- Some more performance improvements
+- Minor bugfixes
+- Improve Python 3 support
+- More permissive in integer types accepted some places
+- Make ufl pass almost all flake8 tests
+- Add bitbucket pipelines testing
+- Improve documentation
+
+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
+- 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
+
+1.5.0 (2015-01-12)
+------------------
+
+- Require Python 2.7
+- Python 3 support
+- Change to py.test
+- Rewrite parts of expression representation core, providing
+ significant optimizations in speed and memory use, as well
+ as a more elaborate type metadata system for internal use
+- Use expr.ufl_shape instead of ufl.shape()
+- Use expr.ufl_indices instead of ufl.indices(),
+ returns tuple of free index ids, not Index objects
+- Use expr.ufl_index_dimensions instead of ufl.index_dimensions(),
+ returns tuple of dimensions ordered corresponding to expr.ufl_indices, not a dict
+- Rewrite core algorithms for expression traversal
+- Add new core algorithms map_expr_dag(), map_integrand_dag(),
+ similar to python map() but applying a callable MultiFunction
+ recursively to each Expr node, without Python recursion
+- Highly recommend rewriting algorithms based on Transformer using
+ map_expr_dag and MultiFunction, avoiding Python recursion overhead
+- Rewrite core algorithms apply_derivatives, apply_restrictions
+- Form signature is now computed without applying derivatives first,
+ introducing smaller overhead on jit cache hits
+- Use form.signature() to compute form signature
+- Use form.arguments() instead of extract_arguments(form)
+- Use form.coefficients() instead of extract_coefficients(form)
+- Small improvement to str and latex output of expressions
+- Allow diff(expr, coefficient) without wrapping coefficient in variable
+- Add keywords to measures: dx(..., degree=3, rule="canonical")
+- Introduce notation from the Periodic Table of the Finite Elements
+- Introduce notation for FEEC families of elements: P-, P, Q-, S
+- Experimental support for high-order geometric domains
+- Algorithms for symbolic rewriting of geometric quantities (used by uflacs)
+- Remove the *Constant* classes, using Coefficient with a Real element instead
+- Add types for MinValue and MaxValue
+- Disable automatic rewriting a+a->2*a, a*a->a**2, a/a->1, these are
+ costly and the compiler should handle them instead
+- Fix signature stability w.r.t. metadata dicts
+- Minor bugfixes, removal of unused code and cleanups
+
+1.4.0 (2014-06-02)
+------------------
+
+- New integral type custom_integral (``*dc``)
+- Add analysis of which coefficients each integral actually uses to optimize assembly
+- Improved svg rendering of cells and sobolevspaces in ipython notebook
+- Add sobolev spaces, use notation "element in HCurl" (HCurl, HDiv, H1, H2, L2)
+- Improved error checking of facet geometry in non-facet integrals
+- Improved restriction handling, restricting continuous coefficients and constants is now optional
+- Introduce notation from the Periodic Table of the Finite Elements (draft)
+- Remove alias "Q" for quadrature element, use "Quadrature"
+- New derivative type ReferenceGrad
+- New discontinuous RT element
+- New geometry types Jacobian, JacobianInverse, JacobianDeterminant
+- New geometry types FacetJacobian, FacetJacobianInverse, FacetJacobianDeterminant
+- New geometry types CellFacetJacobian, CellFacetJacobianInverse, CellFacetJacobianDeterminant
+- New geometry types FacetOrigin, CellOrigin
+- New geometry types CellCoordinate, FacetCoordinate
+- New geometry types CellNormal, CellOrientation, QuadratureWeight
+- Argument (and TestFunction, TrialFunction) now use absolute numbering f.number() instead of relative f.count()
+- New syntax: integrand*dx(domain)
+- New syntax: integrand*dx(1, domain=domain)
+- New syntax: integrand*dx(1, subdomain_data=domain_data)
+- Using domain instead of cell in many places.
+- Deprecated notation 'cell.n', 'cell.x' etc.
+- Recommended new notation: FacetNormal(domain)
+- Experimental: Argument (and TestFunction, TrialFunction) now can have a specified part index for representing block systems
+- Experimental: Domains can now be created with a Coefficient providing coordinates: Domain(Coefficient(VectorElement("CG", domain, 2)))
+- Experimental: New concept Domain: domain = Domain(triangle, geometric_dimension=3, label="MyDomain")
+- Various general optimizations
+- Various minor bugfixes
+- Various docstring improvements
+
+1.3.0 (2014-01-07)
+------------------
+
+- Add cell_avg and facet_avg operators, can be applied to a Coefficient or Argument or restrictions thereof
+- Fix bug in cofactor: now it is transposed the correct way.
+- Add cell.min_facet_edge_length
+- Add cell.max_facet_edge_length
+- Simplify 0^f -> 0 if f is a non-negative scalar value
+- Add atan2 function
+- Allow form+0 -> form
+
+1.2.0 (2013-03-24)
+------------------
+
+- NB! Using shapes such as (1,) and (1,1) instead of () for 1D tensor quantities I, x, grad(f)
+- Add cell.facet_diameter
+- Add new concept Domain
+- Add new concept Region, which is the union of numbered subdomains
+- Add integration over regions (which may be overlapping by sharing subdomains)
+- Add integration over everywhere
+- Add functions cosh, sinh, tanh, Max, Min
+- Generalize jump(v,n) for rank(v) > 2
+- Fix some minor bugs
+
+1.1.0 (2013-01-07)
+------------------
+
+- Add support for pickling of expressions (thanks to Graham Markall)
+- Add shorthand notation A**2 == inner(A, A), special cased for power 2.
+- Add support for measure sum notation f*(dx(0) + dx(3)) == f*dx(0) + f*dx(3)
+- Supporting code for bugfix in PyDOLFIN when comparing test/trial functions
+- Remove support for tuple form notation as this was ambiguous
+- Bugfix in quadrature degree estimation, never returning <0 now
+- Remove use of cmp to accomodate removal from python 3
+
+1.1-alpha-prerelease (2012-11-18)
+---------------------------------
+
+(Not released, snapshot archived with submission of UFL journal paper)
+- Support adding 0 to forms, allowing sum([a])
+- Major memory savings and optimizations.
+- Some bugfixes.
+- Add perp operator.
+- Support nested tuple syntax like MixedElement((U,V),W)
+- Allow outer(a, b, c, ...) by recursive application from left.
+- Add simplification f/f -> 1
+- Add operators <,>,<=,>= in place of lt,gt,le,ge
+
+1.0.0 (2011-12-07)
+------------------
+
+- No changes since rc1.
+
+1.0-rc1 (2011-11-22)
+--------------------
+
+- Added tests covering snippets from UFL chapter in FEniCS book
+- Added more unit tests
+- Added operators diag and diag_vector
+- Added geometric quantities cell.surface_area and cell.facet_area
+- Fixed rtruediv bug
+- Fixed bug with derivatives of elements of type Real with unspecified cell
+
+1.0-beta3 (2011-10-26)
+----------------------
+
+- Added nabla_grad and nabla_div operators
+- Added error function erf(x)
+- Added bessel functions of first and second kind, normal and modified,
+ bessel_J(nu, x), bessel_Y(nu, x), bessel_I(nu, x), bessel_K(nu, x)
+- Extended derivative() to allow indexed coefficient(s) as differentiation variable
+- Made ``*Constant`` use the ``Real`` space instead of ``DG0``
+- Bugfix in adjoint where test and trial functions were in different spaces
+- Bugfix in replace where the argument to a grad was replaced with 0
+- Bugfix in reconstruction of tensor elements
+- Some other minor bugfixes
+
+1.0-beta2 (2011-08-11)
+----------------------
+
+- Support c*form where c depends on a coefficient in a Real space
+
+1.0-beta (2011-07-08)
+---------------------
+
+- Add script ufl-version
+- Added syntax for associating an arbitrary domain data object with a measure:
+ dss = ds[boundaries]; M = f*dss(1) + g*dss(2)
+- Added new operators elem_mult, elem_div, elem_pow and elem_op for
+ elementwise application of scalar operators to tensors of equal shape
+- Added condition operators And(lhs,rhs) and Or(lhs,rhs) and Not(cond)
+- Fixed support for symmetries in subelements of a mixed element
+- Add support for specifying derivatives of coefficients to derivative()
+
+0.9.1 (2011-05-16)
+------------------
+
+- Remove set_foo functions in finite element classes
+- Change license from GPL v3 or later to LGPL v3 or later
+- Change behavior of preprocess(), form.compute_form_data(), form_data.preprocessed_form
+- Allowing grad, div, inner, dot, det, inverse on scalars
+- Simplify Identity(1) -> IntValue(1) automatically
+- Added Levi-Cevita symbol: e = PermutationSymbol(3); e[i,j,k]
+- Fix bug with future division behaviour (ufl does not support floor division)
+- Add subdomain member variables to form class
+- Allow action on forms of arbitrary rank
+
+0.9.0 (2011-02-23)
+------------------
+
+- Allow jump(Sigma, n) for matrix-valued expression Sigma
+- Bug fix in scalar curl operator
+- Bug fix in deviatoric operator
+
+0.5.4 (2010-09-01)
+------------------
+
+- Bug fixes in PartExtracter
+- Do not import x for coordinate
+- Add Circumradius to Cell (Cell.circumradius)
+- Add CellVolume to Cell (Cell.volume)
+
+0.5.3 (2010-07-01)
+------------------
+
+- Rename ElementRestriction --> RestrictedElement
+- Experimental import of x from tetrahedron
+- Make lhs/rhs work for resrictions
+- Redefine operator + for FiniteElements and replace + by *
+- Rename ElementUnion -> EnrichedElement
+- Add support for tan() and inverse trigonometric functions
+
+0.5.2 (2010-02-15)
+------------------
+
+- Attach form data to preprocessed form, accessible by form.form_data()
+
+0.5.1 (2010-02-03)
+------------------
+
+- Fix bug in propagate_restriction
+
+0.5.0 (2010-02-01)
+------------------
+
+- Several interface changes in FormData class
+- Introduce call preprocess(form) to be called at beginning of compilation
+- Rename BasisFunction --> Argument
+- Rename Function --> Coefficient
+
+0.4.1 (2009-12-04)
+------------------
+
+- Redefine grad().T --> grad()
+- New meaning of estimate_max_polynomial_degree
+- New function estimate_total_polynomial_degree
+- Allow degree = None and cell = None for elements
+
+0.4.0 (2009-09-23)
+------------------
+
+- Extensions for ElementRestriction (restrict FiniteElement to Cell)
+- Bug fix for lhs/rhs with list tensor types
+- Add new log function set_prefix
+- Add new log function log(level, message)
+- Added macro cell integral ``*dE``
+- Added mechanism to add additional integral types
+- Added LiftingOperator and LiftingFunction
+- Added ElementRestriction
+
+0.3.0 (2009-05-28)
+------------------
+
+- Some critical bugfixes, in particular in differentiation.
+- Added form operators "system" and "sensitivity_rhs".
+- diff can take form as argument, applies to all integrands.
+- Rudimentary precedence handling for better
+ use of parentheses in str(expression).
+- Added script ufl2py, mainly for debugging purposes.
+- Crude implementation of estimate_max_polynomial_degree
+ for quadrature degree estimation.
+- Improved manual.
+
+0.2.0 (2009-04-07)
+------------------
+
+- Initial release of UFL.
+
+0.1.0 (unreleased)
+------------------
+
+- Unreleased development versions of UFL.
diff --git a/MANIFEST.in b/MANIFEST.in
new file mode 100644
index 0000000..5069eb5
--- /dev/null
+++ b/MANIFEST.in
@@ -0,0 +1,8 @@
+include AUTHORS
+include COPYING
+include COPYING.LESSER
+include ChangeLog
+recursive-include demo *
+recursive-include doc *
+recursive-include test *
+global-exclude __pycache__ *.pyc
diff --git a/README.rst b/README.rst
index 32db38c..61a6e53 100644
--- a/README.rst
+++ b/README.rst
@@ -42,7 +42,7 @@ Code Coverage
=============
Code coverage reports can be viewed at
-https://coveralls.io/repos/bitbucket/fenics-project/ufl.
+https://coveralls.io/bitbucket/fenics-project/ufl.
.. image:: https://coveralls.io/repos/bitbucket/fenics-project/ufl/badge.svg?branch=master
:target: https://coveralls.io/bitbucket/fenics-project/ufl?branch=master
diff --git a/doc/sphinx/source/conf.py b/doc/sphinx/source/conf.py
index d662b0e..d8a68e5 100644
--- a/doc/sphinx/source/conf.py
+++ b/doc/sphinx/source/conf.py
@@ -15,6 +15,8 @@
import sys
import os
import shlex
+import pkg_resources
+import datetime
# 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
@@ -53,19 +55,11 @@ master_doc = 'index'
# General information about the project.
project = u'Unified Form Language (UFL)'
-copyright = u'2016, FEniCS Project'
+this_year = datetime.date.today().year
+copyright = u'%s, FEniCS Project' % this_year
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 = ufl_version
-# The full version, including alpha/beta/rc tags.
-release = ufl_version
+version = pkg_resources.get_distribution("ufl").version
+release = version
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
diff --git a/doc/sphinx/source/releases.rst b/doc/sphinx/source/releases.rst
index 796ed29..ee8b507 100644
--- a/doc/sphinx/source/releases.rst
+++ b/doc/sphinx/source/releases.rst
@@ -9,5 +9,7 @@ Release notes
:maxdepth: 2
releases/next
+ releases/v2017.1.0
+ releases/v2016.2.0
releases/v2016.1.0
releases/v1.6.0
diff --git a/doc/sphinx/source/releases/next.rst b/doc/sphinx/source/releases/next.rst
index a8dff28..cdcd4a3 100644
--- a/doc/sphinx/source/releases/next.rst
+++ b/doc/sphinx/source/releases/next.rst
@@ -11,26 +11,6 @@ Summary of changes
be published (and renamed) to list the most important
changes in the new release.
-- 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
-- ``OuterProductCell`` and ``OuterProductElement`` are merged into
- ``TensorProductCell`` and ``TensorProductElement`` respectively
-- Better degree estimation for quadrilaterals
-- Expansion rules for Q, DQ, RTCE, RTCF, NCE and NCF on tensor product
- cells
-- Add discontinuous Taylor elements
-- Add support for the mapping ``double covariant Piola`` in ``uflacs``
-- Add support for the mapping ``double contravariant Piola`` in ``uflacs``
-- Support for tensor-valued subelements in ``uflacs`` fixed
-- Replacing ``Discontinuous Lagrange Trace`` with ``HDiv Trace`` and removing ``TraceElement``
-- Assigning ``Discontinuous Lagrange Trace`` and ``DGT`` as aliases for ``HDiv Trace``
-
Detailed changes
================
diff --git a/doc/sphinx/source/releases/v2016.2.0.rst b/doc/sphinx/source/releases/v2016.2.0.rst
new file mode 100644
index 0000000..44c21cb
--- /dev/null
+++ b/doc/sphinx/source/releases/v2016.2.0.rst
@@ -0,0 +1,67 @@
+===========================
+Changes in version 2016.2.0
+===========================
+
+UFL 2016.2.0 was released on 2016-11-30.
+
+
+Summary of changes
+==================
+
+- 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
+- ``OuterProductCell`` and ``OuterProductElement`` are merged into
+ ``TensorProductCell`` and ``TensorProductElement`` respectively
+- Better degree estimation for quadrilaterals
+- Expansion rules for Q, DQ, RTCE, RTCF, NCE and NCF on tensor product
+ cells
+- Add discontinuous Taylor elements
+- Add support for the mapping ``double covariant Piola`` in ``uflacs``
+- Add support for the mapping ``double contravariant Piola`` in ``uflacs``
+- Support for tensor-valued subelements in ``uflacs`` fixed
+- Replacing ``Discontinuous Lagrange Trace`` with ``HDiv Trace`` and removing ``TraceElement``
+- Assigning ``Discontinuous Lagrange Trace`` and ``DGT`` as aliases for ``HDiv Trace``
+
+Detailed changes
+================
+
+- Add call operator syntax to Form to replace arguments and
+ coefficients. This makes it easier to e.g. express the norm
+ defined by a bilinear form as a functional. Example usage::
+ # Equivalent to replace(a, {u: f, v: f})
+ M = a(f, f)
+ # Equivalent to replace(a, {f:1})
+ c = a(coefficients={f:1})
+- Add call operator syntax to Form to replace arguments and coefficients::
+ a(f, g) == replace(a, {u: f, v: g})
+ a(coefficients={f:1}) == replace(a, {f:1})
+- Add @ operator to Form: form @ f == action(form, f) (python 3.5+ only)
+- Reduce noise in Mesh str such that print(form) gets more short and readable
+- Fix repeated split(function) for arbitrary nested elements
+- EnrichedElement: Remove +/* warning
+ In the distant past, A + B => MixedElement([A, B]). The change that
+ A + B => EnrichedElement([A, B]) was made in d622c74 (22 March 2010).
+ A warning was introduced in fcbc5ff (26 March 2010) that the meaning of
+ "+" had changed, and that users wanting a MixedElement should use "*"
+ instead. People have, presumably, been seeing this warning for 6 1/2
+ years by now, so it's probably safe to remove.
+- Rework TensorProductElement implementation, replaces OuterProductElement
+- Rework TensorProductCell implementation, replaces OuterProductCell
+- Remove OuterProductVectorElement and OuterProductTensorElement
+- Add FacetElement and InteriorElement
+- Add Hellan-Herrmann-Johnson element
+- Add support for double covariant and contravariant mappings in mixed elements
+- Support discontinuous Taylor elements on all simplices
+- Some more performance improvements
+- Minor bugfixes
+- Improve Python 3 support
+- More permissive in integer types accepted some places
+- Make ufl pass almost all flake8 tests
+- Add bitbucket pipelines testing
+- Improve documentation
diff --git a/doc/sphinx/source/releases/v2017.1.0.rst b/doc/sphinx/source/releases/v2017.1.0.rst
new file mode 100644
index 0000000..fae6181
--- /dev/null
+++ b/doc/sphinx/source/releases/v2017.1.0.rst
@@ -0,0 +1,19 @@
+===========================
+Changes in version 2017.1.0
+===========================
+
+UFL 2017.1.0 was released on 2017-05-09.
+
+Summary of changes
+==================
+
+- Add the ``DirectionalSobolevSpace`` subclass of ``SobolevSpace``. This
+ allows one to use spaces where elements have varying continuity in
+ different spatial directions.
+- Add ``sobolev_space`` methods for ``HDiv`` and ``HCurl`` finite
+ elements.
+- Add ``sobolev_space`` methods for ``TensorProductElement`` and
+ ``EnrichedElement``. The smallest shared Sobolev space will be
+ returned for enriched elements. For the tensor product elements, a
+ ``DirectionalSobolevSpace`` is returned depending on the order of the
+ spaces associated with the component elements.
diff --git a/setup.cfg b/setup.cfg
index dc791ed..f597cbd 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,3 +1,3 @@
[flake8]
-ignore = E501,E226,W503,E127,E123,E128,E265
+ignore = E305,E501,E226,W503,E127,E123,E128,E265
exclude = .git,__pycache__,doc/sphinx/source/conf.py,build,dist,test
diff --git a/setup.py b/setup.py
index 684278c..dd79757 100755
--- a/setup.py
+++ b/setup.py
@@ -3,10 +3,8 @@ from __future__ import print_function
from setuptools import setup
from os.path import join, split
-import re
import sys
import platform
-import codecs
module_name = "ufl"
@@ -14,10 +12,7 @@ if sys.version_info < (2, 7):
print("Python 2.7 or higher required, please upgrade.")
sys.exit(1)
-# __init__.py has UTF-8 characters. Works in Python 2 and 3.
-version = re.findall('__version__ = "(.*)"',
- codecs.open(join(module_name, '__init__.py'), 'r',
- encoding='utf-8').read())[0]
+version = "2017.1.0"
url = "https://bitbucket.org/fenics-project/%s/" % module_name
tarball = None
diff --git a/test/test_sobolevspace.py b/test/test_sobolevspace.py
index 0b32494..65c665b 100755
--- a/test/test_sobolevspace.py
+++ b/test/test_sobolevspace.py
@@ -5,10 +5,25 @@ __authors__ = "David Ham"
__date__ = "2014-03-04"
import pytest
-from ufl import FiniteElement, triangle
-from ufl.sobolevspace import SobolevSpace
+from ufl import (EnrichedElement, TensorProductElement,
+ FiniteElement, triangle, interval,
+ quadrilateral, HDiv, HCurl)
+from ufl.sobolevspace import SobolevSpace, DirectionalSobolevSpace
from ufl import H2, H1, HDiv, HCurl, L2
+
+# Construct directional Sobolev spaces, with varying smoothness in
+# spatial coordinates
+H0dx0dy = DirectionalSobolevSpace((0, 0))
+H1dx1dy = DirectionalSobolevSpace((1, 1))
+H2dx2dy = DirectionalSobolevSpace((2, 2))
+H1dx = DirectionalSobolevSpace((1, 0))
+H1dy = DirectionalSobolevSpace((0, 1))
+H000 = DirectionalSobolevSpace((0, 0, 0))
+H1dz = DirectionalSobolevSpace((0, 0, 1))
+H1dh = DirectionalSobolevSpace((1, 1, 0))
+H2dhH1dz = DirectionalSobolevSpace((2, 2, 1))
+
# TODO: Add construction of all elements with periodic table notation here.
@@ -17,6 +32,25 @@ def test_inclusion():
assert not H2 > H1 # Not included
assert HDiv <= HDiv # Reflexivity
assert H2 < L2 # Transitivity
+ assert H1 > H2
+ assert L2 > H1
+
+
+def test_directional_space_relations():
+ assert H0dx0dy == L2
+ assert H1dx1dy == H1
+ assert H2dx2dy == H2
+ assert H1dx1dy <= HDiv
+ assert H1dx1dy <= HCurl
+ assert H2dx2dy <= H1dx1dy
+ assert H2dhH1dz < H1
+ assert H1dz > H2dhH1dz
+ assert H1dh < L2
+ assert H1dz < L2
+ assert L2 > H1dx
+ assert L2 > H1dy
+ assert not H1dh <= HDiv
+ assert not H1dh <= HCurl
def test_repr():
@@ -34,13 +68,24 @@ def test_contains_l2():
FiniteElement("DG", triangle, 1),
FiniteElement("DG", triangle, 2),
FiniteElement("CR", triangle, 1),
+ # Tensor product elements:
+ TensorProductElement(FiniteElement("DG", interval, 1),
+ FiniteElement("DG", interval, 1)),
+ TensorProductElement(FiniteElement("DG", interval, 1),
+ FiniteElement("CG", interval, 2)),
+ # Enriched element:
+ EnrichedElement(FiniteElement("DG", triangle, 1),
+ FiniteElement("B", triangle, 3))
]
for l2_element in l2_elements:
assert l2_element in L2
+ assert l2_element in H0dx0dy
assert l2_element not in H1
+ assert l2_element not in H1dx1dy
assert l2_element not in HCurl
assert l2_element not in HDiv
assert l2_element not in H2
+ assert l2_element not in H2dx2dy
def test_contains_h1():
@@ -52,13 +97,24 @@ def test_contains_h1():
FiniteElement("AW", triangle),
FiniteElement("HER", triangle),
FiniteElement("MTW", triangle),
+ # Tensor product elements:
+ TensorProductElement(FiniteElement("CG", interval, 1),
+ FiniteElement("CG", interval, 1)),
+ TensorProductElement(FiniteElement("CG", interval, 2),
+ FiniteElement("CG", interval, 2)),
+ # Enriched elements:
+ EnrichedElement(FiniteElement("CG", triangle, 2),
+ FiniteElement("B", triangle, 3))
]
for h1_element in h1_elements:
assert h1_element in H1
+ assert h1_element in H1dx1dy
assert h1_element in HDiv
assert h1_element in HCurl
assert h1_element in L2
+ assert h1_element in H0dx0dy
assert h1_element not in H2
+ assert h1_element not in H2dx2dy
def test_contains_h2():
@@ -68,10 +124,13 @@ def test_contains_h2():
]
for h2_element in h2_elements:
assert h2_element in H2
+ assert h2_element in H2dx2dy
assert h2_element in H1
+ assert h2_element in H1dx1dy
assert h2_element in HDiv
assert h2_element in HCurl
assert h2_element in L2
+ assert h2_element in H0dx0dy
def test_contains_hdiv():
@@ -79,23 +138,119 @@ def test_contains_hdiv():
FiniteElement("RT", triangle, 1),
FiniteElement("BDM", triangle, 1),
FiniteElement("BDFM", triangle, 2),
+ # HDiv elements:
+ HDiv(TensorProductElement(FiniteElement("DG", triangle, 1),
+ FiniteElement("CG", interval, 2))),
+ HDiv(TensorProductElement(FiniteElement("RT", triangle, 1),
+ FiniteElement("DG", interval, 1))),
+ HDiv(TensorProductElement(FiniteElement("N1curl", triangle, 1),
+ FiniteElement("DG", interval, 1)))
]
for hdiv_element in hdiv_elements:
assert hdiv_element in HDiv
assert hdiv_element in L2
+ assert hdiv_element in H0dx0dy
assert hdiv_element not in H1
+ assert hdiv_element not in H1dx1dy
assert hdiv_element not in HCurl
assert hdiv_element not in H2
+ assert hdiv_element not in H2dx2dy
def test_contains_hcurl():
hcurl_elements = [
FiniteElement("N1curl", triangle, 1),
FiniteElement("N2curl", triangle, 1),
+ # HCurl elements:
+ HCurl(TensorProductElement(FiniteElement("CG", triangle, 1),
+ FiniteElement("DG", interval, 1))),
+ HCurl(TensorProductElement(FiniteElement("N1curl", triangle, 1),
+ FiniteElement("CG", interval, 1))),
+ HCurl(TensorProductElement(FiniteElement("RT", triangle, 1),
+ FiniteElement("CG", interval, 1)))
+ ]
+ for hcurl_element in hcurl_elements:
+ assert hcurl_element in HCurl
+ assert hcurl_element in L2
+ assert hcurl_element in H0dx0dy
+ assert hcurl_element not in H1
+ assert hcurl_element not in H1dx1dy
+ assert hcurl_element not in HDiv
+ assert hcurl_element not in H2
+ assert hcurl_element not in H2dx2dy
+
+
+def test_enriched_elements_hdiv():
+ A = FiniteElement("CG", interval, 1)
+ B = FiniteElement("DG", interval, 0)
+ AxB = TensorProductElement(A, B)
+ BxA = TensorProductElement(B, A)
+ C = FiniteElement("RTCF", quadrilateral, 1)
+ D = FiniteElement("DQ", quadrilateral, 0)
+ Q1 = TensorProductElement(C, B)
+ Q2 = TensorProductElement(D, A)
+ hdiv_elements = [
+ EnrichedElement(HDiv(AxB), HDiv(BxA)),
+ EnrichedElement(HDiv(Q1), HDiv(Q2))
+ ]
+ for hdiv_element in hdiv_elements:
+ assert hdiv_element in HDiv
+ assert hdiv_element in L2
+ assert hdiv_element in H0dx0dy
+ assert hdiv_element not in H1
+ assert hdiv_element not in H1dx1dy
+ assert hdiv_element not in HCurl
+ assert hdiv_element not in H2
+ assert hdiv_element not in H2dx2dy
+
+
+def test_enriched_elements_hcurl():
+ A = FiniteElement("CG", interval, 1)
+ B = FiniteElement("DG", interval, 0)
+ AxB = TensorProductElement(A, B)
+ BxA = TensorProductElement(B, A)
+ C = FiniteElement("RTCE", quadrilateral, 1)
+ D = FiniteElement("DQ", quadrilateral, 0)
+ Q1 = TensorProductElement(C, B)
+ Q2 = TensorProductElement(D, A)
+ hcurl_elements = [
+ EnrichedElement(HCurl(AxB), HCurl(BxA)),
+ EnrichedElement(HCurl(Q1), HCurl(Q2))
]
for hcurl_element in hcurl_elements:
assert hcurl_element in HCurl
assert hcurl_element in L2
+ assert hcurl_element in H0dx0dy
assert hcurl_element not in H1
+ assert hcurl_element not in H1dx1dy
assert hcurl_element not in HDiv
assert hcurl_element not in H2
+ assert hcurl_element not in H2dx2dy
+
+
+def test_varying_continuity_elements():
+ P1DG_t = FiniteElement("DG", triangle, 1)
+ P1DG_i = FiniteElement("DG", interval, 1)
+ P1 = FiniteElement("CG", interval, 1)
+ P2 = FiniteElement("CG", interval, 2)
+ P3 = FiniteElement("CG", interval, 3)
+ RT1 = FiniteElement("RT", triangle, 1)
+ ARG = FiniteElement("ARG", triangle, 1)
+
+ # Tensor product elements
+ P1DGP2 = TensorProductElement(P1DG_t, P2)
+ P1P1DG = TensorProductElement(P1, P1DG_i)
+ P1DGP1 = TensorProductElement(P1DG_i, P1)
+ RT1DG1 = TensorProductElement(RT1, P1DG_i)
+ P2P3 = TensorProductElement(P2, P3)
+ ARGP3 = TensorProductElement(ARG, P3)
+
+ assert P1DGP2 in H1dz and P1DGP2 in L2
+ assert P1DGP2 not in H1dh
+ assert P1DGP1 in H1dy and P1DGP2 in L2
+ assert P1P1DG in H1dx and P1P1DG in L2
+ assert P1P1DG not in H1dx1dy
+ assert RT1DG1 in H000 and RT1DG1 in L2
+ assert P2P3 in H1dx1dy and P2P3 in H1
+ assert ARG in H2dx2dy
+ assert ARGP3 in H2dhH1dz
diff --git a/test/test_tensoralgebra.py b/test/test_tensoralgebra.py
index a25a059..dc2b002 100755
--- a/test/test_tensoralgebra.py
+++ b/test/test_tensoralgebra.py
@@ -157,19 +157,21 @@ def test_tr(self, A):
self.assertEqualValues(C, D)
-def xtest_det(self, A):
+def test_det(self, A):
+ dims = (0, 1)
C = det(A)
- D = zero() # FIXME: Add expected value here
+ D = sum((-A[i, 0]*A[0, i] if i !=0 else A[i-1, -1]*A[i, 0]) for i in dims)
self.assertEqualValues(C, D)
-def xtest_cofac(self, A):
+def test_cofac(self, A):
C = cofac(A)
- D = 0*C # FIXME: Add expected value here
+ D = as_matrix([[(-A[i,j] if i != j else A[i,j]) for j in (-1,0)] for i in (-1,0)])
self.assertEqualValues(C, D)
def xtest_inv(self, A):
C = inv(A)
- D = 0*C # FIXME: Add expected value here
+ detA = sum((-A[i, 0]*A[0, i] if i !=0 else A[i-1, -1]*A[i, 0]) for i in (0,1))
+ D = as_matrix([[(-A[i,j] if i != j else A[i,j]) for j in (-1,0)] for i in (-1,0)]) / detA # FIXME: Test fails probably due to integer division
self.assertEqualValues(C, D)
diff --git a/ufl/__init__.py b/ufl/__init__.py
index e474abc..674c14c 100644
--- a/ufl/__init__.py
+++ b/ufl/__init__.py
@@ -77,6 +77,7 @@ A very brief overview of the language contents follows:
- VectorElement
- TensorElement
- EnrichedElement
+ - NodalEnrichedElement
- RestrictedElement
- TensorProductElement
- HDivElement
@@ -241,7 +242,9 @@ A very brief overview of the language contents follows:
# Modified by Lawrence Mitchell, 2014
# Modified by Massimiliano Leoni, 2016
-__version__ = "2016.2.0"
+import pkg_resources
+
+__version__ = pkg_resources.get_distribution("ufl").version
########## README
# Imports here should be what the user sees when doing "from ufl import *",
@@ -276,7 +279,7 @@ from ufl.sobolevspace import L2, H1, H2, HDiv, HCurl
# Finite elements classes
from ufl.finiteelement import FiniteElementBase, FiniteElement, \
MixedElement, VectorElement, TensorElement, EnrichedElement, \
- RestrictedElement, TensorProductElement, \
+ NodalEnrichedElement, RestrictedElement, TensorProductElement, \
HDivElement, HCurlElement, BrokenElement, \
FacetElement, InteriorElement
@@ -376,7 +379,7 @@ __all__ = as_native_strings([
'Jacobian', 'JacobianDeterminant', 'JacobianInverse',
'FiniteElementBase', 'FiniteElement',
'MixedElement', 'VectorElement', 'TensorElement', 'EnrichedElement',
- 'RestrictedElement', 'TensorProductElement',
+ 'NodalEnrichedElement', 'RestrictedElement', 'TensorProductElement',
'HDivElement', 'HCurlElement',
'BrokenElement', 'FacetElement', 'InteriorElement',
'register_element', 'show_elements',
diff --git a/ufl/algorithms/compute_form_data.py b/ufl/algorithms/compute_form_data.py
index 3388bbf..f700bf7 100644
--- a/ufl/algorithms/compute_form_data.py
+++ b/ufl/algorithms/compute_form_data.py
@@ -30,7 +30,6 @@ from ufl.algorithms.analysis import extract_coefficients, extract_sub_elements,
from ufl.algorithms.formdata import FormData
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
@@ -103,9 +102,7 @@ def _compute_element_mapping(form):
# Reconstruct element and add to map
if reconstruct:
- element_mapping[element] = reconstruct_element(element,
- element.family(),
- cell, degree)
+ element_mapping[element] = element.reconstruct(cell=cell, degree=degree)
else:
element_mapping[element] = element
diff --git a/ufl/algorithms/elementtransformations.py b/ufl/algorithms/elementtransformations.py
index 0c364c9..9e68c70 100644
--- a/ufl/algorithms/elementtransformations.py
+++ b/ufl/algorithms/elementtransformations.py
@@ -23,7 +23,7 @@
from ufl.utils.py23 import as_native_strings
from ufl.log import error
from ufl.finiteelement import FiniteElement, VectorElement, TensorElement, \
- MixedElement, EnrichedElement
+ MixedElement, EnrichedElement, NodalEnrichedElement
__all__ = as_native_strings(['increase_order', 'tear'])
@@ -38,7 +38,7 @@ def change_regularity(element, family):
For a given finite element, return the corresponding space
specified by 'family'.
"""
- return _change_family(element, family)
+ return element.reconstruct(family=family)
def tear(element):
@@ -46,42 +46,18 @@ def tear(element):
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 %s" % repr(element))
-
-
def _increase_degree(element, degree_rise):
if isinstance(element, (FiniteElement, VectorElement, TensorElement)):
- return reconstruct_element(element, element.family(), element.cell(),
- element.degree() + degree_rise)
+ return element.reconstruct(degree=(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 %s" % repr(element))
-
-
-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()])
+ elif isinstance(element, NodalEnrichedElement):
+ return NodalEnrichedElement([_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 %s" % repr(element))
diff --git a/ufl/constantvalue.py b/ufl/constantvalue.py
index e2b0e0e..da5f1fe 100644
--- a/ufl/constantvalue.py
+++ b/ufl/constantvalue.py
@@ -42,10 +42,10 @@ precision = None
def format_float(x):
"Format float value based on global UFL precision."
- if precision is None:
- return "%s" % repr(x)
+ if precision:
+ return "{:.{prec}}".format(float(x), prec=precision)
else:
- return ("%%.%dg" % precision) % x
+ return "{}".format(float(x))
# --- Base classes for constant types ---
diff --git a/ufl/exproperators.py b/ufl/exproperators.py
index 77920ae..3a43561 100644
--- a/ufl/exproperators.py
+++ b/ufl/exproperators.py
@@ -103,6 +103,8 @@ def _as_tensor(self, 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
@@ -178,6 +180,7 @@ def _mult(a, b):
return p
+
# --- Extend Expr with algebraic operators ---
_valid_types = (Expr, numbers.Real, numbers.Integral)
@@ -188,6 +191,8 @@ def _mul(self, o):
return NotImplemented
o = as_ufl(o)
return _mult(self, o)
+
+
Expr.__mul__ = _mul
@@ -196,6 +201,8 @@ def _rmul(self, o):
return NotImplemented
o = as_ufl(o)
return _mult(o, self)
+
+
Expr.__rmul__ = _rmul
@@ -203,6 +210,8 @@ def _add(self, o):
if not isinstance(o, _valid_types):
return NotImplemented
return Sum(self, o)
+
+
Expr.__add__ = _add
@@ -214,6 +223,8 @@ def _radd(self, o):
# needed for sum([a,b])
return self
return Sum(o, self)
+
+
Expr.__radd__ = _radd
@@ -221,6 +232,8 @@ def _sub(self, o):
if not isinstance(o, _valid_types):
return NotImplemented
return Sum(self, -o)
+
+
Expr.__sub__ = _sub
@@ -228,6 +241,8 @@ def _rsub(self, o):
if not isinstance(o, _valid_types):
return NotImplemented
return Sum(o, -self)
+
+
Expr.__rsub__ = _rsub
@@ -240,6 +255,8 @@ def _div(self, o):
d = Division(self[ii], o)
return as_tensor(d, ii)
return Division(self, o)
+
+
Expr.__div__ = _div
Expr.__truediv__ = _div
@@ -248,6 +265,8 @@ def _rdiv(self, o):
if not isinstance(o, _valid_types):
return NotImplemented
return Division(o, self)
+
+
Expr.__rdiv__ = _rdiv
Expr.__rtruediv__ = _rdiv
@@ -258,6 +277,8 @@ def _pow(self, o):
if o == 2 and self.ufl_shape:
return Inner(self, self)
return Power(self, o)
+
+
Expr.__pow__ = _pow
@@ -265,17 +286,23 @@ def _rpow(self, o):
if not isinstance(o, _valid_types):
return NotImplemented
return Power(o, self)
+
+
Expr.__rpow__ = _rpow
# TODO: Add Negated class for this? Might simplify reductions in Add.
def _neg(self):
return -1*self
+
+
Expr.__neg__ = _neg
def _abs(self):
return Abs(self)
+
+
Expr.__abs__ = _abs
@@ -312,6 +339,8 @@ def _call(self, arg, mapping=None, component=()):
return _restrict(self, arg)
else:
return _eval(self, arg, mapping, component)
+
+
Expr.__call__ = _call
@@ -321,6 +350,8 @@ def _transpose(self):
"""Transpose a rank-2 tensor expression. For more general transpose
operations of higher order tensor expressions, use indexing and Tensor."""
return Transposed(self)
+
+
Expr.T = property(_transpose)
@@ -461,6 +492,7 @@ def _getitem(self, component):
return a
+
Expr.__getitem__ = _getitem
@@ -479,4 +511,5 @@ def _dx(self, *ii):
# Take all components, applying repeated index sums in the [] operation
return d.__getitem__((Ellipsis,) + ii)
+
Expr.dx = _dx
diff --git a/ufl/finiteelement/__init__.py b/ufl/finiteelement/__init__.py
index 3499046..2201bd9 100644
--- a/ufl/finiteelement/__init__.py
+++ b/ufl/finiteelement/__init__.py
@@ -30,6 +30,7 @@ from ufl.finiteelement.mixedelement import MixedElement
from ufl.finiteelement.mixedelement import VectorElement
from ufl.finiteelement.mixedelement import TensorElement
from ufl.finiteelement.enrichedelement import EnrichedElement
+from ufl.finiteelement.enrichedelement import NodalEnrichedElement
from ufl.finiteelement.restrictedelement import RestrictedElement
from ufl.finiteelement.tensorproductelement import TensorProductElement
from ufl.finiteelement.hdivcurl import HDivElement, HCurlElement
@@ -45,6 +46,7 @@ __all_classes__ = [
"VectorElement",
"TensorElement",
"EnrichedElement",
+ "NodalEnrichedElement",
"RestrictedElement",
"TensorProductElement",
"HDivElement",
diff --git a/ufl/finiteelement/brokenelement.py b/ufl/finiteelement/brokenelement.py
index 7bc2e92..8263fc2 100644
--- a/ufl/finiteelement/brokenelement.py
+++ b/ufl/finiteelement/brokenelement.py
@@ -42,6 +42,9 @@ class BrokenElement(FiniteElementBase):
def mapping(self):
return self._element.mapping()
+ def reconstruct(self, **kwargs):
+ return BrokenElement(self._element.reconstruct(**kwargs))
+
def __str__(self):
return "BrokenElement(%s)" % str(self._element)
diff --git a/ufl/finiteelement/elementlist.py b/ufl/finiteelement/elementlist.py
index 07cb7f2..e776041 100644
--- a/ufl/finiteelement/elementlist.py
+++ b/ufl/finiteelement/elementlist.py
@@ -83,6 +83,7 @@ def show_elements():
print("Defined on cellnames: %s" % (cellnames,))
print()
+
# FIXME: Consider cleanup of element names. Use notation from periodic
# table as the main, keep old names as compatibility aliases.
diff --git a/ufl/finiteelement/enrichedelement.py b/ufl/finiteelement/enrichedelement.py
index 6a581bf..cb702ce 100644
--- a/ufl/finiteelement/enrichedelement.py
+++ b/ufl/finiteelement/enrichedelement.py
@@ -30,8 +30,8 @@ from ufl.finiteelement.finiteelementbase import FiniteElementBase
# @six.python_2_unicode_compatible
-class EnrichedElement(FiniteElementBase):
- """The vector sum of two finite element spaces:
+class EnrichedElementBase(FiniteElementBase):
+ """The vector sum of several finite element spaces:
.. math:: \\textrm{EnrichedElement}(V, Q) = \\{v + q | v \\in V, q \\in Q\\}.
"""
@@ -68,23 +68,58 @@ class EnrichedElement(FiniteElementBase):
# if not all(e.mapping() == mapping for e in elements[1:]):
# error("Element mapping mismatch.")
+ # Get name of subclass: EnrichedElement or NodalEnrichedElement
+ class_name = as_native_str(self.__class__.__name__)
+
# Initialize element data
- FiniteElementBase.__init__(self, "EnrichedElement", cell, degree,
+ FiniteElementBase.__init__(self, class_name, cell, degree,
quad_scheme, value_shape,
reference_value_shape)
# Cache repr string
- self._repr = as_native_str("EnrichedElement(%s)" %
- ", ".join(repr(e) for e in self._elements))
+ self._repr = as_native_str("%s(%s)" %
+ (class_name, ", ".join(repr(e) for e in self._elements)))
+
+ def mapping(self):
+ return self._elements[0].mapping()
+
+ def sobolev_space(self):
+ "Return the underlying Sobolev space."
+ elements = [e for e in self._elements]
+ if all(e.sobolev_space() == elements[0].sobolev_space()
+ for e in elements):
+ return elements[0].sobolev_space()
+ else:
+ # Find smallest shared Sobolev space over all sub elements
+ spaces = [e.sobolev_space() for e in elements]
+ superspaces = [{s} | set(s.parents) for s in spaces]
+ intersect = set.intersection(*superspaces)
+ for s in intersect.copy():
+ for parent in s.parents:
+ intersect.discard(parent)
+
+ sobolev_space, = intersect
+ return sobolev_space
+
+ def reconstruct(self, **kwargs):
+ return type(self)(*[e.reconstruct(**kwargs) for e in self._elements])
+
+class EnrichedElement(EnrichedElementBase):
+ """The vector sum of several finite element spaces:
+
+ .. math:: \\textrm{EnrichedElement}(V, Q) = \\{v + q | v \\in V, q \\in Q\\}.
+
+ Dual basis is a concatenation of subelements dual bases;
+ primal basis is a concatenation of subelements primal bases;
+ resulting element is not nodal even when subelements are.
+ Structured basis may be exploited in form compilers.
+ """
def is_cellwise_constant(self):
"""Return whether the basis functions of this
element is spatially constant over each cell."""
return all(e.is_cellwise_constant() for e in self._elements)
- def mapping(self):
- return self._elements[0].mapping()
-
def __str__(self):
"Format as string for pretty printing."
return "<%s>" % " + ".join(str(e) for e in self._elements)
@@ -92,3 +127,26 @@ class EnrichedElement(FiniteElementBase):
def shortstr(self):
"Format as string for pretty printing."
return "<%s>" % " + ".join(e.shortstr() for e in self._elements)
+
+
+class NodalEnrichedElement(EnrichedElementBase):
+ """The vector sum of several finite element spaces:
+
+ .. math:: \\textrm{EnrichedElement}(V, Q) = \\{v + q | v \\in V, q \\in Q\\}.
+
+ Primal basis is reorthogonalized to dual basis which is
+ a concatenation of subelements dual bases; resulting
+ element is nodal.
+ """
+ def is_cellwise_constant(self):
+ """Return whether the basis functions of this
+ element is spatially constant over each cell."""
+ return False
+
+ def __str__(self):
+ "Format as string for pretty printing."
+ return "<Nodal enriched element(%s)>" % ", ".join(str(e) for e in self._elements)
+
+ def shortstr(self):
+ "Format as string for pretty printing."
+ return "NodalEnriched(%s)" % ", ".join(e.shortstr() for e in self._elements)
diff --git a/ufl/finiteelement/facetelement.py b/ufl/finiteelement/facetelement.py
index 806917a..e611d76 100644
--- a/ufl/finiteelement/facetelement.py
+++ b/ufl/finiteelement/facetelement.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright (C) 2014 Andrew T. T. McRae
+# Copyright (C) 2017 Miklós Homolya
#
# This file is part of UFL.
#
@@ -15,37 +15,13 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with UFL. If not, see <http://www.gnu.org/licenses/>.
-#
-# Modified by Massimiliano Leoni, 2016
-
-# import six
-from ufl.utils.py23 import as_native_str
-from ufl.finiteelement.finiteelementbase import FiniteElementBase
-
-
-# @six.python_2_unicode_compatible
-class FacetElement(FiniteElementBase):
- """A version of an existing Finite Element space in which all dofs
- associated with the interior have been discarded."""
- def __init__(self, element):
- self._element = element
- self._repr = as_native_str("FacetElement(%s)" % repr(element))
-
- family = "FacetElement"
- 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, cell, degree,
- quad_scheme, value_shape, reference_value_shape)
- def mapping(self):
- return self._element.mapping()
+from ufl.finiteelement.restrictedelement import RestrictedElement
+from ufl.log import deprecate
- def __str__(self):
- return "FacetElement(%s)" % str(self._element)
- def shortstr(self):
- "Format as string for pretty printing."
- return "FacetElement(%s)" % str(self._element.shortstr())
+def FacetElement(element):
+ """Constructs the restriction of a finite element to the facets of the
+ cell."""
+ deprecate('FacetElement(element) is deprecated, please use element["facet"] instead.')
+ return RestrictedElement(element, restriction_domain="facet")
diff --git a/ufl/finiteelement/finiteelement.py b/ufl/finiteelement/finiteelement.py
index e04c54a..6cbfb81 100644
--- a/ufl/finiteelement/finiteelement.py
+++ b/ufl/finiteelement/finiteelement.py
@@ -43,6 +43,7 @@ class FiniteElement(FiniteElementBase):
"_short_name",
"_sobolev_space",
"_mapping",
+ "_variant",
))
def __new__(cls,
@@ -50,29 +51,31 @@ class FiniteElement(FiniteElementBase):
cell=None,
degree=None,
form_degree=None,
- quad_scheme=None):
+ quad_scheme=None,
+ variant=None):
"""Intercepts construction to expand CG, DG, RTCE and RTCF
spaces on TensorProductCells."""
if cell is not None:
cell = as_cell(cell)
if isinstance(cell, TensorProductCell):
- family, short_name, degree, value_shape, reference_value_shape, sobolev_space, mapping = \
- canonical_element_description(family, cell, degree, form_degree)
-
# Delay import to avoid circular dependency at module load time
from ufl.finiteelement.tensorproductelement import TensorProductElement
from ufl.finiteelement.enrichedelement import EnrichedElement
from ufl.finiteelement.hdivcurl import HDivElement as HDiv, HCurlElement as HCurl
+ family, short_name, degree, value_shape, reference_value_shape, sobolev_space, mapping = \
+ canonical_element_description(family, cell, degree, form_degree)
+
if family in ["RTCF", "RTCE"]:
- if cell._cells[0].cellname() != "interval":
+ cell_h, cell_v = cell.sub_cells()
+ if cell_h.cellname() != "interval":
error("%s is available on TensorProductCell(interval, interval) only." % family)
- if cell._cells[1].cellname() != "interval":
+ if cell_v.cellname() != "interval":
error("%s is available on TensorProductCell(interval, interval) only." % family)
- C_elt = FiniteElement("CG", "interval", degree, 0, quad_scheme)
- D_elt = FiniteElement("DG", "interval", degree - 1, 1, quad_scheme)
+ C_elt = FiniteElement("CG", "interval", degree, variant=variant)
+ D_elt = FiniteElement("DG", "interval", degree - 1, variant=variant)
CxD_elt = TensorProductElement(C_elt, D_elt, cell=cell)
DxC_elt = TensorProductElement(D_elt, C_elt, cell=cell)
@@ -83,48 +86,48 @@ class FiniteElement(FiniteElementBase):
return EnrichedElement(HCurl(CxD_elt), HCurl(DxC_elt))
elif family == "NCF":
- if cell._cells[0].cellname() != "quadrilateral":
+ cell_h, cell_v = cell.sub_cells()
+ if cell_h.cellname() != "quadrilateral":
error("%s is available on TensorProductCell(quadrilateral, interval) only." % family)
- if cell._cells[1].cellname() != "interval":
+ if cell_v.cellname() != "interval":
error("%s is available on TensorProductCell(quadrilateral, interval) only." % family)
- Qc_elt = FiniteElement("RTCF", "quadrilateral", degree, 1, quad_scheme)
- Qd_elt = FiniteElement("DQ", "quadrilateral", degree - 1, 2, quad_scheme)
+ Qc_elt = FiniteElement("RTCF", "quadrilateral", degree, variant=variant)
+ Qd_elt = FiniteElement("DQ", "quadrilateral", degree - 1, variant=variant)
- Id_elt = FiniteElement("DG", "interval", degree - 1, 1, quad_scheme)
- Ic_elt = FiniteElement("CG", "interval", degree, 0, quad_scheme)
+ Id_elt = FiniteElement("DG", "interval", degree - 1, variant=variant)
+ Ic_elt = FiniteElement("CG", "interval", degree, variant=variant)
return EnrichedElement(HDiv(TensorProductElement(Qc_elt, Id_elt, cell=cell)),
HDiv(TensorProductElement(Qd_elt, Ic_elt, cell=cell)))
elif family == "NCE":
- if cell._cells[0].cellname() != "quadrilateral":
+ cell_h, cell_v = cell.sub_cells()
+ if cell_h.cellname() != "quadrilateral":
error("%s is available on TensorProductCell(quadrilateral, interval) only." % family)
- if cell._cells[1].cellname() != "interval":
+ if cell_v.cellname() != "interval":
error("%s is available on TensorProductCell(quadrilateral, interval) only." % family)
- Qc_elt = FiniteElement("Q", "quadrilateral", degree, 0, quad_scheme)
- Qd_elt = FiniteElement("RTCE", "quadrilateral", degree, 1, quad_scheme)
+ Qc_elt = FiniteElement("Q", "quadrilateral", degree, variant=variant)
+ Qd_elt = FiniteElement("RTCE", "quadrilateral", degree, variant=variant)
- Id_elt = FiniteElement("DG", "interval", degree - 1, 1, quad_scheme)
- Ic_elt = FiniteElement("CG", "interval", degree, 0, quad_scheme)
+ Id_elt = FiniteElement("DG", "interval", degree - 1, variant=variant)
+ Ic_elt = FiniteElement("CG", "interval", degree, variant=variant)
return EnrichedElement(HCurl(TensorProductElement(Qc_elt, Id_elt, cell=cell)),
HCurl(TensorProductElement(Qd_elt, Ic_elt, cell=cell)))
elif family == "Q":
- return TensorProductElement(FiniteElement("CG", cell._cells[0], degree, 0, quad_scheme),
- FiniteElement("CG", cell._cells[1], degree, 0, quad_scheme),
+ return TensorProductElement(*[FiniteElement("CG", c, degree, variant=variant)
+ for c in cell.sub_cells()],
cell=cell)
elif family == "DQ":
- family_A = "DG" if cell._cells[0].cellname() in simplices else "DQ"
- family_B = "DG" if cell._cells[1].cellname() in simplices else "DQ"
- elem_A = FiniteElement(family_A, cell._cells[0], degree,
- cell._cells[0].topological_dimension(), quad_scheme)
- elem_B = FiniteElement(family_B, cell._cells[1], degree,
- cell._cells[1].topological_dimension(), quad_scheme)
- return TensorProductElement(elem_A, elem_B, cell=cell)
+ def dq_family(cell):
+ return "DG" if cell.cellname() in simplices else "DQ"
+ return TensorProductElement(*[FiniteElement(dq_family(c), c, degree, variant=variant)
+ for c in cell.sub_cells()],
+ cell=cell)
return super(FiniteElement, cls).__new__(cls)
@@ -133,7 +136,8 @@ class FiniteElement(FiniteElementBase):
cell=None,
degree=None,
form_degree=None,
- quad_scheme=None):
+ quad_scheme=None,
+ variant=None):
"""Create finite element.
*Arguments*
@@ -148,6 +152,8 @@ class FiniteElement(FiniteElementBase):
viewed as k-form)
quad_scheme
The quadrature scheme (optional)
+ variant
+ Hint for the local basis function variant (optional)
"""
# Note: Unfortunately, dolfin sometimes passes None for
# cell. Until this is fixed, allow it:
@@ -161,6 +167,7 @@ class FiniteElement(FiniteElementBase):
self._sobolev_space = sobolev_space
self._mapping = mapping
self._short_name = short_name
+ self._variant = variant
# Finite elements on quadrilaterals have an IrreducibleInt as degree
if cell is not None:
@@ -168,6 +175,10 @@ class FiniteElement(FiniteElementBase):
from ufl.algorithms.estimate_degrees import IrreducibleInt
degree = IrreducibleInt(degree)
+ # Type check variant
+ if variant is not None and not isinstance(variant, str):
+ raise ValueError("Illegal variant: must be string or None")
+
# Initialize element data
FiniteElementBase.__init__(self, family, cell, degree, quad_scheme,
value_shape, reference_value_shape)
@@ -178,8 +189,13 @@ class FiniteElement(FiniteElementBase):
quad_str = ""
else:
quad_str = ", quad_scheme=%s" % repr(qs)
- self._repr = as_native_str("FiniteElement(%s, %s, %s%s)" % (
- repr(self.family()), repr(self.cell()), repr(self.degree()), quad_str))
+ v = self.variant()
+ if v is None:
+ var_str = ""
+ else:
+ var_str = ", variant=%s" % repr(qs)
+ self._repr = as_native_str("FiniteElement(%s, %s, %s%s%s)" % (
+ repr(self.family()), repr(self.cell()), repr(self.degree()), quad_str, var_str))
assert '"' not in self._repr
def mapping(self):
@@ -189,17 +205,33 @@ class FiniteElement(FiniteElementBase):
"Return the underlying Sobolev space."
return self._sobolev_space
+ def variant(self):
+ return self._variant
+
+ def reconstruct(self, family=None, cell=None, degree=None):
+ """Construct a new FiniteElement object with some properties
+ replaced with new values."""
+ if family is None:
+ family = self.family()
+ if cell is None:
+ cell = self.cell()
+ if degree is None:
+ degree = self.degree()
+ return FiniteElement(family, cell, degree, quad_scheme=self.quadrature_scheme(), variant=self.variant())
+
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.cell())
+ v = self.variant()
+ v = "" if v is None else "(%s)" % v
+ return "<%s%s%s%s on a %s>" % (self._short_name, istr(self.degree()),
+ qs, v, self.cell())
def shortstr(self):
"Format as string for pretty printing."
- return "%s%s(%s)" % (self._short_name, istr(self.degree()),
- istr(self.quadrature_scheme()))
+ return "%s%s(%s,%s)" % (self._short_name, istr(self.degree()),
+ istr(self.quadrature_scheme()), istr(self.variant()))
def __getnewargs__(self):
"""Return the arguments which pickle needs to recreate the object."""
@@ -207,4 +239,5 @@ class FiniteElement(FiniteElementBase):
self.cell(),
self.degree(),
None,
- self.quadrature_scheme())
+ self.quadrature_scheme(),
+ self.variant())
diff --git a/ufl/finiteelement/finiteelementbase.py b/ufl/finiteelement/finiteelementbase.py
index 1e30297..0b3141c 100644
--- a/ufl/finiteelement/finiteelementbase.py
+++ b/ufl/finiteelement/finiteelementbase.py
@@ -38,7 +38,6 @@ class FiniteElementBase(object):
"_family",
"_cell",
"_degree",
- "_form_degree",
"_quad_scheme",
"_value_shape",
"_reference_value_shape",
diff --git a/ufl/finiteelement/hdivcurl.py b/ufl/finiteelement/hdivcurl.py
index 431e4b1..584f689 100644
--- a/ufl/finiteelement/hdivcurl.py
+++ b/ufl/finiteelement/hdivcurl.py
@@ -22,6 +22,7 @@
from ufl.utils.py23 import as_native_str
from ufl.utils.py23 import as_native_strings
from ufl.finiteelement.finiteelementbase import FiniteElementBase
+from ufl.sobolevspace import HDiv, HCurl
# @six.python_2_unicode_compatible
@@ -48,6 +49,13 @@ class HDivElement(FiniteElementBase):
def mapping(self):
return "contravariant Piola"
+ def sobolev_space(self):
+ "Return the underlying Sobolev space."
+ return HDiv
+
+ def reconstruct(self, **kwargs):
+ return HDivElement(self._element.reconstruct(**kwargs))
+
def __str__(self):
return "HDivElement(%s)" % str(self._element)
@@ -81,6 +89,13 @@ class HCurlElement(FiniteElementBase):
def mapping(self):
return "covariant Piola"
+ def sobolev_space(self):
+ "Return the underlying Sobolev space."
+ return HCurl
+
+ def reconstruct(self, **kwargs):
+ return HCurlElement(self._element.reconstruct(**kwargs))
+
def __str__(self):
return "HCurlElement(%s)" % str(self._element)
diff --git a/ufl/finiteelement/interiorelement.py b/ufl/finiteelement/interiorelement.py
index 30092bd..e1762d7 100644
--- a/ufl/finiteelement/interiorelement.py
+++ b/ufl/finiteelement/interiorelement.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright (C) 2014 Andrew T. T. McRae
+# Copyright (C) 2017 Miklós Homolya
#
# This file is part of UFL.
#
@@ -15,37 +15,13 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with UFL. If not, see <http://www.gnu.org/licenses/>.
-#
-# Modified by Massimiliano Leoni, 2016
-
-# import six
-from ufl.utils.py23 import as_native_str
-from ufl.finiteelement.finiteelementbase import FiniteElementBase
-
-
-# @six.python_2_unicode_compatible
-class InteriorElement(FiniteElementBase):
- """A version of an existing Finite Element space in which only the dofs
- associated with the interior have been kept."""
- def __init__(self, element):
- self._element = element
- self._repr = as_native_str("InteriorElement(%s)" % repr(element))
-
- family = "InteriorElement"
- 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, cell, degree,
- quad_scheme, value_shape, reference_value_shape)
- def mapping(self):
- return self._element.mapping()
+from ufl.finiteelement.restrictedelement import RestrictedElement
+from ufl.log import deprecate
- def __str__(self):
- return "InteriorElement(%s)" % str(self._element)
- def shortstr(self):
- "Format as string for pretty printing."
- return "InteriorElement(%s)" % str(self._element.shortstr())
+def InteriorElement(element):
+ """Constructs the restriction of a finite element to the interior of
+ the cell."""
+ deprecate('InteriorElement(element) is deprecated, please use element["interior"] instead.')
+ return RestrictedElement(element, restriction_domain="interior")
diff --git a/ufl/finiteelement/mixedelement.py b/ufl/finiteelement/mixedelement.py
index d706684..5a54f93 100644
--- a/ufl/finiteelement/mixedelement.py
+++ b/ufl/finiteelement/mixedelement.py
@@ -245,6 +245,9 @@ class MixedElement(FiniteElementBase):
i, e = self.extract_component(component)
return e.degree()
+ def reconstruct(self, **kwargs):
+ return MixedElement(*[e.reconstruct(**kwargs) for e in self.sub_elements()])
+
def __str__(self):
"Format as string for pretty printing."
tmp = ", ".join(str(element) for element in self._sub_elements)
@@ -313,12 +316,15 @@ class VectorElement(MixedElement):
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(%s, dim=%d)" % (
repr(sub_element), len(self._sub_elements))
+ def reconstruct(self, **kwargs):
+ sub_element = self._sub_element.reconstruct(**kwargs)
+ return VectorElement(sub_element, dim=len(self.sub_elements()))
+
def __str__(self):
"Format as string for pretty printing."
return ("<vector element with %d components of %s>" %
@@ -470,6 +476,10 @@ class TensorElement(MixedElement):
A component is a tuple of one or more ints."""
return self._symmetry
+ def reconstruct(self, **kwargs):
+ sub_element = self._sub_element.reconstruct(**kwargs)
+ return TensorElement(sub_element, shape=self._shape, symmetry=self._symmetry)
+
def __str__(self):
"Format as string for pretty printing."
if self._symmetry:
diff --git a/ufl/finiteelement/restrictedelement.py b/ufl/finiteelement/restrictedelement.py
index 18b4bd5..467f993 100644
--- a/ufl/finiteelement/restrictedelement.py
+++ b/ufl/finiteelement/restrictedelement.py
@@ -73,6 +73,10 @@ class RestrictedElement(FiniteElementBase):
"Return the domain onto which the element is restricted."
return self._restriction_domain
+ def reconstruct(self, **kwargs):
+ element = self._element.reconstruct(**kwargs)
+ return RestrictedElement(element, self._restriction_domain)
+
def __str__(self):
"Format as string for pretty printing."
return "<%s>|_{%s}" % (self._element, self._restriction_domain)
diff --git a/ufl/finiteelement/tensorproductelement.py b/ufl/finiteelement/tensorproductelement.py
index 59ccff0..7a482e5 100644
--- a/ufl/finiteelement/tensorproductelement.py
+++ b/ufl/finiteelement/tensorproductelement.py
@@ -28,6 +28,7 @@ from itertools import chain
from ufl.log import error
from ufl.utils.py23 import as_native_strings
from ufl.cell import TensorProductCell, as_cell
+from ufl.sobolevspace import DirectionalSobolevSpace
from ufl.finiteelement.finiteelementbase import FiniteElementBase
@@ -90,6 +91,22 @@ class TensorProductElement(FiniteElementBase):
else:
return "undefined"
+ def sobolev_space(self):
+ "Return the underlying Sobolev space of the TensorProductElement."
+ elements = self._sub_elements
+ if all(e.sobolev_space() == elements[0].sobolev_space()
+ for e in elements):
+ return elements[0].sobolev_space()
+ else:
+ # Generate a DirectionalSobolevSpace which contains
+ # continuity information parametrized by spatial index
+ orders = []
+ for e in elements:
+ e_dim = e.cell().geometric_dimension()
+ e_order = (e.sobolev_space()._order,) * e_dim
+ orders.extend(e_order)
+ return DirectionalSobolevSpace(orders)
+
def num_sub_elements(self):
"Return number of subelements."
return len(self._sub_elements)
@@ -98,6 +115,9 @@ class TensorProductElement(FiniteElementBase):
"Return subelements (factors)."
return self._sub_elements
+ def reconstruct(self, cell=None):
+ return TensorProductElement(*self.sub_elements(), cell=cell)
+
def __str__(self):
"Pretty-print."
return "TensorProductElement(%s, cell=%s)" \
diff --git a/ufl/functionspace.py b/ufl/functionspace.py
index 94c4816..714847c 100644
--- a/ufl/functionspace.py
+++ b/ufl/functionspace.py
@@ -44,6 +44,19 @@ class AbstractFunctionSpace(object):
@attach_operators_from_hash_data
class FunctionSpace(AbstractFunctionSpace):
def __init__(self, domain, element):
+ if domain is None:
+ # DOLFIN hack
+ # TODO: Is anything expected from element.cell() in this case?
+ pass
+ else:
+ try:
+ domain_cell = domain.ufl_cell()
+ except AttributeError:
+ error("Expected non-abstract domain for initalization of function space.")
+ else:
+ if element.cell() != domain_cell:
+ error("Non-matching cell of finite element and domain.")
+
AbstractFunctionSpace.__init__(self)
self._ufl_domain = domain
self._ufl_element = element
diff --git a/ufl/log.py b/ufl/log.py
index 9430b72..e398a51 100644
--- a/ufl/log.py
+++ b/ufl/log.py
@@ -54,6 +54,7 @@ def emit(self, record):
self.stream.write(format_string % message)
self.flush()
+
# Colors if the terminal supports it (disabled e.g. when piped to
# file)
if sys.stdout.isatty() and sys.stderr.isatty():
@@ -218,8 +219,7 @@ class Logger:
self._log.removeHandler(self._handler)
self._log.addHandler(handler)
self._handler = handler
- handler.emit = types.MethodType(emit, self._handler,
- self._handler.__class__)
+ handler.emit = types.MethodType(emit, self._handler)
def get_logger(self):
"Return message logger."
diff --git a/ufl/operators.py b/ufl/operators.py
index 27ab33a..e58eeff 100644
--- a/ufl/operators.py
+++ b/ufl/operators.py
@@ -417,6 +417,8 @@ def curl(f):
"UFL operator: Take the curl of *f*."
f = as_ufl(f)
return Curl(f)
+
+
rot = curl
diff --git a/ufl/sobolevspace.py b/ufl/sobolevspace.py
index 1360b55..f0814d8 100644
--- a/ufl/sobolevspace.py
+++ b/ufl/sobolevspace.py
@@ -23,12 +23,15 @@ symbolic reasoning about the spaces in which finite elements lie."""
#
# Modified by Martin Alnaes 2014
# Modified by Lizao Li 2015
+# Modified by Thomas Gibson 2017
#import six
from ufl.utils.py23 import as_native_str
+from functools import total_ordering
# @six.python_2_unicode_compatible
+ at total_ordering
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
@@ -46,6 +49,16 @@ class SobolevSpace(object):
p = frozenset(parents or [])
# Ensure that the inclusion operations are transitive.
self.parents = p.union(*[p_.parents for p_ in p])
+ self._order = {"L2": 0,
+ "H1": 1,
+ "H2": 2,
+ # Order for the elements below is taken from
+ # its parent Sobolev space
+ "HDiv": 0,
+ "HCurl": 0,
+ "HEin": 0,
+ "HDivDiv": 0,
+ "DirectionalH": 0}[self.name]
def __unicode__(self):
# Only in python 2
@@ -73,6 +86,12 @@ class SobolevSpace(object):
def __hash__(self):
return hash(("SobolevSpace", self.name))
+ def __getitem__(self, spatial_index):
+ """Returns the Sobolev space associated with a particular
+ spatial coordinate.
+ """
+ return self
+
def __contains__(self, other):
"""Implement `fe in s` where `fe` is a
:class:`~finiteelement.FiniteElement` and `s` is a
@@ -86,24 +105,9 @@ class SobolevSpace(object):
def __lt__(self, other):
"""In common with intrinsic Python sets, < indicates "is a proper
- subset of."""
+ subset of"."""
return other in self.parents
- def __le__(self, other):
- """In common with intrinsic Python sets, <= indicates "is a subset
- of." """
- return (self == other) or (other in self.parents)
-
- def __gt__(self, other):
- """In common with intrinsic Python sets, > indicates "is a proper
- superset of."""
- return self in other.parents
-
- def __ge__(self, other):
- """In common with intrinsic Python sets, >= indicates "is a superset
- 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":
@@ -115,10 +119,90 @@ class SobolevSpace(object):
raise NotImplementedError("SobolevSpace has no call operator (only the specific HDiv and HCurl instances).")
+ at total_ordering
+class DirectionalSobolevSpace(SobolevSpace):
+ """Symbolic representation of a Sobolev space with varying smoothness
+ in differerent spatial directions.
+ """
+
+ def __init__(self, orders):
+ """Instantiate a DirectionalSobolevSpace object.
+
+ :arg orders: an iterable of orders of weak derivatives, where
+ the position denotes in what spatial variable the
+ smoothness requirement is enforced.
+ """
+ assert all(isinstance(x, int) for x in orders), (
+ "Order must be an integer."
+ )
+ assert all(x < 3 for x in orders), (
+ "Not implemented for orders greater than 2"
+ )
+ name = "DirectionalH"
+ parents = [L2]
+ super(DirectionalSobolevSpace, self).__init__(name, parents)
+ self._orders = tuple(orders)
+ self._spatial_indices = range(len(self._orders))
+
+ def __getitem__(self, spatial_index):
+ """Returns the Sobolev space associated with a particular
+ spatial coordinate.
+ """
+ if spatial_index not in range(len(self._orders)):
+ raise IndexError("Spatial index out of range.")
+ spaces = {0: L2,
+ 1: H1,
+ 2: H2}
+ return spaces[self._orders[spatial_index]]
+
+ def __contains__(self, other):
+ """Implement `fe in s` where `fe` is a
+ :class:`~finiteelement.FiniteElement` and `s` is a
+ :class:`DirectionalSobolevSpace`"""
+ if isinstance(other, SobolevSpace):
+ raise TypeError("Unable to test for inclusion of a " +
+ "SobolevSpace in another SobolevSpace. " +
+ "Did you mean to use <= instead?")
+ return (other.sobolev_space() == self or
+ all(self[i] in other.sobolev_space().parents
+ for i in self._spatial_indices))
+
+ def __eq__(self, other):
+ if isinstance(other, DirectionalSobolevSpace):
+ return self._orders == other._orders
+ return all(self[i] == other for i in self._spatial_indices)
+
+ def __lt__(self, other):
+ """In common with intrinsic Python sets, < indicates "is a proper
+ subset of."""
+ if isinstance(other, DirectionalSobolevSpace):
+ if self._spatial_indices != other._spatial_indices:
+ return False
+ return any(self._orders[i] > other._orders[i]
+ for i in self._spatial_indices)
+
+ if other in [HDiv, HCurl]:
+ return all(self._orders[i] >= 1 for i in self._spatial_indices)
+ elif other.name in ["HDivDiv", "HEin"]:
+ # Don't know how these spaces compare
+ return NotImplementedError(
+ "Don't know how to compare with %s" % other.name
+ )
+ else:
+ return any(self._orders[i] > other._order
+ for i in self._spatial_indices)
+
+ def __str__(self):
+ return self.name + "(%s)" % ", ".join(map(str, self._orders))
+
+ def _repr_latex_(self):
+ return "H(%s)" % ", ".join(map(str, self._orders))
+
+
L2 = SobolevSpace("L2")
HDiv = SobolevSpace("HDiv", [L2])
HCurl = SobolevSpace("HCurl", [L2])
H1 = SobolevSpace("H1", [HDiv, HCurl, L2])
-H2 = SobolevSpace("H2", [H1])
+H2 = SobolevSpace("H2", [H1, HDiv, HCurl, L2])
HEin = SobolevSpace("HEin", [L2])
HDivDiv = SobolevSpace("HDivDiv", [L2])
diff --git a/ufl/utils/sorting.py b/ufl/utils/sorting.py
index 044ab93..6c1dc62 100644
--- a/ufl/utils/sorting.py
+++ b/ufl/utils/sorting.py
@@ -110,7 +110,7 @@ def canonicalize_metadata(metadata):
for value in values:
if isinstance(value, (dict, list, tuple)):
value = canonicalize_metadata(value)
- elif isinstance(value, (int, float, string_types)):
+ elif isinstance(value, (int, float, string_types)) or value is None:
value = str(value)
else:
warning("Applying str() to a metadata value of type {0}, don't know if this is safe.".format(type(value).__name__))
--
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