From 4843c3770cc44e5c6c41ff24652719456701995c Mon Sep 17 00:00:00 2001 From: "Jack S. Hale" Date: Mon, 9 Oct 2023 11:27:54 +0200 Subject: [PATCH 01/16] Bump version. (#217) --- setup.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index 7bcb914d4..ccd6223e3 100644 --- a/setup.cfg +++ b/setup.cfg @@ -2,7 +2,7 @@ # future [metadata] name = fenics-ufl -version = 2023.2.0.dev0 +version = 2023.3.0.dev0 author = FEniCS Project Contributors email = fenics-dev@googlegroups.com maintainer = FEniCS Project Steering Council From 5a5faf2a546da51d0c514e969b4bddb9ba6f9a9e Mon Sep 17 00:00:00 2001 From: "Garth N. Wells" Date: Fri, 13 Oct 2023 17:01:07 +0100 Subject: [PATCH 02/16] Add Spack test to Actions CI (#218) * Add Spack CI test * Update comment --- .github/workflows/spack.yml | 88 +++++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 .github/workflows/spack.yml diff --git a/.github/workflows/spack.yml b/.github/workflows/spack.yml new file mode 100644 index 000000000..5baa0d00c --- /dev/null +++ b/.github/workflows/spack.yml @@ -0,0 +1,88 @@ +name: Spack build + +on: + # Uncomment the below 'push' to trigger on push + # push: + # branches: + # - "**" + schedule: + # '*' is a special character in YAML, so string must be quoted + - cron: "0 2 * * WED" + workflow_dispatch: + inputs: + spack_repo: + description: "Spack repository to test" + default: "spack/spack" + type: string + spack_ref: + description: "Spack repository branch/tag to test" + default: "develop" + type: string + +jobs: + build: + runs-on: ubuntu-latest + container: ubuntu:latest + steps: + - name: Get Spack + if: github.event_name != 'workflow_dispatch' + uses: actions/checkout@v4 + with: + path: ./spack + repository: spack/spack + - name: Get Spack + if: github.event_name == 'workflow_dispatch' + uses: actions/checkout@v4 + with: + path: ./spack + repository: ${{ github.event.inputs.spack_repo }} + ref: ${{ github.event.inputs.spack_ref }} + + - name: Install Spack requirements + run: | + apt-get -y update + apt-get install -y bzip2 curl file git gzip make patch python3-minimal tar xz-utils + apt-get install -y g++ gfortran # compilers + + - name: Insall UFL development version via Spack + run: | + . ./spack/share/spack/setup-env.sh + spack env create main + spack env activate main + spack add py-fenics-ufl@main + spack install + - name: Get UFL code (to access test files) + uses: actions/checkout@v4 + with: + path: ./ufl-main + - name: Run tests (development version) + run: | + . ./spack/share/spack/setup-env.sh + spack env create main-test + spack env activate main-test + spack add py-fencis-ufl@main py-pytest + cd ufl-main/test/ + pytest -n auto . + + - name: Install UFL release version via Spack + run: | + . ./spack/share/spack/setup-env.sh + spack env create release + spack env activate release + spack add py-fenics-ufl + spack install + - name: Get UFL release code (to access test files) + uses: actions/checkout@v4 + with: + ref: v2023.2.0 + # ref: v${{ github.event.inputs.spack_branch }} + path: ./ufl-release + - name: Run tests (release version) + run: | + . ./spack/share/spack/setup-env.sh + spack env create release-test + spack env activate release-test + spack add py-fenics-ufl py-pytest + spack install + cd ufl-release/test/ + pytest -n auto . From 66b78e1ab18a5bc292ba3bf48fc726ed890c2597 Mon Sep 17 00:00:00 2001 From: "Garth N. Wells" Date: Mon, 16 Oct 2023 08:49:15 +0100 Subject: [PATCH 03/16] Add Spack test to CI (#219) * Fix Spack CI * Fix typo * Add badge * Simplify * Remove verose output --- .github/workflows/spack.yml | 47 ++++++++----------------------------- README.rst | 4 ++++ 2 files changed, 14 insertions(+), 37 deletions(-) diff --git a/.github/workflows/spack.yml b/.github/workflows/spack.yml index 5baa0d00c..7af85df62 100644 --- a/.github/workflows/spack.yml +++ b/.github/workflows/spack.yml @@ -24,6 +24,12 @@ jobs: runs-on: ubuntu-latest container: ubuntu:latest steps: + - name: Install Spack requirements + run: | + apt-get -y update + apt-get install -y bzip2 curl file git gzip make patch python3-minimal tar xz-utils + apt-get install -y g++ gfortran # compilers + - name: Get Spack if: github.event_name != 'workflow_dispatch' uses: actions/checkout@v4 @@ -38,51 +44,18 @@ jobs: repository: ${{ github.event.inputs.spack_repo }} ref: ${{ github.event.inputs.spack_ref }} - - name: Install Spack requirements - run: | - apt-get -y update - apt-get install -y bzip2 curl file git gzip make patch python3-minimal tar xz-utils - apt-get install -y g++ gfortran # compilers - - - name: Insall UFL development version via Spack + - name: Install UFL development version and run tests run: | . ./spack/share/spack/setup-env.sh spack env create main spack env activate main spack add py-fenics-ufl@main - spack install - - name: Get UFL code (to access test files) - uses: actions/checkout@v4 - with: - path: ./ufl-main - - name: Run tests (development version) - run: | - . ./spack/share/spack/setup-env.sh - spack env create main-test - spack env activate main-test - spack add py-fencis-ufl@main py-pytest - cd ufl-main/test/ - pytest -n auto . + spack install --test=root - - name: Install UFL release version via Spack + - name: Install UFL release version and run tests run: | . ./spack/share/spack/setup-env.sh spack env create release spack env activate release spack add py-fenics-ufl - spack install - - name: Get UFL release code (to access test files) - uses: actions/checkout@v4 - with: - ref: v2023.2.0 - # ref: v${{ github.event.inputs.spack_branch }} - path: ./ufl-release - - name: Run tests (release version) - run: | - . ./spack/share/spack/setup-env.sh - spack env create release-test - spack env activate release-test - spack add py-fenics-ufl py-pytest - spack install - cd ufl-release/test/ - pytest -n auto . + spack install --test=root diff --git a/README.rst b/README.rst index 0d43d1e08..452963331 100644 --- a/README.rst +++ b/README.rst @@ -13,6 +13,8 @@ https://www.fenicsproject.org .. image:: https://github.com/FEniCS/ufl/workflows/UFL%20CI/badge.svg :target: https://github.com/FEniCS/ufl/workflows/UFL%20CI +.. image:: https://github.com/FEniCS/ufl/actions/workflows/spack.yml/badge.svg + :target: https://github.com/FEniCS/ufl/actions/workflows/spack.yml .. image:: https://coveralls.io/repos/github/FEniCS/ufl/badge.svg?branch=master :target: https://coveralls.io/github/FEniCS/ufl?branch=master :alt: Coverage Status @@ -20,11 +22,13 @@ https://www.fenicsproject.org :target: https://fenics.readthedocs.io/projects/ufl/en/latest/?badge=latest :alt: Documentation Status + Documentation ============= Documentation can be viewed at https://fenics-ufl.readthedocs.org/. + License ======= From ff3f69f89e6d33f3ea10970846d504997f097916 Mon Sep 17 00:00:00 2001 From: Matthew Scroggs Date: Mon, 16 Oct 2023 13:16:13 +0100 Subject: [PATCH 04/16] AbstractFiniteElement (#197) * Remove a lots of finiteelement stuff * __all_classes__ * remove mixedelement * remove use of deleted elements * remove print * working on element interface * working on new elements * Add broken demos * make (almost) all tests pass * Move demos out of broken folder, fix a few tests * Hermite is H1 * flake8 * symmetric elements * subelements in splitting test * fix another test * MixedElasticity demo * fix final test * rename FiniteElementBase -> AbstractFiniteElement * doc * Template structure for pullback * working on pull back * fixes * skip flake8 on pull_back for now * flake and pydocstyle * update tests * basix branch * flake8 * start making ffc tests pass * fix imports * flake * isort * make test pass * ffc branch * as_domain in measure (if domain is not None) * corrections * dolfinx branch * Add some types * no product needed * sub_rsh * () * flattened... not property * documentation * remove component functions * flake, remove unused function * remove _is_linear * put function back * remove is_cellwise_constant * remove is_globally_constant * remove sorting of elements by hash * improve doc * working on new pull back classes * update pull backs in demos and tests * flake8 * add mixed pull back * symmetric pull back * flake * flake8 * a -> A * Get (physical) value shape from the pull back * tidy up element classes * flake * add embedded super- and sub-degrees * Add Legacy elements (for TSFC support) * flake * tsfc branch * don't look up components for scalar-valued elements * sub_elements() -> sub_elements * sobolev_space() -> sobolev_space * is_identity rather than checking type * abstractproperty * docs * use is_identity * doc * simplify pullback code * tweak docstrings * clarify * property and custom pullback * () * Use IdentityPullBack if mixed elements sub elements all use it * Docstring * fixes in legacy * l -> L * physical pull back * @property in legacy * fixing legacy elements * fix symmetry ordering * generalise symmetric pull back to sub-elements with a value size * update AUTHORS * fdlake * fix when element may not be fully initialised * set self._sub_elements before they're needed * update index order in legacy * Rename pull_back -> pullback and improve docs * rename pullback in tests * fix embedded degrees * flake * pullback rename in demos * replace degree() with embedded_superdegree * embedded degrees in hdivcurl * fix more embedded degrees * correct blockshape * max not sum * Revert "max not sum" This reverts commit 145fbb4b928a77dc39ad08ca80dd4f127d0951ba. * only for testing * set branches to main --- .github/workflows/tsfc-tests.yml | 2 +- AUTHORS | 1 + demo/Constant.py | 15 +- demo/ConvectionJacobi.py | 9 +- demo/ConvectionJacobi2.py | 9 +- demo/ConvectionVector.py | 9 +- demo/Elasticity.py | 9 +- demo/EnergyNorm.py | 9 +- demo/Equation.py | 10 +- demo/ExplicitConvection.py | 9 +- demo/FEEC.py | 56 -- demo/FunctionOperators.py | 10 +- demo/H1norm.py | 9 +- demo/HarmonicMap.py | 12 +- demo/HarmonicMap2.py | 14 +- demo/Heat.py | 10 +- demo/HornSchunck.py | 12 +- demo/HyperElasticity.py | 16 +- demo/HyperElasticity1D.py | 10 +- demo/L2norm.py | 9 +- demo/Mass.py | 9 +- demo/MassAD.py | 9 +- demo/MixedElasticity.py | 22 +- demo/MixedMixedElement.py | 9 +- demo/MixedPoisson.py | 14 +- demo/MixedPoisson2.py | 14 +- demo/NavierStokes.py | 9 +- demo/NeumannProblem.py | 10 +- demo/NonlinearPoisson.py | 10 +- demo/P5tet.py | 7 +- demo/P5tri.py | 7 +- demo/Poisson.py | 10 +- demo/PoissonDG.py | 11 +- demo/PoissonSystem.py | 10 +- demo/PowAD.py | 10 +- demo/ProjectionSystem.py | 10 +- demo/QuadratureElement.py | 14 +- demo/RestrictedElement.py | 29 - demo/Stiffness.py | 9 +- demo/StiffnessAD.py | 10 +- demo/Stokes.py | 14 +- demo/StokesEquation.py | 15 +- demo/SubDomain.py | 10 +- demo/SubDomains.py | 9 +- demo/TensorWeightedPoisson.py | 12 +- demo/VectorLaplaceGradCurl.py | 16 +- demo/_TensorProductElement.py | 13 +- setup.cfg | 3 + test/test_algorithms.py | 39 +- test/test_apply_algebra_lowering.py | 27 +- test/test_apply_function_pullbacks.py | 101 ++-- test/test_apply_restrictions.py | 14 +- test/test_arithmetic.py | 17 +- test/test_automatic_differentiation.py | 25 +- test/test_book_snippets.py | 547 ----------------- test/test_change_to_local.py | 11 +- test/test_change_to_reference_frame.py | 21 +- test/test_check_arities.py | 15 +- test/test_classcoverage.py | 38 +- test/test_complex.py | 33 +- test/test_conditionals.py | 14 +- test/test_degree_estimation.py | 44 +- test/test_derivative.py | 292 +++++---- test/test_diff.py | 13 +- test/test_domains.py | 134 +++-- test/test_duals.py | 61 +- test/test_equals.py | 33 +- test/test_evaluate.py | 65 ++- test/test_expand_indices.py | 15 +- test/test_external_operator.py | 16 +- test/test_ffcforms.py | 132 ++--- test/test_form.py | 33 +- test/test_grad.py | 13 +- test/test_illegal.py | 11 +- test/test_indexing.py | 7 +- test/test_indices.py | 67 ++- test/test_interpolate.py | 13 +- test/{test_elements.py => test_legacy.py} | 49 +- test/test_lhs_rhs.py | 19 +- test/test_measures.py | 12 +- test/test_mixed_function_space.py | 25 +- test/test_new_ad.py | 35 +- test/test_pickle.py | 132 +++-- test/test_piecewise_checks.py | 61 +- test/test_reference_shapes.py | 49 +- test/test_scratch.py | 31 +- test/test_signature.py | 109 ++-- test/test_simplify.py | 26 +- test/test_sobolevspace.py | 142 +---- test/test_split.py | 36 +- test/test_str.py | 40 +- test/test_strip_forms.py | 16 +- test/test_tensoralgebra.py | 9 +- ufl/__init__.py | 174 ++---- ufl/action.py | 9 +- ufl/adjoint.py | 4 +- ufl/algebra.py | 8 +- ufl/algorithms/__init__.py | 83 +-- ufl/algorithms/analysis.py | 19 +- ufl/algorithms/apply_algebra_lowering.py | 12 +- ufl/algorithms/apply_derivatives.py | 34 +- ufl/algorithms/apply_function_pullbacks.py | 158 +---- ufl/algorithms/apply_geometry_lowering.py | 22 +- ufl/algorithms/apply_integral_scaling.py | 6 +- ufl/algorithms/apply_restrictions.py | 2 +- ufl/algorithms/balancing.py | 4 +- ufl/algorithms/change_to_reference.py | 14 +- ufl/algorithms/check_arities.py | 6 +- ufl/algorithms/check_restrictions.py | 2 +- ufl/algorithms/checks.py | 13 +- ufl/algorithms/comparison_checker.py | 6 +- ufl/algorithms/compute_form_data.py | 40 +- .../coordinate_derivative_helpers.py | 6 +- ufl/algorithms/domain_analysis.py | 9 +- ufl/algorithms/estimate_degrees.py | 10 +- ufl/algorithms/expand_compounds.py | 1 + ufl/algorithms/expand_indices.py | 14 +- ufl/algorithms/formdata.py | 2 +- ufl/algorithms/formfiles.py | 19 +- ufl/algorithms/formsplitter.py | 6 +- ufl/algorithms/formtransformations.py | 13 +- ufl/algorithms/map_integrands.py | 8 +- ufl/algorithms/remove_complex_nodes.py | 4 +- ufl/algorithms/renumbering.py | 6 +- ufl/algorithms/replace.py | 8 +- ufl/algorithms/replace_derivative_nodes.py | 6 +- ufl/algorithms/signature.py | 10 +- ufl/algorithms/strip_terminal_data.py | 9 +- ufl/algorithms/traversal.py | 8 +- ufl/argument.py | 25 +- ufl/averaging.py | 2 +- ufl/cell.py | 4 +- ufl/checks.py | 41 +- ufl/classes.py | 69 +-- ufl/coefficient.py | 26 +- ufl/compound_expressions.py | 6 +- ufl/conditional.py | 8 +- ufl/constant.py | 2 +- ufl/constantvalue.py | 8 +- ufl/core/base_form_operator.py | 4 +- ufl/core/expr.py | 6 - ufl/core/interpolate.py | 10 +- ufl/core/multiindex.py | 4 +- ufl/core/operator.py | 2 - ufl/core/ufl_type.py | 7 +- ufl/corealg/map_dag.py | 2 +- ufl/differentiation.py | 6 +- ufl/domain.py | 141 +---- ufl/exprcontainers.py | 6 +- ufl/exproperators.py | 24 +- ufl/finiteelement.py | 368 ++++++++++++ ufl/finiteelement/__init__.py | 42 -- ufl/form.py | 1 + ufl/formatting/ufl2unicode.py | 6 +- ufl/formoperators.py | 50 +- ufl/functionspace.py | 4 +- ufl/geometry.py | 4 +- ufl/index_combination_utils.py | 2 +- ufl/indexed.py | 8 +- ufl/indexsum.py | 8 +- ufl/integral.py | 2 +- ufl/legacy/__init__.py | 25 + .../brokenelement.py | 9 +- ufl/{finiteelement => legacy}/elementlist.py | 6 +- .../enrichedelement.py | 37 +- .../finiteelement.py | 24 +- .../finiteelementbase.py | 68 ++- ufl/{finiteelement => legacy}/hdivcurl.py | 49 +- ufl/{finiteelement => legacy}/mixedelement.py | 106 +++- .../restrictedelement.py | 17 +- .../tensorproductelement.py | 36 +- ufl/mathfunctions.py | 6 +- ufl/matrix.py | 7 +- ufl/measure.py | 20 +- ufl/objects.py | 5 +- ufl/operators.py | 51 +- ufl/precedence.py | 8 +- ufl/pullback.py | 552 ++++++++++++++++++ ufl/referencevalue.py | 4 +- ufl/restriction.py | 4 +- ufl/sobolevspace.py | 23 +- ufl/sorting.py | 9 +- ufl/split_functions.py | 26 +- ufl/tensoralgebra.py | 6 +- ufl/tensors.py | 10 +- ufl/utils/sequences.py | 1 + ufl/variable.py | 8 +- 187 files changed, 3094 insertions(+), 2883 deletions(-) delete mode 100644 demo/FEEC.py delete mode 100644 demo/RestrictedElement.py delete mode 100755 test/test_book_snippets.py rename test/{test_elements.py => test_legacy.py} (81%) mode change 100755 => 100644 create mode 100644 ufl/finiteelement.py delete mode 100644 ufl/finiteelement/__init__.py create mode 100644 ufl/legacy/__init__.py rename ufl/{finiteelement => legacy}/brokenelement.py (86%) rename ufl/{finiteelement => legacy}/elementlist.py (99%) rename ufl/{finiteelement => legacy}/enrichedelement.py (83%) rename ufl/{finiteelement => legacy}/finiteelement.py (93%) rename ufl/{finiteelement => legacy}/finiteelementbase.py (81%) rename ufl/{finiteelement => legacy}/hdivcurl.py (81%) rename ufl/{finiteelement => legacy}/mixedelement.py (86%) rename ufl/{finiteelement => legacy}/restrictedelement.py (91%) rename ufl/{finiteelement => legacy}/tensorproductelement.py (83%) create mode 100644 ufl/pullback.py diff --git a/.github/workflows/tsfc-tests.yml b/.github/workflows/tsfc-tests.yml index 06b10a13d..a861b4728 100644 --- a/.github/workflows/tsfc-tests.yml +++ b/.github/workflows/tsfc-tests.yml @@ -32,7 +32,7 @@ jobs: with: path: ./tsfc repository: firedrakeproject/tsfc - ref: master + ref: mscroggs/newfl-legacy - name: Install tsfc run: | cd tsfc diff --git a/AUTHORS b/AUTHORS index c3c6f37c6..5866e1c39 100644 --- a/AUTHORS +++ b/AUTHORS @@ -31,3 +31,4 @@ Contributors: | Nacime Bouziani | Reuben W. Hill | Nacime Bouziani + | Matthew Scroggs diff --git a/demo/Constant.py b/demo/Constant.py index 0a2205978..96acaf22c 100644 --- a/demo/Constant.py +++ b/demo/Constant.py @@ -18,20 +18,23 @@ # Modified by Martin Sandve Alnes, 2009 # # Test form for scalar and vector constants. -from ufl import (Coefficient, Constant, FiniteElement, FunctionSpace, Mesh, TestFunction, TrialFunction, VectorConstant, - VectorElement, dot, dx, grad, inner, triangle) +from ufl import (Coefficient, Constant, FunctionSpace, Mesh, TestFunction, TrialFunction, VectorConstant, dot, dx, grad, + inner, triangle) +from ufl.finiteelement import FiniteElement +from ufl.pullback import identity_pullback +from ufl.sobolevspace import H1 cell = triangle -element = FiniteElement("Lagrange", cell, 1) -domain = Mesh(VectorElement("Lagrange", cell, 1)) +element = FiniteElement("Lagrange", cell, 1, (), identity_pullback, H1) +domain = Mesh(FiniteElement("Lagrange", cell, 1, (2, ), identity_pullback, H1)) space = FunctionSpace(domain, element) v = TestFunction(space) u = TrialFunction(space) f = Coefficient(space) -c = Constant(space) -d = VectorConstant(space) +c = Constant(domain) +d = VectorConstant(domain) a = c * dot(grad(v), grad(u)) * dx L = inner(d, grad(v)) * dx diff --git a/demo/ConvectionJacobi.py b/demo/ConvectionJacobi.py index bf059ade6..5f3b2da10 100644 --- a/demo/ConvectionJacobi.py +++ b/demo/ConvectionJacobi.py @@ -2,10 +2,13 @@ # Author: Martin Sandve Alnes # Date: 2008-10-03 # -from ufl import Coefficient, FunctionSpace, Mesh, TestFunction, TrialFunction, VectorElement, dot, dx, grad, triangle +from ufl import Coefficient, FunctionSpace, Mesh, TestFunction, TrialFunction, dot, dx, grad, triangle +from ufl.finiteelement import FiniteElement +from ufl.pullback import identity_pullback +from ufl.sobolevspace import H1 -element = VectorElement("Lagrange", triangle, 1) -domain = Mesh(VectorElement("Lagrange", triangle, 1)) +element = FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1) +domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) space = FunctionSpace(domain, element) u = TrialFunction(space) diff --git a/demo/ConvectionJacobi2.py b/demo/ConvectionJacobi2.py index 7096cd32d..c88108a17 100644 --- a/demo/ConvectionJacobi2.py +++ b/demo/ConvectionJacobi2.py @@ -2,10 +2,13 @@ # Author: Martin Sandve Alnes # Date: 2008-10-03 # -from ufl import Coefficient, FunctionSpace, Mesh, TestFunction, TrialFunction, VectorElement, dx, i, j, triangle +from ufl import Coefficient, FunctionSpace, Mesh, TestFunction, TrialFunction, dx, i, j, triangle +from ufl.finiteelement import FiniteElement +from ufl.pullback import identity_pullback +from ufl.sobolevspace import H1 -element = VectorElement("Lagrange", triangle, 1) -domain = Mesh(VectorElement("Lagrange", triangle, 1)) +element = FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1) +domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) space = FunctionSpace(domain, element) u = TrialFunction(space) diff --git a/demo/ConvectionVector.py b/demo/ConvectionVector.py index d16a52ee5..e83e60698 100644 --- a/demo/ConvectionVector.py +++ b/demo/ConvectionVector.py @@ -2,10 +2,13 @@ # Author: Martin Sandve Alnes # Date: 2008-10-03 # -from ufl import Coefficient, FunctionSpace, Mesh, TestFunction, VectorElement, dot, dx, grad, triangle +from ufl import Coefficient, FunctionSpace, Mesh, TestFunction, dot, dx, grad, triangle +from ufl.finiteelement import FiniteElement +from ufl.pullback import identity_pullback +from ufl.sobolevspace import H1 -element = VectorElement("Lagrange", triangle, 1) -domain = Mesh(VectorElement("Lagrange", triangle, 1)) +element = FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1) +domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) space = FunctionSpace(domain, element) v = TestFunction(space) diff --git a/demo/Elasticity.py b/demo/Elasticity.py index 0061c7135..73513d5d6 100644 --- a/demo/Elasticity.py +++ b/demo/Elasticity.py @@ -3,10 +3,13 @@ # Modified by: Martin Sandve Alnes # Date: 2009-01-12 # -from ufl import FunctionSpace, Mesh, TestFunction, TrialFunction, VectorElement, dx, grad, inner, tetrahedron +from ufl import FunctionSpace, Mesh, TestFunction, TrialFunction, dx, grad, inner, tetrahedron +from ufl.finiteelement import FiniteElement +from ufl.pullback import identity_pullback +from ufl.sobolevspace import H1 -element = VectorElement("Lagrange", tetrahedron, 1) -domain = Mesh(VectorElement("Lagrange", tetrahedron, 1)) +element = FiniteElement("Lagrange", tetrahedron, 1, (3, ), identity_pullback, H1) +domain = Mesh(FiniteElement("Lagrange", tetrahedron, 1, (3, ), identity_pullback, H1)) space = FunctionSpace(domain, element) v = TestFunction(space) diff --git a/demo/EnergyNorm.py b/demo/EnergyNorm.py index 64bc8d88e..30f1ade40 100644 --- a/demo/EnergyNorm.py +++ b/demo/EnergyNorm.py @@ -17,10 +17,13 @@ # # This example demonstrates how to define a functional, here # the energy norm (squared) for a reaction-diffusion problem. -from ufl import Coefficient, FiniteElement, FunctionSpace, Mesh, VectorElement, dot, dx, grad, tetrahedron +from ufl import Coefficient, FunctionSpace, Mesh, dot, dx, grad, tetrahedron +from ufl.finiteelement import FiniteElement +from ufl.pullback import identity_pullback +from ufl.sobolevspace import H1 -element = FiniteElement("Lagrange", tetrahedron, 1) -domain = Mesh(VectorElement("Lagrange", tetrahedron, 1)) +element = FiniteElement("Lagrange", tetrahedron, 1, (), identity_pullback, H1) +domain = Mesh(FiniteElement("Lagrange", tetrahedron, 1, (3, ), identity_pullback, H1)) space = FunctionSpace(domain, element) v = Coefficient(space) diff --git a/demo/Equation.py b/demo/Equation.py index 94c5ef445..33625545b 100644 --- a/demo/Equation.py +++ b/demo/Equation.py @@ -34,11 +34,13 @@ # the unknown u to the right-hand side, all terms may # be listed on one line and left- and right-hand sides # extracted by lhs() and rhs(). -from ufl import (Coefficient, FiniteElement, FunctionSpace, Mesh, TestFunction, TrialFunction, VectorElement, dot, dx, - grad, lhs, rhs, triangle) +from ufl import Coefficient, FunctionSpace, Mesh, TestFunction, TrialFunction, dot, dx, grad, lhs, rhs, triangle +from ufl.finiteelement import FiniteElement +from ufl.pullback import identity_pullback +from ufl.sobolevspace import H1 -element = FiniteElement("Lagrange", triangle, 1) -domain = Mesh(VectorElement("Lagrange", triangle, 1)) +element = FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1) +domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) space = FunctionSpace(domain, element) k = 0.1 diff --git a/demo/ExplicitConvection.py b/demo/ExplicitConvection.py index f3b684d4d..c56d27376 100644 --- a/demo/ExplicitConvection.py +++ b/demo/ExplicitConvection.py @@ -2,10 +2,13 @@ # Author: Martin Sandve Alnes # Date: 2008-10-03 # -from ufl import Coefficient, FunctionSpace, Mesh, TestFunction, TrialFunction, VectorElement, dot, dx, grad, triangle +from ufl import Coefficient, FunctionSpace, Mesh, TestFunction, TrialFunction, dot, dx, grad, triangle +from ufl.finiteelement import FiniteElement +from ufl.pullback import identity_pullback +from ufl.sobolevspace import H1 -element = VectorElement("Lagrange", triangle, 1) -domain = Mesh(VectorElement("Lagrange", triangle, 1)) +element = FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1) +domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) space = FunctionSpace(domain, element) u = TrialFunction(space) diff --git a/demo/FEEC.py b/demo/FEEC.py deleted file mode 100644 index cf79198e7..000000000 --- a/demo/FEEC.py +++ /dev/null @@ -1,56 +0,0 @@ -# Copyright (C) 2010 Marie Rognes -# -# This file is part of UFL. -# -# UFL is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# UFL is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with UFL. If not, see . - -""" -This demo illustrates the FEEC notation - - V = FiniteElement("P Lambda", cell, r, k) - V = FiniteElement("P- Lambda", cell, r, k) - -and their aliases. -""" -from ufl import (FiniteElement, FunctionSpace, Mesh, TestFunction, TestFunctions, TrialFunction, TrialFunctions, - VectorElement, dx) -from ufl import exterior_derivative as d -from ufl import inner, interval, tetrahedron, triangle - -cells = [interval, triangle, tetrahedron] -r = 1 - -for cell in cells: - for family in ["P Lambda", "P- Lambda"]: - tdim = cell.topological_dimension() - for k in range(0, tdim + 1): - - # Testing exterior derivative - V = FiniteElement(family, cell, r, form_degree=k) - domain = Mesh(VectorElement("Lagrange", cell, 1)) - space = FunctionSpace(domain, V) - v = TestFunction(space) - u = TrialFunction(space) - - a = inner(d(u), d(v)) * dx - - # Testing mixed formulation of Hodge Laplace - if k > 0 and k < tdim + 1: - S = FiniteElement(family, cell, r, form_degree=k - 1) - W = S * V - mixed_space = FunctionSpace(domain, W) - (sigma, u) = TrialFunctions(mixed_space) - (tau, v) = TestFunctions(mixed_space) - - a = (inner(sigma, tau) - inner(d(tau), u) + inner(d(sigma), v) + inner(d(u), d(v))) * dx diff --git a/demo/FunctionOperators.py b/demo/FunctionOperators.py index 79fa43244..075e7b8c1 100644 --- a/demo/FunctionOperators.py +++ b/demo/FunctionOperators.py @@ -16,11 +16,13 @@ # along with UFL. If not, see . # # Test form for operators on Coefficients. -from ufl import (Coefficient, FiniteElement, FunctionSpace, Mesh, TestFunction, TrialFunction, VectorElement, dot, dx, - grad, max_value, sqrt, triangle) +from ufl import Coefficient, FunctionSpace, Mesh, TestFunction, TrialFunction, dot, dx, grad, max_value, sqrt, triangle +from ufl.finiteelement import FiniteElement +from ufl.pullback import identity_pullback +from ufl.sobolevspace import H1 -element = FiniteElement("Lagrange", triangle, 1) -domain = Mesh(VectorElement("Lagrange", triangle, 1)) +element = FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1) +domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) space = FunctionSpace(domain, element) v = TestFunction(space) diff --git a/demo/H1norm.py b/demo/H1norm.py index 2dd54a12a..9da6b28e4 100644 --- a/demo/H1norm.py +++ b/demo/H1norm.py @@ -2,10 +2,13 @@ # Author: Martin Sandve Alnes # Date: 2008-10-03 # -from ufl import Coefficient, FiniteElement, FunctionSpace, Mesh, VectorElement, dot, dx, grad, triangle +from ufl import Coefficient, FunctionSpace, Mesh, dot, dx, grad, triangle +from ufl.finiteelement import FiniteElement +from ufl.pullback import identity_pullback +from ufl.sobolevspace import H1 -element = FiniteElement("Lagrange", triangle, 1) -domain = Mesh(VectorElement("Lagrange", triangle, 1)) +element = FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1) +domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) space = FunctionSpace(domain, element) f = Coefficient(space) diff --git a/demo/HarmonicMap.py b/demo/HarmonicMap.py index 854fae28f..8aa3ee5d2 100644 --- a/demo/HarmonicMap.py +++ b/demo/HarmonicMap.py @@ -3,13 +3,15 @@ # Author: Martin Alnes # Date: 2009-04-09 # -from ufl import (Coefficient, FiniteElement, FunctionSpace, Mesh, VectorElement, derivative, dot, dx, grad, inner, - triangle) +from ufl import Coefficient, FunctionSpace, Mesh, derivative, dot, dx, grad, inner, triangle +from ufl.finiteelement import FiniteElement +from ufl.pullback import identity_pullback +from ufl.sobolevspace import H1 cell = triangle -X = VectorElement("Lagrange", cell, 1) -Y = FiniteElement("Lagrange", cell, 1) -domain = Mesh(VectorElement("Lagrange", cell, 1)) +X = FiniteElement("Lagrange", cell, 1, (2, ), identity_pullback, H1) +Y = FiniteElement("Lagrange", cell, 1, (), identity_pullback, H1) +domain = Mesh(FiniteElement("Lagrange", cell, 1, (2, ), identity_pullback, H1)) X_space = FunctionSpace(domain, X) Y_space = FunctionSpace(domain, Y) diff --git a/demo/HarmonicMap2.py b/demo/HarmonicMap2.py index 811d8b666..dfc47c3b1 100644 --- a/demo/HarmonicMap2.py +++ b/demo/HarmonicMap2.py @@ -3,14 +3,16 @@ # Author: Martin Alnes # Date: 2009-04-09 # -from ufl import (Coefficient, FiniteElement, FunctionSpace, Mesh, VectorElement, derivative, dot, dx, grad, inner, - split, triangle) +from ufl import Coefficient, FunctionSpace, Mesh, derivative, dot, dx, grad, inner, split, triangle +from ufl.finiteelement import FiniteElement, MixedElement +from ufl.pullback import identity_pullback +from ufl.sobolevspace import H1 cell = triangle -X = VectorElement("Lagrange", cell, 1) -Y = FiniteElement("Lagrange", cell, 1) -M = X * Y -domain = Mesh(VectorElement("Lagrange", cell, 1)) +X = FiniteElement("Lagrange", cell, 1, (2, ), identity_pullback, H1) +Y = FiniteElement("Lagrange", cell, 1, (), identity_pullback, H1) +M = MixedElement([X, Y]) +domain = Mesh(FiniteElement("Lagrange", cell, 1, (2, ), identity_pullback, H1)) space = FunctionSpace(domain, M) u = Coefficient(space) diff --git a/demo/Heat.py b/demo/Heat.py index 241cc708e..f695341b2 100644 --- a/demo/Heat.py +++ b/demo/Heat.py @@ -20,12 +20,14 @@ # The bilinear form a(v, u1) and linear form L(v) for # one backward Euler step with the heat equation. # -from ufl import (Coefficient, Constant, FiniteElement, FunctionSpace, Mesh, TestFunction, TrialFunction, VectorElement, - dot, dx, grad, triangle) +from ufl import Coefficient, Constant, FunctionSpace, Mesh, TestFunction, TrialFunction, dot, dx, grad, triangle +from ufl.finiteelement import FiniteElement +from ufl.pullback import identity_pullback +from ufl.sobolevspace import H1 cell = triangle -element = FiniteElement("Lagrange", cell, 1) -domain = Mesh(VectorElement("Lagrange", cell, 1)) +element = FiniteElement("Lagrange", cell, 1, (), identity_pullback, H1) +domain = Mesh(FiniteElement("Lagrange", cell, 1, (2, ), identity_pullback, H1)) space = FunctionSpace(domain, element) v = TestFunction(space) # Test function diff --git a/demo/HornSchunck.py b/demo/HornSchunck.py index 139b8a0e3..d3ec43840 100644 --- a/demo/HornSchunck.py +++ b/demo/HornSchunck.py @@ -3,14 +3,16 @@ # http://code.google.com/p/debiosee/wiki/DemosOptiocFlowHornSchunck # but not tested so this could contain errors! # -from ufl import (Coefficient, Constant, FiniteElement, FunctionSpace, Mesh, VectorElement, derivative, dot, dx, grad, - inner, triangle) +from ufl import Coefficient, Constant, FunctionSpace, Mesh, derivative, dot, dx, grad, inner, triangle +from ufl.finiteelement import FiniteElement +from ufl.pullback import identity_pullback +from ufl.sobolevspace import H1 # Finite element spaces for scalar and vector fields cell = triangle -S = FiniteElement("CG", cell, 1) -V = VectorElement("CG", cell, 1) -domain = Mesh(VectorElement("Lagrange", cell, 1)) +S = FiniteElement("Lagrange", cell, 1, (), identity_pullback, H1) +V = FiniteElement("Lagrange", cell, 1, (2, ), identity_pullback, H1) +domain = Mesh(FiniteElement("Lagrange", cell, 1, (2, ), identity_pullback, H1)) S_space = FunctionSpace(domain, S) V_space = FunctionSpace(domain, V) diff --git a/demo/HyperElasticity.py b/demo/HyperElasticity.py index 289afbb0a..abbc8314c 100644 --- a/demo/HyperElasticity.py +++ b/demo/HyperElasticity.py @@ -3,22 +3,24 @@ # Date: 2008-12-22 # +from ufl import (Coefficient, Constant, FacetNormal, FunctionSpace, Identity, Mesh, SpatialCoordinate, TestFunction, + TrialFunction, derivative, det, diff, dot, ds, dx, exp, grad, inner, inv, tetrahedron, tr, variable) +from ufl.finiteelement import FiniteElement # Modified by Garth N. Wells, 2009 -from ufl import (Coefficient, Constant, FacetNormal, FiniteElement, FunctionSpace, Identity, Mesh, SpatialCoordinate, - TensorElement, TestFunction, TrialFunction, VectorElement, derivative, det, diff, dot, ds, dx, exp, - grad, inner, inv, tetrahedron, tr, variable) +from ufl.pullback import identity_pullback +from ufl.sobolevspace import H1 # Cell and its properties cell = tetrahedron -domain = Mesh(VectorElement("Lagrange", cell, 1)) +domain = Mesh(FiniteElement("Lagrange", cell, 1, (3, ), identity_pullback, H1)) d = cell.geometric_dimension() N = FacetNormal(domain) x = SpatialCoordinate(domain) # Elements -u_element = VectorElement("CG", cell, 2) -p_element = FiniteElement("CG", cell, 1) -A_element = TensorElement("CG", cell, 1) +u_element = FiniteElement("Lagrange", cell, 2, (3, ), identity_pullback, H1) +p_element = FiniteElement("Lagrange", cell, 1, (), identity_pullback, H1) +A_element = FiniteElement("Lagrange", cell, 1, (3, 3), identity_pullback, H1) # Spaces u_space = FunctionSpace(domain, u_element) diff --git a/demo/HyperElasticity1D.py b/demo/HyperElasticity1D.py index aaacc2ce9..9dfcbed96 100644 --- a/demo/HyperElasticity1D.py +++ b/demo/HyperElasticity1D.py @@ -2,12 +2,14 @@ # Author: Martin Sandve Alnes # Date: 2008-10-03 # -from ufl import (Coefficient, Constant, FiniteElement, FunctionSpace, Mesh, VectorElement, derivative, dx, exp, - interval, variable) +from ufl import Coefficient, Constant, FunctionSpace, Mesh, derivative, dx, exp, interval, variable +from ufl.finiteelement import FiniteElement +from ufl.pullback import identity_pullback +from ufl.sobolevspace import H1 cell = interval -element = FiniteElement("CG", cell, 2) -domain = Mesh(VectorElement("Lagrange", cell, 1)) +element = FiniteElement("Lagrange", cell, 2, (), identity_pullback, H1) +domain = Mesh(FiniteElement("Lagrange", cell, 1, (1, ), identity_pullback, H1)) space = FunctionSpace(domain, element) u = Coefficient(space) diff --git a/demo/L2norm.py b/demo/L2norm.py index ca8d78cda..31050a9df 100644 --- a/demo/L2norm.py +++ b/demo/L2norm.py @@ -2,10 +2,13 @@ # Author: Martin Sandve Alnes # Date: 2008-10-03 # -from ufl import Coefficient, FiniteElement, FunctionSpace, Mesh, VectorElement, dx, triangle +from ufl import Coefficient, FunctionSpace, Mesh, dx, triangle +from ufl.finiteelement import FiniteElement +from ufl.pullback import identity_pullback +from ufl.sobolevspace import H1 -element = FiniteElement("Lagrange", triangle, 1) -domain = Mesh(VectorElement("Lagrange", triangle, 1)) +element = FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1) +domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) space = FunctionSpace(domain, element) f = Coefficient(space) diff --git a/demo/Mass.py b/demo/Mass.py index a1603c146..4f083140b 100644 --- a/demo/Mass.py +++ b/demo/Mass.py @@ -20,10 +20,13 @@ # Last changed: 2009-03-02 # # The bilinear form for a mass matrix. -from ufl import FiniteElement, FunctionSpace, Mesh, TestFunction, TrialFunction, VectorElement, dx, triangle +from ufl import FunctionSpace, Mesh, TestFunction, TrialFunction, dx, triangle +from ufl.finiteelement import FiniteElement +from ufl.pullback import identity_pullback +from ufl.sobolevspace import H1 -element = FiniteElement("Lagrange", triangle, 1) -domain = Mesh(VectorElement("Lagrange", triangle, 1)) +element = FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1) +domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) space = FunctionSpace(domain, element) u = TrialFunction(space) diff --git a/demo/MassAD.py b/demo/MassAD.py index 57dabdc7a..ed36e8c2c 100644 --- a/demo/MassAD.py +++ b/demo/MassAD.py @@ -2,10 +2,13 @@ # Author: Martin Sandve Alnes # Date: 2008-10-28 # -from ufl import Coefficient, FiniteElement, FunctionSpace, Mesh, VectorElement, derivative, dx, triangle +from ufl import Coefficient, FunctionSpace, Mesh, derivative, dx, triangle +from ufl.finiteelement import FiniteElement +from ufl.pullback import identity_pullback +from ufl.sobolevspace import H1 -element = FiniteElement("Lagrange", triangle, 1) -domain = Mesh(VectorElement("Lagrange", triangle, 1)) +element = FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1) +domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) space = FunctionSpace(domain, element) u = Coefficient(space) diff --git a/demo/MixedElasticity.py b/demo/MixedElasticity.py index 0fac9d36b..6fe1a96a3 100644 --- a/demo/MixedElasticity.py +++ b/demo/MixedElasticity.py @@ -17,8 +17,11 @@ # # First added: 2008-10-03 # Last changed: 2011-07-22 -from ufl import (FunctionSpace, Mesh, MixedElement, TestFunctions, TrialFunctions, VectorElement, as_vector, div, dot, - dx, inner, skew, tetrahedron, tr) +from ufl import (FunctionSpace, Mesh, TestFunctions, TrialFunctions, as_vector, div, dot, dx, inner, skew, tetrahedron, + tr) +from ufl.finiteelement import FiniteElement, MixedElement +from ufl.pullback import contravariant_piola, identity_pullback +from ufl.sobolevspace import H1, L2, HDiv def skw(tau): @@ -32,18 +35,13 @@ def skw(tau): # Finite element exterior calculus syntax r = 1 -S = VectorElement("P Lambda", cell, r, form_degree=n - 1) -V = VectorElement("P Lambda", cell, r - 1, form_degree=n) -Q = VectorElement("P Lambda", cell, r - 1, form_degree=n) +S = FiniteElement("vector BDM", cell, r, (3, 3), contravariant_piola, HDiv) +V = FiniteElement("Discontinuous Lagrange", cell, r - 1, (3, ), identity_pullback, L2) +Q = FiniteElement("Discontinuous Lagrange", cell, r - 1, (3, ), identity_pullback, L2) -# Alternative syntax: -# S = VectorElement("BDM", cell, r) -# V = VectorElement("Discontinuous Lagrange", cell, r-1) -# Q = VectorElement("Discontinuous Lagrange", cell, r-1) +W = MixedElement([S, V, Q]) -W = MixedElement(S, V, Q) - -domain = Mesh(VectorElement("Lagrange", cell, 1)) +domain = Mesh(FiniteElement("Lagrange", cell, 1, (3, ), identity_pullback, H1)) space = FunctionSpace(domain, W) (sigma, u, gamma) = TrialFunctions(space) diff --git a/demo/MixedMixedElement.py b/demo/MixedMixedElement.py index ddffe3c97..0d1b1df20 100644 --- a/demo/MixedMixedElement.py +++ b/demo/MixedMixedElement.py @@ -16,8 +16,11 @@ # along with UFL. If not, see . # # A mixed element of mixed elements -from ufl import FiniteElement, triangle +from ufl import triangle +from ufl.finiteelement import FiniteElement, MixedElement +from ufl.pullback import identity_pullback +from ufl.sobolevspace import H1 -P3 = FiniteElement("Lagrange", triangle, 3) +P3 = FiniteElement("Lagrange", triangle, 3, (), identity_pullback, H1) -element = (P3 * P3) * (P3 * P3) +element = MixedElement([[P3, P3], [P3, P3]]) diff --git a/demo/MixedPoisson.py b/demo/MixedPoisson.py index 57a112521..1580372a3 100644 --- a/demo/MixedPoisson.py +++ b/demo/MixedPoisson.py @@ -23,15 +23,17 @@ # a mixed formulation of Poisson's equation with BDM # (Brezzi-Douglas-Marini) elements. # -from ufl import (Coefficient, FiniteElement, FunctionSpace, Mesh, TestFunctions, TrialFunctions, VectorElement, div, - dot, dx, triangle) +from ufl import Coefficient, FunctionSpace, Mesh, TestFunctions, TrialFunctions, div, dot, dx, triangle +from ufl.finiteelement import FiniteElement, MixedElement +from ufl.pullback import contravariant_piola, identity_pullback +from ufl.sobolevspace import H1, HDiv cell = triangle -BDM1 = FiniteElement("Brezzi-Douglas-Marini", cell, 1) -DG0 = FiniteElement("Discontinuous Lagrange", cell, 0) +BDM1 = FiniteElement("Brezzi-Douglas-Marini", cell, 1, (2, ), contravariant_piola, HDiv) +DG0 = FiniteElement("Discontinuous Lagrange", cell, 0, (), identity_pullback, H1) -element = BDM1 * DG0 -domain = Mesh(VectorElement("Lagrange", cell, 1)) +element = MixedElement([BDM1, DG0]) +domain = Mesh(FiniteElement("Lagrange", cell, 1, (2, ), identity_pullback, H1)) space = FunctionSpace(domain, element) dg0_space = FunctionSpace(domain, DG0) diff --git a/demo/MixedPoisson2.py b/demo/MixedPoisson2.py index 4711bb97e..29268a309 100644 --- a/demo/MixedPoisson2.py +++ b/demo/MixedPoisson2.py @@ -3,14 +3,16 @@ # Modified by: Martin Sandve Alnes # Date: 2009-02-12 # -from ufl import (FacetNormal, FiniteElement, FunctionSpace, Mesh, TestFunctions, TrialFunctions, VectorElement, div, - dot, ds, dx, tetrahedron) +from ufl import FacetNormal, FunctionSpace, Mesh, TestFunctions, TrialFunctions, div, dot, ds, dx, tetrahedron +from ufl.finiteelement import FiniteElement, MixedElement +from ufl.pullback import contravariant_piola, identity_pullback +from ufl.sobolevspace import H1, HDiv cell = tetrahedron -RT = FiniteElement("Raviart-Thomas", cell, 1) -DG = FiniteElement("DG", cell, 0) -MX = RT * DG -domain = Mesh(VectorElement("Lagrange", cell, 1)) +RT = FiniteElement("Raviart-Thomas", cell, 1, (3, ), contravariant_piola, HDiv) +DG = FiniteElement("DG", cell, 0, (), identity_pullback, H1) +MX = MixedElement([RT, DG]) +domain = Mesh(FiniteElement("Lagrange", cell, 1, (3, ), identity_pullback, H1)) space = FunctionSpace(domain, MX) (u, p) = TrialFunctions(space) diff --git a/demo/NavierStokes.py b/demo/NavierStokes.py index 0dc87bdda..14dfa5f56 100644 --- a/demo/NavierStokes.py +++ b/demo/NavierStokes.py @@ -21,11 +21,14 @@ # # The bilinear form for the nonlinear term in the # Navier-Stokes equations with fixed convective velocity. -from ufl import Coefficient, FunctionSpace, Mesh, TestFunction, TrialFunction, VectorElement, dot, dx, grad, tetrahedron +from ufl import Coefficient, FunctionSpace, Mesh, TestFunction, TrialFunction, dot, dx, grad, tetrahedron +from ufl.finiteelement import FiniteElement +from ufl.pullback import identity_pullback +from ufl.sobolevspace import H1 cell = tetrahedron -element = VectorElement("Lagrange", cell, 1) -domain = Mesh(VectorElement("Lagrange", cell, 1)) +element = FiniteElement("Lagrange", cell, 1, (3, ), identity_pullback, H1) +domain = Mesh(FiniteElement("Lagrange", cell, 1, (3, ), identity_pullback, H1)) space = FunctionSpace(domain, element) v = TestFunction(space) diff --git a/demo/NeumannProblem.py b/demo/NeumannProblem.py index 85563e411..d384c4315 100644 --- a/demo/NeumannProblem.py +++ b/demo/NeumannProblem.py @@ -17,11 +17,13 @@ # # The bilinear form a(v, u) and linear form L(v) for # Poisson's equation with Neumann boundary conditions. -from ufl import (Coefficient, FunctionSpace, Mesh, TestFunction, TrialFunction, VectorElement, ds, dx, grad, inner, - triangle) +from ufl import Coefficient, FunctionSpace, Mesh, TestFunction, TrialFunction, ds, dx, grad, inner, triangle +from ufl.finiteelement import FiniteElement +from ufl.pullback import identity_pullback +from ufl.sobolevspace import H1 -element = VectorElement("Lagrange", triangle, 1) -domain = Mesh(VectorElement("Lagrange", triangle, 1)) +element = FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1) +domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) space = FunctionSpace(domain, element) v = TestFunction(space) diff --git a/demo/NonlinearPoisson.py b/demo/NonlinearPoisson.py index f71b2c41c..7604459b5 100644 --- a/demo/NonlinearPoisson.py +++ b/demo/NonlinearPoisson.py @@ -1,8 +1,10 @@ -from ufl import (Coefficient, FiniteElement, FunctionSpace, Mesh, TestFunction, TrialFunction, VectorElement, dot, dx, - grad, triangle) +from ufl import Coefficient, FunctionSpace, Mesh, TestFunction, TrialFunction, dot, dx, grad, triangle +from ufl.finiteelement import FiniteElement +from ufl.pullback import identity_pullback +from ufl.sobolevspace import H1 -element = FiniteElement("Lagrange", triangle, 1) -domain = Mesh(VectorElement("Lagrange", triangle, 1)) +element = FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1) +domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) space = FunctionSpace(domain, element) v = TestFunction(space) diff --git a/demo/P5tet.py b/demo/P5tet.py index 44dbb7fb5..5114b6724 100644 --- a/demo/P5tet.py +++ b/demo/P5tet.py @@ -16,6 +16,9 @@ # along with UFL. If not, see . # # A fifth degree Lagrange finite element on a tetrahedron -from ufl import FiniteElement, tetrahedron +from ufl import tetrahedron +from ufl.finiteelement import FiniteElement +from ufl.pullback import identity_pullback +from ufl.sobolevspace import H1 -element = FiniteElement("Lagrange", tetrahedron, 5) +element = FiniteElement("Lagrange", tetrahedron, 5, (), identity_pullback, H1) diff --git a/demo/P5tri.py b/demo/P5tri.py index eb616e896..ebfd6ba6f 100644 --- a/demo/P5tri.py +++ b/demo/P5tri.py @@ -16,6 +16,9 @@ # along with UFL. If not, see . # # A fifth degree Lagrange finite element on a triangle -from ufl import FiniteElement, triangle +from ufl import triangle +from ufl.finiteelement import FiniteElement +from ufl.pullback import identity_pullback +from ufl.sobolevspace import H1 -element = FiniteElement("Lagrange", triangle, 5) +element = FiniteElement("Lagrange", triangle, 5, (), identity_pullback, H1) diff --git a/demo/Poisson.py b/demo/Poisson.py index 470b844ec..779273391 100644 --- a/demo/Poisson.py +++ b/demo/Poisson.py @@ -21,11 +21,13 @@ # Last changed: 2009-03-02 # # The bilinear form a(v, u) and linear form L(v) for Poisson's equation. -from ufl import (Coefficient, FiniteElement, FunctionSpace, Mesh, TestFunction, TrialFunction, VectorElement, dx, grad, - inner, triangle) +from ufl import Coefficient, FunctionSpace, Mesh, TestFunction, TrialFunction, dx, grad, inner, triangle +from ufl.finiteelement import FiniteElement +from ufl.pullback import identity_pullback +from ufl.sobolevspace import H1 -element = FiniteElement("Lagrange", triangle, 1) -domain = Mesh(VectorElement("Lagrange", triangle, 1)) +element = FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1) +domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) space = FunctionSpace(domain, element) u = TrialFunction(space) diff --git a/demo/PoissonDG.py b/demo/PoissonDG.py index 90c0c3af1..fd289213a 100644 --- a/demo/PoissonDG.py +++ b/demo/PoissonDG.py @@ -21,12 +21,15 @@ # The bilinear form a(v, u) and linear form L(v) for # Poisson's equation in a discontinuous Galerkin (DG) # formulation. -from ufl import (Coefficient, Constant, FacetNormal, FiniteElement, FunctionSpace, Mesh, TestFunction, TrialFunction, - VectorElement, avg, dot, dS, ds, dx, grad, inner, jump, triangle) +from ufl import (Coefficient, Constant, FacetNormal, FunctionSpace, Mesh, TestFunction, TrialFunction, avg, dot, dS, ds, + dx, grad, inner, jump, triangle) +from ufl.finiteelement import FiniteElement +from ufl.pullback import identity_pullback +from ufl.sobolevspace import H1, L2 cell = triangle -element = FiniteElement("Discontinuous Lagrange", cell, 1) -domain = Mesh(VectorElement("Lagrange", cell, 1)) +element = FiniteElement("Discontinuous Lagrange", cell, 1, (), identity_pullback, L2) +domain = Mesh(FiniteElement("Lagrange", cell, 1, (2, ), identity_pullback, H1)) space = FunctionSpace(domain, element) v = TestFunction(space) diff --git a/demo/PoissonSystem.py b/demo/PoissonSystem.py index d42a96eca..29967d025 100644 --- a/demo/PoissonSystem.py +++ b/demo/PoissonSystem.py @@ -21,12 +21,14 @@ # # The bilinear form a(v, u) and linear form L(v) for # Poisson's equation in system form (vector-valued). -from ufl import (Coefficient, FunctionSpace, Mesh, TestFunction, TrialFunction, VectorElement, dot, dx, grad, inner, - triangle) +from ufl import Coefficient, FunctionSpace, Mesh, TestFunction, TrialFunction, dot, dx, grad, inner, triangle +from ufl.finiteelement import FiniteElement +from ufl.pullback import identity_pullback +from ufl.sobolevspace import H1 cell = triangle -element = VectorElement("Lagrange", cell, 1) -domain = Mesh(VectorElement("Lagrange", cell, 1)) +element = FiniteElement("Lagrange", cell, 1, (2, ), identity_pullback, H1) +domain = Mesh(FiniteElement("Lagrange", cell, 1, (2, ), identity_pullback, H1)) space = FunctionSpace(domain, element) v = TestFunction(space) diff --git a/demo/PowAD.py b/demo/PowAD.py index 1c0ad4e92..106f1f799 100644 --- a/demo/PowAD.py +++ b/demo/PowAD.py @@ -2,11 +2,13 @@ # Author: Martin Sandve Alnes # Date: 2008-10-03 # -from ufl import (Coefficient, FiniteElement, FunctionSpace, Mesh, TestFunction, TrialFunction, VectorElement, - derivative, dx, triangle) +from ufl import Coefficient, FunctionSpace, Mesh, TestFunction, TrialFunction, derivative, dx, triangle +from ufl.finiteelement import FiniteElement +from ufl.pullback import identity_pullback +from ufl.sobolevspace import H1 -element = FiniteElement("Lagrange", triangle, 1) -domain = Mesh(VectorElement("Lagrange", triangle, 1)) +element = FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1) +domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) space = FunctionSpace(domain, element) v = TestFunction(space) diff --git a/demo/ProjectionSystem.py b/demo/ProjectionSystem.py index e082b99e1..7548a0b22 100644 --- a/demo/ProjectionSystem.py +++ b/demo/ProjectionSystem.py @@ -1,8 +1,10 @@ -from ufl import (Coefficient, FiniteElement, FunctionSpace, Mesh, TestFunction, TrialFunction, VectorElement, dx, - triangle) +from ufl import Coefficient, FunctionSpace, Mesh, TestFunction, TrialFunction, dx, triangle +from ufl.finiteelement import FiniteElement +from ufl.pullback import identity_pullback +from ufl.sobolevspace import H1 -element = FiniteElement("Lagrange", triangle, 1) -domain = Mesh(VectorElement("Lagrange", triangle, 1)) +element = FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1) +domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) space = FunctionSpace(domain, element) v = TestFunction(space) u = TrialFunction(space) diff --git a/demo/QuadratureElement.py b/demo/QuadratureElement.py index 3ab666e29..4e01cd31b 100644 --- a/demo/QuadratureElement.py +++ b/demo/QuadratureElement.py @@ -20,15 +20,17 @@ # # The linearised bilinear form a(u,v) and linear form L(v) for # the nonlinear equation - div (1+u) grad u = f (non-linear Poisson) -from ufl import (Coefficient, FiniteElement, FunctionSpace, Mesh, TestFunction, TrialFunction, VectorElement, dot, dx, - grad, i, triangle) +from ufl import Coefficient, FunctionSpace, Mesh, TestFunction, TrialFunction, dot, dx, grad, i, triangle +from ufl.finiteelement import FiniteElement +from ufl.pullback import identity_pullback +from ufl.sobolevspace import H1 -element = FiniteElement("Lagrange", triangle, 2) -domain = Mesh(VectorElement("Lagrange", triangle, 1)) +element = FiniteElement("Lagrange", triangle, 2, (), identity_pullback, H1) +domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) space = FunctionSpace(domain, element) -QE = FiniteElement("Quadrature", triangle, 2, quad_scheme="default") -sig = VectorElement("Quadrature", triangle, 1, quad_scheme="default") +QE = FiniteElement("Quadrature", triangle, 2, (), identity_pullback, H1) +sig = FiniteElement("Quadrature", triangle, 1, (2, ), identity_pullback, H1) qe_space = FunctionSpace(domain, QE) sig_space = FunctionSpace(domain, sig) diff --git a/demo/RestrictedElement.py b/demo/RestrictedElement.py deleted file mode 100644 index 6a07fa274..000000000 --- a/demo/RestrictedElement.py +++ /dev/null @@ -1,29 +0,0 @@ -# Copyright (C) 2009 Kristian B. Oelgaard -# -# This file is part of UFL. -# -# UFL is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# UFL is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with UFL. If not, see . -# -# Restriction of a finite element. -# The below syntax show how one can restrict a higher order Lagrange element -# to only take into account those DOFs that live on the facets. -from ufl import FiniteElement, FunctionSpace, Mesh, TestFunction, TrialFunction, VectorElement, avg, dS, ds, triangle - -# Restricted element -CG_R = FiniteElement("Lagrange", triangle, 4)["facet"] -domain = Mesh(VectorElement("Lagrange", triangle, 1)) -space = FunctionSpace(domain, CG_R) -u_r = TrialFunction(space) -v_r = TestFunction(space) -a = avg(v_r) * avg(u_r) * dS + v_r * u_r * ds diff --git a/demo/Stiffness.py b/demo/Stiffness.py index 43f10ea2e..32bf00a54 100644 --- a/demo/Stiffness.py +++ b/demo/Stiffness.py @@ -2,10 +2,13 @@ # Author: Martin Sandve Alnes # Date: 2008-10-03 # -from ufl import FiniteElement, FunctionSpace, Mesh, TestFunction, TrialFunction, VectorElement, dot, dx, grad, triangle +from ufl import FunctionSpace, Mesh, TestFunction, TrialFunction, dot, dx, grad, triangle +from ufl.finiteelement import FiniteElement +from ufl.pullback import identity_pullback +from ufl.sobolevspace import H1 -element = FiniteElement("Lagrange", triangle, 1) -domain = Mesh(VectorElement("Lagrange", triangle, 1)) +element = FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1) +domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) space = FunctionSpace(domain, element) u = TrialFunction(space) diff --git a/demo/StiffnessAD.py b/demo/StiffnessAD.py index affadc86b..59a8bcba4 100644 --- a/demo/StiffnessAD.py +++ b/demo/StiffnessAD.py @@ -2,11 +2,13 @@ # Author: Martin Sandve Alnes # Date: 2008-10-30 # -from ufl import (Coefficient, FiniteElement, FunctionSpace, Mesh, VectorElement, action, adjoint, derivative, dx, grad, - inner, triangle) +from ufl import Coefficient, FunctionSpace, Mesh, action, adjoint, derivative, dx, grad, inner, triangle +from ufl.finiteelement import FiniteElement +from ufl.pullback import identity_pullback +from ufl.sobolevspace import H1 -element = FiniteElement("Lagrange", triangle, 1) -domain = Mesh(VectorElement("Lagrange", triangle, 1)) +element = FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1) +domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) space = FunctionSpace(domain, element) w = Coefficient(space) diff --git a/demo/Stokes.py b/demo/Stokes.py index f128f7779..b4d240ffd 100644 --- a/demo/Stokes.py +++ b/demo/Stokes.py @@ -20,14 +20,16 @@ # # The bilinear form a(v, u) and Linear form L(v) for the Stokes # equations using a mixed formulation (Taylor-Hood elements). -from ufl import (Coefficient, FiniteElement, FunctionSpace, Mesh, TestFunctions, TrialFunctions, VectorElement, div, - dot, dx, grad, inner, triangle) +from ufl import Coefficient, FunctionSpace, Mesh, TestFunctions, TrialFunctions, div, dot, dx, grad, inner, triangle +from ufl.finiteelement import FiniteElement, MixedElement +from ufl.pullback import identity_pullback +from ufl.sobolevspace import H1 cell = triangle -P2 = VectorElement("Lagrange", cell, 2) -P1 = FiniteElement("Lagrange", cell, 1) -TH = P2 * P1 -domain = Mesh(VectorElement("Lagrange", cell, 1)) +P2 = FiniteElement("Lagrange", cell, 2, (2, ), identity_pullback, H1) +P1 = FiniteElement("Lagrange", cell, 1, (), identity_pullback, H1) +TH = MixedElement([P2, P1]) +domain = Mesh(FiniteElement("Lagrange", cell, 1, (2, ), identity_pullback, H1)) space = FunctionSpace(domain, TH) p2_space = FunctionSpace(domain, P2) diff --git a/demo/StokesEquation.py b/demo/StokesEquation.py index ea0c62bd7..949551846 100644 --- a/demo/StokesEquation.py +++ b/demo/StokesEquation.py @@ -19,14 +19,17 @@ # equations using a mixed formulation (Taylor-Hood elements) in # combination with the lhs() and rhs() operators to extract the # bilinear and linear forms from an expression F = 0. -from ufl import (Coefficient, FiniteElement, FunctionSpace, Mesh, TestFunctions, TrialFunctions, VectorElement, div, - dot, dx, grad, inner, lhs, rhs, triangle) +from ufl import (Coefficient, FunctionSpace, Mesh, TestFunctions, TrialFunctions, div, dot, dx, grad, inner, lhs, rhs, + triangle) +from ufl.finiteelement import FiniteElement, MixedElement +from ufl.pullback import identity_pullback +from ufl.sobolevspace import H1 cell = triangle -P2 = VectorElement("Lagrange", cell, 2) -P1 = FiniteElement("Lagrange", cell, 1) -TH = P2 * P1 -domain = Mesh(VectorElement("Lagrange", cell, 1)) +P2 = FiniteElement("Lagrange", cell, 2, (2, ), identity_pullback, H1) +P1 = FiniteElement("Lagrange", cell, 1, (), identity_pullback, H1) +TH = MixedElement([P2, P1]) +domain = Mesh(FiniteElement("Lagrange", cell, 1, (2, ), identity_pullback, H1)) space = FunctionSpace(domain, TH) p2_space = FunctionSpace(domain, P2) diff --git a/demo/SubDomain.py b/demo/SubDomain.py index c55e59d75..4205a7790 100644 --- a/demo/SubDomain.py +++ b/demo/SubDomain.py @@ -17,11 +17,13 @@ # # This example illustrates how to define a form over a # given subdomain of a mesh, in this case a functional. -from ufl import (Coefficient, FiniteElement, FunctionSpace, Mesh, TestFunction, TrialFunction, VectorElement, ds, dx, - tetrahedron) +from ufl import Coefficient, FunctionSpace, Mesh, TestFunction, TrialFunction, ds, dx, tetrahedron +from ufl.finiteelement import FiniteElement +from ufl.pullback import identity_pullback +from ufl.sobolevspace import H1 -element = FiniteElement("CG", tetrahedron, 1) -domain = Mesh(VectorElement("Lagrange", tetrahedron, 1)) +element = FiniteElement("Lagrange", tetrahedron, 1, (), identity_pullback, H1) +domain = Mesh(FiniteElement("Lagrange", tetrahedron, 1, (3, ), identity_pullback, H1)) space = FunctionSpace(domain, element) v = TestFunction(space) diff --git a/demo/SubDomains.py b/demo/SubDomains.py index f8a19caa6..55e9ddbe5 100644 --- a/demo/SubDomains.py +++ b/demo/SubDomains.py @@ -17,10 +17,13 @@ # # This simple example illustrates how forms can be defined on different sub domains. # It is supported for all three integral types. -from ufl import FiniteElement, FunctionSpace, Mesh, TestFunction, TrialFunction, VectorElement, ds, dS, dx, tetrahedron +from ufl import FunctionSpace, Mesh, TestFunction, TrialFunction, ds, dS, dx, tetrahedron +from ufl.finiteelement import FiniteElement +from ufl.pullback import identity_pullback +from ufl.sobolevspace import H1 -element = FiniteElement("CG", tetrahedron, 1) -domain = Mesh(VectorElement("Lagrange", tetrahedron, 1)) +element = FiniteElement("Lagrange", tetrahedron, 1, (), identity_pullback, H1) +domain = Mesh(FiniteElement("Lagrange", tetrahedron, 1, (3, ), identity_pullback, H1)) space = FunctionSpace(domain, element) v = TestFunction(space) diff --git a/demo/TensorWeightedPoisson.py b/demo/TensorWeightedPoisson.py index 0c76f32bf..6ebc8e3c1 100644 --- a/demo/TensorWeightedPoisson.py +++ b/demo/TensorWeightedPoisson.py @@ -17,12 +17,14 @@ # # The bilinear form a(v, u) and linear form L(v) for # tensor-weighted Poisson's equation. -from ufl import (Coefficient, FiniteElement, FunctionSpace, Mesh, TensorElement, TestFunction, TrialFunction, - VectorElement, dx, grad, inner, triangle) +from ufl import Coefficient, FunctionSpace, Mesh, TestFunction, TrialFunction, dx, grad, inner, triangle +from ufl.finiteelement import FiniteElement +from ufl.pullback import identity_pullback +from ufl.sobolevspace import H1, L2 -P1 = FiniteElement("Lagrange", triangle, 1) -P0 = TensorElement("Discontinuous Lagrange", triangle, 0, shape=(2, 2)) -domain = Mesh(VectorElement("Lagrange", triangle, 1)) +P1 = FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1) +P0 = FiniteElement("Discontinuous Lagrange", triangle, 0, (2, 2), identity_pullback, L2) +domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) p1_space = FunctionSpace(domain, P1) p0_space = FunctionSpace(domain, P0) diff --git a/demo/VectorLaplaceGradCurl.py b/demo/VectorLaplaceGradCurl.py index e8e281baf..f5d9b863f 100644 --- a/demo/VectorLaplaceGradCurl.py +++ b/demo/VectorLaplaceGradCurl.py @@ -18,8 +18,10 @@ # The bilinear form a(v, u) and linear form L(v) for the Hodge Laplace # problem using 0- and 1-forms. Intended to demonstrate use of Nedelec # elements. -from ufl import (Coefficient, FiniteElement, FunctionSpace, Mesh, TestFunctions, TrialFunctions, VectorElement, curl, - dx, grad, inner, tetrahedron) +from ufl import Coefficient, FunctionSpace, Mesh, TestFunctions, TrialFunctions, curl, dx, grad, inner, tetrahedron +from ufl.finiteelement import FiniteElement, MixedElement +from ufl.pullback import covariant_piola, identity_pullback +from ufl.sobolevspace import H1, HCurl def HodgeLaplaceGradCurl(space, fspace): @@ -36,13 +38,13 @@ def HodgeLaplaceGradCurl(space, fspace): cell = tetrahedron order = 1 -GRAD = FiniteElement("Lagrange", cell, order) -CURL = FiniteElement("N1curl", cell, order) +GRAD = FiniteElement("Lagrange", cell, order, (), identity_pullback, H1) +CURL = FiniteElement("N1curl", cell, order, (3, ), covariant_piola, HCurl) -VectorLagrange = VectorElement("Lagrange", cell, order + 1) +VectorLagrange = FiniteElement("Lagrange", cell, order + 1, (3, ), identity_pullback, H1) -domain = Mesh(VectorElement("Lagrange", cell, 1)) -space = FunctionSpace(domain, GRAD * CURL) +domain = Mesh(FiniteElement("Lagrange", cell, 1, (3, ), identity_pullback, H1)) +space = FunctionSpace(domain, MixedElement([GRAD, CURL])) fspace = FunctionSpace(domain, VectorLagrange) a, L = HodgeLaplaceGradCurl(space, fspace) diff --git a/demo/_TensorProductElement.py b/demo/_TensorProductElement.py index 373feee99..9e6fb6ef0 100644 --- a/demo/_TensorProductElement.py +++ b/demo/_TensorProductElement.py @@ -17,12 +17,15 @@ # # First added: 2012-08-16 # Last changed: 2012-08-16 -from ufl import (FiniteElement, FunctionSpace, Mesh, TensorProductElement, TestFunction, TrialFunction, dx, interval, - tetrahedron, triangle) +from ufl import (FunctionSpace, Mesh, TensorProductElement, TestFunction, TrialFunction, dx, interval, tetrahedron, + triangle) +from ufl.finiteelement import FiniteElement +from ufl.pullback import identity_pullback +from ufl.sobolevspace import H1, L2 -V0 = FiniteElement("CG", triangle, 1) -V1 = FiniteElement("DG", interval, 0) -V2 = FiniteElement("DG", tetrahedron, 0) +V0 = FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1) +V1 = FiniteElement("DG", interval, 0, (), identity_pullback, L2) +V2 = FiniteElement("DG", tetrahedron, 0, (), identity_pullback, L2) V = TensorProductElement(V0, V1, V2) diff --git a/setup.cfg b/setup.cfg index ccd6223e3..4bbece177 100644 --- a/setup.cfg +++ b/setup.cfg @@ -59,7 +59,10 @@ ci = [flake8] max-line-length = 120 +builtins = ufl exclude = doc/sphinx/source/conf.py +per-file-ignores = + */__init__.py: F401 [pydocstyle] convention = google diff --git a/test/test_algorithms.py b/test/test_algorithms.py index 9beba0d9c..4b87c2851 100755 --- a/test/test_algorithms.py +++ b/test/test_algorithms.py @@ -6,23 +6,26 @@ import pytest -from ufl import (Argument, Coefficient, FacetNormal, FiniteElement, FunctionSpace, Mesh, TestFunction, TrialFunction, - VectorElement, adjoint, div, dot, ds, dx, grad, inner, triangle) +from ufl import (Argument, Coefficient, FacetNormal, FunctionSpace, Mesh, TestFunction, TrialFunction, adjoint, div, + dot, ds, dx, grad, inner, triangle) from ufl.algorithms import (expand_derivatives, expand_indices, extract_arguments, extract_coefficients, extract_elements, extract_unique_elements) from ufl.corealg.traversal import post_traversal, pre_traversal, unique_post_traversal, unique_pre_traversal +from ufl.finiteelement import FiniteElement +from ufl.pullback import identity_pullback +from ufl.sobolevspace import H1 # TODO: add more tests, covering all utility algorithms @pytest.fixture(scope='module') def element(): - return FiniteElement("CG", triangle, 1) + return FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1) @pytest.fixture(scope='module') def domain(): - return Mesh(VectorElement("CG", triangle, 1)) + return Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) @pytest.fixture(scope='module') @@ -45,10 +48,10 @@ def coefficients(space): @pytest.fixture -def forms(arguments, coefficients, space): +def forms(arguments, coefficients, domain): v, u = arguments c, f = coefficients - n = FacetNormal(space) + n = FacetNormal(domain) a = u * v * dx L = f * v * dx b = u * v * dx(0) + inner(c * grad(u), grad(v)) * dx(1) + dot(n, grad(u)) * v * ds + f * v * dx @@ -64,13 +67,13 @@ def test_extract_coefficients_vs_fixture(coefficients, forms): assert coefficients == tuple(extract_coefficients(forms[2])) -def test_extract_elements_and_extract_unique_elements(forms, domain): +def test_extract_elements_and_extract_unique_elements(forms, element, domain): b = forms[2] integrals = b.integrals_by_type("cell") integrals[0].integrand() - element1 = FiniteElement("CG", triangle, 1) - element2 = FiniteElement("CG", triangle, 1) + element1 = element + element2 = element space1 = FunctionSpace(domain, element1) space2 = FunctionSpace(domain, element2) @@ -83,9 +86,7 @@ def test_extract_elements_and_extract_unique_elements(forms, domain): assert extract_unique_elements(a) == (element1,) -def test_pre_and_post_traversal(domain): - element = FiniteElement("CG", "triangle", 1) - space = FunctionSpace(domain, element) +def test_pre_and_post_traversal(space): v = TestFunction(space) f = Coefficient(space) g = Coefficient(space) @@ -103,7 +104,7 @@ def test_pre_and_post_traversal(domain): def test_expand_indices(domain): - element = FiniteElement("Lagrange", triangle, 2) + element = FiniteElement("Lagrange", triangle, 2, (), identity_pullback, H1) space = FunctionSpace(domain, element) v = TestFunction(space) u = TrialFunction(space) @@ -124,8 +125,8 @@ def evaluate(form): def test_adjoint(domain): cell = triangle - V1 = FiniteElement("CG", cell, 1) - V2 = FiniteElement("CG", cell, 2) + V1 = FiniteElement("Lagrange", cell, 1, (), identity_pullback, H1) + V2 = FiniteElement("Lagrange", cell, 2, (), identity_pullback, H1) s1 = FunctionSpace(domain, V1) s2 = FunctionSpace(domain, V2) @@ -139,17 +140,17 @@ def test_adjoint(domain): assert u2.number() < v2.number() a = u * v * dx - a_arg_degrees = [arg.ufl_element().degree() for arg in extract_arguments(a)] + a_arg_degrees = [arg.ufl_element().embedded_superdegree for arg in extract_arguments(a)] assert a_arg_degrees == [2, 1] b = adjoint(a) - b_arg_degrees = [arg.ufl_element().degree() for arg in extract_arguments(b)] + b_arg_degrees = [arg.ufl_element().embedded_superdegree for arg in extract_arguments(b)] assert b_arg_degrees == [1, 2] c = adjoint(a, (u2, v2)) - c_arg_degrees = [arg.ufl_element().degree() for arg in extract_arguments(c)] + c_arg_degrees = [arg.ufl_element().embedded_superdegree for arg in extract_arguments(c)] assert c_arg_degrees == [1, 2] d = adjoint(b) - d_arg_degrees = [arg.ufl_element().degree() for arg in extract_arguments(d)] + d_arg_degrees = [arg.ufl_element().embedded_superdegree for arg in extract_arguments(d)] assert d_arg_degrees == [2, 1] diff --git a/test/test_apply_algebra_lowering.py b/test/test_apply_algebra_lowering.py index 7b5c6db23..e1f496eb9 100755 --- a/test/test_apply_algebra_lowering.py +++ b/test/test_apply_algebra_lowering.py @@ -1,51 +1,60 @@ import pytest -from ufl import (Coefficient, FiniteElement, FunctionSpace, Index, Mesh, TensorElement, VectorElement, as_tensor, - interval, sqrt, tetrahedron, triangle) +from ufl import Coefficient, FunctionSpace, Index, Mesh, as_tensor, interval, sqrt, triangle from ufl.algorithms.renumbering import renumber_indices from ufl.compound_expressions import cross_expr, determinant_expr, inverse_expr +from ufl.finiteelement import FiniteElement +from ufl.pullback import identity_pullback +from ufl.sobolevspace import H1 @pytest.fixture def A0(request): return Coefficient(FunctionSpace( - Mesh(VectorElement("CG", interval, 1)), FiniteElement("CG", interval, 1))) + Mesh(FiniteElement("Lagrange", interval, 1, (1, ), identity_pullback, H1)), + FiniteElement("Lagrange", interval, 1, (), identity_pullback, H1))) @pytest.fixture def A1(request): return Coefficient(FunctionSpace( - Mesh(VectorElement("CG", interval, 1)), TensorElement("CG", interval, 1))) + Mesh(FiniteElement("Lagrange", interval, 1, (1, ), identity_pullback, H1)), + FiniteElement("Lagrange", interval, 1, (1, 1), identity_pullback, H1))) @pytest.fixture def A2(request): return Coefficient(FunctionSpace( - Mesh(VectorElement("CG", triangle, 1)), TensorElement("CG", triangle, 1))) + Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)), + FiniteElement("Lagrange", triangle, 1, (2, 2), identity_pullback, H1))) @pytest.fixture def A3(request): return Coefficient(FunctionSpace( - Mesh(VectorElement("CG", tetrahedron, 1)), TensorElement("CG", tetrahedron, 1))) + Mesh(FiniteElement("Lagrange", triangle, 1, (3, ), identity_pullback, H1)), + FiniteElement("Lagrange", triangle, 1, (3, 3), identity_pullback, H1))) @pytest.fixture def A21(request): return Coefficient(FunctionSpace( - Mesh(VectorElement("CG", triangle, 1)), TensorElement("CG", triangle, 1, shape=(2, 1)))) + Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)), + FiniteElement("Lagrange", triangle, 1, (2, 1), identity_pullback, H1))) @pytest.fixture def A31(request): return Coefficient(FunctionSpace( - Mesh(VectorElement("CG", triangle, 1)), TensorElement("CG", triangle, 1, shape=(3, 1)))) + Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)), + FiniteElement("Lagrange", triangle, 1, (3, 1), identity_pullback, H1))) @pytest.fixture def A32(request): return Coefficient(FunctionSpace( - Mesh(VectorElement("CG", triangle, 1)), TensorElement("CG", triangle, 1, shape=(3, 2)))) + Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)), + FiniteElement("Lagrange", triangle, 1, (3, 2), identity_pullback, H1))) def test_determinant0(A0): diff --git a/test/test_apply_function_pullbacks.py b/test/test_apply_function_pullbacks.py index e352d09e9..3cb73e898 100755 --- a/test/test_apply_function_pullbacks.py +++ b/test/test_apply_function_pullbacks.py @@ -1,15 +1,17 @@ import numpy -from ufl import (Cell, Coefficient, FiniteElement, FunctionSpace, Mesh, TensorElement, VectorElement, as_tensor, - as_vector, dx, indices, triangle) -from ufl.algorithms.apply_function_pullbacks import apply_single_function_pullbacks +from ufl import Cell, Coefficient, FunctionSpace, Mesh, as_tensor, as_vector, dx, indices, triangle from ufl.algorithms.renumbering import renumber_indices from ufl.classes import Jacobian, JacobianDeterminant, JacobianInverse, ReferenceValue +from ufl.finiteelement import FiniteElement, MixedElement, SymmetricElement +from ufl.pullback import (contravariant_piola, covariant_piola, double_contravariant_piola, double_covariant_piola, + identity_pullback, l2_piola) +from ufl.sobolevspace import H1, L2, HCurl, HDiv, HDivDiv, HEin def check_single_function_pullback(g, mappings): expected = mappings[g] - actual = apply_single_function_pullbacks(ReferenceValue(g), g.ufl_element()) + actual = g.ufl_element().pullback.apply(ReferenceValue(g)) assert expected.ufl_shape == actual.ufl_shape for idx in numpy.ndindex(actual.ufl_shape): rexp = renumber_indices(expected[idx]) @@ -33,30 +35,34 @@ def check_single_function_pullback(g, mappings): def test_apply_single_function_pullbacks_triangle3d(): triangle3d = Cell("triangle", geometric_dimension=3) cell = triangle3d - domain = Mesh(VectorElement("Lagrange", cell, 1)) - - UL2 = FiniteElement("DG L2", cell, 1) - U0 = FiniteElement("DG", cell, 0) - U = FiniteElement("CG", cell, 1) - V = VectorElement("CG", cell, 1) - Vd = FiniteElement("RT", cell, 1) - Vc = FiniteElement("N1curl", cell, 1) - T = TensorElement("CG", cell, 1) - S = TensorElement("CG", cell, 1, symmetry=True) - COV2T = FiniteElement("Regge", cell, 0) # (0, 2)-symmetric tensors - CONTRA2T = FiniteElement("HHJ", cell, 0) # (2, 0)-symmetric tensors - - Uml2 = UL2*UL2 - Um = U*U - Vm = U*V - Vdm = V*Vd - Vcm = Vd*Vc - Tm = Vc*T - Sm = T*S - - Vd0 = Vd*U0 # case from failing ffc demo - - W = S*T*Vc*Vd*V*U + domain = Mesh(FiniteElement("Lagrange", cell, 1, (3, ), identity_pullback, H1)) + + UL2 = FiniteElement("Discontinuous Lagrange", cell, 1, (), l2_piola, L2) + U0 = FiniteElement("Discontinuous Lagrange", cell, 0, (), identity_pullback, L2) + U = FiniteElement("Lagrange", cell, 1, (), identity_pullback, H1) + V = FiniteElement("Lagrange", cell, 1, (3, ), identity_pullback, H1) + Vd = FiniteElement("Raviart-Thomas", cell, 1, (2, ), contravariant_piola, HDiv) + Vc = FiniteElement("N1curl", cell, 1, (2, ), covariant_piola, HCurl) + T = FiniteElement("Lagrange", cell, 1, (3, 3), identity_pullback, H1) + S = SymmetricElement( + {(0, 0): 0, (1, 0): 1, (2, 0): 2, (0, 1): 1, (1, 1): 3, (2, 1): 4, (0, 2): 2, (1, 2): 4, (2, 2): 5}, + [FiniteElement("Lagrange", cell, 1, (), identity_pullback, H1) for _ in range(6)]) + # (0, 2)-symmetric tensors + COV2T = FiniteElement("Regge", cell, 0, (2, 2), double_covariant_piola, HEin) + # (2, 0)-symmetric tensors + CONTRA2T = FiniteElement("HHJ", cell, 0, (2, 2), double_contravariant_piola, HDivDiv) + + Uml2 = MixedElement([UL2, UL2]) + Um = MixedElement([U, U]) + Vm = MixedElement([U, V]) + Vdm = MixedElement([V, Vd]) + Vcm = MixedElement([Vd, Vc]) + Tm = MixedElement([Vc, T]) + Sm = MixedElement([T, S]) + + Vd0 = MixedElement([Vd, U0]) # case from failing ffc demo + + W = MixedElement([S, T, Vc, Vd, V, U]) ul2 = Coefficient(FunctionSpace(domain, UL2)) u = Coefficient(FunctionSpace(domain, U)) @@ -229,25 +235,26 @@ def test_apply_single_function_pullbacks_triangle3d(): def test_apply_single_function_pullbacks_triangle(): cell = triangle - domain = Mesh(VectorElement("Lagrange", cell, 1)) - - Ul2 = FiniteElement("DG L2", cell, 1) - U = FiniteElement("CG", cell, 1) - V = VectorElement("CG", cell, 1) - Vd = FiniteElement("RT", cell, 1) - Vc = FiniteElement("N1curl", cell, 1) - T = TensorElement("CG", cell, 1) - S = TensorElement("CG", cell, 1, symmetry=True) - - Uml2 = Ul2*Ul2 - Um = U*U - Vm = U*V - Vdm = V*Vd - Vcm = Vd*Vc - Tm = Vc*T - Sm = T*S - - W = S*T*Vc*Vd*V*U + domain = Mesh(FiniteElement("Lagrange", cell, 1, (2, ), identity_pullback, H1)) + + Ul2 = FiniteElement("Discontinuous Lagrange", cell, 1, (), l2_piola, L2) + U = FiniteElement("Lagrange", cell, 1, (), identity_pullback, H1) + V = FiniteElement("Lagrange", cell, 1, (2, ), identity_pullback, H1) + Vd = FiniteElement("Raviart-Thomas", cell, 1, (2, ), contravariant_piola, HDiv) + Vc = FiniteElement("N1curl", cell, 1, (2, ), covariant_piola, HCurl) + T = FiniteElement("Lagrange", cell, 1, (2, 2), identity_pullback, H1) + S = SymmetricElement({(0, 0): 0, (0, 1): 1, (1, 0): 1, (1, 1): 2}, [ + FiniteElement("Lagrange", cell, 1, (), identity_pullback, H1) for i in range(3)]) + + Uml2 = MixedElement([Ul2, Ul2]) + Um = MixedElement([U, U]) + Vm = MixedElement([U, V]) + Vdm = MixedElement([V, Vd]) + Vcm = MixedElement([Vd, Vc]) + Tm = MixedElement([Vc, T]) + Sm = MixedElement([T, S]) + + W = MixedElement([S, T, Vc, Vd, V, U]) ul2 = Coefficient(FunctionSpace(domain, Ul2)) u = Coefficient(FunctionSpace(domain, U)) diff --git a/test/test_apply_restrictions.py b/test/test_apply_restrictions.py index 860b76d48..efb9ad514 100755 --- a/test/test_apply_restrictions.py +++ b/test/test_apply_restrictions.py @@ -1,18 +1,20 @@ from pytest import raises -from ufl import (Coefficient, FacetNormal, FiniteElement, FunctionSpace, Mesh, SpatialCoordinate, VectorElement, - as_tensor, grad, i, triangle) +from ufl import Coefficient, FacetNormal, FunctionSpace, Mesh, SpatialCoordinate, as_tensor, grad, i, triangle from ufl.algorithms.apply_restrictions import apply_default_restrictions, apply_restrictions from ufl.algorithms.renumbering import renumber_indices +from ufl.finiteelement import FiniteElement +from ufl.pullback import identity_pullback +from ufl.sobolevspace import H1, L2 def test_apply_restrictions(): cell = triangle - V0 = FiniteElement("DG", cell, 0) - V1 = FiniteElement("Lagrange", cell, 1) - V2 = FiniteElement("Lagrange", cell, 2) + V0 = FiniteElement("Discontinuous Lagrange", cell, 0, (), identity_pullback, L2) + V1 = FiniteElement("Lagrange", cell, 1, (), identity_pullback, H1) + V2 = FiniteElement("Lagrange", cell, 2, (), identity_pullback, H1) - domain = Mesh(VectorElement("Lagrange", cell, 1)) + domain = Mesh(FiniteElement("Lagrange", cell, 1, (2, ), identity_pullback, H1)) v0_space = FunctionSpace(domain, V0) v1_space = FunctionSpace(domain, V1) v2_space = FunctionSpace(domain, V2) diff --git a/test/test_arithmetic.py b/test/test_arithmetic.py index f32e7bcfa..858f9ddd6 100755 --- a/test/test_arithmetic.py +++ b/test/test_arithmetic.py @@ -1,6 +1,9 @@ -from ufl import (Identity, Mesh, SpatialCoordinate, VectorElement, as_matrix, as_ufl, as_vector, elem_div, elem_mult, - elem_op, sin, tetrahedron, triangle) +from ufl import (Identity, Mesh, SpatialCoordinate, as_matrix, as_ufl, as_vector, elem_div, elem_mult, elem_op, sin, + tetrahedron, triangle) from ufl.classes import ComplexValue, Division, FloatValue, IntValue +from ufl.finiteelement import FiniteElement +from ufl.pullback import identity_pullback +from ufl.sobolevspace import H1 def test_scalar_casting(self): @@ -16,13 +19,13 @@ def test_scalar_casting(self): def test_ufl_float_division(self): - domain = Mesh(VectorElement("Lagrange", triangle, 1)) + domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) d = SpatialCoordinate(domain)[0] / 10.0 # TODO: Use mock instead of x self.assertIsInstance(d, Division) def test_float_ufl_division(self): - domain = Mesh(VectorElement("Lagrange", triangle, 1)) + domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) d = 3.14 / SpatialCoordinate(domain)[0] # TODO: Use mock instead of x self.assertIsInstance(d, Division) @@ -65,7 +68,7 @@ def test_elem_mult(self): def test_elem_mult_on_matrices(self): - domain = Mesh(VectorElement("Lagrange", triangle, 1)) + domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) A = as_matrix(((1, 2), (3, 4))) B = as_matrix(((4, 5), (6, 7))) @@ -83,7 +86,7 @@ def test_elem_mult_on_matrices(self): def test_elem_div(self): - domain = Mesh(VectorElement("Lagrange", tetrahedron, 1)) + domain = Mesh(FiniteElement("Lagrange", tetrahedron, 1, (3, ), identity_pullback, H1)) x, y, z = SpatialCoordinate(domain) A = as_matrix(((x, y, z), (3, 4, 5))) B = as_matrix(((7, 8, 9), (z, x, y))) @@ -91,7 +94,7 @@ def test_elem_div(self): def test_elem_op(self): - domain = Mesh(VectorElement("Lagrange", tetrahedron, 1)) + domain = Mesh(FiniteElement("Lagrange", tetrahedron, 1, (3, ), identity_pullback, H1)) x, y, z = SpatialCoordinate(domain) A = as_matrix(((x, y, z), (3, 4, 5))) self.assertEqual(elem_op(sin, A), as_matrix(((sin(x), sin(y), sin(z)), diff --git a/test/test_automatic_differentiation.py b/test/test_automatic_differentiation.py index f8b2e840b..37bfd02f1 100755 --- a/test/test_automatic_differentiation.py +++ b/test/test_automatic_differentiation.py @@ -8,24 +8,27 @@ import pytest from ufl import (And, Argument, CellDiameter, CellVolume, Circumradius, Coefficient, Constant, FacetArea, FacetNormal, - FiniteElement, FunctionSpace, Identity, Jacobian, JacobianDeterminant, JacobianInverse, - MaxCellEdgeLength, MaxFacetEdgeLength, Mesh, MinCellEdgeLength, MinFacetEdgeLength, Not, Or, - PermutationSymbol, SpatialCoordinate, TensorElement, VectorElement, acos, as_matrix, as_tensor, as_ufl, - as_vector, asin, atan, bessel_I, bessel_J, bessel_K, bessel_Y, cofac, conditional, cos, cross, - derivative, det, dev, diff, dot, eq, erf, exp, ge, grad, gt, indices, inner, interval, inv, le, ln, lt, - ne, outer, replace, sin, skew, sqrt, sym, tan, tetrahedron, tr, triangle, variable) + FunctionSpace, Identity, Jacobian, JacobianDeterminant, JacobianInverse, MaxCellEdgeLength, + MaxFacetEdgeLength, Mesh, MinCellEdgeLength, MinFacetEdgeLength, Not, Or, PermutationSymbol, + SpatialCoordinate, acos, as_matrix, as_tensor, as_ufl, as_vector, asin, atan, bessel_I, bessel_J, + bessel_K, bessel_Y, cofac, conditional, cos, cross, derivative, det, dev, diff, dot, eq, erf, exp, ge, + grad, gt, indices, inner, interval, inv, le, ln, lt, ne, outer, replace, sin, skew, sqrt, sym, tan, + tetrahedron, tr, triangle, variable) from ufl.algorithms import expand_derivatives from ufl.conditional import Conditional from ufl.corealg.traversal import unique_post_traversal +from ufl.finiteelement import FiniteElement +from ufl.pullback import identity_pullback +from ufl.sobolevspace import H1, L2 class ExpressionCollection(object): def __init__(self, cell): self.cell = cell - domain = Mesh(VectorElement("Lagrange", cell, 1)) - d = cell.geometric_dimension() + domain = Mesh(FiniteElement("Lagrange", cell, 1, (d, ), identity_pullback, H1)) + x = SpatialCoordinate(domain) n = FacetNormal(domain) c = CellVolume(domain) @@ -45,9 +48,9 @@ def __init__(self, cell): ident = Identity(d) eps = PermutationSymbol(d) - U = FiniteElement("U", cell, None) - V = VectorElement("U", cell, None) - W = TensorElement("U", cell, None) + U = FiniteElement("Undefined", cell, None, (), identity_pullback, L2) + V = FiniteElement("Undefined", cell, None, (d, ), identity_pullback, L2) + W = FiniteElement("Undefined", cell, None, (d, d), identity_pullback, L2) u_space = FunctionSpace(domain, U) v_space = FunctionSpace(domain, V) diff --git a/test/test_book_snippets.py b/test/test_book_snippets.py deleted file mode 100755 index ed1088405..000000000 --- a/test/test_book_snippets.py +++ /dev/null @@ -1,547 +0,0 @@ -"""Test book snippets. - -This file contains snippets from the FEniCS book, -and allows us to test that these can still run -with future versions of UFL. Please don't change -these and please do keep UFL compatible with these -snippets as long as possible. -""" - -from ufl import (Argument, Cell, Coefficient, Coefficients, Constant, Dx, FiniteElement, Identity, Index, MixedElement, - SpatialCoordinate, TensorConstant, TensorElement, TestFunction, TestFunctions, TrialFunction, - TrialFunctions, VectorConstant, VectorElement, action, adjoint, as_matrix, as_tensor, as_ufl, - conditional, cos, derivative, diff, dot, ds, dx, exp, grad, i, indices, inner, j, k, lt, outer, pi, - replace, sensitivity_rhs, sin, split, system, tetrahedron, tr, triangle, variable) -from ufl.algorithms import (Transformer, compute_form_data, expand_compounds, expand_derivatives, expand_indices, - post_traversal, tree_format) -from ufl.corealg.multifunction import MultiFunction - - -def test_uflcode_269(self): - # Finite element spaces - cell = tetrahedron - element = VectorElement("Lagrange", cell, 1) - - # Form arguments - phi0 = TestFunction(element) - phi1 = TrialFunction(element) - u = Coefficient(element) - c1 = Constant(cell) - c2 = Constant(cell) - - # Deformation gradient Fij = dXi/dxj - ident = Identity(cell.geometric_dimension()) - F = ident + grad(u) - - # Right Cauchy-Green strain tensor C with invariants - C = variable(F.T*F) - I_C = tr(C) - II_C = (I_C**2 - tr(C*C))/2 - - # Mooney-Rivlin constitutive law - W = c1*(I_C-3) + c2*(II_C-3) - - # Second Piola-Kirchoff stress tensor - S = 2*diff(W, C) - - # Weak forms - L = inner(F*S, grad(phi0))*dx - derivative(L, u, phi1) - - -def test_uflcode_316(self): - shapestring = 'triangle' - cell = Cell(shapestring) # noqa: F841 - - -def test_uflcode_323(self): - cell = tetrahedron # noqa: F841 - - -def test_uflcode_356(self): - cell = tetrahedron - - P = FiniteElement("Lagrange", cell, 1) - V = VectorElement("Lagrange", cell, 2) - T = TensorElement("DG", cell, 0, symmetry=True) - - TH = V*P # noqa: F841 - ME = MixedElement(T, V, P) # noqa: F841 - - -def test_uflcode_400(self): - V = FiniteElement("CG", triangle, 1) - f = Coefficient(V) - g = Coefficient(V) - h = Coefficient(V) - w = Coefficient(V) - v = TestFunction(V) - u = TrialFunction(V) - # ... - a = w*dot(grad(u), grad(v))*dx # noqa: F841 - L = f*v*dx + g**2*v*ds(0) + h*v*ds(1) # noqa: F841 - - -def test_uflcode_469(self): - V = FiniteElement("CG", triangle, 1) - f = Coefficient(V) - g = Coefficient(V) - h = Coefficient(V) - v = TestFunction(V) - # ... - dx02 = dx(0, {"integration_order": 2}) - dx14 = dx(1, {"integration_order": 4}) - dx12 = dx(1, {"integration_order": 2}) - L = f*v*dx02 + g*v*dx14 + h*v*dx12 # noqa: F841 - - -def test_uflcode_552(self): - element = FiniteElement("CG", triangle, 1) - # ... - phi = Argument(element, 2) # noqa: F841 - v = TestFunction(element) # noqa: F841 - u = TrialFunction(element) # noqa: F841 - - -def test_uflcode_563(self): - cell = triangle - element = FiniteElement("CG", cell, 1) - # ... - w = Coefficient(element) # noqa: F841 - c = Constant(cell) # noqa: F841 - v = VectorConstant(cell) # noqa: F841 - M = TensorConstant(cell) # noqa: F841 - - -def test_uflcode_574(self): - V0 = FiniteElement("CG", triangle, 1) - V1 = V0 - # ... - V = V0*V1 - u = Coefficient(V) - u0, u1 = split(u) - - -def test_uflcode_582(self): - V0 = FiniteElement("CG", triangle, 1) - V1 = V0 - # ... - V = V0*V1 - u = Coefficient(V) - u0, u1 = split(u) - # ... - v0, v1 = TestFunctions(V) - u0, u1 = TrialFunctions(V) - f0, f1 = Coefficients(V) - - -def test_uflcode_644(self): - V = VectorElement("CG", triangle, 1) - u = Coefficient(V) - v = Coefficient(V) - # ... - A = outer(u, v) - Aij = A[i, j] # noqa: F841 - - -def test_uflcode_651(self): - V = VectorElement("CG", triangle, 1) - u = Coefficient(V) - v = Coefficient(V) - # ... - Aij = v[j]*u[i] - A = as_tensor(Aij, (i, j)) # noqa: F841 - - -def test_uflcode_671(self): - i = Index() # noqa: F841 - j, k, l = indices(3) # noqa: F841, E741 - - -def test_uflcode_684(self): - V = VectorElement("CG", triangle, 1) - v = Coefficient(V) - # ... - th = pi/2 - A = as_matrix([[cos(th), -sin(th)], - [sin(th), cos(th)]]) - u = A*v # noqa: F841 - - -def test_uflcode_824(self): - V = VectorElement("CG", triangle, 1) - f = Coefficient(V) - # ... - df = Dx(f, i) - df = f.dx(i) # noqa: F841 - - -def test_uflcode_886(self): - cell = triangle - # ... - x = SpatialCoordinate(cell) # Original: x = cell.x - g = sin(x[0]) - v = variable(g) - f = exp(v**2) - h = diff(f, v) # noqa: F841 - # ... - # print v - # print h - - -def test_uflcode_930(self): - condition = lt(1, 0) - true_value = 1 - false_value = 0 - # ... - f = conditional(condition, true_value, false_value) # noqa: F841 - - -def test_uflcode_1026(self): - element = FiniteElement("CG", triangle, 1) - # ... - v = TestFunction(element) - u = TrialFunction(element) - w = Coefficient(element) - f = 0.5*w**2*dx - F = derivative(f, w, v) - J = derivative(F, w, u) # noqa: F841 - - -def test_uflcode_1050(self): - Vx = VectorElement("Lagrange", triangle, 1) - Vy = FiniteElement("Lagrange", triangle, 1) - u = Coefficient(Vx*Vy) - x, y = split(u) - f = inner(grad(x), grad(x))*dx + y*dot(x, x)*dx - F = derivative(f, u) - J = derivative(F, u) # noqa: F841 - - -def test_uflcode_1085(self): - cell = triangle - # ... - V = VectorElement("Lagrange", cell, 1) - T = TensorElement("Lagrange", cell, 1) - u = TrialFunction(V) - v = TestFunction(V) - M = Coefficient(T) - a = M[i, j]*u[k].dx(j)*v[k].dx(i)*dx - astar = adjoint(a) # noqa: F841 - - -def test_uflcode_1120(self): - cell = triangle - # ... - V = FiniteElement("Lagrange", cell, 1) - v = TestFunction(V) - f = Coefficient(V) - g = Coefficient(V) - L = f**2 / (2*g)*v*dx - L2 = replace(L, {f: g, g: 3}) # noqa: F841 - L3 = g**2 / 6*v*dx # noqa: F841 - - -def test_uflcode_1157(self): - cell = triangle - # ... - V = FiniteElement("Lagrange", cell, 1) - u = TrialFunction(V) - v = TestFunction(V) - f = Coefficient(V) - pde = u*v*dx - f*v*dx - a, L = system(pde) - - -def test_uflcode_1190(self): - cell = triangle - element = FiniteElement("Lagrange", cell, 1) - V = element - u = TrialFunction(V) - v = TestFunction(V) - f = Coefficient(V) - c = variable(Coefficient(V)) - pde = c*u*v*dx - c*f*v*dx - a, L = system(pde) - # ... - u = Coefficient(element) - sL = diff(L, c) - action(diff(a, c), u) # noqa: F841 - - -def test_uflcode_1195(self): - cell = triangle - element = FiniteElement("Lagrange", cell, 1) - V = element - u = TrialFunction(V) - v = TestFunction(V) - f = Coefficient(V) - c = variable(Coefficient(V)) - pde = c*u*v*dx - c*f*v*dx - a, L = system(pde) - u = Coefficient(element) - # ... - sL = sensitivity_rhs(a, u, L, c) # noqa: F841 - - -def test_uflcode_1365(self): - e = 0 - v = variable(e) - f = sin(v) - g = diff(f, v) # noqa: F841 - - -def test_python_1446(self): - cell = triangle - V = FiniteElement("Lagrange", cell, 1) - u = TrialFunction(V) - v = TestFunction(V) - c = Constant(cell) - f = Coefficient(V) - e = c*f**2*u*v # noqa: F841 - - # The linearized Graph functionality has been removed from UFL: - # from ufl.algorithms import Graph, partition - # G = Graph(e) - # V, E, = G - - # print(("str(e) = %s\n" % str(e))) - # print(("\n".join("V[%d] = %s" % (i, v) for (i, v) in enumerate(V)), "\n")) - # print(("\n".join("E[%d] = %s" % (i, e) for (i, e) in enumerate(E)), "\n")) - - -def test_python_1512(self): - cell = triangle - V = FiniteElement("Lagrange", cell, 1) - u = TrialFunction(V) - v = TestFunction(V) - c = Constant(cell) - f = Coefficient(V) - e = c*f**2*u*v # noqa: F841 - - # The linearized Graph functionality has been removed from UFL: - # from ufl.algorithms import Graph, partition - # G = Graph(e) - # V, E, = G - # ... - # Vin = G.Vin() - # Vout = G.Vout() - - -def test_python_1557(self): - cell = triangle - V = FiniteElement("Lagrange", cell, 1) - u = TrialFunction(V) - v = TestFunction(V) - c = Constant(cell) - f = Coefficient(V) - e = c*f**2*u*v # noqa: F841 - - # The linearized Graph functionality has been removed from UFL: - # from ufl.algorithms import Graph, partition - # G = Graph(e) - # V, E, = G - # ... - # partitions, keys = partition(G) - # for deps in sorted(partitions.keys()): - # P = partitions[deps] - # print "The following depends on", tuple(deps) - # for i in sorted(P): - # print "V[%d] = %s" % (i, V[i]) - # ... - # v = V[i] - - -def test_uflcode_1901(self): - cell = triangle - element = FiniteElement("Lagrange", cell, 1) - # ... - v = Argument(element, 2) # noqa: F841 - w = Coefficient(element) # noqa: F841 - - -def test_python_1942(self): - def walk(expression, pre_action, post_action): - pre_action(expression) - for o in expression.ufl_operands: - walk(o) - post_action(expression) - - -def test_python_1955(self): - def post_traversal(root): - for o in root.ufl_operands: - yield post_traversal(o) - yield root - - -def test_python_1963(self): - def post_action(e): - # print str(e) - pass - cell = triangle - V = FiniteElement("Lagrange", cell, 1) - u = TrialFunction(V) - v = TestFunction(V) - c = Constant(cell) - f = Coefficient(V) - expression = c*f**2*u*v - # ... - for e in post_traversal(expression): - post_action(e) - - -def test_python_1990(self): - expression = as_ufl(3) - - def int_operation(x): - return 7 - - result = int_operation(expression) - self.assertTrue(result == 7) - - -def test_python_2024(self): - class ExampleFunction(MultiFunction): - - def __init__(self): - MultiFunction.__init__(self) - - def terminal(self, expression): - return "Got a Terminal subtype %s." % type(expression) - - def operator(self, expression): - return "Got an Operator subtype %s." % type(expression) - - def argument(self, expression): - return "Got an Argument." - - def sum(self, expression): - return "Got a Sum." - - m = ExampleFunction() - - cell = triangle - element = FiniteElement("Lagrange", cell, 1) - x = SpatialCoordinate(cell) # Original: x = cell.x - if 0: - print((m(Argument(element, 2)))) - print((m(x))) - print((m(x[0] + x[1]))) - print((m(x[0] * x[1]))) - - -def test_python_2066(self): - def apply(e, multifunction): - ops = [apply(o, multifunction) for o in e.ufl_operands] - return multifunction(e, *ops) - - -def test_python_2087(self): - class Replacer(Transformer): - - def __init__(self, mapping): - Transformer.__init__(self) - self.mapping = mapping - - def operator(self, e, *ops): - return e._ufl_expr_reconstruct_(*ops) - - def terminal(self, e): - return self.mapping.get(e, e) - - f = Constant(triangle) - r = Replacer({f: f**2}) - g = r.visit(2*f) # noqa: F841 - - -def test_python_2189(self): - V = FiniteElement("Lagrange", triangle, 1) - u = TestFunction(V) - v = TrialFunction(V) - f = Coefficient(V) - - # Note no *dx! This is an expression, not a form. - a = dot(grad(f*u), grad(v)) - - ac = expand_compounds(a) - ad = expand_derivatives(ac) - ai = expand_indices(ad) - - af = tree_format(a) # noqa: F841 - acf = tree_format(ac) # noqa: F841 - adf = "\n", tree_format(ad) # noqa: F841 - aif = tree_format(ai) # noqa: F841 - - if 0: - print(("\na: ", str(a), "\n", tree_format(a))) - print(("\nac:", str(ac), "\n", tree_format(ac))) - print(("\nad:", str(ad), "\n", tree_format(ad))) - print(("\nai:", str(ai), "\n", tree_format(ai))) - - -def test_python_2328(self): - cell = triangle - x = SpatialCoordinate(cell) # Original: x = cell.x - e = x[0] + x[1] - # print e((0.5, 0.7)) # prints 1.2 - # ... - self.assertEqual(e((0.5, 0.7)), 1.2) - - -def test_python_2338(self): - cell = triangle - x = SpatialCoordinate(cell) # Original: x = cell.x - # ... - c = Constant(cell) - e = c*(x[0] + x[1]) - # print e((0.5, 0.7), { c: 10 }) # prints 12.0 - # ... - self.assertEqual(e((0.5, 0.7), {c: 10}), 12.0) - - -def test_python_2349(self): - element = VectorElement("Lagrange", triangle, 1) - c = Constant(triangle) - f = Coefficient(element) - e = c*(f[0] + f[1]) - - def fh(x): - return (x[0], x[1]) - # print e((0.5, 0.7), { c: 10, f: fh }) # prints 12.0 - # ... - self.assertEqual(e((0.5, 0.7), {c: 10, f: fh}), 12.0) - - -def test_python_2364(self): - element = FiniteElement("Lagrange", triangle, 1) - g = Coefficient(element) - e = g**2 + g.dx(0)**2 + g.dx(1)**2 - - def gh(x, der=()): - if der == (): - return x[0]*x[1] - if der == (0,): - return x[1] - if der == (1,): - return x[0] - # print e((2, 3), { g: gh }) # prints 49 - # ... - self.assertEqual(e((2, 3), {g: gh}), 49) - - -def test_python_2462(self): - cell = triangle - element = FiniteElement("Lagrange", cell, 1) - V = element - u = TrialFunction(V) - v = TestFunction(V) - f = Coefficient(V) - c = variable(Coefficient(V)) - pde = c*u*v*dx - c*f*v*dx - a, L = system(pde) - u = Coefficient(element) - myform = a - # ... - # print repr(preprocess(myform).preprocessed_form) - # ... - r = repr(compute_form_data(myform).preprocessed_form) # noqa: F841 diff --git a/test/test_change_to_local.py b/test/test_change_to_local.py index 80bdb2101..f0789fbba 100755 --- a/test/test_change_to_local.py +++ b/test/test_change_to_local.py @@ -1,16 +1,19 @@ """Tests of the change to local representaiton algorithms.""" -from ufl import Coefficient, FiniteElement, FunctionSpace, Mesh, VectorElement, as_tensor, grad, indices, triangle +from ufl import Coefficient, FunctionSpace, Mesh, as_tensor, grad, indices, triangle from ufl.algorithms import change_to_reference_grad from ufl.algorithms.renumbering import renumber_indices from ufl.classes import JacobianInverse, ReferenceGrad +from ufl.finiteelement import FiniteElement +from ufl.pullback import identity_pullback +from ufl.sobolevspace import H1 def test_change_to_reference_grad(): cell = triangle - domain = Mesh(cell) - U = FunctionSpace(domain, FiniteElement("CG", cell, 1)) - V = FunctionSpace(domain, VectorElement("CG", cell, 1)) + domain = Mesh(FiniteElement("Lagrange", cell, 1, (2, ), identity_pullback, H1)) + U = FunctionSpace(domain, FiniteElement("Lagrange", cell, 1, (), identity_pullback, H1)) + V = FunctionSpace(domain, FiniteElement("Lagrange", cell, 1, (2, ), identity_pullback, H1)) u = Coefficient(U) v = Coefficient(V) Jinv = JacobianInverse(domain) diff --git a/test/test_change_to_reference_frame.py b/test/test_change_to_reference_frame.py index a7346b195..9b46b510c 100755 --- a/test/test_change_to_reference_frame.py +++ b/test/test_change_to_reference_frame.py @@ -1,7 +1,10 @@ """Tests of the change to reference frame algorithm.""" -from ufl import Coefficient, FiniteElement, FunctionSpace, Mesh, TensorElement, VectorElement, triangle +from ufl import Coefficient, FunctionSpace, Mesh, triangle from ufl.classes import Expr, ReferenceValue +from ufl.finiteelement import FiniteElement +from ufl.pullback import contravariant_piola, identity_pullback +from ufl.sobolevspace import H1, HDiv def change_to_reference_frame(expr): @@ -10,11 +13,11 @@ def change_to_reference_frame(expr): def test_change_unmapped_form_arguments_to_reference_frame(): - U = FiniteElement("CG", triangle, 1) - V = VectorElement("CG", triangle, 1) - T = TensorElement("CG", triangle, 1) + U = FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1) + V = FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1) + T = FiniteElement("Lagrange", triangle, 1, (2, 2), identity_pullback, H1) - domain = Mesh(VectorElement("Lagrange", triangle, 1)) + domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) u_space = FunctionSpace(domain, U) v_space = FunctionSpace(domain, V) t_space = FunctionSpace(domain, T) @@ -28,9 +31,9 @@ def test_change_unmapped_form_arguments_to_reference_frame(): def test_change_hdiv_form_arguments_to_reference_frame(): - V = FiniteElement("RT", triangle, 1) + V = FiniteElement("Raviart-Thomas", triangle, 1, (2, ), contravariant_piola, HDiv) - domain = Mesh(VectorElement("Lagrange", triangle, 1)) + domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) v_space = FunctionSpace(domain, V) expr = Coefficient(v_space) @@ -38,9 +41,9 @@ def test_change_hdiv_form_arguments_to_reference_frame(): def test_change_hcurl_form_arguments_to_reference_frame(): - V = FiniteElement("RT", triangle, 1) + V = FiniteElement("Raviart-Thomas", triangle, 1, (2, ), contravariant_piola, HDiv) - domain = Mesh(VectorElement("Lagrange", triangle, 1)) + domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) v_space = FunctionSpace(domain, V) expr = Coefficient(v_space) diff --git a/test/test_check_arities.py b/test/test_check_arities.py index 0d5521ac2..37ce7a26d 100755 --- a/test/test_check_arities.py +++ b/test/test_check_arities.py @@ -1,16 +1,19 @@ import pytest -from ufl import (Coefficient, FacetNormal, FunctionSpace, Mesh, SpatialCoordinate, TestFunction, TrialFunction, - VectorElement, adjoint, cofac, conj, derivative, ds, dx, grad, inner, tetrahedron) +from ufl import (Coefficient, FacetNormal, FunctionSpace, Mesh, SpatialCoordinate, TestFunction, TrialFunction, adjoint, + cofac, conj, derivative, ds, dx, grad, inner, tetrahedron) from ufl.algorithms.check_arities import ArityMismatch from ufl.algorithms.compute_form_data import compute_form_data +from ufl.finiteelement import FiniteElement +from ufl.pullback import identity_pullback +from ufl.sobolevspace import H1 def test_check_arities(): # Code from bitbucket issue #49 cell = tetrahedron - D = Mesh(cell) - V = FunctionSpace(D, VectorElement("P", cell, 2)) + D = Mesh(FiniteElement("Lagrange", cell, 1, (3, ), identity_pullback, H1)) + V = FunctionSpace(D, FiniteElement("Lagrange", cell, 2, (3, ), identity_pullback, H1)) dv = TestFunction(V) du = TrialFunction(V) @@ -33,8 +36,8 @@ def test_check_arities(): def test_complex_arities(): cell = tetrahedron - D = Mesh(cell) - V = FunctionSpace(D, VectorElement("P", cell, 2)) + D = Mesh(FiniteElement("Lagrange", cell, 1, (3, ), identity_pullback, H1)) + V = FunctionSpace(D, FiniteElement("Lagrange", cell, 2, (3, ), identity_pullback, H1)) v = TestFunction(V) u = TrialFunction(V) diff --git a/test/test_classcoverage.py b/test/test_classcoverage.py index 7b7d6f583..9a61261e3 100755 --- a/test/test_classcoverage.py +++ b/test/test_classcoverage.py @@ -4,18 +4,20 @@ import ufl from ufl import * # noqa: F403, F401 from ufl import (And, Argument, CellDiameter, CellVolume, Circumradius, Coefficient, Constant, FacetArea, FacetNormal, - FiniteElement, FunctionSpace, Identity, Jacobian, JacobianDeterminant, JacobianInverse, - MaxFacetEdgeLength, Mesh, MinFacetEdgeLength, MixedElement, Not, Or, PermutationSymbol, - SpatialCoordinate, TensorConstant, TensorElement, VectorConstant, VectorElement, acos, action, - as_matrix, as_tensor, as_ufl, as_vector, asin, atan, cell_avg, cofac, conditional, cos, cosh, cross, - curl, derivative, det, dev, diff, div, dot, ds, dS, dx, eq, exp, facet_avg, ge, grad, gt, i, inner, - inv, j, k, l, le, ln, lt, nabla_div, nabla_grad, ne, outer, rot, sin, sinh, skew, sqrt, sym, tan, tanh, - tetrahedron, tr, transpose, triangle, variable) + FunctionSpace, Identity, Jacobian, JacobianDeterminant, JacobianInverse, MaxFacetEdgeLength, Mesh, + MinFacetEdgeLength, Not, Or, PermutationSymbol, SpatialCoordinate, TensorConstant, VectorConstant, + acos, action, as_matrix, as_tensor, as_ufl, as_vector, asin, atan, cell_avg, cofac, conditional, cos, + cosh, cross, curl, derivative, det, dev, diff, div, dot, ds, dS, dx, eq, exp, facet_avg, ge, grad, gt, + i, inner, inv, j, k, l, le, ln, lt, nabla_div, nabla_grad, ne, outer, rot, sin, sinh, skew, sqrt, sym, + tan, tanh, tetrahedron, tr, transpose, triangle, variable) from ufl.algorithms import * # noqa: F403, F401 from ufl.classes import * # noqa: F403, F401 from ufl.classes import (Acos, Asin, Atan, CellCoordinate, Cos, Cosh, Exp, Expr, FacetJacobian, FacetJacobianDeterminant, FacetJacobianInverse, FloatValue, IntValue, Ln, Outer, Sin, Sinh, Sqrt, Tan, Tanh, all_ufl_classes) +from ufl.finiteelement import FiniteElement, MixedElement +from ufl.pullback import identity_pullback +from ufl.sobolevspace import H1 has_repr = set() has_dict = set() @@ -106,15 +108,15 @@ def testAll(self): cell = triangle dim = cell.geometric_dimension() - e0 = FiniteElement("CG", cell, 1) - e1 = VectorElement("CG", cell, 1) - e2 = TensorElement("CG", cell, 1) - e3 = MixedElement(e0, e1, e2) + e0 = FiniteElement("Lagrange", cell, 1, (), identity_pullback, H1) + e1 = FiniteElement("Lagrange", cell, 1, (2, ), identity_pullback, H1) + e2 = FiniteElement("Lagrange", cell, 1, (2, 2), identity_pullback, H1) + e3 = MixedElement([e0, e1, e2]) - e13D = VectorElement("CG", tetrahedron, 1) + e13D = FiniteElement("Lagrange", tetrahedron, 1, (3, ), identity_pullback, H1) - domain = Mesh(VectorElement("Lagrange", cell, 1)) - domain3D = Mesh(VectorElement("Lagrange", tetrahedron, 1)) + domain = Mesh(FiniteElement("Lagrange", cell, 1, (dim, ), identity_pullback, H1)) + domain3D = Mesh(FiniteElement("Lagrange", tetrahedron, 1, (3, ), identity_pullback, H1)) e0_space = FunctionSpace(domain, e0) e1_space = FunctionSpace(domain, e1) e2_space = FunctionSpace(domain, e2) @@ -134,7 +136,7 @@ def testAll(self): _test_object(v0, (), ()) _test_object(v1, (dim,), ()) _test_object(v2, (dim, dim), ()) - _test_object(v3, (dim*dim+dim+1,), ()) + _test_object(v3, (1 + dim + dim ** 2, ), ()) f0 = Coefficient(e0_space) f1 = Coefficient(e1_space) @@ -144,7 +146,7 @@ def testAll(self): _test_object(f0, (), ()) _test_object(f1, (dim,), ()) _test_object(f2, (dim, dim), ()) - _test_object(f3, (dim*dim+dim+1,), ()) + _test_object(f3, (1 + dim + dim ** 2, ), ()) c = Constant(domain) _test_object(c, (), ()) @@ -227,7 +229,7 @@ def testAll(self): a = variable(v2) _test_object(a, (dim, dim), ()) a = variable(v3) - _test_object(a, (dim*dim+dim+1,), ()) + _test_object(a, (1 + dim + dim ** 2, ), ()) a = variable(f0) _test_object(a, (), ()) a = variable(f1) @@ -235,7 +237,7 @@ def testAll(self): a = variable(f2) _test_object(a, (dim, dim), ()) a = variable(f3) - _test_object(a, (dim*dim+dim+1,), ()) + _test_object(a, (1 + dim + dim ** 2, ), ()) # a = MultiIndex() diff --git a/test/test_complex.py b/test/test_complex.py index 999485ef1..6f3eda840 100755 --- a/test/test_complex.py +++ b/test/test_complex.py @@ -2,9 +2,9 @@ import pytest -from ufl import (Coefficient, FiniteElement, FunctionSpace, Mesh, TestFunction, TrialFunction, VectorElement, as_tensor, - as_ufl, atan, conditional, conj, cos, cosh, dot, dx, exp, ge, grad, gt, imag, inner, le, ln, lt, - max_value, min_value, outer, real, sin, sqrt, triangle) +from ufl import (Coefficient, FunctionSpace, Mesh, TestFunction, TrialFunction, as_tensor, as_ufl, atan, conditional, + conj, cos, cosh, dot, dx, exp, ge, grad, gt, imag, inner, le, ln, lt, max_value, min_value, outer, + real, sin, sqrt, triangle) from ufl.algebra import Conj, Imag, Real from ufl.algorithms import estimate_total_polynomial_degree from ufl.algorithms.apply_algebra_lowering import apply_algebra_lowering @@ -12,6 +12,9 @@ from ufl.algorithms.formtransformations import compute_form_adjoint from ufl.algorithms.remove_complex_nodes import remove_complex_nodes from ufl.constantvalue import ComplexValue, Zero +from ufl.finiteelement import FiniteElement +from ufl.pullback import identity_pullback +from ufl.sobolevspace import H1 def test_conj(self): @@ -45,8 +48,8 @@ def test_imag(self): def test_compute_form_adjoint(self): cell = triangle - element = FiniteElement('Lagrange', cell, 1) - domain = Mesh(VectorElement('Lagrange', cell, 1)) + element = FiniteElement('Lagrange', cell, 1, (), identity_pullback, H1) + domain = Mesh(FiniteElement("Lagrange", cell, 1, (2, ), identity_pullback, H1)) space = FunctionSpace(domain, element) u = TrialFunction(space) @@ -74,8 +77,8 @@ def test_complex_algebra(self): def test_automatic_simplification(self): cell = triangle - element = FiniteElement("Lagrange", cell, 1) - domain = Mesh(VectorElement('Lagrange', cell, 1)) + element = FiniteElement("Lagrange", cell, 1, (), identity_pullback, H1) + domain = Mesh(FiniteElement("Lagrange", cell, 1, (2, ), identity_pullback, H1)) space = FunctionSpace(domain, element) v = TestFunction(space) @@ -88,8 +91,8 @@ def test_automatic_simplification(self): def test_apply_algebra_lowering_complex(self): cell = triangle - element = FiniteElement("Lagrange", cell, 1) - domain = Mesh(VectorElement('Lagrange', cell, 1)) + element = FiniteElement("Lagrange", cell, 1, (), identity_pullback, H1) + domain = Mesh(FiniteElement("Lagrange", cell, 1, (2, ), identity_pullback, H1)) space = FunctionSpace(domain, element) v = TestFunction(space) @@ -117,8 +120,8 @@ def test_apply_algebra_lowering_complex(self): def test_remove_complex_nodes(self): cell = triangle - element = FiniteElement("Lagrange", cell, 1) - domain = Mesh(VectorElement('Lagrange', cell, 1)) + element = FiniteElement("Lagrange", cell, 1, (), identity_pullback, H1) + domain = Mesh(FiniteElement("Lagrange", cell, 1, (2, ), identity_pullback, H1)) space = FunctionSpace(domain, element) u = TrialFunction(space) @@ -140,8 +143,8 @@ def test_remove_complex_nodes(self): def test_comparison_checker(self): cell = triangle - element = FiniteElement("Lagrange", cell, 1) - domain = Mesh(VectorElement('Lagrange', cell, 1)) + element = FiniteElement("Lagrange", cell, 1, (), identity_pullback, H1) + domain = Mesh(FiniteElement("Lagrange", cell, 1, (2, ), identity_pullback, H1)) space = FunctionSpace(domain, element) u = TrialFunction(space) @@ -168,8 +171,8 @@ def test_comparison_checker(self): def test_complex_degree_handling(self): cell = triangle - element = FiniteElement("Lagrange", cell, 3) - domain = Mesh(VectorElement('Lagrange', cell, 1)) + element = FiniteElement("Lagrange", cell, 3, (), identity_pullback, H1) + domain = Mesh(FiniteElement("Lagrange", cell, 1, (2, ), identity_pullback, H1)) space = FunctionSpace(domain, element) v = TestFunction(space) diff --git a/test/test_conditionals.py b/test/test_conditionals.py index e16fb1fc1..7b60054d5 100755 --- a/test/test_conditionals.py +++ b/test/test_conditionals.py @@ -3,23 +3,25 @@ import pytest -from ufl import (Coefficient, FiniteElement, FunctionSpace, Mesh, VectorElement, conditional, eq, ge, gt, le, lt, ne, - triangle) +from ufl import Coefficient, FunctionSpace, Mesh, conditional, eq, ge, gt, le, lt, ne, triangle from ufl.classes import EQ, GE, GT, LE, LT, NE +from ufl.finiteelement import FiniteElement +from ufl.pullback import identity_pullback +from ufl.sobolevspace import H1 @pytest.fixture def f(): - element = FiniteElement("Lagrange", triangle, 1) - domain = Mesh(VectorElement('Lagrange', triangle, 1)) + element = FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1) + domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) space = FunctionSpace(domain, element) return Coefficient(space) @pytest.fixture def g(): - element = FiniteElement("Lagrange", triangle, 1) - domain = Mesh(VectorElement('Lagrange', triangle, 1)) + element = FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1) + domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) space = FunctionSpace(domain, element) return Coefficient(space) diff --git a/test/test_degree_estimation.py b/test/test_degree_estimation.py index e0d6c05cd..dbadffef6 100755 --- a/test/test_degree_estimation.py +++ b/test/test_degree_estimation.py @@ -1,36 +1,30 @@ __authors__ = "Martin Sandve Alnæs" __date__ = "2008-03-12 -- 2009-01-28" - -from ufl import (Argument, Coefficient, Coefficients, FacetNormal, FiniteElement, FunctionSpace, Mesh, - SpatialCoordinate, TensorProductElement, VectorElement, cos, div, dot, grad, i, inner, nabla_div, - nabla_grad, sin, tan, triangle) +from ufl import (Argument, Coefficient, Coefficients, FacetNormal, FunctionSpace, Mesh, SpatialCoordinate, cos, div, + dot, grad, i, inner, nabla_div, nabla_grad, sin, tan, triangle) from ufl.algorithms import estimate_total_polynomial_degree +from ufl.finiteelement import FiniteElement, MixedElement +from ufl.pullback import identity_pullback +from ufl.sobolevspace import H1 def test_total_degree_estimation(): - V1 = FiniteElement("CG", triangle, 1) - V2 = FiniteElement("CG", triangle, 2) - VV = VectorElement("CG", triangle, 3) - VM = V1 * V2 - O1 = TensorProductElement(V1, V1) - O2 = TensorProductElement(V2, V1) + V1 = FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1) + V2 = FiniteElement("Lagrange", triangle, 2, (), identity_pullback, H1) + VV = FiniteElement("Lagrange", triangle, 3, (2, ), identity_pullback, H1) + VM = MixedElement([V1, V2]) - domain = Mesh(VectorElement("Lagrange", triangle, 1)) - tensor_domain = Mesh(VectorElement("Lagrange", O1.cell(), 1)) + domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) v1_space = FunctionSpace(domain, V1) v2_space = FunctionSpace(domain, V2) vv_space = FunctionSpace(domain, VV) vm_space = FunctionSpace(domain, VM) - o1_space = FunctionSpace(tensor_domain, O1) - o2_space = FunctionSpace(tensor_domain, O2) v1 = Argument(v1_space, 2) v2 = Argument(v2_space, 3) f1, f2 = Coefficients(vm_space) - u1 = Coefficient(o1_space) - u2 = Coefficient(o2_space) vv = Argument(vv_space, 4) vu = Argument(vv_space, 5) @@ -81,18 +75,6 @@ def test_total_degree_estimation(): assert estimate_total_polynomial_degree(f2 ** 3 * v1) == 7 assert estimate_total_polynomial_degree(f2 ** 3 * v1 + f1 * v1) == 7 - # outer product tuple-degree tests - assert estimate_total_polynomial_degree(u1) == (1, 1) - assert estimate_total_polynomial_degree(u2) == (2, 1) - # derivatives should do nothing (don't know in which direction they act) - assert estimate_total_polynomial_degree(grad(u2)) == (2, 1) - assert estimate_total_polynomial_degree(u1 * u1) == (2, 2) - assert estimate_total_polynomial_degree(u2 * u1) == (3, 2) - assert estimate_total_polynomial_degree(u2 * u2) == (4, 2) - assert estimate_total_polynomial_degree(u1 ** 3) == (3, 3) - assert estimate_total_polynomial_degree(u1 ** 3 + u2 * u2) == (4, 3) - assert estimate_total_polynomial_degree(u2 ** 2 * u1) == (5, 3) - # Math functions of constant values are constant values nx, ny = FacetNormal(domain) e = nx ** 2 @@ -115,9 +97,9 @@ def test_some_compound_types(): etpd = estimate_total_polynomial_degree - P2 = FiniteElement("CG", triangle, 2) - V2 = VectorElement("CG", triangle, 2) - domain = Mesh(VectorElement("Lagrange", triangle, 1)) + P2 = FiniteElement("Lagrange", triangle, 2, (), identity_pullback, H1) + V2 = FiniteElement("Lagrange", triangle, 2, (2, ), identity_pullback, H1) + domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) u = Coefficient(FunctionSpace(domain, P2)) v = Coefficient(FunctionSpace(domain, V2)) diff --git a/test/test_derivative.py b/test/test_derivative.py index 4b1972f27..63528d97f 100755 --- a/test/test_derivative.py +++ b/test/test_derivative.py @@ -3,12 +3,11 @@ from itertools import chain -from ufl import (CellDiameter, CellVolume, Circumradius, Coefficient, Constant, FacetArea, FacetNormal, FiniteElement, - FunctionSpace, Identity, Index, Jacobian, JacobianInverse, Mesh, SpatialCoordinate, TensorElement, - TestFunction, TrialFunction, VectorElement, acos, as_matrix, as_tensor, as_vector, asin, atan, - conditional, cos, derivative, diff, dot, dx, exp, i, indices, inner, interval, j, k, ln, lt, - nabla_grad, outer, quadrilateral, replace, sign, sin, split, sqrt, tan, tetrahedron, triangle, - variable, zero) +from ufl import (CellDiameter, CellVolume, Circumradius, Coefficient, Constant, FacetArea, FacetNormal, FunctionSpace, + Identity, Index, Jacobian, JacobianInverse, Mesh, SpatialCoordinate, TestFunction, TrialFunction, acos, + as_matrix, as_tensor, as_vector, asin, atan, conditional, cos, derivative, diff, dot, dx, exp, i, + indices, inner, interval, j, k, ln, lt, nabla_grad, outer, quadrilateral, replace, sign, sin, split, + sqrt, tan, tetrahedron, triangle, variable, zero) from ufl.algorithms import compute_form_data, expand_indices, strip_variables from ufl.algorithms.apply_algebra_lowering import apply_algebra_lowering from ufl.algorithms.apply_derivatives import apply_derivatives @@ -16,6 +15,9 @@ from ufl.classes import Indexed, MultiIndex, ReferenceGrad from ufl.constantvalue import as_ufl from ufl.domain import extract_unique_domain +from ufl.finiteelement import FiniteElement, MixedElement +from ufl.pullback import identity_pullback +from ufl.sobolevspace import H1, L2 def assertEqualBySampling(actual, expected): @@ -72,8 +74,8 @@ def make_value(c): def _test(self, f, df): cell = triangle - element = FiniteElement("CG", cell, 1) - domain = Mesh(VectorElement("Lagrange", cell, 1)) + element = FiniteElement("Lagrange", cell, 1, (), identity_pullback, H1) + domain = Mesh(FiniteElement("Lagrange", cell, 1, (2, ), identity_pullback, H1)) space = FunctionSpace(domain, element) v = TestFunction(space) @@ -103,175 +105,242 @@ def _test(self, f, df): def testScalarLiteral(self): - def f(w): return as_ufl(1) + def f(w): + return as_ufl(1) + + def df(w, v): + return zero() - def df(w, v): return zero() _test(self, f, df) def testIdentityLiteral(self): - def f(w): return Identity(2)[i, i] + def f(w): + return Identity(2)[i, i] + + def df(w, v): + return zero() - def df(w, v): return zero() _test(self, f, df) # --- Form arguments def testCoefficient(self): - def f(w): return w + def f(w): + return w + + def df(w, v): + return v - def df(w, v): return v _test(self, f, df) def testArgument(self): - def f(w): return TestFunction(FunctionSpace(Mesh(VectorElement("Lagrange", triangle, 1)), - FiniteElement("CG", triangle, 1))) + def f(w): + return TestFunction(FunctionSpace( + Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)), + FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1))) - def df(w, v): return zero() + def df(w, v): + return zero() _test(self, f, df) # --- Geometry def testSpatialCoordinate(self): - def f(w): return SpatialCoordinate(Mesh(VectorElement("Lagrange", triangle, 1)))[0] + def f(w): + return SpatialCoordinate( + Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)))[0] + + def df(w, v): + return zero() - def df(w, v): return zero() _test(self, f, df) def testFacetNormal(self): - def f(w): return FacetNormal(Mesh(VectorElement("Lagrange", triangle, 1)))[0] + def f(w): + return FacetNormal( + Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)))[0] - def df(w, v): return zero() - _test(self, f, df) + def df(w, v): + return zero() -# def testCellSurfaceArea(self): -# def f(w): return CellSurfaceArea(Mesh(VectorElement("Lagrange", triangle, 1))) -# def df(w, v): return zero() -# _test(self, f, df) + _test(self, f, df) def testFacetArea(self): - def f(w): return FacetArea(Mesh(VectorElement("Lagrange", triangle, 1))) + def f(w): + return FacetArea( + Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1))) + + def df(w, v): + return zero() - def df(w, v): return zero() _test(self, f, df) def testCellDiameter(self): - def f(w): return CellDiameter(Mesh(VectorElement("Lagrange", triangle, 1))) + def f(w): + return CellDiameter( + Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1))) + + def df(w, v): + return zero() - def df(w, v): return zero() _test(self, f, df) def testCircumradius(self): - def f(w): return Circumradius(Mesh(VectorElement("Lagrange", triangle, 1))) + def f(w): + return Circumradius(Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1))) - def df(w, v): return zero() + def df(w, v): + return zero() _test(self, f, df) def testCellVolume(self): - def f(w): return CellVolume(Mesh(VectorElement("Lagrange", triangle, 1))) + def f(w): + return CellVolume(Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1))) + + def df(w, v): + return zero() - def df(w, v): return zero() _test(self, f, df) # --- Basic operators def testSum(self): - def f(w): return w + 1 + def f(w): + return w + 1 + + def df(w, v): + return v - def df(w, v): return v _test(self, f, df) def testProduct(self): - def f(w): return 3*w + def f(w): + return 3*w + + def df(w, v): + return 3*v - def df(w, v): return 3*v _test(self, f, df) def testPower(self): - def f(w): return w**3 + def f(w): + return w**3 + + def df(w, v): + return 3*w**2*v - def df(w, v): return 3*w**2*v _test(self, f, df) def testDivision(self): - def f(w): return w / 3.0 + def f(w): + return w / 3.0 + + def df(w, v): + return v / 3.0 - def df(w, v): return v / 3.0 _test(self, f, df) def testDivision2(self): - def f(w): return 3.0 / w + def f(w): + return 3.0 / w + + def df(w, v): + return -3.0 * v / w**2 - def df(w, v): return -3.0 * v / w**2 _test(self, f, df) def testExp(self): - def f(w): return exp(w) + def f(w): + return exp(w) + + def df(w, v): + return v*exp(w) - def df(w, v): return v*exp(w) _test(self, f, df) def testLn(self): - def f(w): return ln(w) + def f(w): + return ln(w) + + def df(w, v): + return v / w - def df(w, v): return v / w _test(self, f, df) def testCos(self): - def f(w): return cos(w) + def f(w): + return cos(w) + + def df(w, v): + return -v*sin(w) - def df(w, v): return -v*sin(w) _test(self, f, df) def testSin(self): - def f(w): return sin(w) + def f(w): + return sin(w) + + def df(w, v): + return v*cos(w) - def df(w, v): return v*cos(w) _test(self, f, df) def testTan(self): - def f(w): return tan(w) + def f(w): + return tan(w) + + def df(w, v): + return v*2.0/(cos(2.0*w) + 1.0) - def df(w, v): return v*2.0/(cos(2.0*w) + 1.0) _test(self, f, df) def testAcos(self): - def f(w): return acos(w/1000) + def f(w): + return acos(w/1000) + + def df(w, v): + return -(v/1000)/sqrt(1.0 - (w/1000)**2) - def df(w, v): return -(v/1000)/sqrt(1.0 - (w/1000)**2) _test(self, f, df) def testAsin(self): - def f(w): return asin(w/1000) + def f(w): + return asin(w/1000) + + def df(w, v): + return (v/1000)/sqrt(1.0 - (w/1000)**2) - def df(w, v): return (v/1000)/sqrt(1.0 - (w/1000)**2) _test(self, f, df) def testAtan(self): - def f(w): return atan(w) + def f(w): + return atan(w) + + def df(w, v): + return v/(1.0 + w**2) - def df(w, v): return v/(1.0 + w**2) _test(self, f, df) # FIXME: Add the new erf and bessel_* @@ -280,19 +349,26 @@ def df(w, v): return v/(1.0 + w**2) def testAbs(self): - def f(w): return abs(w) + def f(w): + return abs(w) + + def df(w, v): + return sign(w)*v - def df(w, v): return sign(w)*v _test(self, f, df) def testConditional(self): # This will fail without bugfix in derivative - def cond(w): return lt(w, 1.0) + def cond(w): + return lt(w, 1.0) - def f(w): return conditional(cond(w), 2*w, 3*w) + def f(w): + return conditional(cond(w), 2*w, 3*w) + + def df(w, v): + return (conditional(cond(w), 1, 0) * 2*v + + conditional(cond(w), 0, 1) * 3*v) - def df(w, v): return (conditional(cond(w), 1, 0) * 2*v + - conditional(cond(w), 0, 1) * 3*v) _test(self, f, df) # --- Tensor algebra basics @@ -306,7 +382,9 @@ def f(w): i, = indices(1) return a[i]*b[i] - def df(w, v): return 3*v + 4*2*w*v + 5*3*w**2*v + def df(w, v): + return 3*v + 4*2*w*v + 5*3*w**2*v + _test(self, f, df) @@ -336,8 +414,8 @@ def testListTensor(self): def test_single_scalar_coefficient_derivative(self): cell = triangle - V = FiniteElement("CG", cell, 1) - domain = Mesh(VectorElement("Lagrange", cell, 1)) + V = FiniteElement("Lagrange", cell, 1, (), identity_pullback, H1) + domain = Mesh(FiniteElement("Lagrange", cell, 1, (2, ), identity_pullback, H1)) space = FunctionSpace(domain, V) u = Coefficient(space) v = TestFunction(space) @@ -348,8 +426,8 @@ def test_single_scalar_coefficient_derivative(self): def test_single_vector_coefficient_derivative(self): cell = triangle - V = VectorElement("CG", cell, 1) - domain = Mesh(VectorElement("Lagrange", cell, 1)) + V = FiniteElement("Lagrange", cell, 1, (2, ), identity_pullback, H1) + domain = Mesh(FiniteElement("Lagrange", cell, 1, (2, ), identity_pullback, H1)) space = FunctionSpace(domain, V) u = Coefficient(space) v = TestFunction(space) @@ -361,10 +439,10 @@ def test_single_vector_coefficient_derivative(self): def test_multiple_coefficient_derivative(self): cell = triangle - V = FiniteElement("CG", cell, 1) - W = VectorElement("CG", cell, 1) - M = V*W - domain = Mesh(VectorElement("Lagrange", cell, 1)) + V = FiniteElement("Lagrange", cell, 1, (), identity_pullback, H1) + W = FiniteElement("Lagrange", cell, 1, (2, ), identity_pullback, H1) + M = MixedElement([V, W]) + domain = Mesh(FiniteElement("Lagrange", cell, 1, (2, ), identity_pullback, H1)) v_space = FunctionSpace(domain, V) w_space = FunctionSpace(domain, W) m_space = FunctionSpace(domain, M) @@ -387,9 +465,9 @@ def test_multiple_coefficient_derivative(self): def test_indexed_coefficient_derivative(self): cell = triangle ident = Identity(cell.geometric_dimension()) - V = FiniteElement("CG", cell, 1) - W = VectorElement("CG", cell, 1) - domain = Mesh(VectorElement("Lagrange", cell, 1)) + V = FiniteElement("Lagrange", cell, 1, (), identity_pullback, H1) + W = FiniteElement("Lagrange", cell, 1, (2, ), identity_pullback, H1) + domain = Mesh(FiniteElement("Lagrange", cell, 1, (2, ), identity_pullback, H1)) v_space = FunctionSpace(domain, V) w_space = FunctionSpace(domain, W) u = Coefficient(w_space) @@ -409,10 +487,10 @@ def test_indexed_coefficient_derivative(self): def test_multiple_indexed_coefficient_derivative(self): cell = tetrahedron - V = FiniteElement("CG", cell, 1) - V2 = V*V - W = VectorElement("CG", cell, 1) - domain = Mesh(VectorElement("Lagrange", cell, 1)) + V = FiniteElement("Lagrange", cell, 1, (), identity_pullback, H1) + V2 = MixedElement([V, V]) + W = FiniteElement("Lagrange", cell, 1, (3, ), identity_pullback, H1) + domain = Mesh(FiniteElement("Lagrange", cell, 1, (3, ), identity_pullback, H1)) v2_space = FunctionSpace(domain, V2) w_space = FunctionSpace(domain, W) u = Coefficient(w_space) @@ -428,10 +506,10 @@ def test_multiple_indexed_coefficient_derivative(self): def test_segregated_derivative_of_convection(self): cell = tetrahedron - V = FiniteElement("CG", cell, 1) - W = VectorElement("CG", cell, 1) + V = FiniteElement("Lagrange", cell, 1, (), identity_pullback, H1) + W = FiniteElement("Lagrange", cell, 1, (3, ), identity_pullback, H1) - domain = Mesh(VectorElement("Lagrange", cell, 1)) + domain = Mesh(FiniteElement("Lagrange", cell, 1, (3, ), identity_pullback, H1)) v_space = FunctionSpace(domain, V) w_space = FunctionSpace(domain, W) @@ -467,9 +545,9 @@ def test_segregated_derivative_of_convection(self): def test_coefficient_derivatives(self): - V = FiniteElement("Lagrange", triangle, 1) + V = FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1) - domain = Mesh(VectorElement("Lagrange", triangle, 1)) + domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) space = FunctionSpace(domain, V) dv = TestFunction(space) @@ -493,10 +571,10 @@ def test_coefficient_derivatives(self): def test_vector_coefficient_scalar_derivatives(self): - V = FiniteElement("Lagrange", triangle, 1) - VV = VectorElement("Lagrange", triangle, 1) + V = FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1) + VV = FiniteElement("vector Lagrange", triangle, 1, (2, ), identity_pullback, H1) - domain = Mesh(VectorElement("Lagrange", triangle, 1)) + domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) v_space = FunctionSpace(domain, V) vv_space = FunctionSpace(domain, VV) @@ -521,10 +599,10 @@ def test_vector_coefficient_scalar_derivatives(self): def test_vector_coefficient_derivatives(self): - V = VectorElement("Lagrange", triangle, 1) - VV = TensorElement("Lagrange", triangle, 1) + V = FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1) + VV = FiniteElement("Lagrange", triangle, 1, (2, 2), identity_pullback, H1) - domain = Mesh(VectorElement("Lagrange", triangle, 1)) + domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) v_space = FunctionSpace(domain, V) vv_space = FunctionSpace(domain, VV) @@ -550,10 +628,10 @@ def test_vector_coefficient_derivatives(self): def test_vector_coefficient_derivatives_of_product(self): - V = VectorElement("Lagrange", triangle, 1) - VV = TensorElement("Lagrange", triangle, 1) + V = FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1) + VV = FiniteElement("Lagrange", triangle, 1, (2, 2), identity_pullback, H1) - domain = Mesh(VectorElement("Lagrange", triangle, 1)) + domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) v_space = FunctionSpace(domain, V) vv_space = FunctionSpace(domain, VV) @@ -591,8 +669,8 @@ def test_vector_coefficient_derivatives_of_product(self): def testHyperElasticity(self): cell = interval - element = FiniteElement("CG", cell, 2) - domain = Mesh(VectorElement("Lagrange", cell, 1)) + element = FiniteElement("Lagrange", cell, 2, (), identity_pullback, H1) + domain = Mesh(FiniteElement("Lagrange", cell, 1, (1, ), identity_pullback, H1)) space = FunctionSpace(domain, element) w = Coefficient(space) v = TestFunction(space) @@ -669,9 +747,9 @@ def Nw(x, derivatives): def test_mass_derived_from_functional(self): cell = triangle - V = FiniteElement("CG", cell, 1) + V = FiniteElement("Lagrange", cell, 1, (), identity_pullback, H1) - domain = Mesh(VectorElement("Lagrange", cell, 1)) + domain = Mesh(FiniteElement("Lagrange", cell, 1, (2, ), identity_pullback, H1)) space = FunctionSpace(domain, V) v = TestFunction(space) @@ -691,9 +769,9 @@ def test_mass_derived_from_functional(self): def test_derivative_replace_works_together(self): cell = triangle - V = FiniteElement("CG", cell, 1) + V = FiniteElement("Lagrange", cell, 1, (), identity_pullback, H1) - domain = Mesh(VectorElement("Lagrange", cell, 1)) + domain = Mesh(FiniteElement("Lagrange", cell, 1, (2, ), identity_pullback, H1)) space = FunctionSpace(domain, V) v = TestFunction(space) @@ -716,8 +794,8 @@ def test_derivative_replace_works_together(self): def test_index_simplification_handles_repeated_indices(self): - mesh = Mesh(VectorElement("P", quadrilateral, 1)) - V = FunctionSpace(mesh, TensorElement("DQ", quadrilateral, 0)) + mesh = Mesh(FiniteElement("Lagrange", quadrilateral, 1, (2, ), identity_pullback, H1)) + V = FunctionSpace(mesh, FiniteElement("DQ", quadrilateral, 0, (2, 2), identity_pullback, L2)) K = JacobianInverse(mesh) G = outer(Identity(2), Identity(2)) i, j, k, l, m, n = indices(6) @@ -735,7 +813,7 @@ def test_index_simplification_handles_repeated_indices(self): def test_index_simplification_reference_grad(self): - mesh = Mesh(VectorElement("P", quadrilateral, 1)) + mesh = Mesh(FiniteElement("Lagrange", quadrilateral, 1, (2, ), identity_pullback, H1)) i, = indices(1) A = as_tensor(Indexed(Jacobian(mesh), MultiIndex((i, i))), (i,)) expr = apply_derivatives(apply_geometry_lowering( @@ -748,8 +826,8 @@ def test_index_simplification_reference_grad(self): # --- Scratch space def test_foobar(self): - element = VectorElement("Lagrange", triangle, 1) - domain = Mesh(VectorElement("Lagrange", triangle, 1)) + element = FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1) + domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) space = FunctionSpace(domain, element) v = TestFunction(space) diff --git a/test/test_diff.py b/test/test_diff.py index fd1ef4f34..5a53e5c8d 100755 --- a/test/test_diff.py +++ b/test/test_diff.py @@ -3,10 +3,13 @@ import pytest -from ufl import (Coefficient, FiniteElement, FunctionSpace, Mesh, SpatialCoordinate, VectorElement, as_vector, atan, - cos, diff, exp, indices, ln, sin, tan, triangle, variable) +from ufl import (Coefficient, FunctionSpace, Mesh, SpatialCoordinate, as_vector, atan, cos, diff, exp, indices, ln, sin, + tan, triangle, variable) from ufl.algorithms import expand_derivatives from ufl.constantvalue import as_ufl +from ufl.finiteelement import FiniteElement +from ufl.pullback import identity_pullback +from ufl.sobolevspace import H1 def get_variables(): @@ -170,16 +173,16 @@ def df(v): def testCoefficient(): - coord_elem = VectorElement("P", triangle, 1, dim=3) + coord_elem = FiniteElement("Lagrange", triangle, 1, (3, ), identity_pullback, H1) mesh = Mesh(coord_elem) - V = FunctionSpace(mesh, FiniteElement("P", triangle, 1)) + V = FunctionSpace(mesh, FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1)) v = Coefficient(V) assert round(expand_derivatives(diff(v, v))-1.0, 7) == 0 def testDiffX(): cell = triangle - domain = Mesh(VectorElement("Lagrange", cell, 1)) + domain = Mesh(FiniteElement("Lagrange", cell, 1, (2, ), identity_pullback, H1)) x = SpatialCoordinate(domain) f = x[0] ** 2 * x[1] ** 2 i, = indices(1) diff --git a/test/test_domains.py b/test/test_domains.py index 88ee286ab..cb9df4ca0 100755 --- a/test/test_domains.py +++ b/test/test_domains.py @@ -1,24 +1,31 @@ + """Tests of domain language and attaching domains to forms.""" import pytest from mockobjects import MockMesh -from ufl import (Cell, Coefficient, Constant, FiniteElement, FunctionSpace, Mesh, VectorElement, ds, dS, dx, hexahedron, - interval, quadrilateral, tetrahedron, triangle) +import ufl # noqa: F401 +from ufl import (Cell, Coefficient, Constant, FunctionSpace, Mesh, ds, dS, dx, hexahedron, interval, quadrilateral, + tetrahedron, triangle) from ufl.algorithms import compute_form_data +from ufl.finiteelement import FiniteElement +from ufl.pullback import IdentityPullback # noqa: F401 +from ufl.pullback import identity_pullback +from ufl.sobolevspace import H1 -all_cells = (interval, triangle, tetrahedron, - quadrilateral, hexahedron) +all_cells = (interval, triangle, tetrahedron, quadrilateral, hexahedron) def test_construct_domains_from_cells(): for cell in all_cells: - Mesh(VectorElement("Lagrange", cell, 1)) + d = cell.geometric_dimension() + Mesh(FiniteElement("Lagrange", cell, 1, (d, ), identity_pullback, H1)) def test_construct_domains_with_names(): for cell in all_cells: - e = VectorElement("Lagrange", cell, 1) + d = cell.geometric_dimension() + e = FiniteElement("Lagrange", cell, 1, (d, ), identity_pullback, H1) D2 = Mesh(e, ufl_id=2) D3 = Mesh(e, ufl_id=3) D3b = Mesh(e, ufl_id=3) @@ -29,9 +36,13 @@ def test_construct_domains_with_names(): def test_domains_sort_by_name(): # This ordering is rather arbitrary, but at least this shows sorting is # working - domains1 = [Mesh(VectorElement("Lagrange", cell, 1), ufl_id=hash(cell.cellname())) + domains1 = [Mesh(FiniteElement("Lagrange", cell, 1, (cell.geometric_dimension(), ), + identity_pullback, H1), + ufl_id=hash(cell.cellname())) for cell in all_cells] - domains2 = [Mesh(VectorElement("Lagrange", cell, 1), ufl_id=hash(cell.cellname())) + domains2 = [Mesh(FiniteElement("Lagrange", cell, 1, (cell.geometric_dimension(), ), + identity_pullback, H1), + ufl_id=hash(cell.cellname())) for cell in sorted(all_cells)] sdomains = sorted(domains1, key=lambda D: (D.geometric_dimension(), D.topological_dimension(), @@ -42,19 +53,19 @@ def test_domains_sort_by_name(): def test_topdomain_creation(): - D = Mesh(interval) + D = Mesh(FiniteElement("Lagrange", interval, 1, (1, ), identity_pullback, H1)) assert D.geometric_dimension() == 1 - D = Mesh(triangle) + D = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) assert D.geometric_dimension() == 2 - D = Mesh(tetrahedron) + D = Mesh(FiniteElement("Lagrange", tetrahedron, 1, (3, ), identity_pullback, H1)) assert D.geometric_dimension() == 3 def test_cell_legacy_case(): # Passing cell like old code does - D = Mesh(VectorElement("Lagrange", triangle, 1)) + D = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) - V = FiniteElement("CG", triangle, 1) + V = FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1) f = Coefficient(FunctionSpace(D, V)) assert f.ufl_domains() == (D, ) @@ -64,9 +75,9 @@ def test_cell_legacy_case(): def test_simple_domain_case(): # Creating domain from just cell with label like new dolfin will do - D = Mesh(triangle, ufl_id=3) + D = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1), ufl_id=3) - V = FunctionSpace(D, FiniteElement("CG", D.ufl_cell(), 1)) + V = FunctionSpace(D, FiniteElement("Lagrange", D.ufl_cell(), 1, (), identity_pullback, "H1")) f = Coefficient(V) assert f.ufl_domains() == (D, ) @@ -79,11 +90,11 @@ def test_creating_domains_with_coordinate_fields(): # FIXME: Rewrite for new ap # Mesh with P2 representation of coordinates cell = triangle - P2 = VectorElement("CG", cell, 2) + P2 = FiniteElement("Lagrange", cell, 2, (2, ), identity_pullback, H1) domain = Mesh(P2) # Piecewise linear function space over quadratic mesh - element = FiniteElement("CG", cell, 1) + element = FiniteElement("Lagrange", cell, 1, (), identity_pullback, H1) V = FunctionSpace(domain, element) f = Coefficient(V) @@ -105,66 +116,79 @@ def test_join_domains(): mesh7 = MockMesh(7) mesh8 = MockMesh(8) triangle3 = Cell("triangle", geometric_dimension=3) - xa = VectorElement("CG", triangle, 1) - xb = VectorElement("CG", triangle, 1) + xa = FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1) + xb = FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1) # Equal domains are joined - assert 1 == len(join_domains([Mesh(triangle, ufl_id=7), - Mesh(triangle, ufl_id=7)])) - assert 1 == len(join_domains([Mesh(triangle, ufl_id=7, cargo=mesh7), - Mesh(triangle, ufl_id=7, cargo=mesh7)])) + assert 1 == len(join_domains([ + Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1), ufl_id=7), + Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1), ufl_id=7)])) + assert 1 == len(join_domains([ + Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1), ufl_id=7, cargo=mesh7), + Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1), ufl_id=7, cargo=mesh7)])) assert 1 == len(join_domains([Mesh(xa, ufl_id=3), Mesh(xa, ufl_id=3)])) # Different domains are not joined - assert 2 == len(join_domains([Mesh(triangle), Mesh(triangle)])) - assert 2 == len(join_domains([Mesh(triangle, ufl_id=7), - Mesh(triangle, ufl_id=8)])) - assert 2 == len(join_domains([Mesh(triangle, ufl_id=7), - Mesh(quadrilateral, ufl_id=8)])) + assert 2 == len(join_domains([ + Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)), + Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1))])) + assert 2 == len(join_domains([ + Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1), ufl_id=7), + Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1), ufl_id=8)])) + assert 2 == len(join_domains([ + Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1), ufl_id=7), + Mesh(FiniteElement("Lagrange", quadrilateral, 1, (2, ), identity_pullback, H1), ufl_id=8)])) assert 2 == len(join_domains([Mesh(xa, ufl_id=7), Mesh(xa, ufl_id=8)])) assert 2 == len(join_domains([Mesh(xa), Mesh(xb)])) - # Incompatible cells require labeling - # self.assertRaises(BaseException, lambda: join_domains([Mesh(triangle), Mesh(triangle3)])) # FIXME: Figure out - # self.assertRaises(BaseException, lambda: join_domains([Mesh(triangle), - # Mesh(quadrilateral)])) # FIXME: Figure out - # Incompatible coordinates require labeling - xc = Coefficient(FunctionSpace(Mesh(triangle), VectorElement("CG", triangle, 1))) - xd = Coefficient(FunctionSpace(Mesh(triangle), VectorElement("CG", triangle, 1))) + xc = Coefficient(FunctionSpace( + Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)), + FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1))) + xd = Coefficient(FunctionSpace( + Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)), + FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1))) with pytest.raises(BaseException): join_domains([Mesh(xc), Mesh(xd)]) # Incompatible data is checked if and only if the domains are the same - assert 2 == len(join_domains([Mesh(triangle, ufl_id=7, cargo=mesh7), - Mesh(triangle, ufl_id=8, cargo=mesh8)])) - assert 2 == len(join_domains([Mesh(triangle, ufl_id=7, cargo=mesh7), - Mesh(quadrilateral, ufl_id=8, cargo=mesh8)])) + assert 2 == len(join_domains([ + Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1), ufl_id=7, cargo=mesh7), + Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1), ufl_id=8, cargo=mesh8)])) + assert 2 == len(join_domains([ + Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1), ufl_id=7, cargo=mesh7), + Mesh(FiniteElement("Lagrange", quadrilateral, 1, (2, ), identity_pullback, H1), + ufl_id=8, cargo=mesh8)])) # Geometric dimensions must match with pytest.raises(BaseException): - join_domains([Mesh(triangle), - Mesh(triangle3)]) + join_domains([ + Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)), + Mesh(FiniteElement("Lagrange", triangle3, 1, (3, ), identity_pullback, H1))]) with pytest.raises(BaseException): - join_domains([Mesh(triangle, ufl_id=7, cargo=mesh7), - Mesh(triangle3, ufl_id=8, cargo=mesh8)]) + join_domains([ + Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1), ufl_id=7, cargo=mesh7), + Mesh(FiniteElement("Lagrange", triangle3, 1, (3, ), identity_pullback, H1), ufl_id=8, cargo=mesh8)]) # Cargo and mesh ids must match with pytest.raises(BaseException): - Mesh(triangle, ufl_id=7, cargo=mesh8) + Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1), ufl_id=7, cargo=mesh8) # Nones are removed - assert 2 == len(join_domains([None, Mesh(triangle, ufl_id=3), - None, Mesh(triangle, ufl_id=3), - None, Mesh(triangle, ufl_id=4)])) - assert 2 == len(join_domains([Mesh(triangle, ufl_id=7), None, - Mesh(quadrilateral, ufl_id=8)])) - assert None not in join_domains([Mesh(triangle3, ufl_id=7), None, - Mesh(tetrahedron, ufl_id=8)]) + assert 2 == len(join_domains([ + None, Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1), ufl_id=3), + None, Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1), ufl_id=3), + None, Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1), ufl_id=4)])) + assert 2 == len(join_domains([ + Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1), ufl_id=7), None, + Mesh(FiniteElement("Lagrange", quadrilateral, 1, (2, ), identity_pullback, H1), ufl_id=8)])) + assert None not in join_domains([ + Mesh(FiniteElement("Lagrange", triangle3, 1, (3, ), identity_pullback, H1), ufl_id=7), None, + Mesh(FiniteElement("Lagrange", tetrahedron, 1, (3, ), identity_pullback, H1), ufl_id=8)]) def test_everywhere_integrals_with_backwards_compatibility(): - D = Mesh(triangle) + D = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) - V = FunctionSpace(D, FiniteElement("CG", triangle, 1)) + V = FunctionSpace(D, FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1)) f = Coefficient(V) a = f * dx @@ -184,9 +208,9 @@ def test_everywhere_integrals_with_backwards_compatibility(): def test_merge_sort_integral_data(): - D = Mesh(triangle) + D = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) - V = FunctionSpace(D, FiniteElement("CG", triangle, 1)) + V = FunctionSpace(D, FiniteElement("CG", triangle, 1, (), identity_pullback, H1)) u = Coefficient(V) c = Constant(D) diff --git a/test/test_duals.py b/test/test_duals.py index 7b68d3cca..b82736e0f 100644 --- a/test/test_duals.py +++ b/test/test_duals.py @@ -3,24 +3,27 @@ import pytest -from ufl import (Action, Adjoint, Argument, Coargument, Coefficient, Cofunction, FiniteElement, FormSum, FunctionSpace, - Matrix, Mesh, MixedFunctionSpace, TestFunction, TrialFunction, VectorElement, action, adjoint, - derivative, dx, inner, interval, tetrahedron, triangle) +from ufl import (Action, Adjoint, Argument, Coargument, Coefficient, Cofunction, FormSum, FunctionSpace, Matrix, Mesh, + MixedFunctionSpace, TestFunction, TrialFunction, action, adjoint, derivative, dx, inner, interval, + tetrahedron, triangle) from ufl.algorithms.ad import expand_derivatives from ufl.constantvalue import Zero from ufl.duals import is_dual, is_primal +from ufl.finiteelement import FiniteElement from ufl.form import ZeroBaseForm +from ufl.pullback import identity_pullback +from ufl.sobolevspace import H1 def test_mixed_functionspace(self): # Domains - domain_3d = Mesh(VectorElement("Lagrange", tetrahedron, 1)) - domain_2d = Mesh(VectorElement("Lagrange", triangle, 1)) - domain_1d = Mesh(VectorElement("Lagrange", interval, 1)) + domain_3d = Mesh(FiniteElement("Lagrange", tetrahedron, 1, (3, ), identity_pullback, H1)) + domain_2d = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) + domain_1d = Mesh(FiniteElement("Lagrange", interval, 1, (1, ), identity_pullback, H1)) # Finite elements - f_1d = FiniteElement("CG", interval, 1) - f_2d = FiniteElement("CG", triangle, 1) - f_3d = FiniteElement("CG", tetrahedron, 1) + f_1d = FiniteElement("Lagrange", interval, 1, (), identity_pullback, H1) + f_2d = FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1) + f_3d = FiniteElement("Lagrange", tetrahedron, 1, (), identity_pullback, H1) # Function spaces V_3d = FunctionSpace(domain_3d, f_3d) V_2d = FunctionSpace(domain_2d, f_2d) @@ -47,8 +50,8 @@ def test_mixed_functionspace(self): def test_dual_coefficients(): - domain_2d = Mesh(VectorElement("Lagrange", triangle, 1)) - f_2d = FiniteElement("CG", triangle, 1) + domain_2d = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) + f_2d = FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1) V = FunctionSpace(domain_2d, f_2d) V_dual = V.dual() @@ -70,8 +73,8 @@ def test_dual_coefficients(): def test_dual_arguments(): - domain_2d = Mesh(VectorElement("Lagrange", triangle, 1)) - f_2d = FiniteElement("CG", triangle, 1) + domain_2d = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) + f_2d = FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1) V = FunctionSpace(domain_2d, f_2d) V_dual = V.dual() @@ -93,8 +96,8 @@ def test_dual_arguments(): def test_addition(): - domain_2d = Mesh(VectorElement("Lagrange", triangle, 1)) - f_2d = FiniteElement("CG", triangle, 1) + domain_2d = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) + f_2d = FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1) V = FunctionSpace(domain_2d, f_2d) V_dual = V.dual() @@ -131,8 +134,8 @@ def test_addition(): def test_scalar_mult(): - domain_2d = Mesh(VectorElement("Lagrange", triangle, 1)) - f_2d = FiniteElement("CG", triangle, 1) + domain_2d = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) + f_2d = FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1) V = FunctionSpace(domain_2d, f_2d) V_dual = V.dual() @@ -149,8 +152,8 @@ def test_scalar_mult(): def test_adjoint(): - domain_2d = Mesh(VectorElement("Lagrange", triangle, 1)) - f_2d = FiniteElement("CG", triangle, 1) + domain_2d = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) + f_2d = FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1) V = FunctionSpace(domain_2d, f_2d) a = Matrix(V, V) @@ -168,11 +171,11 @@ def test_adjoint(): def test_action(): - domain_2d = Mesh(VectorElement("Lagrange", triangle, 1)) - f_2d = FiniteElement("CG", triangle, 1) + domain_2d = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) + f_2d = FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1) V = FunctionSpace(domain_2d, f_2d) - domain_1d = Mesh(VectorElement("Lagrange", interval, 1)) - f_1d = FiniteElement("CG", interval, 1) + domain_1d = Mesh(FiniteElement("Lagrange", interval, 1, (1, ), identity_pullback, H1)) + f_1d = FiniteElement("Lagrange", interval, 1, (), identity_pullback, H1) U = FunctionSpace(domain_1d, f_1d) a = Matrix(V, U) @@ -228,11 +231,11 @@ def test_action(): def test_differentiation(): - domain_2d = Mesh(VectorElement("Lagrange", triangle, 1)) - f_2d = FiniteElement("CG", triangle, 1) + domain_2d = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) + f_2d = FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1) V = FunctionSpace(domain_2d, f_2d) - domain_1d = Mesh(VectorElement("Lagrange", interval, 1)) - f_1d = FiniteElement("CG", interval, 1) + domain_1d = Mesh(FiniteElement("Lagrange", interval, 1, (1, ), identity_pullback, H1)) + f_1d = FiniteElement("Lagrange", interval, 1, (), identity_pullback, H1) U = FunctionSpace(domain_1d, f_1d) u = Coefficient(U) @@ -291,8 +294,8 @@ def test_differentiation(): def test_zero_base_form_mult(): - domain_2d = Mesh(VectorElement("Lagrange", triangle, 1)) - f_2d = FiniteElement("CG", triangle, 1) + domain_2d = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) + f_2d = FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1) V = FunctionSpace(domain_2d, f_2d) v = Argument(V, 0) Z = ZeroBaseForm((v, v)) diff --git a/test/test_equals.py b/test/test_equals.py index 184d71c16..ddfa5058c 100755 --- a/test/test_equals.py +++ b/test/test_equals.py @@ -1,14 +1,17 @@ """Test of expression comparison.""" -from ufl import Coefficient, Cofunction, FiniteElement, FunctionSpace, Mesh, VectorElement, triangle +from ufl import Coefficient, Cofunction, FunctionSpace, Mesh, triangle +from ufl.finiteelement import FiniteElement +from ufl.pullback import identity_pullback +from ufl.sobolevspace import H1 def test_comparison_of_coefficients(): - V = FiniteElement("CG", triangle, 1) - U = FiniteElement("CG", triangle, 2) - Ub = FiniteElement("CG", triangle, 2) + V = FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1) + U = FiniteElement("Lagrange", triangle, 2, (), identity_pullback, H1) + Ub = FiniteElement("Lagrange", triangle, 2, (), identity_pullback, H1) - domain = Mesh(VectorElement("Lagrange", triangle, 1)) + domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) v_space = FunctionSpace(domain, V) u_space = FunctionSpace(domain, U) ub_space = FunctionSpace(domain, Ub) @@ -36,11 +39,11 @@ def test_comparison_of_coefficients(): def test_comparison_of_cofunctions(): - V = FiniteElement("CG", triangle, 1) - U = FiniteElement("CG", triangle, 2) - Ub = FiniteElement("CG", triangle, 2) + V = FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1) + U = FiniteElement("Lagrange", triangle, 2, (), identity_pullback, H1) + Ub = FiniteElement("Lagrange", triangle, 2, (), identity_pullback, H1) - domain = Mesh(VectorElement("Lagrange", triangle, 1)) + domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) v_space = FunctionSpace(domain, V) u_space = FunctionSpace(domain, U) ub_space = FunctionSpace(domain, Ub) @@ -68,8 +71,8 @@ def test_comparison_of_cofunctions(): def test_comparison_of_products(): - V = FiniteElement("CG", triangle, 1) - domain = Mesh(VectorElement("Lagrange", triangle, 1)) + V = FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1) + domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) v_space = FunctionSpace(domain, V) v = Coefficient(v_space) u = Coefficient(v_space) @@ -82,8 +85,8 @@ def test_comparison_of_products(): def test_comparison_of_sums(): - V = FiniteElement("CG", triangle, 1) - domain = Mesh(VectorElement("Lagrange", triangle, 1)) + V = FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1) + domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) v_space = FunctionSpace(domain, V) v = Coefficient(v_space) u = Coefficient(v_space) @@ -96,8 +99,8 @@ def test_comparison_of_sums(): def test_comparison_of_deeply_nested_expression(): - V = FiniteElement("CG", triangle, 1) - domain = Mesh(VectorElement("Lagrange", triangle, 1)) + V = FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1) + domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) v_space = FunctionSpace(domain, V) v = Coefficient(v_space, count=1) u = Coefficient(v_space, count=1) diff --git a/test/test_evaluate.py b/test/test_evaluate.py index 61e8af9e9..964740aac 100755 --- a/test/test_evaluate.py +++ b/test/test_evaluate.py @@ -3,10 +3,13 @@ import math -from ufl import (Argument, Coefficient, FiniteElement, FunctionSpace, Identity, Mesh, SpatialCoordinate, VectorElement, - as_matrix, as_vector, cos, cross, det, dev, dot, exp, i, indices, inner, j, ln, outer, sin, skew, sqrt, - sym, tan, tetrahedron, tr, triangle) +from ufl import (Argument, Coefficient, FunctionSpace, Identity, Mesh, SpatialCoordinate, as_matrix, as_vector, cos, + cross, det, dev, dot, exp, i, indices, inner, j, ln, outer, sin, skew, sqrt, sym, tan, tetrahedron, tr, + triangle) from ufl.constantvalue import as_ufl +from ufl.finiteelement import FiniteElement +from ufl.pullback import identity_pullback +from ufl.sobolevspace import H1 def testScalars(): @@ -40,7 +43,7 @@ def testIdentity(): def testCoords(): cell = triangle - domain = Mesh(VectorElement("Lagrange", cell, 1)) + domain = Mesh(FiniteElement("Lagrange", cell, 1, (2, ), identity_pullback, H1)) x = SpatialCoordinate(domain) s = x[0] + x[1] e = s((5, 7)) @@ -50,8 +53,8 @@ def testCoords(): def testFunction1(): cell = triangle - element = FiniteElement("CG", cell, 1) - domain = Mesh(VectorElement("Lagrange", cell, 1)) + element = FiniteElement("Lagrange", cell, 1, (), identity_pullback, H1) + domain = Mesh(FiniteElement("Lagrange", cell, 1, (2, ), identity_pullback, H1)) space = FunctionSpace(domain, element) f = Coefficient(space) s = 3 * f @@ -62,8 +65,8 @@ def testFunction1(): def testFunction2(): cell = triangle - element = FiniteElement("CG", cell, 1) - domain = Mesh(VectorElement("Lagrange", cell, 1)) + element = FiniteElement("Lagrange", cell, 1, (), identity_pullback, H1) + domain = Mesh(FiniteElement("Lagrange", cell, 1, (2, ), identity_pullback, H1)) space = FunctionSpace(domain, element) f = Coefficient(space) @@ -77,8 +80,8 @@ def g(x): def testArgument2(): cell = triangle - element = FiniteElement("CG", cell, 1) - domain = Mesh(VectorElement("Lagrange", cell, 1)) + element = FiniteElement("Lagrange", cell, 1, (), identity_pullback, H1) + domain = Mesh(FiniteElement("Lagrange", cell, 1, (2, ), identity_pullback, H1)) space = FunctionSpace(domain, element) f = Argument(space, 2) @@ -92,7 +95,7 @@ def g(x): def testAlgebra(): cell = triangle - domain = Mesh(VectorElement("Lagrange", cell, 1)) + domain = Mesh(FiniteElement("Lagrange", cell, 1, (2, ), identity_pullback, H1)) x = SpatialCoordinate(domain) s = 3 * (x[0] + x[1]) - 7 + x[0] ** (x[1] / 2) e = s((5, 7)) @@ -102,7 +105,7 @@ def testAlgebra(): def testIndexSum(): cell = triangle - domain = Mesh(VectorElement("Lagrange", cell, 1)) + domain = Mesh(FiniteElement("Lagrange", cell, 1, (2, ), identity_pullback, H1)) x = SpatialCoordinate(domain) i, = indices(1) s = x[i] * x[i] @@ -113,7 +116,7 @@ def testIndexSum(): def testIndexSum2(): cell = triangle - domain = Mesh(VectorElement("Lagrange", cell, 1)) + domain = Mesh(FiniteElement("Lagrange", cell, 1, (2, ), identity_pullback, H1)) x = SpatialCoordinate(domain) ident = Identity(cell.geometric_dimension()) i, j = indices(2) @@ -125,7 +128,7 @@ def testIndexSum2(): def testMathFunctions(): - domain = Mesh(VectorElement("Lagrange", triangle, 1)) + domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) x = SpatialCoordinate(domain)[0] s = sin(x) @@ -160,7 +163,7 @@ def testMathFunctions(): def testListTensor(): - domain = Mesh(VectorElement("Lagrange", triangle, 1)) + domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) x, y = SpatialCoordinate(domain) m = as_matrix([[x, y], [-y, -x]]) @@ -177,7 +180,7 @@ def testListTensor(): def testComponentTensor1(): - domain = Mesh(VectorElement("Lagrange", triangle, 1)) + domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) x = SpatialCoordinate(domain) m = as_vector(x[i], i) @@ -188,7 +191,7 @@ def testComponentTensor1(): def testComponentTensor2(): - domain = Mesh(VectorElement("Lagrange", triangle, 1)) + domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) x = SpatialCoordinate(domain) xx = outer(x, x) @@ -201,7 +204,7 @@ def testComponentTensor2(): def testComponentTensor3(): - domain = Mesh(VectorElement("Lagrange", triangle, 1)) + domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) x = SpatialCoordinate(domain) xx = outer(x, x) @@ -214,8 +217,8 @@ def testComponentTensor3(): def testCoefficient(): - V = FiniteElement("CG", triangle, 1) - domain = Mesh(VectorElement("Lagrange", triangle, 1)) + V = FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1) + domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) space = FunctionSpace(domain, V) f = Coefficient(space) e = f ** 2 @@ -226,8 +229,8 @@ def eval_f(x): def testCoefficientDerivative(): - V = FiniteElement("CG", triangle, 1) - domain = Mesh(VectorElement("Lagrange", triangle, 1)) + V = FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1) + domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) space = FunctionSpace(domain, V) f = Coefficient(space) e = f.dx(0) ** 2 + f.dx(1) ** 2 @@ -248,7 +251,7 @@ def eval_f(x, derivatives): def test_dot(): - domain = Mesh(VectorElement("Lagrange", triangle, 1)) + domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) x = SpatialCoordinate(domain) s = dot(x, 2 * x) e = s((5, 7)) @@ -257,7 +260,7 @@ def test_dot(): def test_inner(): - domain = Mesh(VectorElement("Lagrange", triangle, 1)) + domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) x = SpatialCoordinate(domain) xx = as_matrix(((2 * x[0], 3 * x[0]), (2 * x[1], 3 * x[1]))) s = inner(xx, 2 * xx) @@ -267,7 +270,7 @@ def test_inner(): def test_outer(): - domain = Mesh(VectorElement("Lagrange", triangle, 1)) + domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) x = SpatialCoordinate(domain) xx = outer(outer(x, x), as_vector((2, 3))) s = inner(xx, 2 * xx) @@ -277,7 +280,7 @@ def test_outer(): def test_cross(): - domain = Mesh(VectorElement("Lagrange", tetrahedron, 1)) + domain = Mesh(FiniteElement("Lagrange", tetrahedron, 1, (3, ), identity_pullback, H1)) x = SpatialCoordinate(domain) xv = (3, 5, 7) @@ -310,7 +313,7 @@ def test_cross(): def xtest_dev(): - domain = Mesh(VectorElement("Lagrange", triangle, 1)) + domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) x = SpatialCoordinate(domain) xv = (5, 7) xx = outer(x, x) @@ -322,7 +325,7 @@ def xtest_dev(): def test_skew(): - domain = Mesh(VectorElement("Lagrange", triangle, 1)) + domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) x = SpatialCoordinate(domain) xv = (5, 7) xx = outer(x, x) @@ -334,7 +337,7 @@ def test_skew(): def test_sym(): - domain = Mesh(VectorElement("Lagrange", triangle, 1)) + domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) x = SpatialCoordinate(domain) xv = (5, 7) xx = outer(x, x) @@ -346,7 +349,7 @@ def test_sym(): def test_tr(): - domain = Mesh(VectorElement("Lagrange", triangle, 1)) + domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) x = SpatialCoordinate(domain) xv = (5, 7) xx = outer(x, x) @@ -357,7 +360,7 @@ def test_tr(): def test_det2D(): - domain = Mesh(VectorElement("Lagrange", triangle, 1)) + domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) x = SpatialCoordinate(domain) xv = (5, 7) a, b = 6.5, -4 diff --git a/test/test_expand_indices.py b/test/test_expand_indices.py index 023b4ccb1..f8314df2c 100755 --- a/test/test_expand_indices.py +++ b/test/test_expand_indices.py @@ -8,10 +8,13 @@ import pytest -from ufl import (Coefficient, FiniteElement, FunctionSpace, Identity, Mesh, TensorElement, VectorElement, as_tensor, - cos, det, div, dot, dx, exp, grad, i, inner, j, k, l, ln, nabla_div, nabla_grad, outer, sin, triangle) +from ufl import (Coefficient, FunctionSpace, Identity, Mesh, as_tensor, cos, det, div, dot, dx, exp, grad, i, inner, j, + k, l, ln, nabla_div, nabla_grad, outer, sin, triangle) from ufl.algorithms import compute_form_data, expand_derivatives, expand_indices from ufl.algorithms.renumbering import renumber_indices +from ufl.finiteelement import FiniteElement +from ufl.pullback import identity_pullback +from ufl.sobolevspace import H1 # TODO: Test expand_indices2 throuroughly for correctness, then efficiency: # expand_indices, expand_indices2 = expand_indices2, expand_indices @@ -21,10 +24,10 @@ class Fixture: def __init__(self): cell = triangle - element = FiniteElement("Lagrange", cell, 1) - velement = VectorElement("Lagrange", cell, 1) - telement = TensorElement("Lagrange", cell, 1) - domain = Mesh(VectorElement("Lagrange", cell, 1)) + element = FiniteElement("Lagrange", cell, 1, (), identity_pullback, H1) + velement = FiniteElement("Lagrange", cell, 1, (2, ), identity_pullback, H1) + telement = FiniteElement("Lagrange", cell, 1, (2, 2), identity_pullback, H1) + domain = Mesh(FiniteElement("Lagrange", cell, 1, (2, ), identity_pullback, H1)) space = FunctionSpace(domain, element) vspace = FunctionSpace(domain, velement) tspace = FunctionSpace(domain, telement) diff --git a/test/test_external_operator.py b/test/test_external_operator.py index 13f4eaa65..0cdebb422 100644 --- a/test/test_external_operator.py +++ b/test/test_external_operator.py @@ -5,35 +5,37 @@ import pytest -# This imports everything external code will see from ufl -from ufl import (Action, Argument, Coefficient, Constant, FiniteElement, Form, FunctionSpace, Mesh, TestFunction, - TrialFunction, VectorElement, action, adjoint, cos, derivative, dx, inner, sin, triangle) +from ufl import (Action, Argument, Coefficient, Constant, Form, FunctionSpace, Mesh, TestFunction, TrialFunction, + action, adjoint, cos, derivative, dx, inner, sin, triangle) from ufl.algorithms import expand_derivatives from ufl.algorithms.apply_derivatives import apply_derivatives from ufl.core.external_operator import ExternalOperator +from ufl.finiteelement import FiniteElement from ufl.form import BaseForm +from ufl.pullback import identity_pullback +from ufl.sobolevspace import H1 @pytest.fixture def domain_2d(): - return Mesh(VectorElement("Lagrange", triangle, 1)) + return Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) @pytest.fixture def V1(domain_2d): - f1 = FiniteElement("CG", triangle, 1) + f1 = FiniteElement("CG", triangle, 1, (), identity_pullback, H1) return FunctionSpace(domain_2d, f1) @pytest.fixture def V2(domain_2d): - f1 = FiniteElement("CG", triangle, 2) + f1 = FiniteElement("CG", triangle, 2, (), identity_pullback, H1) return FunctionSpace(domain_2d, f1) @pytest.fixture def V3(domain_2d): - f1 = FiniteElement("CG", triangle, 3) + f1 = FiniteElement("CG", triangle, 3, (), identity_pullback, H1) return FunctionSpace(domain_2d, f1) diff --git a/test/test_ffcforms.py b/test/test_ffcforms.py index a0dbf7e82..5ab8778ab 100755 --- a/test/test_ffcforms.py +++ b/test/test_ffcforms.py @@ -13,14 +13,17 @@ # Examples copied from the FFC demo directory, examples contributed # by Johan Jansson, Kristian Oelgaard, Marie Rognes, and Garth Wells. -from ufl import (Coefficient, Constant, Dx, FacetNormal, FiniteElement, FunctionSpace, Mesh, TensorElement, - TestFunction, TestFunctions, TrialFunction, TrialFunctions, VectorConstant, VectorElement, avg, curl, - div, dot, ds, dS, dx, grad, i, inner, j, jump, lhs, rhs, sqrt, tetrahedron, triangle) +from ufl import (Coefficient, Constant, Dx, FacetNormal, FunctionSpace, Mesh, TestFunction, TestFunctions, + TrialFunction, TrialFunctions, VectorConstant, avg, curl, div, dot, ds, dS, dx, grad, i, inner, j, + jump, lhs, rhs, sqrt, tetrahedron, triangle) +from ufl.finiteelement import FiniteElement, MixedElement +from ufl.pullback import contravariant_piola, covariant_piola, identity_pullback +from ufl.sobolevspace import H1, L2, HCurl, HDiv def testConstant(): - element = FiniteElement("Lagrange", "triangle", 1) - domain = Mesh(VectorElement("Lagrange", "triangle", 1)) + element = FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1) + domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) space = FunctionSpace(domain, element) v = TestFunction(space) @@ -36,8 +39,8 @@ def testConstant(): def testElasticity(): - element = VectorElement("Lagrange", "tetrahedron", 1) - domain = Mesh(VectorElement("Lagrange", "tetrahedron", 1)) + element = FiniteElement("Lagrange", tetrahedron, 1, (3, ), identity_pullback, H1) + domain = Mesh(FiniteElement("Lagrange", tetrahedron, 1, (3, ), identity_pullback, H1)) space = FunctionSpace(domain, element) v = TestFunction(space) @@ -52,8 +55,8 @@ def eps(v): def testEnergyNorm(): - element = FiniteElement("Lagrange", "tetrahedron", 1) - domain = Mesh(VectorElement("Lagrange", "tetrahedron", 1)) + element = FiniteElement("Lagrange", tetrahedron, 1, (), identity_pullback, H1) + domain = Mesh(FiniteElement("Lagrange", tetrahedron, 1, (3, ), identity_pullback, H1)) space = FunctionSpace(domain, element) v = Coefficient(space) @@ -61,8 +64,8 @@ def testEnergyNorm(): def testEquation(): - element = FiniteElement("Lagrange", "triangle", 1) - domain = Mesh(VectorElement("Lagrange", "triangle", 1)) + element = FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1) + domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) space = FunctionSpace(domain, element) k = 0.1 @@ -78,8 +81,8 @@ def testEquation(): def testFunctionOperators(): - element = FiniteElement("Lagrange", "triangle", 1) - domain = Mesh(VectorElement("Lagrange", "triangle", 1)) + element = FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1) + domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) space = FunctionSpace(domain, element) v = TestFunction(space) @@ -93,8 +96,8 @@ def testFunctionOperators(): def testHeat(): - element = FiniteElement("Lagrange", "triangle", 1) - domain = Mesh(VectorElement("Lagrange", "triangle", 1)) + element = FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1) + domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) space = FunctionSpace(domain, element) v = TestFunction(space) @@ -109,8 +112,8 @@ def testHeat(): def testMass(): - element = FiniteElement("Lagrange", "tetrahedron", 3) - domain = Mesh(VectorElement("Lagrange", "tetrahedron", 1)) + element = FiniteElement("Lagrange", tetrahedron, 3, (), identity_pullback, H1) + domain = Mesh(FiniteElement("Lagrange", tetrahedron, 1, (3, ), identity_pullback, H1)) space = FunctionSpace(domain, element) v = TestFunction(space) @@ -120,19 +123,18 @@ def testMass(): def testMixedMixedElement(): - P3 = FiniteElement("Lagrange", "triangle", 3) - - element = (P3 * P3) * (P3 * P3) # noqa: F841 + P3 = FiniteElement("Lagrange", triangle, 3, (), identity_pullback, H1) + MixedElement([[P3, P3], [P3, P3]]) def testMixedPoisson(): q = 1 - BDM = FiniteElement("Brezzi-Douglas-Marini", "triangle", q) - DG = FiniteElement("Discontinuous Lagrange", "triangle", q - 1) + BDM = FiniteElement("Brezzi-Douglas-Marini", triangle, q, (2, ), contravariant_piola, HDiv) + DG = FiniteElement("Discontinuous Lagrange", triangle, q - 1, (), identity_pullback, L2) - mixed_element = BDM * DG - domain = Mesh(VectorElement("Lagrange", "triangle", 1)) + mixed_element = MixedElement([BDM, DG]) + domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) space = FunctionSpace(domain, mixed_element) (tau, w) = TestFunctions(space) @@ -145,8 +147,8 @@ def testMixedPoisson(): def testNavierStokes(): - element = VectorElement("Lagrange", "tetrahedron", 1) - domain = Mesh(VectorElement("Lagrange", "tetrahedron", 1)) + element = FiniteElement("Lagrange", tetrahedron, 1, (3, ), identity_pullback, H1) + domain = Mesh(FiniteElement("Lagrange", tetrahedron, 1, (3, ), identity_pullback, H1)) space = FunctionSpace(domain, element) v = TestFunction(space) @@ -159,8 +161,8 @@ def testNavierStokes(): def testNeumannProblem(): - element = VectorElement("Lagrange", "triangle", 1) - domain = Mesh(VectorElement("Lagrange", "triangle", 1)) + element = FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1) + domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) space = FunctionSpace(domain, element) v = TestFunction(space) @@ -176,8 +178,8 @@ def testNeumannProblem(): def testOptimization(): - element = FiniteElement("Lagrange", "triangle", 3) - domain = Mesh(VectorElement("Lagrange", "triangle", 1)) + element = FiniteElement("Lagrange", triangle, 3, (), identity_pullback, H1) + domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) space = FunctionSpace(domain, element) v = TestFunction(space) @@ -189,16 +191,16 @@ def testOptimization(): def testP5tet(): - element = FiniteElement("Lagrange", tetrahedron, 5) # noqa: F841 + FiniteElement("Lagrange", tetrahedron, 5, (), identity_pullback, H1) def testP5tri(): - element = FiniteElement("Lagrange", triangle, 5) # noqa: F841 + FiniteElement("Lagrange", triangle, 5, (), identity_pullback, H1) def testPoissonDG(): - element = FiniteElement("Discontinuous Lagrange", triangle, 1) - domain = Mesh(VectorElement("Lagrange", "triangle", 1)) + element = FiniteElement("Discontinuous Lagrange", triangle, 1, (), identity_pullback, L2) + domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) space = FunctionSpace(domain, element) v = TestFunction(space) @@ -236,8 +238,8 @@ def testPoissonDG(): def testPoisson(): - element = FiniteElement("Lagrange", "triangle", 1) - domain = Mesh(VectorElement("Lagrange", "triangle", 1)) + element = FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1) + domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) space = FunctionSpace(domain, element) v = TestFunction(space) @@ -250,8 +252,8 @@ def testPoisson(): def testPoissonSystem(): - element = VectorElement("Lagrange", "triangle", 1) - domain = Mesh(VectorElement("Lagrange", "triangle", 1)) + element = FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1) + domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) space = FunctionSpace(domain, element) v = TestFunction(space) @@ -270,8 +272,8 @@ def testProjection(): # in FFC for a while. For DOLFIN, the current (global) L^2 # projection can be extended to handle also local projections. - P1 = FiniteElement("Lagrange", "triangle", 1) - domain = Mesh(VectorElement("Lagrange", "triangle", 1)) + P1 = FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1) + domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) space = FunctionSpace(domain, P1) v = TestFunction(space) # noqa: F841 @@ -285,16 +287,16 @@ def testProjection(): def testQuadratureElement(): - element = FiniteElement("Lagrange", "triangle", 2) + element = FiniteElement("Lagrange", triangle, 2, (), identity_pullback, H1) # FFC notation: - # QE = QuadratureElement("triangle", 3) - # sig = VectorQuadratureElement("triangle", 3) + # QE = QuadratureElement(triangle, 3) + # sig = VectorQuadratureElement(triangle, 3) - QE = FiniteElement("Quadrature", "triangle", 3) - sig = VectorElement("Quadrature", "triangle", 3) + QE = FiniteElement("Quadrature", triangle, 3, (), identity_pullback, L2) + sig = FiniteElement("Quadrature", triangle, 3, (2, ), identity_pullback, L2) - domain = Mesh(VectorElement("Lagrange", "triangle", 1)) + domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) space = FunctionSpace(domain, element) v = TestFunction(space) @@ -311,11 +313,11 @@ def testQuadratureElement(): def testStokes(): # UFLException: Shape mismatch in sum. - P2 = VectorElement("Lagrange", "triangle", 2) - P1 = FiniteElement("Lagrange", "triangle", 1) - TH = P2 * P1 + P2 = FiniteElement("Lagrange", triangle, 2, (2, ), identity_pullback, H1) + P1 = FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1) + TH = MixedElement([P2, P1]) - domain = Mesh(VectorElement("Lagrange", "triangle", 1)) + domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) th_space = FunctionSpace(domain, TH) p2_space = FunctionSpace(domain, P2) @@ -332,8 +334,8 @@ def testStokes(): def testSubDomain(): - element = FiniteElement("CG", "tetrahedron", 1) - domain = Mesh(VectorElement("Lagrange", "tetrahedron", 1)) + element = FiniteElement("Lagrange", tetrahedron, 1, (), identity_pullback, H1) + domain = Mesh(FiniteElement("Lagrange", tetrahedron, 1, (3, ), identity_pullback, H1)) space = FunctionSpace(domain, element) f = Coefficient(space) @@ -342,8 +344,8 @@ def testSubDomain(): def testSubDomains(): - element = FiniteElement("CG", "tetrahedron", 1) - domain = Mesh(VectorElement("Lagrange", "tetrahedron", 1)) + element = FiniteElement("Lagrange", tetrahedron, 1, (), identity_pullback, H1) + domain = Mesh(FiniteElement("Lagrange", tetrahedron, 1, (3, ), identity_pullback, H1)) space = FunctionSpace(domain, element) v = TestFunction(space) @@ -355,8 +357,8 @@ def testSubDomains(): def testTensorWeightedPoisson(): # FFC notation: - # P1 = FiniteElement("Lagrange", "triangle", 1) - # P0 = FiniteElement("Discontinuous Lagrange", "triangle", 0) + # P1 = FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1) + # P0 = FiniteElement("Discontinuous Lagrange", triangle, 0, (), identity_pullback, L2) # # v = TestFunction(P1) # u = TrialFunction(P1) @@ -371,10 +373,10 @@ def testTensorWeightedPoisson(): # # a = dot(grad(v), mult(C, grad(u)))*dx - P1 = FiniteElement("Lagrange", "triangle", 1) - P0 = TensorElement("Discontinuous Lagrange", "triangle", 0, shape=(2, 2)) + P1 = FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1) + P0 = FiniteElement("Discontinuous Lagrange", triangle, 0, (2, 2), identity_pullback, L2) - domain = Mesh(VectorElement("Lagrange", "triangle", 1)) + domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) p1_space = FunctionSpace(domain, P1) p0_space = FunctionSpace(domain, P0) @@ -401,16 +403,16 @@ def HodgeLaplaceGradCurl(space, fspace): return [a, L] - shape = "tetrahedron" + shape = tetrahedron order = 1 - GRAD = FiniteElement("Lagrange", shape, order) + GRAD = FiniteElement("Lagrange", shape, order, (), identity_pullback, H1) # FFC notation: CURL = FiniteElement("Nedelec", shape, order-1) - CURL = FiniteElement("N1curl", shape, order) + CURL = FiniteElement("N1curl", shape, order, (3, ), covariant_piola, HCurl) - VectorLagrange = VectorElement("Lagrange", shape, order + 1) - domain = Mesh(VectorElement("Lagrange", shape, 1)) + VectorLagrange = FiniteElement("Lagrange", shape, order + 1, (3, ), identity_pullback, H1) + domain = Mesh(FiniteElement("Lagrange", shape, 1, (3, ), identity_pullback, H1)) - [a, L] = HodgeLaplaceGradCurl(FunctionSpace(domain, GRAD * CURL), + [a, L] = HodgeLaplaceGradCurl(FunctionSpace(domain, MixedElement([GRAD, CURL])), FunctionSpace(domain, VectorLagrange)) diff --git a/test/test_form.py b/test/test_form.py index 554ed85c5..30ca5117a 100755 --- a/test/test_form.py +++ b/test/test_form.py @@ -1,27 +1,30 @@ import pytest -from ufl import (Coefficient, Cofunction, FiniteElement, Form, FormSum, FunctionSpace, Mesh, SpatialCoordinate, - TestFunction, TrialFunction, VectorElement, dot, ds, dx, grad, inner, nabla_grad, triangle) +from ufl import (Coefficient, Cofunction, Form, FormSum, FunctionSpace, Mesh, SpatialCoordinate, TestFunction, + TrialFunction, dot, ds, dx, grad, inner, nabla_grad, triangle) +from ufl.finiteelement import FiniteElement from ufl.form import BaseForm +from ufl.pullback import identity_pullback +from ufl.sobolevspace import H1 @pytest.fixture def element(): cell = triangle - element = FiniteElement("Lagrange", cell, 1) + element = FiniteElement("Lagrange", cell, 1, (), identity_pullback, H1) return element @pytest.fixture def domain(): cell = triangle - return Mesh(VectorElement("Lagrange", cell, 1)) + return Mesh(FiniteElement("Lagrange", cell, 1, (2, ), identity_pullback, H1)) @pytest.fixture def mass(domain): cell = triangle - element = FiniteElement("Lagrange", cell, 1) + element = FiniteElement("Lagrange", cell, 1, (), identity_pullback, H1) space = FunctionSpace(domain, element) v = TestFunction(space) u = TrialFunction(space) @@ -31,7 +34,7 @@ def mass(domain): @pytest.fixture def stiffness(domain): cell = triangle - element = FiniteElement("Lagrange", cell, 1) + element = FiniteElement("Lagrange", cell, 1, (), identity_pullback, H1) space = FunctionSpace(domain, element) v = TestFunction(space) u = TrialFunction(space) @@ -41,7 +44,7 @@ def stiffness(domain): @pytest.fixture def convection(domain): cell = triangle - element = VectorElement("Lagrange", cell, 1) + element = FiniteElement("Lagrange", cell, 1, (2, ), identity_pullback, H1) space = FunctionSpace(domain, element) v = TestFunction(space) u = TrialFunction(space) @@ -52,7 +55,7 @@ def convection(domain): @pytest.fixture def load(domain): cell = triangle - element = FiniteElement("Lagrange", cell, 1) + element = FiniteElement("Lagrange", cell, 1, (), identity_pullback, H1) space = FunctionSpace(domain, element) f = Coefficient(space) v = TestFunction(space) @@ -62,7 +65,7 @@ def load(domain): @pytest.fixture def boundary_load(domain): cell = triangle - element = FiniteElement("Lagrange", cell, 1) + element = FiniteElement("Lagrange", cell, 1, (), identity_pullback, H1) space = FunctionSpace(domain, element) f = Coefficient(space) v = TestFunction(space) @@ -100,8 +103,8 @@ def test_form_coefficients(element, domain): def test_form_domains(): cell = triangle - domain = Mesh(VectorElement("Lagrange", cell, 1)) - element = FiniteElement("Lagrange", cell, 1) + element = FiniteElement("Lagrange", cell, 1, (), identity_pullback, H1) + domain = Mesh(FiniteElement("Lagrange", cell, 1, (2, ), identity_pullback, H1)) V = FunctionSpace(domain, element) v = TestFunction(V) @@ -132,8 +135,8 @@ def test_form_integrals(mass, boundary_load): def test_form_call(): - domain = Mesh(VectorElement("Lagrange", triangle, 1)) - element = FiniteElement("Lagrange", triangle, 1) + element = FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1) + domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) V = FunctionSpace(domain, element) v = TestFunction(V) u = TrialFunction(V) @@ -151,8 +154,8 @@ def test_form_call(): def test_formsum(mass): - domain = Mesh(VectorElement("Lagrange", triangle, 1)) - element = FiniteElement("Lagrange", triangle, 1) + element = FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1) + domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) V = FunctionSpace(domain, element) v = Cofunction(V.dual()) diff --git a/test/test_grad.py b/test/test_grad.py index 4b5d9a996..21b269e2f 100755 --- a/test/test_grad.py +++ b/test/test_grad.py @@ -1,8 +1,11 @@ """Test use of grad in various situations.""" -from ufl import (Coefficient, Constant, FiniteElement, TensorConstant, TensorElement, VectorConstant, VectorElement, - div, dx, grad, indices, inner, interval, tetrahedron, triangle) +from ufl import (Coefficient, Constant, TensorConstant, VectorConstant, div, dx, grad, indices, inner, interval, + tetrahedron, triangle) from ufl.algorithms import compute_form_data +from ufl.finiteelement import FiniteElement +from ufl.pullback import identity_pullback +from ufl.sobolevspace import H1 def xtest_grad_div_curl_properties_in_1D(self): @@ -20,9 +23,9 @@ def xtest_grad_div_curl_properties_in_3D(self): def _test_grad_div_curl_properties(self, cell): d = cell.geometric_dimension() - S = FiniteElement("CG", cell, 1) - V = VectorElement("CG", cell, 1) - T = TensorElement("CG", cell, 1) + S = FiniteElement("Lagrange", cell, 1, (), identity_pullback, H1) + V = FiniteElement("Lagrange", cell, 1, (d, ), identity_pullback, H1) + T = FiniteElement("Lagrange", cell, 1, (d, d), identity_pullback, H1) cs = Constant(cell) cv = VectorConstant(cell) diff --git a/test/test_illegal.py b/test/test_illegal.py index 40409ef6d..9931946a4 100755 --- a/test/test_illegal.py +++ b/test/test_illegal.py @@ -1,23 +1,26 @@ import pytest -from ufl import Argument, Coefficient, FiniteElement, FunctionSpace, Mesh, VectorElement +from ufl import Argument, Coefficient, FunctionSpace, Mesh, triangle +from ufl.finiteelement import FiniteElement +from ufl.pullback import identity_pullback +from ufl.sobolevspace import H1 # TODO: Add more illegal expressions to check! @pytest.fixture def selement(): - return FiniteElement("Lagrange", "triangle", 1) + return FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1) @pytest.fixture def velement(): - return VectorElement("Lagrange", "triangle", 1) + return FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1) @pytest.fixture def domain(): - return Mesh(VectorElement("Lagrange", "triangle", 1)) + return Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) @pytest.fixture diff --git a/test/test_indexing.py b/test/test_indexing.py index 5b7450e9b..4ffdc7eb5 100755 --- a/test/test_indexing.py +++ b/test/test_indexing.py @@ -1,12 +1,15 @@ import pytest -from ufl import Index, Mesh, SpatialCoordinate, VectorElement, outer, triangle +from ufl import Index, Mesh, SpatialCoordinate, outer, triangle from ufl.classes import FixedIndex, Indexed, MultiIndex, Outer, Zero +from ufl.finiteelement import FiniteElement +from ufl.pullback import identity_pullback +from ufl.sobolevspace import H1 @pytest.fixture def domain(): - return Mesh(VectorElement("Lagrange", triangle, 1)) + return Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) @pytest.fixture diff --git a/test/test_indices.py b/test/test_indices.py index e1ab15587..49cf16a1a 100755 --- a/test/test_indices.py +++ b/test/test_indices.py @@ -1,15 +1,18 @@ import pytest -from ufl import (Argument, Coefficient, FunctionSpace, Mesh, TensorElement, TestFunction, TrialFunction, VectorElement, - as_matrix, as_tensor, as_vector, cos, dx, exp, i, indices, j, k, l, outer, sin, triangle) +from ufl import (Argument, Coefficient, FunctionSpace, Mesh, TestFunction, TrialFunction, as_matrix, as_tensor, + as_vector, cos, dx, exp, i, indices, j, k, l, outer, sin, triangle) from ufl.classes import IndexSum +from ufl.finiteelement import FiniteElement +from ufl.pullback import identity_pullback +from ufl.sobolevspace import H1 # TODO: add more expressions to test as many possible combinations of index notation as feasible... def test_vector_indices(self): - element = VectorElement("CG", "triangle", 1) - domain = Mesh(VectorElement("Lagrange", "triangle", 1)) + element = FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1) + domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) space = FunctionSpace(domain, element) u = Argument(space, 2) f = Coefficient(space) @@ -18,8 +21,8 @@ def test_vector_indices(self): def test_tensor_indices(self): - element = TensorElement("CG", "triangle", 1) - domain = Mesh(VectorElement("Lagrange", "triangle", 1)) + element = FiniteElement("Lagrange", triangle, 1, (2, 2), identity_pullback, H1) + domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) space = FunctionSpace(domain, element) u = Argument(space, 2) f = Coefficient(space) @@ -31,8 +34,8 @@ def test_tensor_indices(self): def test_indexed_sum1(self): - element = VectorElement("CG", "triangle", 1) - domain = Mesh(VectorElement("Lagrange", "triangle", 1)) + element = FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1) + domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) space = FunctionSpace(domain, element) u = Argument(space, 2) f = Coefficient(space) @@ -42,8 +45,8 @@ def test_indexed_sum1(self): def test_indexed_sum2(self): - element = VectorElement("CG", "triangle", 1) - domain = Mesh(VectorElement("Lagrange", "triangle", 1)) + element = FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1) + domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) space = FunctionSpace(domain, element) v = Argument(space, 2) u = Argument(space, 3) @@ -54,8 +57,8 @@ def test_indexed_sum2(self): def test_indexed_sum3(self): - element = VectorElement("CG", "triangle", 1) - domain = Mesh(VectorElement("Lagrange", "triangle", 1)) + element = FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1) + domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) space = FunctionSpace(domain, element) u = Argument(space, 2) f = Coefficient(space) @@ -64,8 +67,8 @@ def test_indexed_sum3(self): def test_indexed_function1(self): - element = VectorElement("CG", "triangle", 1) - domain = Mesh(VectorElement("Lagrange", "triangle", 1)) + element = FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1) + domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) space = FunctionSpace(domain, element) v = Argument(space, 2) u = Argument(space, 3) @@ -75,8 +78,8 @@ def test_indexed_function1(self): def test_indexed_function2(self): - element = VectorElement("CG", "triangle", 1) - domain = Mesh(VectorElement("Lagrange", "triangle", 1)) + element = FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1) + domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) space = FunctionSpace(domain, element) v = Argument(space, 2) u = Argument(space, 3) @@ -92,8 +95,8 @@ def test_indexed_function2(self): def test_indexed_function3(self): - element = VectorElement("CG", "triangle", 1) - domain = Mesh(VectorElement("Lagrange", "triangle", 1)) + element = FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1) + domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) space = FunctionSpace(domain, element) Argument(space, 2) u = Argument(space, 3) @@ -103,8 +106,8 @@ def test_indexed_function3(self): def test_vector_from_indices(self): - element = VectorElement("CG", "triangle", 1) - domain = Mesh(VectorElement("Lagrange", "triangle", 1)) + element = FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1) + domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) space = FunctionSpace(domain, element) v = TestFunction(space) u = TrialFunction(space) @@ -121,8 +124,8 @@ def test_vector_from_indices(self): def test_matrix_from_indices(self): - element = VectorElement("CG", "triangle", 1) - domain = Mesh(VectorElement("Lagrange", "triangle", 1)) + element = FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1) + domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) space = FunctionSpace(domain, element) v = TestFunction(space) u = TrialFunction(space) @@ -139,8 +142,8 @@ def test_matrix_from_indices(self): def test_vector_from_list(self): - element = VectorElement("CG", "triangle", 1) - domain = Mesh(VectorElement("Lagrange", "triangle", 1)) + element = FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1) + domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) space = FunctionSpace(domain, element) v = TestFunction(space) u = TrialFunction(space) @@ -153,8 +156,8 @@ def test_vector_from_list(self): def test_matrix_from_list(self): - element = VectorElement("CG", "triangle", 1) - domain = Mesh(VectorElement("Lagrange", "triangle", 1)) + element = FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1) + domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) space = FunctionSpace(domain, element) v = TestFunction(space) u = TrialFunction(space) @@ -174,8 +177,8 @@ def test_matrix_from_list(self): def test_tensor(self): - element = VectorElement("CG", "triangle", 1) - domain = Mesh(VectorElement("Lagrange", "triangle", 1)) + element = FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1) + domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) space = FunctionSpace(domain, element) v = TestFunction(space) u = TrialFunction(space) @@ -214,8 +217,8 @@ def test_tensor(self): def test_indexed(self): - element = VectorElement("CG", "triangle", 1) - domain = Mesh(VectorElement("Lagrange", "triangle", 1)) + element = FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1) + domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) space = FunctionSpace(domain, element) v = TestFunction(space) u = TrialFunction(space) @@ -235,8 +238,8 @@ def test_indexed(self): def test_spatial_derivative(self): cell = triangle - element = VectorElement("CG", cell, 1) - domain = Mesh(VectorElement("Lagrange", cell, 1)) + element = FiniteElement("Lagrange", cell, 1, (2, ), identity_pullback, H1) + domain = Mesh(FiniteElement("Lagrange", cell, 1, (2, ), identity_pullback, H1)) space = FunctionSpace(domain, element) v = TestFunction(space) u = TrialFunction(space) diff --git a/test/test_interpolate.py b/test/test_interpolate.py index 6deadc07d..71f3cd145 100644 --- a/test/test_interpolate.py +++ b/test/test_interpolate.py @@ -5,29 +5,32 @@ import pytest -from ufl import (Action, Adjoint, Argument, Coefficient, FiniteElement, FunctionSpace, Mesh, TestFunction, - TrialFunction, VectorElement, action, adjoint, derivative, dx, grad, inner, replace, triangle) +from ufl import (Action, Adjoint, Argument, Coefficient, FunctionSpace, Mesh, TestFunction, TrialFunction, action, + adjoint, derivative, dx, grad, inner, replace, triangle) from ufl.algorithms.ad import expand_derivatives from ufl.algorithms.analysis import (extract_arguments, extract_arguments_and_coefficients, extract_base_form_operators, extract_coefficients) from ufl.algorithms.expand_indices import expand_indices from ufl.core.interpolate import Interpolate +from ufl.finiteelement import FiniteElement +from ufl.pullback import identity_pullback +from ufl.sobolevspace import H1 @pytest.fixture def domain_2d(): - return Mesh(VectorElement("Lagrange", triangle, 1)) + return Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) @pytest.fixture def V1(domain_2d): - f1 = FiniteElement("CG", triangle, 1) + f1 = FiniteElement("CG", triangle, 1, (), identity_pullback, H1) return FunctionSpace(domain_2d, f1) @pytest.fixture def V2(domain_2d): - f1 = FiniteElement("CG", triangle, 2) + f1 = FiniteElement("CG", triangle, 2, (), identity_pullback, H1) return FunctionSpace(domain_2d, f1) diff --git a/test/test_elements.py b/test/test_legacy.py old mode 100755 new mode 100644 similarity index 81% rename from test/test_elements.py rename to test/test_legacy.py index 370679662..fd3de052f --- a/test/test_elements.py +++ b/test/test_legacy.py @@ -1,9 +1,26 @@ -from ufl import (Coefficient, FiniteElement, FunctionSpace, Mesh, MixedElement, TensorElement, VectorElement, - WithMapping, dx, hexahedron, inner, interval, quadrilateral, tetrahedron, triangle) +from ufl import (H1, Coefficient, FunctionSpace, Mesh, dx, hexahedron, identity_pullback, inner, interval, + quadrilateral, tetrahedron, triangle) +from ufl.legacy import FiniteElement, MixedElement, TensorElement, VectorElement, WithMapping all_cells = (interval, triangle, tetrahedron, quadrilateral, hexahedron) -# TODO: cover all valid element definitions + +def test_legacy_vs_new(): + from ufl.finiteelement import FiniteElement as NewFiniteElement + e = FiniteElement("Lagrange", triangle, 1) + new_e = NewFiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1) + assert e.sobolev_space == new_e.sobolev_space + assert e.pullback == new_e.pullback + assert e.embedded_superdegree == new_e.embedded_superdegree + assert e.embedded_subdegree == new_e.embedded_subdegree + assert e.cell == new_e.cell + assert e.reference_value_shape == new_e.reference_value_shape + assert e.value_shape == new_e.value_shape + assert e.reference_value_size == new_e.reference_value_size + assert e.value_size == new_e.value_size + assert e.num_sub_elements == new_e.num_sub_elements + assert e.sub_elements == new_e.sub_elements + assert e.is_cellwise_constant() == new_e.is_cellwise_constant() def test_scalar_galerkin(): @@ -11,12 +28,12 @@ def test_scalar_galerkin(): for p in range(1, 10): for family in ("Lagrange", "CG", "Discontinuous Lagrange", "DG", "Discontinuous Lagrange L2", "DG L2"): element = FiniteElement(family, cell, p) - assert element.value_shape() == () + assert element.value_shape == () assert element == eval(repr(element)) for p in range(1, 10): for family in ("TDG", "Discontinuous Taylor"): element = FiniteElement(family, interval, p) - assert element.value_shape() == () + assert element.value_shape == () def test_vector_galerkin(): @@ -27,7 +44,7 @@ def test_vector_galerkin(): for p in range(1, 10): for family in ("Lagrange", "CG", "Discontinuous Lagrange", "DG", "Discontinuous Lagrange L2", "DG L2"): element = VectorElement(family, cell, p) - assert element.value_shape() == shape + assert element.value_shape == shape assert element == eval(repr(element)) for i in range(dim): c = element.extract_component(i) @@ -42,7 +59,7 @@ def test_tensor_galerkin(): for p in range(1, 10): for family in ("Lagrange", "CG", "Discontinuous Lagrange", "DG", "Discontinuous Lagrange L2", "DG L2"): element = TensorElement(family, cell, p) - assert element.value_shape() == shape + assert element.value_shape == shape assert element == eval(repr(element)) for i in range(dim): for j in range(dim): @@ -65,7 +82,7 @@ def test_tensor_symmetry(): family, cell, p, shape=(dim, dim), symmetry=s) else: element = TensorElement(family, cell, p, symmetry=s) - assert element.value_shape(), (dim == dim) + assert element.value_shape, (dim == dim) assert element == eval(repr(element)) for i in range(dim): for j in range(dim): @@ -80,6 +97,8 @@ def test_mixed_tensor_symmetries(): V = VectorElement('CG', triangle, 1) T = TensorElement('CG', triangle, 1, symmetry=True) + print(T.pullback) + # M has dimension 4+1, symmetries are 2->1 M = T * S domain = Mesh(VectorElement("Lagrange", triangle, 1)) @@ -106,7 +125,7 @@ def test_bdm(): for cell in (triangle, tetrahedron): dim = cell.geometric_dimension() element = FiniteElement("BDM", cell, 1) - assert element.value_shape() == (dim,) + assert element.value_shape == (dim,) assert element == eval(repr(element)) @@ -114,14 +133,14 @@ def test_vector_bdm(): for cell in (triangle, tetrahedron): dim = cell.geometric_dimension() element = VectorElement("BDM", cell, 1) - assert element.value_shape(), (dim == dim) + assert element.value_shape, (dim == dim) assert element == eval(repr(element)) def test_mtw(): cell = triangle element = FiniteElement("MTW", cell, 3) - assert element.value_shape() == (cell.geometric_dimension(), ) + assert element.value_shape == (cell.geometric_dimension(), ) assert element == eval(repr(element)) assert element.mapping() == "contravariant Piola" @@ -133,8 +152,8 @@ def test_mixed(): pelement = FiniteElement("CG", cell, 1) TH1 = MixedElement(velement, pelement) TH2 = velement * pelement - assert TH1.value_shape() == (dim + 1,) - assert TH2.value_shape() == (dim + 1,) + assert TH1.value_shape == (dim + 1,) + assert TH2.value_shape == (dim + 1,) assert repr(TH1) == repr(TH2) assert TH1 == eval(repr(TH2)) assert TH2 == eval(repr(TH1)) @@ -147,8 +166,8 @@ def test_nested_mixed(): pelement = FiniteElement("CG", cell, 1) TH1 = MixedElement((velement, pelement), pelement) TH2 = velement * pelement * pelement - assert TH1.value_shape() == (dim + 2,) - assert TH2.value_shape() == (dim + 2,) + assert TH1.value_shape == (dim + 2,) + assert TH2.value_shape == (dim + 2,) assert repr(TH1) == repr(TH2) assert TH1 == eval(repr(TH2)) assert TH2 == eval(repr(TH1)) diff --git a/test/test_lhs_rhs.py b/test/test_lhs_rhs.py index 4593a8aa1..2be0e584c 100755 --- a/test/test_lhs_rhs.py +++ b/test/test_lhs_rhs.py @@ -3,13 +3,16 @@ # First added: 2011-11-09 # Last changed: 2011-11-09 -from ufl import (Argument, Coefficient, Constant, FiniteElement, FunctionSpace, Mesh, TestFunction, TrialFunction, - VectorElement, action, derivative, ds, dS, dx, exp, interval, system) +from ufl import (Argument, Coefficient, Constant, FunctionSpace, Mesh, TestFunction, TrialFunction, action, derivative, + ds, dS, dx, exp, interval, system) +from ufl.finiteelement import FiniteElement +from ufl.pullback import identity_pullback +from ufl.sobolevspace import H1 def test_lhs_rhs_simple(): - V = FiniteElement("CG", interval, 1) - domain = Mesh(VectorElement("Lagrange", interval, 1)) + V = FiniteElement("Lagrange", interval, 1, (), identity_pullback, H1) + domain = Mesh(FiniteElement("Lagrange", interval, 1, (1, ), identity_pullback, H1)) space = FunctionSpace(domain, V) v = TestFunction(space) u = TrialFunction(space) @@ -37,8 +40,8 @@ def test_lhs_rhs_simple(): def test_lhs_rhs_derivatives(): - V = FiniteElement("CG", interval, 1) - domain = Mesh(VectorElement("Lagrange", interval, 1)) + V = FiniteElement("Lagrange", interval, 1, (), identity_pullback, H1) + domain = Mesh(FiniteElement("Lagrange", interval, 1, (1, ), identity_pullback, H1)) space = FunctionSpace(domain, V) v = TestFunction(space) u = TrialFunction(space) @@ -54,8 +57,8 @@ def test_lhs_rhs_derivatives(): def test_lhs_rhs_slightly_obscure(): - V = FiniteElement("CG", interval, 1) - domain = Mesh(VectorElement("Lagrange", interval, 1)) + V = FiniteElement("Lagrange", interval, 1, (), identity_pullback, H1) + domain = Mesh(FiniteElement("Lagrange", interval, 1, (1, ), identity_pullback, H1)) space = FunctionSpace(domain, V) u = TrialFunction(space) w = Argument(space, 2) diff --git a/test/test_measures.py b/test/test_measures.py index 86a8ea68c..2a31d51e1 100755 --- a/test/test_measures.py +++ b/test/test_measures.py @@ -2,7 +2,10 @@ from mockobjects import MockMesh, MockMeshFunction -from ufl import Cell, Coefficient, FiniteElement, FunctionSpace, Measure, Mesh, as_ufl, dC, dI, dO, triangle +from ufl import Cell, Coefficient, FunctionSpace, Measure, Mesh, as_ufl, dC, dI, dO, triangle +from ufl.finiteelement import FiniteElement +from ufl.pullback import identity_pullback +from ufl.sobolevspace import H1 def test_construct_forms_from_default_measures(): @@ -57,7 +60,7 @@ def test_construct_forms_from_default_measures(): # Check that we can create a basic form with default measure one = as_ufl(1) - one * dx(Mesh(triangle)) + one * dx(Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1))) def test_foo(): @@ -67,7 +70,8 @@ def test_foo(): tdim = 2 cell = Cell("triangle", gdim) mymesh = MockMesh(9) - mydomain = Mesh(cell, ufl_id=9, cargo=mymesh) + mydomain = Mesh(FiniteElement("Lagrange", cell, 1, (gdim, ), identity_pullback, H1), + ufl_id=9, cargo=mymesh) assert cell.topological_dimension() == tdim assert cell.geometric_dimension() == gdim @@ -79,7 +83,7 @@ def test_foo(): assert mydomain.ufl_cargo() == mymesh # Define a coefficient for use in tests below - V = FunctionSpace(mydomain, FiniteElement("CG", cell, 1)) + V = FunctionSpace(mydomain, FiniteElement("Lagrange", cell, 1, (), identity_pullback, H1)) f = Coefficient(V) # Test definition of a custom measure with explicit parameters diff --git a/test/test_mixed_function_space.py b/test/test_mixed_function_space.py index 84af918e6..6e3e68356 100644 --- a/test/test_mixed_function_space.py +++ b/test/test_mixed_function_space.py @@ -1,20 +1,23 @@ __authors__ = "Cecile Daversin Catty" __date__ = "2019-03-26 -- 2019-03-26" -from ufl import (FiniteElement, FunctionSpace, Measure, Mesh, MixedFunctionSpace, TestFunctions, TrialFunctions, - VectorElement, interval, tetrahedron, triangle) +from ufl import (FunctionSpace, Measure, Mesh, MixedFunctionSpace, TestFunctions, TrialFunctions, interval, tetrahedron, + triangle) from ufl.algorithms.formsplitter import extract_blocks +from ufl.finiteelement import FiniteElement +from ufl.pullback import identity_pullback +from ufl.sobolevspace import H1 def test_mixed_functionspace(self): # Domains - domain_3d = Mesh(VectorElement("Lagrange", tetrahedron, 1)) - domain_2d = Mesh(VectorElement("Lagrange", triangle, 1)) - domain_1d = Mesh(VectorElement("Lagrange", interval, 1)) + domain_3d = Mesh(FiniteElement("Lagrange", tetrahedron, 1, (3, ), identity_pullback, H1)) + domain_2d = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) + domain_1d = Mesh(FiniteElement("Lagrange", interval, 1, (1, ), identity_pullback, H1)) # Finite elements - f_1d = FiniteElement("CG", interval, 1) - f_2d = FiniteElement("CG", triangle, 1) - f_3d = FiniteElement("CG", tetrahedron, 1) + f_1d = FiniteElement("Lagrange", interval, 1, (), identity_pullback, H1) + f_2d = FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1) + f_3d = FiniteElement("Lagrange", tetrahedron, 1, (), identity_pullback, H1) # Function spaces V_3d = FunctionSpace(domain_3d, f_3d) V_2d = FunctionSpace(domain_2d, f_2d) @@ -33,9 +36,9 @@ def test_mixed_functionspace(self): (v_3d, v_2d, v_1d) = TestFunctions(V) # Measures - dx3 = Measure("dx", domain=V_3d) - dx2 = Measure("dx", domain=V_2d) - dx1 = Measure("dx", domain=V_1d) + dx3 = Measure("dx", domain=domain_3d) + dx2 = Measure("dx", domain=domain_2d) + dx1 = Measure("dx", domain=domain_1d) # Mixed variational form # LHS diff --git a/test/test_new_ad.py b/test/test_new_ad.py index 8643dd886..be6b6d403 100755 --- a/test/test_new_ad.py +++ b/test/test_new_ad.py @@ -1,8 +1,11 @@ -from ufl import (CellVolume, Coefficient, Constant, FacetNormal, FiniteElement, FunctionSpace, Identity, Mesh, - SpatialCoordinate, TestFunction, VectorConstant, VectorElement, as_ufl, cos, derivative, diff, exp, - grad, ln, sin, tan, triangle, variable, zero) +from ufl import (CellVolume, Coefficient, Constant, FacetNormal, FunctionSpace, Identity, Mesh, SpatialCoordinate, + TestFunction, VectorConstant, as_ufl, cos, derivative, diff, exp, grad, ln, sin, tan, triangle, + variable, zero) from ufl.algorithms.apply_derivatives import GenericDerivativeRuleset, GradRuleset, apply_derivatives from ufl.algorithms.renumbering import renumber_indices +from ufl.finiteelement import FiniteElement +from ufl.pullback import identity_pullback +from ufl.sobolevspace import H1, L2 # Note: the old tests in test_automatic_differentiation.py are a bit messy # but still cover many things that are not in here yet. @@ -15,10 +18,10 @@ def test_apply_derivatives_doesnt_change_expression_without_derivatives(): cell = triangle d = cell.geometric_dimension() - V0 = FiniteElement("DG", cell, 0) - V1 = FiniteElement("Lagrange", cell, 1) + V0 = FiniteElement("Discontinuous Lagrange", cell, 0, (), identity_pullback, L2) + V1 = FiniteElement("Lagrange", cell, 1, (), identity_pullback, H1) - domain = Mesh(VectorElement("Lagrange", cell, 1)) + domain = Mesh(FiniteElement("Lagrange", cell, 1, (d, ), identity_pullback, H1)) v0_space = FunctionSpace(domain, V0) v1_space = FunctionSpace(domain, V1) @@ -84,9 +87,9 @@ def test_literal_derivatives_are_zero(): for v in variables: assert apply_derivatives(diff(lit, v)) == zero(lit.ufl_shape + v.ufl_shape) - V0 = FiniteElement("DG", cell, 0) - V1 = FiniteElement("Lagrange", cell, 1) - domain = Mesh(VectorElement("Lagrange", cell, 1)) + V0 = FiniteElement("Discontinuous Lagrange", cell, 0, (), identity_pullback, L2) + V1 = FiniteElement("Lagrange", cell, 1, (), identity_pullback, H1) + domain = Mesh(FiniteElement("Lagrange", cell, 1, (d, ), identity_pullback, H1)) v0_space = FunctionSpace(domain, V0) v1_space = FunctionSpace(domain, V1) u0 = Coefficient(v0_space) @@ -109,14 +112,14 @@ def test_grad_ruleset(): cell = triangle d = cell.geometric_dimension() - V0 = FiniteElement("DG", cell, 0) - V1 = FiniteElement("Lagrange", cell, 1) - V2 = FiniteElement("Lagrange", cell, 2) - W0 = VectorElement("DG", cell, 0) - W1 = VectorElement("Lagrange", cell, 1) - W2 = VectorElement("Lagrange", cell, 2) + V0 = FiniteElement("Discontinuous Lagrange", cell, 0, (), identity_pullback, L2) + V1 = FiniteElement("Lagrange", cell, 1, (), identity_pullback, H1) + V2 = FiniteElement("Lagrange", cell, 2, (), identity_pullback, H1) + W0 = FiniteElement("Discontinuous Lagrange", cell, 0, (2, ), identity_pullback, L2) + W1 = FiniteElement("Lagrange", cell, 1, (d, ), identity_pullback, H1) + W2 = FiniteElement("Lagrange", cell, 2, (d, ), identity_pullback, H1) - domain = Mesh(VectorElement("Lagrange", cell, 1)) + domain = Mesh(FiniteElement("Lagrange", cell, 1, (d, ), identity_pullback, H1)) v0_space = FunctionSpace(domain, V0) v1_space = FunctionSpace(domain, V1) v2_space = FunctionSpace(domain, V2) diff --git a/test/test_pickle.py b/test/test_pickle.py index 3189bde2b..5ab4026f7 100755 --- a/test/test_pickle.py +++ b/test/test_pickle.py @@ -10,17 +10,20 @@ import pickle -from ufl import (Coefficient, Constant, Dx, FacetNormal, FiniteElement, FunctionSpace, Identity, Mesh, TensorElement, - TestFunction, TestFunctions, TrialFunction, TrialFunctions, VectorConstant, VectorElement, avg, curl, - div, dot, dS, ds, dx, grad, i, inner, j, jump, lhs, rhs, sqrt, tetrahedron, triangle) +from ufl import (Coefficient, Constant, Dx, FacetNormal, FunctionSpace, Identity, Mesh, TestFunction, TestFunctions, + TrialFunction, TrialFunctions, VectorConstant, avg, curl, div, dot, dS, ds, dx, grad, i, inner, j, + jump, lhs, rhs, sqrt, tetrahedron, triangle) from ufl.algorithms import compute_form_data +from ufl.finiteelement import FiniteElement, MixedElement +from ufl.pullback import contravariant_piola, covariant_piola, identity_pullback +from ufl.sobolevspace import H1, L2, HCurl, HDiv p = pickle.HIGHEST_PROTOCOL def testConstant(): - element = FiniteElement("Lagrange", "triangle", 1) - domain = Mesh(VectorElement("Lagrange", "triangle", 1)) + element = FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1) + domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) space = FunctionSpace(domain, element) v = TestFunction(space) @@ -44,8 +47,8 @@ def testConstant(): def testElasticity(): - element = VectorElement("Lagrange", "tetrahedron", 1) - domain = Mesh(VectorElement("Lagrange", "tetrahedron", 1)) + element = FiniteElement("Lagrange", tetrahedron, 1, (3, ), identity_pullback, H1) + domain = Mesh(FiniteElement("Lagrange", tetrahedron, 1, (3, ), identity_pullback, H1)) space = FunctionSpace(domain, element) v = TestFunction(space) @@ -65,8 +68,8 @@ def eps(v): def testEnergyNorm(): - element = FiniteElement("Lagrange", "tetrahedron", 1) - domain = Mesh(VectorElement("Lagrange", "tetrahedron", 1)) + element = FiniteElement("Lagrange", tetrahedron, 1, (), identity_pullback, H1) + domain = Mesh(FiniteElement("Lagrange", tetrahedron, 1, (3, ), identity_pullback, H1)) space = FunctionSpace(domain, element) v = Coefficient(space) @@ -79,8 +82,8 @@ def testEnergyNorm(): def testEquation(): - element = FiniteElement("Lagrange", "triangle", 1) - domain = Mesh(VectorElement("Lagrange", "triangle", 1)) + element = FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1) + domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) space = FunctionSpace(domain, element) k = 0.1 @@ -104,8 +107,8 @@ def testEquation(): def testFunctionOperators(): - element = FiniteElement("Lagrange", "triangle", 1) - domain = Mesh(VectorElement("Lagrange", "triangle", 1)) + element = FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1) + domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) space = FunctionSpace(domain, element) v = TestFunction(space) @@ -125,8 +128,8 @@ def testFunctionOperators(): def testHeat(): - element = FiniteElement("Lagrange", "triangle", 1) - domain = Mesh(VectorElement("Lagrange", "triangle", 1)) + element = FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1) + domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) space = FunctionSpace(domain, element) v = TestFunction(space) @@ -149,8 +152,8 @@ def testHeat(): def testMass(): - element = FiniteElement("Lagrange", "tetrahedron", 3) - domain = Mesh(VectorElement("Lagrange", "tetrahedron", 1)) + element = FiniteElement("Lagrange", tetrahedron, 3, (), identity_pullback, H1) + domain = Mesh(FiniteElement("Lagrange", tetrahedron, 1, (3, ), identity_pullback, H1)) space = FunctionSpace(domain, element) v = TestFunction(space) @@ -165,9 +168,9 @@ def testMass(): def testMixedMixedElement(): - P3 = FiniteElement("Lagrange", "triangle", 3) + P3 = FiniteElement("Lagrange", triangle, 3, (), identity_pullback, H1) - element = (P3 * P3) * (P3 * P3) + element = MixedElement([[P3, P3], [P3, P3]]) element_pickle = pickle.dumps(element, p) element_restore = pickle.loads(element_pickle) @@ -178,11 +181,11 @@ def testMixedMixedElement(): def testMixedPoisson(): q = 1 - BDM = FiniteElement("Brezzi-Douglas-Marini", "triangle", q) - DG = FiniteElement("Discontinuous Lagrange", "triangle", q - 1) + BDM = FiniteElement("Brezzi-Douglas-Marini", triangle, q, (2, ), contravariant_piola, HDiv) + DG = FiniteElement("Discontinuous Lagrange", triangle, q - 1, (), identity_pullback, L2) - mixed_element = BDM * DG - domain = Mesh(VectorElement("Lagrange", "triangle", 1)) + mixed_element = MixedElement([BDM, DG]) + domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) mixed_space = FunctionSpace(domain, mixed_element) dg_space = FunctionSpace(domain, DG) @@ -204,8 +207,8 @@ def testMixedPoisson(): def testNavierStokes(): - element = VectorElement("Lagrange", "tetrahedron", 1) - domain = Mesh(VectorElement("Lagrange", "tetrahedron", 1)) + element = FiniteElement("Lagrange", tetrahedron, 1, (3, ), identity_pullback, H1) + domain = Mesh(FiniteElement("Lagrange", tetrahedron, 1, (3, ), identity_pullback, H1)) space = FunctionSpace(domain, element) v = TestFunction(space) @@ -223,8 +226,8 @@ def testNavierStokes(): def testNeumannProblem(): - element = VectorElement("Lagrange", "triangle", 1) - domain = Mesh(VectorElement("Lagrange", "triangle", 1)) + element = FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1) + domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) space = FunctionSpace(domain, element) v = TestFunction(space) @@ -248,8 +251,8 @@ def testNeumannProblem(): def testOptimization(): - element = FiniteElement("Lagrange", "triangle", 3) - domain = Mesh(VectorElement("Lagrange", "triangle", 1)) + element = FiniteElement("Lagrange", triangle, 3, (), identity_pullback, H1) + domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) space = FunctionSpace(domain, element) v = TestFunction(space) @@ -269,7 +272,7 @@ def testOptimization(): def testP5tet(): - element = FiniteElement("Lagrange", tetrahedron, 5) + element = FiniteElement("Lagrange", tetrahedron, 5, (), identity_pullback, H1) element_pickle = pickle.dumps(element, p) element_restore = pickle.loads(element_pickle) @@ -278,15 +281,15 @@ def testP5tet(): def testP5tri(): - element = FiniteElement("Lagrange", triangle, 5) + element = FiniteElement("Lagrange", triangle, 5, (), identity_pullback, H1) element_pickle = pickle.dumps(element, p) pickle.loads(element_pickle) def testPoissonDG(): - element = FiniteElement("Discontinuous Lagrange", triangle, 1) - domain = Mesh(VectorElement("Lagrange", triangle, 1)) + element = FiniteElement("Discontinuous Lagrange", triangle, 1, (), identity_pullback, L2) + domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) space = FunctionSpace(domain, element) v = TestFunction(space) @@ -332,8 +335,8 @@ def testPoissonDG(): def testPoisson(): - element = FiniteElement("Lagrange", "triangle", 1) - domain = Mesh(VectorElement("Lagrange", "triangle", 1)) + element = FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1) + domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) space = FunctionSpace(domain, element) v = TestFunction(space) @@ -354,8 +357,8 @@ def testPoisson(): def testPoissonSystem(): - element = VectorElement("Lagrange", "triangle", 1) - domain = Mesh(VectorElement("Lagrange", "triangle", 1)) + element = FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1) + domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) space = FunctionSpace(domain, element) v = TestFunction(space) @@ -378,16 +381,16 @@ def testPoissonSystem(): def testQuadratureElement(): - element = FiniteElement("Lagrange", "triangle", 2) + element = FiniteElement("Lagrange", triangle, 2, (), identity_pullback, H1) # FFC notation: - # QE = QuadratureElement("triangle", 3) - # sig = VectorQuadratureElement("triangle", 3) + # QE = QuadratureElement(triangle, 3) + # sig = VectorQuadratureElement(triangle, 3) - QE = FiniteElement("Quadrature", "triangle", 3) - sig = VectorElement("Quadrature", "triangle", 3) + QE = FiniteElement("Quadrature", triangle, 3, (), identity_pullback, L2) + sig = FiniteElement("Quadrature", triangle, 3, (2, ), identity_pullback, L2) - domain = Mesh(VectorElement("Lagrange", "triangle", 1)) + domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) space = FunctionSpace(domain, element) qe_space = FunctionSpace(domain, QE) sig_space = FunctionSpace(domain, sig) @@ -414,11 +417,11 @@ def testQuadratureElement(): def testStokes(): # UFLException: Shape mismatch in sum. - P2 = VectorElement("Lagrange", "triangle", 2) - P1 = FiniteElement("Lagrange", "triangle", 1) - TH = P2 * P1 + P2 = FiniteElement("Lagrange", triangle, 2, (2, ), identity_pullback, H1) + P1 = FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1) + TH = MixedElement([P2, P1]) - domain = Mesh(VectorElement("Lagrange", "triangle", 1)) + domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) th_space = FunctionSpace(domain, TH) p2_space = FunctionSpace(domain, P2) @@ -443,8 +446,8 @@ def testStokes(): def testSubDomain(): - element = FiniteElement("CG", "tetrahedron", 1) - domain = Mesh(VectorElement("Lagrange", "tetrahedron", 1)) + element = FiniteElement("Lagrange", tetrahedron, 1, (), identity_pullback, H1) + domain = Mesh(FiniteElement("Lagrange", tetrahedron, 1, (3, ), identity_pullback, H1)) space = FunctionSpace(domain, element) f = Coefficient(space) @@ -458,8 +461,8 @@ def testSubDomain(): def testSubDomains(): - element = FiniteElement("CG", "tetrahedron", 1) - domain = Mesh(VectorElement("Lagrange", "tetrahedron", 1)) + element = FiniteElement("Lagrange", tetrahedron, 1, (), identity_pullback, H1) + domain = Mesh(FiniteElement("Lagrange", tetrahedron, 1, (3, ), identity_pullback, H1)) space = FunctionSpace(domain, element) v = TestFunction(space) @@ -476,8 +479,8 @@ def testSubDomains(): def testTensorWeightedPoisson(): # FFC notation: - # P1 = FiniteElement("Lagrange", "triangle", 1) - # P0 = FiniteElement("Discontinuous Lagrange", "triangle", 0) + # P1 = FiniteElement("Lagrange", triangle, 1) + # P0 = FiniteElement("Discontinuous Lagrange", triangle, 0) # # v = TestFunction(P1) # u = TrialFunction(P1) @@ -492,10 +495,10 @@ def testTensorWeightedPoisson(): # # a = dot(grad(v), mult(C, grad(u)))*dx - P1 = FiniteElement("Lagrange", "triangle", 1) - P0 = TensorElement("Discontinuous Lagrange", "triangle", 0, shape=(2, 2)) + P1 = FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1) + P0 = FiniteElement("Discontinuous Lagrange", triangle, 0, (2, 2), identity_pullback, L2) - domain = Mesh(VectorElement("Lagrange", "triangle", 1)) + domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) p1_space = FunctionSpace(domain, P1) p0_space = FunctionSpace(domain, P0) @@ -527,18 +530,19 @@ def HodgeLaplaceGradCurl(space, fspace): return [a, L] - shape = "tetrahedron" + shape = tetrahedron order = 1 - GRAD = FiniteElement("Lagrange", shape, order) + GRAD = FiniteElement("Lagrange", shape, order, (), identity_pullback, H1) # FFC notation: CURL = FiniteElement("Nedelec", shape, order-1) - CURL = FiniteElement("N1curl", shape, order) + CURL = FiniteElement("N1curl", shape, order, (3, ), covariant_piola, HCurl) - VectorLagrange = VectorElement("Lagrange", shape, order + 1) - domain = Mesh(VectorElement("Lagrange", shape, 1)) + VectorLagrange = FiniteElement("Lagrange", shape, order + 1, (3, ), identity_pullback, H1) + domain = Mesh(FiniteElement("Lagrange", shape, 1, (3, ), identity_pullback, H1)) - [a, L] = HodgeLaplaceGradCurl(FunctionSpace(domain, GRAD * CURL), FunctionSpace(domain, VectorLagrange)) + [a, L] = HodgeLaplaceGradCurl(FunctionSpace(domain, MixedElement([GRAD, CURL])), + FunctionSpace(domain, VectorLagrange)) a_pickle = pickle.dumps(a, p) a_restore = pickle.loads(a_pickle) @@ -557,8 +561,8 @@ def testIdentity(): def testFormData(): - element = FiniteElement("Lagrange", "tetrahedron", 3) - domain = Mesh(VectorElement("Lagrange", "tetrahedron", 1)) + element = FiniteElement("Lagrange", tetrahedron, 3, (), identity_pullback, H1) + domain = Mesh(FiniteElement("Lagrange", tetrahedron, 1, (3, ), identity_pullback, H1)) space = FunctionSpace(domain, element) v = TestFunction(space) diff --git a/test/test_piecewise_checks.py b/test/test_piecewise_checks.py index 0536b62b1..d5d419d37 100755 --- a/test/test_piecewise_checks.py +++ b/test/test_piecewise_checks.py @@ -3,11 +3,14 @@ import pytest from ufl import (Cell, CellDiameter, CellVolume, Circumradius, Coefficient, Constant, FacetArea, FacetNormal, - FiniteElement, FunctionSpace, Jacobian, JacobianDeterminant, JacobianInverse, MaxFacetEdgeLength, Mesh, - MinFacetEdgeLength, SpatialCoordinate, TestFunction, VectorElement, hexahedron, interval, - quadrilateral, tetrahedron, triangle) + FunctionSpace, Jacobian, JacobianDeterminant, JacobianInverse, MaxFacetEdgeLength, Mesh, + MinFacetEdgeLength, SpatialCoordinate, TestFunction, hexahedron, interval, quadrilateral, tetrahedron, + triangle) from ufl.checks import is_cellwise_constant from ufl.classes import CellCoordinate, FacetJacobian, FacetJacobianDeterminant, FacetJacobianInverse +from ufl.finiteelement import FiniteElement +from ufl.pullback import identity_pullback +from ufl.sobolevspace import H1, L2, HInf def get_domains(): @@ -19,13 +22,15 @@ def get_domains(): tetrahedron, hexahedron, ] - return [Mesh(cell) for cell in all_cells] + return [Mesh(FiniteElement("Lagrange", cell, 1, (cell.geometric_dimension(), ), + identity_pullback, H1)) for cell in all_cells] def get_nonlinear(): domains_with_quadratic_coordinates = [] for D in get_domains(): - V = VectorElement("CG", D.ufl_cell(), 2) + V = FiniteElement("Lagrange", D.ufl_cell(), 2, (D.ufl_cell().geometric_dimension(), ), + identity_pullback, H1) E = Mesh(V) domains_with_quadratic_coordinates.append(E) @@ -48,7 +53,8 @@ def domains(request): domains = get_domains() domains_with_linear_coordinates = [] for D in domains: - V = VectorElement("CG", D.ufl_cell(), 1) + V = FiniteElement("Lagrange", D.ufl_cell(), 1, (D.ufl_cell().geometric_dimension(), ), + identity_pullback, H1) E = Mesh(V) domains_with_linear_coordinates.append(E) @@ -63,11 +69,14 @@ def affine_domains(request): triangle, tetrahedron, ] - affine_domains = [Mesh(cell) for cell in affine_cells] + affine_domains = [Mesh(FiniteElement("Lagrange", cell, 1, (cell.geometric_dimension(), ), + identity_pullback, H1)) + for cell in affine_cells] affine_domains_with_linear_coordinates = [] for D in affine_domains: - V = VectorElement("CG", D.ufl_cell(), 1) + V = FiniteElement("Lagrange", D.ufl_cell(), 1, (D.ufl_cell().geometric_dimension(), ), + identity_pullback, H1) E = Mesh(V) affine_domains_with_linear_coordinates.append(E) @@ -84,10 +93,13 @@ def affine_facet_domains(request): quadrilateral, tetrahedron, ] - affine_facet_domains = [Mesh(cell) for cell in affine_facet_cells] + affine_facet_domains = [Mesh(FiniteElement( + "Lagrange", cell, 1, (cell.geometric_dimension(), ), + identity_pullback, H1)) for cell in affine_facet_cells] affine_facet_domains_with_linear_coordinates = [] for D in affine_facet_domains: - V = VectorElement("CG", D.ufl_cell(), 1) + V = FiniteElement("Lagrange", D.ufl_cell(), 1, (D.ufl_cell().geometric_dimension(), ), + identity_pullback, H1) E = Mesh(V) affine_facet_domains_with_linear_coordinates.append(E) @@ -103,10 +115,13 @@ def nonaffine_domains(request): quadrilateral, hexahedron, ] - nonaffine_domains = [Mesh(cell) for cell in nonaffine_cells] + nonaffine_domains = [Mesh(FiniteElement( + "Lagrange", cell, 1, (cell.geometric_dimension(), ), + identity_pullback, H1)) for cell in nonaffine_cells] nonaffine_domains_with_linear_coordinates = [] for D in nonaffine_domains: - V = VectorElement("CG", D.ufl_cell(), 1) + V = FiniteElement("Lagrange", D.ufl_cell(), 1, (D.ufl_cell().geometric_dimension(), ), + identity_pullback, H1) E = Mesh(V) nonaffine_domains_with_linear_coordinates.append(E) @@ -121,10 +136,13 @@ def nonaffine_facet_domains(request): nonaffine_facet_cells = [ hexahedron, ] - nonaffine_facet_domains = [Mesh(cell) for cell in nonaffine_facet_cells] + nonaffine_facet_domains = [Mesh(FiniteElement( + "Lagrange", cell, 1, (cell.geometric_dimension(), ), + identity_pullback, H1)) for cell in nonaffine_facet_cells] nonaffine_facet_domains_with_linear_coordinates = [] for D in nonaffine_facet_domains: - V = VectorElement("CG", D.ufl_cell(), 1) + V = FiniteElement("Lagrange", D.ufl_cell(), 1, (D.ufl_cell().geometric_dimension(), ), + identity_pullback, H1) E = Mesh(V) nonaffine_facet_domains_with_linear_coordinates.append(E) @@ -159,7 +177,7 @@ def test_coordinates_never_cellwise_constant(domains): def test_coordinates_never_cellwise_constant_vertex(): # The only exception here: - domains = Mesh(Cell("vertex", 3)) + domains = Mesh(FiniteElement("Lagrange", Cell("vertex", 3), 1, (3, ), identity_pullback, H1)) assert domains.ufl_cell().cellname() == "vertex" e = SpatialCoordinate(domains) assert is_cellwise_constant(e) @@ -216,12 +234,14 @@ def test_coefficient_sometimes_cellwise_constant(domains_not_linear): e = Constant(domains_not_linear) assert is_cellwise_constant(e) - V = FiniteElement("DG", domains_not_linear.ufl_cell(), 0) - domain = Mesh(VectorElement("Lagrange", domains_not_linear.ufl_cell(), 1)) + V = FiniteElement("Discontinuous Lagrange", domains_not_linear.ufl_cell(), 0, (), identity_pullback, L2) + d = domains_not_linear.ufl_cell().geometric_dimension() + domain = Mesh(FiniteElement("Lagrange", domains_not_linear.ufl_cell(), 1, (d, ), identity_pullback, H1)) space = FunctionSpace(domain, V) e = Coefficient(space) assert is_cellwise_constant(e) - V = FiniteElement("R", domains_not_linear.ufl_cell(), 0) + + V = FiniteElement("Real", domains_not_linear.ufl_cell(), 0, (), identity_pullback, HInf) space = FunctionSpace(domain, V) e = Coefficient(space) assert is_cellwise_constant(e) @@ -235,8 +255,9 @@ def test_coefficient_sometimes_cellwise_constant(domains_not_linear): def test_coefficient_mostly_not_cellwise_constant(domains_not_linear): - V = FiniteElement("DG", domains_not_linear.ufl_cell(), 1) - domain = Mesh(VectorElement("Lagrange", domains_not_linear.ufl_cell(), 1)) + V = FiniteElement("Discontinuous Lagrange", domains_not_linear.ufl_cell(), 1, (), identity_pullback, L2) + d = domains_not_linear.ufl_cell().geometric_dimension() + domain = Mesh(FiniteElement("Lagrange", domains_not_linear.ufl_cell(), 1, (d, ), identity_pullback, H1)) space = FunctionSpace(domain, V) e = Coefficient(space) assert not is_cellwise_constant(e) diff --git a/test/test_reference_shapes.py b/test/test_reference_shapes.py index db4ac3313..12c5028be 100755 --- a/test/test_reference_shapes.py +++ b/test/test_reference_shapes.py @@ -1,4 +1,7 @@ -from ufl import Cell, FiniteElement, MixedElement, TensorElement, VectorElement +from ufl import Cell +from ufl.finiteelement import FiniteElement, MixedElement, SymmetricElement +from ufl.pullback import contravariant_piola, covariant_piola, identity_pullback +from ufl.sobolevspace import H1, HCurl, HDiv def test_reference_shapes(): @@ -6,30 +9,32 @@ def test_reference_shapes(): cell = Cell("triangle", 3) - V = FiniteElement("N1curl", cell, 1) - assert V.value_shape() == (3,) - assert V.reference_value_shape() == (2,) + V = FiniteElement("N1curl", cell, 1, (2, ), covariant_piola, HCurl) + assert V.value_shape == (3,) + assert V.reference_value_shape == (2,) - U = FiniteElement("RT", cell, 1) - assert U.value_shape() == (3,) - assert U.reference_value_shape() == (2,) + U = FiniteElement("Raviart-Thomas", cell, 1, (2, ), contravariant_piola, HDiv) + assert U.value_shape == (3,) + assert U.reference_value_shape == (2,) - W = FiniteElement("CG", cell, 1) - assert W.value_shape() == () - assert W.reference_value_shape() == () + W = FiniteElement("Lagrange", cell, 1, (), identity_pullback, H1) + assert W.value_shape == () + assert W.reference_value_shape == () - Q = VectorElement("CG", cell, 1) - assert Q.value_shape() == (3,) - assert Q.reference_value_shape() == (3,) + Q = FiniteElement("Lagrange", cell, 1, (3, ), identity_pullback, H1) + assert Q.value_shape == (3,) + assert Q.reference_value_shape == (3,) - T = TensorElement("CG", cell, 1) - assert T.value_shape() == (3, 3) - assert T.reference_value_shape() == (3, 3) + T = FiniteElement("Lagrange", cell, 1, (3, 3), identity_pullback, H1) + assert T.value_shape == (3, 3) + assert T.reference_value_shape == (3, 3) - S = TensorElement("CG", cell, 1, symmetry=True) - assert S.value_shape() == (3, 3) - assert S.reference_value_shape() == (6,) + S = SymmetricElement( + {(0, 0): 0, (1, 0): 1, (2, 0): 2, (0, 1): 1, (1, 1): 3, (2, 1): 4, (0, 2): 2, (1, 2): 4, (2, 2): 5}, + [FiniteElement("Lagrange", cell, 1, (), identity_pullback, H1) for _ in range(6)]) + assert S.value_shape == (3, 3) + assert S.reference_value_shape == (6,) - M = MixedElement(V, U, W) - assert M.value_shape() == (7,) - assert M.reference_value_shape() == (5,) + M = MixedElement([V, U, W]) + assert M.value_shape == (7,) + assert M.reference_value_shape == (5,) diff --git a/test/test_scratch.py b/test/test_scratch.py index 1ccceecc0..32ced3346 100755 --- a/test/test_scratch.py +++ b/test/test_scratch.py @@ -8,9 +8,12 @@ import warnings -from ufl import (Coefficient, FiniteElement, FunctionSpace, Identity, Mesh, TensorElement, TestFunction, VectorElement, - as_matrix, as_tensor, as_vector, dx, grad, indices, inner, outer, triangle) +from ufl import (Coefficient, FunctionSpace, Identity, Mesh, TestFunction, as_matrix, as_tensor, as_vector, dx, grad, + indices, inner, outer, triangle) from ufl.classes import FixedIndex, FormArgument, Grad, Indexed, ListTensor, Zero +from ufl.finiteelement import FiniteElement +from ufl.pullback import identity_pullback +from ufl.sobolevspace import H1 from ufl.tensors import as_scalar, unit_indexed_tensor, unwrap_list_tensor @@ -210,8 +213,8 @@ def test_unwrap_list_tensor(self): def test__forward_coefficient_ad__grad_of_scalar_coefficient(self): - U = FiniteElement("CG", triangle, 1) - domain = Mesh(VectorElement("Lagrange", triangle, 1)) + U = FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1) + domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) space = FunctionSpace(domain, U) u = Coefficient(space) du = TestFunction(space) @@ -236,8 +239,8 @@ def test__forward_coefficient_ad__grad_of_scalar_coefficient(self): def test__forward_coefficient_ad__grad_of_vector_coefficient(self): - V = VectorElement("CG", triangle, 1) - domain = Mesh(VectorElement("Lagrange", triangle, 1)) + V = FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1) + domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) space = FunctionSpace(domain, V) v = Coefficient(space) dv = TestFunction(space) @@ -262,8 +265,8 @@ def test__forward_coefficient_ad__grad_of_vector_coefficient(self): def test__forward_coefficient_ad__grad_of_vector_coefficient__with_component_variation(self): - V = VectorElement("CG", triangle, 1) - domain = Mesh(VectorElement("Lagrange", triangle, 1)) + V = FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1) + domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) space = FunctionSpace(domain, V) v = Coefficient(space) dv = TestFunction(space) @@ -319,8 +322,8 @@ def test__forward_coefficient_ad__grad_of_vector_coefficient__with_component_var def test__forward_coefficient_ad__grad_of_vector_coefficient__with_component_variation_in_list(self): - V = VectorElement("CG", triangle, 1) - domain = Mesh(VectorElement("Lagrange", triangle, 1)) + V = FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1) + domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) space = FunctionSpace(domain, V) v = Coefficient(space) dv = TestFunction(space) @@ -376,8 +379,8 @@ def test__forward_coefficient_ad__grad_of_vector_coefficient__with_component_var def test__forward_coefficient_ad__grad_of_tensor_coefficient(self): - W = TensorElement("CG", triangle, 1) - domain = Mesh(VectorElement("Lagrange", triangle, 1)) + W = FiniteElement("Lagrange", triangle, 1, (2, 2), identity_pullback, H1) + domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) space = FunctionSpace(domain, W) w = Coefficient(space) dw = TestFunction(space) @@ -402,8 +405,8 @@ def test__forward_coefficient_ad__grad_of_tensor_coefficient(self): def test__forward_coefficient_ad__grad_of_tensor_coefficient__with_component_variation(self): - W = TensorElement("CG", triangle, 1) - domain = Mesh(VectorElement("Lagrange", triangle, 1)) + W = FiniteElement("Lagrange", triangle, 1, (2, 2), identity_pullback, H1) + domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) space = FunctionSpace(domain, W) w = Coefficient(space) dw = TestFunction(space) diff --git a/test/test_signature.py b/test/test_signature.py index b0c6cd3d9..4e270d500 100755 --- a/test/test_signature.py +++ b/test/test_signature.py @@ -1,11 +1,13 @@ """Test the computation of form signatures.""" -from ufl import (Argument, CellDiameter, CellVolume, Circumradius, Coefficient, FacetArea, FacetNormal, FiniteElement, - FunctionSpace, Identity, Mesh, SpatialCoordinate, TensorElement, TestFunction, VectorElement, - as_vector, diff, dot, ds, dx, hexahedron, indices, inner, interval, quadrilateral, tetrahedron, - triangle, variable) +from ufl import (Argument, CellDiameter, CellVolume, Circumradius, Coefficient, FacetArea, FacetNormal, FunctionSpace, + Identity, Mesh, SpatialCoordinate, TestFunction, as_vector, diff, dot, ds, dx, hexahedron, indices, + inner, interval, quadrilateral, tetrahedron, triangle, variable) from ufl.algorithms.signature import compute_multiindex_hashdata, compute_terminal_hashdata from ufl.classes import FixedIndex, MultiIndex +from ufl.finiteelement import FiniteElement, SymmetricElement +from ufl.pullback import identity_pullback +from ufl.sobolevspace import H1, L2 # TODO: Test compute_terminal_hashdata # TODO: Check that form argument counts only affect the sig by their relative ordering @@ -19,7 +21,8 @@ def domain_numbering(*cells): renumbering = {} for i, cell in enumerate(cells): - domain = Mesh(VectorElement("Lagrange", cell, 1), ufl_id=i) + d = cell.geometric_dimension() + domain = Mesh(FiniteElement("Lagrange", cell, 1, (d, ), identity_pullback, H1), ufl_id=i) renumbering[domain] = i return renumbering @@ -57,7 +60,7 @@ def test_terminal_hashdata_depends_on_literals(self): def forms(): i, j = indices(2) for d, cell in [(2, triangle), (3, tetrahedron)]: - domain = Mesh(VectorElement("Lagrange", cell, 1), ufl_id=d-2) + domain = Mesh(FiniteElement("Lagrange", cell, 1, (d, ), identity_pullback, H1), ufl_id=d-2) x = SpatialCoordinate(domain) ident = Identity(d) for fv in (1.1, 2.2): @@ -85,9 +88,9 @@ def forms(): i, j = indices(2) cells = (triangle, tetrahedron) for i, cell in enumerate(cells): - domain = Mesh(VectorElement("Lagrange", cell, 1), ufl_id=i) - d = cell.geometric_dimension() + domain = Mesh(FiniteElement("Lagrange", cell, 1, (d, ), identity_pullback, H1), ufl_id=i) + x = SpatialCoordinate(domain) n = FacetNormal(domain) h = CellDiameter(domain) @@ -119,28 +122,38 @@ def forms(): def test_terminal_hashdata_depends_on_form_argument_properties(self): reprs = set() hashes = set() - nelm = 6 + nelm = 5 nreps = 2 # Data cells = (triangle, tetrahedron) degrees = (1, 2) - families = ("CG", "Lagrange", "DG") + families = (("Lagrange", H1), ("Lagrange", H1), ("Discontinuous Lagrange", L2)) def forms(): for rep in range(nreps): for i, cell in enumerate(cells): d = cell.geometric_dimension() - domain = Mesh(VectorElement("Lagrange", cell, 1), ufl_id=i) + domain = Mesh(FiniteElement("Lagrange", cell, 1, (d, ), identity_pullback, H1), ufl_id=i) for degree in degrees: - for family in families: - V = FiniteElement(family, cell, degree) - W = VectorElement(family, cell, degree) - W2 = VectorElement(family, cell, degree, dim=d+1) - T = TensorElement(family, cell, degree) - S = TensorElement(family, cell, degree, symmetry=True) - S2 = TensorElement(family, cell, degree, shape=(d, d), symmetry={(0, 0): (1, 1)}) - elements = [V, W, W2, T, S, S2] + for family, sobolev in families: + V = FiniteElement(family, cell, degree, (), identity_pullback, sobolev) + W = FiniteElement(family, cell, degree, (d, ), identity_pullback, sobolev) + W2 = FiniteElement(family, cell, degree, (d+1, ), identity_pullback, sobolev) + T = FiniteElement(family, cell, degree, (d, d), identity_pullback, sobolev) + if d == 2: + S = SymmetricElement( + {(0, 0): 0, (0, 1): 1, (1, 0): 1, (1, 1): 2}, + [FiniteElement(family, cell, degree, (), identity_pullback, sobolev) + for _ in range(3)]) + else: + assert d == 3 + S = SymmetricElement( + {(0, 0): 0, (0, 1): 1, (0, 2): 2, (1, 0): 1, (1, 1): 3, + (1, 2): 4, (2, 0): 2, (2, 1): 4, (2, 2): 5}, + [FiniteElement(family, cell, degree, (), identity_pullback, sobolev) + for _ in range(6)]) + elements = [V, W, W2, T, S] assert len(elements) == nelm for H in elements[:nelm]: @@ -158,11 +171,10 @@ def forms(): yield compute_terminal_hashdata(expr, renumbering) c, d, r, h = compute_unique_terminal_hashdatas(forms()) - c1 = nreps * len(cells) * len(degrees) * len(families) * nelm * 2 # Number of cases with repetitions + c1 = nreps * len(cells) * len(degrees) * len(families) * nelm * 2 assert c == c1 - c0 = len(cells) * len(degrees) * (len(families)-1) * nelm * 2 # Number of unique cases, "CG" == "Lagrange" - # c0 = len(cells) * len(degrees) * (len(families)) * nelm * 2 # Number of unique cases, "CG" != "Lagrange" + c0 = len(cells) * len(degrees) * (len(families)-1) * nelm * 2 assert d == c0 assert r == c0 assert h == c0 @@ -181,9 +193,10 @@ def test_terminal_hashdata_does_not_depend_on_coefficient_count_values_only_orde def forms(): for rep in range(nreps): for i, cell in enumerate(cells): - domain = Mesh(VectorElement("Lagrange", cell, 1), ufl_id=i) + d = cell.geometric_dimension() + domain = Mesh(FiniteElement("Lagrange", cell, 1, (d, ), identity_pullback, H1), ufl_id=i) for k in counts: - V = FiniteElement("CG", cell, 2) + V = FiniteElement("Lagrange", cell, 2, (), identity_pullback, H1) space = FunctionSpace(domain, V) f = Coefficient(space, count=k) g = Coefficient(space, count=k+2) @@ -219,9 +232,10 @@ def test_terminal_hashdata_does_depend_on_argument_number_values(self): def forms(): for rep in range(nreps): for i, cell in enumerate(cells): - domain = Mesh(VectorElement("Lagrange", cell, 1), ufl_id=i) + d = cell.geometric_dimension() + domain = Mesh(FiniteElement("Lagrange", cell, 1, (d, ), identity_pullback, H1), ufl_id=i) for k in counts: - V = FiniteElement("CG", cell, 2) + V = FiniteElement("Lagrange", cell, 2, (), identity_pullback, H1) space = FunctionSpace(domain, V) f = Argument(space, k) g = Argument(space, k+2) @@ -248,7 +262,8 @@ def test_domain_signature_data_does_not_depend_on_domain_label_value(self): s1s = set() s2s = set() for i, cell in enumerate(cells): - domain = VectorElement("Lagrange", cell, 1) + d = cell.geometric_dimension() + domain = FiniteElement("Lagrange", cell, 1, (d, ), identity_pullback, H1) d0 = Mesh(domain) d1 = Mesh(domain, ufl_id=1) d2 = Mesh(domain, ufl_id=2) @@ -270,14 +285,17 @@ def test_terminal_hashdata_does_not_depend_on_domain_label_value(self): hashes = set() ufl_ids = [1, 2] cells = [triangle, quadrilateral] - domains = [Mesh(cell, ufl_id=ufl_id) for cell in cells for ufl_id in ufl_ids] + domains = [Mesh(FiniteElement("Lagrange", cell, 1, (cell.geometric_dimension(), ), + identity_pullback, H1), + ufl_id=ufl_id) for cell in cells for ufl_id in ufl_ids] nreps = 2 num_exprs = 2 def forms(): for rep in range(nreps): for domain in domains: - V = FunctionSpace(domain, FiniteElement("CG", domain.ufl_cell(), 2)) + V = FunctionSpace(domain, FiniteElement("Lagrange", domain.ufl_cell(), 2, (), + identity_pullback, H1)) f = Coefficient(V, count=0) v = TestFunction(V) x = SpatialCoordinate(domain) @@ -416,11 +434,12 @@ def check_unique_signatures(forms): def test_signature_is_affected_by_element_properties(self): def forms(): - for family in ("CG", "DG"): + for family, sobolev in (("Lagrange", H1), ("Discontinuous Lagrange", L2)): for cell in (triangle, tetrahedron, quadrilateral): - domain = Mesh(VectorElement("Lagrange", cell, 1)) + d = cell.geometric_dimension() + domain = Mesh(FiniteElement("Lagrange", cell, 1, (d, ), identity_pullback, H1)) for degree in (1, 2): - V = FiniteElement(family, cell, degree) + V = FiniteElement(family, cell, degree, (), identity_pullback, sobolev) space = FunctionSpace(domain, V) u = Coefficient(space) v = TestFunction(space) @@ -435,11 +454,12 @@ def forms(): def test_signature_is_affected_by_domains(self): def forms(): for cell in (triangle, tetrahedron): - domain = Mesh(VectorElement("Lagrange", cell, 1)) + d = cell.geometric_dimension() + domain = Mesh(FiniteElement("Lagrange", cell, 1, (d, ), identity_pullback, H1)) for di in (1, 2): for dj in (1, 2): for dk in (1, 2): - V = FiniteElement("CG", cell, 1) + V = FiniteElement("Lagrange", cell, 1, (), identity_pullback, H1) space = FunctionSpace(domain, V) u = Coefficient(space) a = u*dx(di) + 2*u*dx(dj) + 3*u*ds(dk) @@ -450,10 +470,12 @@ def forms(): def test_signature_of_forms_with_diff(self): def forms(): for i, cell in enumerate([triangle, tetrahedron]): - domain = Mesh(VectorElement("Lagrange", cell, 1), ufl_id=i) + d = cell.geometric_dimension() + domain = Mesh(FiniteElement("Lagrange", cell, 1, (d, ), identity_pullback, H1), ufl_id=i) for k in (1, 2, 3): - V = FiniteElement("CG", cell, 1) - W = VectorElement("CG", cell, 1) + d = cell.geometric_dimension() + V = FiniteElement("Lagrange", cell, 1, (), identity_pullback, H1) + W = FiniteElement("Lagrange", cell, 1, (d, ), identity_pullback, H1) v_space = FunctionSpace(domain, V) w_space = FunctionSpace(domain, W) u = Coefficient(v_space) @@ -470,8 +492,8 @@ def forms(): def test_signature_of_form_depend_on_coefficient_numbering_across_integrals(self): cell = triangle - V = FiniteElement("CG", cell, 1) - domain = Mesh(VectorElement("Lagrange", cell, 1)) + V = FiniteElement("Lagrange", cell, 1, (), identity_pullback, H1) + domain = Mesh(FiniteElement("Lagrange", cell, 1, (2, ), identity_pullback, H1)) space = FunctionSpace(domain, V) f = Coefficient(space) g = Coefficient(space) @@ -486,8 +508,9 @@ def test_signature_of_form_depend_on_coefficient_numbering_across_integrals(self def test_signature_of_forms_change_with_operators(self): def forms(): for cell in (triangle, tetrahedron): - V = FiniteElement("CG", cell, 1) - domain = Mesh(VectorElement("Lagrange", cell, 1)) + d = cell.geometric_dimension() + V = FiniteElement("Lagrange", cell, 1, (), identity_pullback, H1) + domain = Mesh(FiniteElement("Lagrange", cell, 1, (d, ), identity_pullback, H1)) space = FunctionSpace(domain, V) u = Coefficient(space) v = Coefficient(space) @@ -495,8 +518,8 @@ def forms(): (u+v)+(u/v), (u+v)*(u/v), (u*v)*(u*v), - (u+v)*(u*v), # (!) same - # (u*v)*(u+v), # (!) same + (u+v)*(u*v), # H1 same + # (u*v)*(u+v), # H1 same (u*v)+(u+v), ] for f in fs: diff --git a/test/test_simplify.py b/test/test_simplify.py index faa9d4e6e..66a176d9d 100755 --- a/test/test_simplify.py +++ b/test/test_simplify.py @@ -1,15 +1,17 @@ import math -from ufl import (Coefficient, FiniteElement, FunctionSpace, Mesh, TestFunction, TrialFunction, VectorConstant, - VectorElement, acos, as_tensor, as_ufl, asin, atan, cos, cosh, dx, exp, i, j, ln, max_value, min_value, - outer, sin, sinh, tan, tanh, triangle) +from ufl import (Coefficient, FunctionSpace, Mesh, TestFunction, TrialFunction, VectorConstant, acos, as_tensor, as_ufl, + asin, atan, cos, cosh, dx, exp, i, j, ln, max_value, min_value, outer, sin, sinh, tan, tanh, triangle) from ufl.algorithms import compute_form_data +from ufl.finiteelement import FiniteElement +from ufl.pullback import identity_pullback +from ufl.sobolevspace import H1 def xtest_zero_times_argument(self): # FIXME: Allow zero forms - element = FiniteElement("CG", triangle, 1) - domain = Mesh(VectorElement("Lagrange", triangle, 1)) + element = FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1) + domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) space = FunctionSpace(domain, element) v = TestFunction(space) u = TrialFunction(space) @@ -22,8 +24,8 @@ def xtest_zero_times_argument(self): def test_divisions(self): - element = FiniteElement("CG", triangle, 1) - domain = Mesh(VectorElement("Lagrange", triangle, 1)) + element = FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1) + domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) space = FunctionSpace(domain, element) f = Coefficient(space) @@ -49,8 +51,8 @@ def test_divisions(self): def test_products(self): - element = FiniteElement("CG", triangle, 1) - domain = Mesh(VectorElement("Lagrange", triangle, 1)) + element = FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1) + domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) space = FunctionSpace(domain, element) f = Coefficient(space) g = Coefficient(space) @@ -71,8 +73,8 @@ def test_products(self): def test_sums(self): - element = FiniteElement("CG", triangle, 1) - domain = Mesh(VectorElement("Lagrange", triangle, 1)) + element = FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1) + domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) space = FunctionSpace(domain, element) f = Coefficient(space) g = Coefficient(space) @@ -122,7 +124,7 @@ def test_mathfunctions(self): def test_indexing(self): - domain = Mesh(VectorElement("Lagrange", triangle, 1)) + domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) u = VectorConstant(domain) v = VectorConstant(domain) diff --git a/test/test_sobolevspace.py b/test/test_sobolevspace.py index ba4eee118..28e42a24d 100755 --- a/test/test_sobolevspace.py +++ b/test/test_sobolevspace.py @@ -3,8 +3,9 @@ from math import inf -from ufl import (H1, H2, L2, EnrichedElement, FiniteElement, HCurl, HDiv, HInf, TensorProductElement, interval, - quadrilateral, triangle) +from ufl import H1, H2, L2, HCurl, HDiv, HInf, triangle +from ufl.finiteelement import FiniteElement +from ufl.pullback import contravariant_piola, covariant_piola, identity_pullback from ufl.sobolevspace import SobolevSpace # noqa: F401 from ufl.sobolevspace import DirectionalSobolevSpace @@ -62,18 +63,9 @@ def xtest_contains_mixed(): def test_contains_l2(): l2_elements = [ - FiniteElement("DG", triangle, 0), - 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)) + FiniteElement("Discontinuous Lagrange", triangle, 0, (), identity_pullback, L2), + FiniteElement("Discontinuous Lagrange", triangle, 1, (), identity_pullback, L2), + FiniteElement("Discontinuous Lagrange", triangle, 2, (), identity_pullback, L2), ] for l2_element in l2_elements: assert l2_element in L2 @@ -89,19 +81,11 @@ def test_contains_l2(): def test_contains_h1(): h1_elements = [ # Standard Lagrange elements: - FiniteElement("CG", triangle, 1), - FiniteElement("CG", triangle, 2), + FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1), + FiniteElement("Lagrange", triangle, 2, (), identity_pullback, H1), # Some special elements: - 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)) + FiniteElement("MTW", triangle, 3, (2, ), contravariant_piola, H1), + FiniteElement("Hermite", triangle, 3, (), "custom", H1), ] for h1_element in h1_elements: assert h1_element in H1 @@ -116,8 +100,8 @@ def test_contains_h1(): def test_contains_h2(): h2_elements = [ - FiniteElement("ARG", triangle, 5), - FiniteElement("MOR", triangle, 2), + FiniteElement("ARG", triangle, 5, (), "custom", H2), + FiniteElement("MOR", triangle, 2, (), "custom", H2), ] for h2_element in h2_elements: assert h2_element in H2 @@ -132,7 +116,7 @@ def test_contains_h2(): def test_contains_hinf(): hinf_elements = [ - FiniteElement("R", triangle, 0) + FiniteElement("Real", triangle, 0, (), identity_pullback, HInf) ] for hinf_element in hinf_elements: assert hinf_element in HInf @@ -148,16 +132,9 @@ def test_contains_hinf(): def test_contains_hdiv(): hdiv_elements = [ - 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))) + FiniteElement("Raviart-Thomas", triangle, 1, (2, ), contravariant_piola, HDiv), + FiniteElement("BDM", triangle, 1, (2, ), contravariant_piola, HDiv), + FiniteElement("BDFM", triangle, 2, (2, ), contravariant_piola, HDiv), ] for hdiv_element in hdiv_elements: assert hdiv_element in HDiv @@ -172,15 +149,8 @@ def test_contains_hdiv(): 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))) + FiniteElement("N1curl", triangle, 1, (2, ), covariant_piola, HCurl), + FiniteElement("N2curl", triangle, 1, (2, ), covariant_piola, HCurl), ] for hcurl_element in hcurl_elements: assert hcurl_element in HCurl @@ -191,79 +161,3 @@ def test_contains_hcurl(): 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, 5) - - # 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_split.py b/test/test_split.py index bce2dbf03..7a16e4a84 100755 --- a/test/test_split.py +++ b/test/test_split.py @@ -1,28 +1,31 @@ __authors__ = "Martin Sandve Alnæs" __date__ = "2009-03-14 -- 2009-03-14" -from ufl import (Coefficient, FiniteElement, FunctionSpace, Mesh, MixedElement, TensorElement, TestFunction, - VectorElement, as_vector, product, split, triangle) +from ufl import Coefficient, FunctionSpace, Mesh, TestFunction, as_vector, product, split, triangle +from ufl.finiteelement import FiniteElement, MixedElement, SymmetricElement +from ufl.pullback import identity_pullback +from ufl.sobolevspace import H1 def test_split(self): cell = triangle - domain = Mesh(VectorElement("Lagrange", cell, 1)) d = cell.geometric_dimension() - f = FiniteElement("CG", cell, 1) - v = VectorElement("CG", cell, 1) - w = VectorElement("CG", cell, 1, dim=d+1) - t = TensorElement("CG", cell, 1) - s = TensorElement("CG", cell, 1, symmetry=True) - r = TensorElement("CG", cell, 1, symmetry={(1, 0): (0, 1)}, shape=(d, d)) - m = MixedElement(f, v, w, t, s, r) + domain = Mesh(FiniteElement("Lagrange", cell, 1, (d, ), identity_pullback, H1)) + f = FiniteElement("Lagrange", cell, 1, (), identity_pullback, H1) + v = FiniteElement("Lagrange", cell, 1, (d, ), identity_pullback, H1, + sub_elements=[f for _ in range(d)]) + w = FiniteElement("Lagrange", cell, 1, (d+1, ), identity_pullback, H1, + sub_elements=[f for _ in range(d + 1)]) + t = FiniteElement("Lagrange", cell, 1, (d, d), identity_pullback, H1, + sub_elements=[f for _ in range(d ** 2)]) + s = SymmetricElement({(0, 0): 0, (0, 1): 1, (1, 0): 1, (1, 1): 2}, [f for _ in range(3)]) + m = MixedElement([f, v, w, t, s, s]) f_space = FunctionSpace(domain, f) v_space = FunctionSpace(domain, v) w_space = FunctionSpace(domain, w) t_space = FunctionSpace(domain, t) s_space = FunctionSpace(domain, s) - r_space = FunctionSpace(domain, r) m_space = FunctionSpace(domain, m) # Check that shapes of all these functions are correct: @@ -31,7 +34,6 @@ def test_split(self): assert (d+1,) == Coefficient(w_space).ufl_shape assert (d, d) == Coefficient(t_space).ufl_shape assert (d, d) == Coefficient(s_space).ufl_shape - assert (d, d) == Coefficient(r_space).ufl_shape # sum of value sizes, not accounting for symmetries: assert (3*d*d + 2*d + 2,) == Coefficient(m_space).ufl_shape @@ -43,8 +45,8 @@ def test_split(self): assert s == 0 # Mixed elements of non-scalar subelements are flattened - v2 = MixedElement(v, v) - m2 = MixedElement(t, t) + v2 = MixedElement([v, v]) + m2 = MixedElement([t, t]) v2_space = FunctionSpace(domain, v2) m2_space = FunctionSpace(domain, m2) # assert d == 2 @@ -58,13 +60,13 @@ def test_split(self): # Split twice on nested mixed elements gets # the innermost scalar subcomponents - t = TestFunction(FunctionSpace(domain, f*v)) + t = TestFunction(FunctionSpace(domain, MixedElement([f, v]))) assert split(t) == (t[0], as_vector((t[1], t[2]))) assert split(split(t)[1]) == (t[1], t[2]) - t = TestFunction(FunctionSpace(domain, f*(f*v))) + t = TestFunction(FunctionSpace(domain, MixedElement([f, [f, v]]))) assert split(t) == (t[0], as_vector((t[1], t[2], t[3]))) assert split(split(t)[1]) == (t[1], as_vector((t[2], t[3]))) - t = TestFunction(FunctionSpace(domain, (v*f)*(f*v))) + t = TestFunction(FunctionSpace(domain, MixedElement([[v, f], [f, v]]))) assert split(t) == (as_vector((t[0], t[1], t[2])), as_vector((t[3], t[4], t[5]))) assert split(split(t)[0]) == (as_vector((t[0], t[1])), t[2]) diff --git a/test/test_str.py b/test/test_str.py index 96683ae38..df80d8215 100755 --- a/test/test_str.py +++ b/test/test_str.py @@ -1,6 +1,9 @@ -from ufl import (CellDiameter, CellVolume, Circumradius, FacetArea, FacetNormal, FiniteElement, FunctionSpace, Index, - Mesh, SpatialCoordinate, TestFunction, TrialFunction, VectorElement, as_matrix, as_ufl, as_vector, - quadrilateral, tetrahedron, triangle) +from ufl import (CellDiameter, CellVolume, Circumradius, FacetArea, FacetNormal, FunctionSpace, Index, Mesh, + SpatialCoordinate, TestFunction, TrialFunction, as_matrix, as_ufl, as_vector, quadrilateral, + tetrahedron, triangle) +from ufl.finiteelement import FiniteElement +from ufl.pullback import identity_pullback +from ufl.sobolevspace import H1 def test_str_int_value(self): @@ -12,7 +15,7 @@ def test_str_float_value(self): def test_str_zero(self): - domain = Mesh(VectorElement("Lagrange", triangle, 1)) + domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) x = SpatialCoordinate(domain) assert str(as_ufl(0)) == "0" assert str(0*x) == "0 (shape (2,))" @@ -25,41 +28,41 @@ def test_str_index(self): def test_str_coordinate(self): - domain = Mesh(VectorElement("Lagrange", triangle, 1)) + domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) assert str(SpatialCoordinate(domain)) == "x" assert str(SpatialCoordinate(domain)[0]) == "x[0]" def test_str_normal(self): - domain = Mesh(VectorElement("Lagrange", triangle, 1)) + domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) assert str(FacetNormal(domain)) == "n" assert str(FacetNormal(domain)[0]) == "n[0]" def test_str_circumradius(self): - domain = Mesh(VectorElement("Lagrange", triangle, 1)) + domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) assert str(Circumradius(domain)) == "circumradius" def test_str_diameter(self): - domain = Mesh(VectorElement("Lagrange", triangle, 1)) + domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) assert str(CellDiameter(domain)) == "diameter" def test_str_facetarea(self): - domain = Mesh(VectorElement("Lagrange", triangle, 1)) + domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) assert str(FacetArea(domain)) == "facetarea" def test_str_volume(self): - domain = Mesh(VectorElement("Lagrange", triangle, 1)) + domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) assert str(CellVolume(domain)) == "volume" def test_str_scalar_argument(self): - domain = Mesh(VectorElement("Lagrange", triangle, 1)) - v = TestFunction(FunctionSpace(domain, FiniteElement("CG", triangle, 1))) - u = TrialFunction(FunctionSpace(domain, FiniteElement("CG", triangle, 1))) + domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) + v = TestFunction(FunctionSpace(domain, FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1))) + u = TrialFunction(FunctionSpace(domain, FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1))) assert str(v) == "v_0" assert str(u) == "v_1" @@ -72,21 +75,21 @@ def test_str_scalar_argument(self): def test_str_list_vector(): - domain = Mesh(VectorElement("Lagrange", tetrahedron, 1)) + domain = Mesh(FiniteElement("Lagrange", tetrahedron, 1, (3, ), identity_pullback, H1)) x, y, z = SpatialCoordinate(domain) v = as_vector((x, y, z)) assert str(v) == ("[%s, %s, %s]" % (x, y, z)) def test_str_list_vector_with_zero(): - domain = Mesh(VectorElement("Lagrange", tetrahedron, 1)) + domain = Mesh(FiniteElement("Lagrange", tetrahedron, 1, (3, ), identity_pullback, H1)) x, y, z = SpatialCoordinate(domain) v = as_vector((x, 0, 0)) assert str(v) == ("[%s, 0, 0]" % (x,)) def test_str_list_matrix(): - domain = Mesh(VectorElement("Lagrange", triangle, 1)) + domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) x, y = SpatialCoordinate(domain) v = as_matrix(((2*x, 3*y), (4*x, 5*y))) @@ -98,7 +101,7 @@ def test_str_list_matrix(): def test_str_list_matrix_with_zero(): - domain = Mesh(VectorElement("Lagrange", triangle, 1)) + domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) x, y = SpatialCoordinate(domain) v = as_matrix(((2*x, 3*y), (0, 0))) @@ -113,5 +116,6 @@ def test_str_list_matrix_with_zero(): def test_str_element(): - elem = FiniteElement("Q", quadrilateral, 1) + elem = FiniteElement("Q", quadrilateral, 1, (), identity_pullback, H1) + assert repr(elem) == "ufl.finiteelement.FiniteElement(\"Q\", quadrilateral, 1, (), IdentityPullback(), H1)" assert str(elem) == "" diff --git a/test/test_strip_forms.py b/test/test_strip_forms.py index 3e8a77608..9a74ac506 100644 --- a/test/test_strip_forms.py +++ b/test/test_strip_forms.py @@ -1,11 +1,13 @@ import gc import sys -from ufl import (Coefficient, Constant, FiniteElement, FunctionSpace, Mesh, TestFunction, TrialFunction, dx, grad, - inner, triangle) +from ufl import Coefficient, Constant, FunctionSpace, Mesh, TestFunction, TrialFunction, dx, grad, inner, triangle from ufl.algorithms import replace_terminal_data, strip_terminal_data from ufl.core.ufl_id import attach_ufl_id from ufl.core.ufl_type import attach_operators_from_hash_data +from ufl.finiteelement import FiniteElement +from ufl.pullback import identity_pullback +from ufl.sobolevspace import H1 MIN_REF_COUNT = 2 """The minimum value returned by sys.getrefcount.""" @@ -51,8 +53,9 @@ def test_strip_form_arguments_strips_data_refs(): assert sys.getrefcount(const_data) == MIN_REF_COUNT cell = triangle - domain = AugmentedMesh(cell, data=mesh_data) - element = FiniteElement("Lagrange", cell, 1) + domain = AugmentedMesh(FiniteElement("Lagrange", cell, 1, (cell.geometric_dimension(), ), + identity_pullback, H1), data=mesh_data) + element = FiniteElement("Lagrange", cell, 1, (), identity_pullback, H1) V = AugmentedFunctionSpace(domain, element, data=fs_data) v = TestFunction(V) @@ -88,8 +91,9 @@ def test_strip_form_arguments_does_not_change_form(): const_data = object() cell = triangle - domain = AugmentedMesh(cell, data=mesh_data) - element = FiniteElement("Lagrange", cell, 1) + domain = AugmentedMesh(FiniteElement("Lagrange", cell, 1, (cell.geometric_dimension(), ), + identity_pullback, H1), data=mesh_data) + element = FiniteElement("Lagrange", cell, 1, (), identity_pullback, H1) V = AugmentedFunctionSpace(domain, element, data=fs_data) v = TestFunction(V) diff --git a/test/test_tensoralgebra.py b/test/test_tensoralgebra.py index d070a3dd4..bdb6ad406 100755 --- a/test/test_tensoralgebra.py +++ b/test/test_tensoralgebra.py @@ -2,9 +2,12 @@ import pytest -from ufl import (FacetNormal, Mesh, VectorElement, as_matrix, as_tensor, as_vector, cofac, cross, det, dev, diag, - diag_vector, dot, inner, inv, outer, perp, skew, sym, tr, transpose, triangle, zero) +from ufl import (FacetNormal, Mesh, as_matrix, as_tensor, as_vector, cofac, cross, det, dev, diag, diag_vector, dot, + inner, inv, outer, perp, skew, sym, tr, transpose, triangle, zero) from ufl.algorithms.remove_complex_nodes import remove_complex_nodes +from ufl.finiteelement import FiniteElement +from ufl.pullback import identity_pullback +from ufl.sobolevspace import H1 @pytest.fixture(scope="module") @@ -63,7 +66,7 @@ def test_inner(self, A, B, u, v): def test_pow2_inner(self, A, u): - domain = Mesh(VectorElement("Lagrange", triangle, 1)) + domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) f = FacetNormal(domain)[0] f2 = f*f assert f2 == remove_complex_nodes(inner(f, f)) diff --git a/ufl/__init__.py b/ufl/__init__.py index 40821e8b7..59c19e61f 100644 --- a/ufl/__init__.py +++ b/ufl/__init__.py @@ -63,7 +63,6 @@ - AbstractDomain - Mesh - MeshView - - TensorProductMesh * Sobolev spaces:: @@ -76,19 +75,15 @@ - HEin - HDivDiv -* Elements:: - - FiniteElement - - MixedElement - - VectorElement - - TensorElement - - EnrichedElement - - NodalEnrichedElement - - RestrictedElement - - TensorProductElement - - HDivElement - - HCurlElement - - BrokenElement +* Pull backs:: + + - identity_pullback + - contravariant_piola + - covariant_piola + - l2_piola + - double_contravariant_piola + - double_covariant_piola * Function spaces:: @@ -248,135 +243,62 @@ __version__ = importlib.metadata.version("fenics-ufl") -# README -# Imports here should be what the user sees -# which means we should _not_ import f.ex. "Grad", but "grad". -# This way we expose the language, the operation "grad", but less -# of the implementation, the particular class "Grad". -########## - -# Utility functions (product is the counterpart of the built-in -# python function sum, can be useful for users as well?) -from ufl.utils.sequences import product - -# Types for geometric quantities - -from ufl.cell import as_cell, AbstractCell, Cell, TensorProductCell -from ufl.domain import as_domain, AbstractDomain, Mesh, MeshView, TensorProductMesh -from ufl.geometry import ( - SpatialCoordinate, - FacetNormal, CellNormal, - CellVolume, CellDiameter, Circumradius, MinCellEdgeLength, MaxCellEdgeLength, - FacetArea, MinFacetEdgeLength, MaxFacetEdgeLength, - Jacobian, JacobianDeterminant, JacobianInverse -) - -# Sobolev spaces -from ufl.sobolevspace import L2, H1, H2, HDiv, HCurl, HEin, HDivDiv, HInf - -# Finite elements classes -from ufl.finiteelement import ( - FiniteElementBase, FiniteElement, MixedElement, VectorElement, TensorElement, EnrichedElement, - NodalEnrichedElement, RestrictedElement, TensorProductElement, HDivElement, HCurlElement, BrokenElement, - WithMapping) - -# Hook to extend predefined element families -from ufl.finiteelement.elementlist import register_element, show_elements - -# Function spaces -from ufl.functionspace import FunctionSpace, MixedFunctionSpace - -# Arguments -from ufl.argument import Argument, Coargument, TestFunction, TrialFunction, Arguments, TestFunctions, TrialFunctions - -# Coefficients -from ufl.coefficient import Coefficient, Cofunction, Coefficients -from ufl.constant import Constant, VectorConstant, TensorConstant - -# Matrices -from ufl.matrix import Matrix - -# Adjoints -from ufl.adjoint import Adjoint +from math import e, pi -# Actions +import ufl.exproperators as __exproperators from ufl.action import Action - -# Interpolates +from ufl.adjoint import Adjoint +from ufl.argument import Argument, Arguments, Coargument, TestFunction, TestFunctions, TrialFunction, TrialFunctions +from ufl.cell import AbstractCell, Cell, TensorProductCell, as_cell +from ufl.coefficient import Coefficient, Coefficients, Cofunction +from ufl.constant import Constant, TensorConstant, VectorConstant +from ufl.constantvalue import Identity, PermutationSymbol, as_ufl, zero +from ufl.core.external_operator import ExternalOperator from ufl.core.interpolate import Interpolate, interpolate - -# Split function -from ufl.split_functions import split - -# Literal constants -from ufl.constantvalue import PermutationSymbol, Identity, zero, as_ufl - -# Indexing of tensor expressions from ufl.core.multiindex import Index, indices - -# Special functions for expression base classes -# (ensure this is imported, since it attaches operators to Expr) -import ufl.exproperators as __exproperators # noqa: F401 - -# Containers for expressions with value rank > 0 -from ufl.tensors import as_tensor, as_vector, as_matrix -from ufl.tensors import unit_vector, unit_vectors, unit_matrix, unit_matrices - -# Operators -from ufl.operators import ( - rank, shape, conj, real, imag, outer, inner, dot, cross, perp, - det, inv, cofac, transpose, tr, diag, diag_vector, dev, skew, sym, - sqrt, exp, ln, erf, cos, sin, tan, acos, asin, atan, atan2, cosh, sinh, tanh, - bessel_J, bessel_Y, bessel_I, bessel_K, eq, ne, le, ge, lt, gt, And, Or, Not, - conditional, sign, max_value, min_value, variable, diff, - Dx, grad, div, curl, rot, nabla_grad, nabla_div, Dn, exterior_derivative, - jump, avg, cell_avg, facet_avg, elem_mult, elem_div, elem_pow, elem_op) - -# External Operator -from ufl.core.external_operator import ExternalOperator - -# Measure classes -from ufl.measure import Measure, register_integral_type, integral_types, custom_integral_types - -# Form class -from ufl.form import Form, BaseForm, FormSum, ZeroBaseForm - -# Integral classes +from ufl.domain import AbstractDomain, Mesh, MeshView +from ufl.finiteelement import AbstractFiniteElement +from ufl.form import BaseForm, Form, FormSum, ZeroBaseForm +from ufl.formoperators import (action, adjoint, derivative, energy_norm, extract_blocks, functional, lhs, replace, rhs, + sensitivity_rhs, system) +from ufl.functionspace import FunctionSpace, MixedFunctionSpace +from ufl.geometry import (CellDiameter, CellNormal, CellVolume, Circumradius, FacetArea, FacetNormal, Jacobian, + JacobianDeterminant, JacobianInverse, MaxCellEdgeLength, MaxFacetEdgeLength, + MinCellEdgeLength, MinFacetEdgeLength, SpatialCoordinate) from ufl.integral import Integral - -# Representations of transformed forms -from ufl.formoperators import (replace, derivative, action, energy_norm, rhs, lhs, - system, functional, adjoint, sensitivity_rhs, extract_blocks) - -# Predefined convenience objects -from ufl.objects import ( - vertex, interval, triangle, tetrahedron, pentatope, tesseract, - quadrilateral, hexahedron, prism, pyramid, facet, - i, j, k, l, p, q, r, s, - dx, ds, dS, dP, - dc, dC, dO, dI, dX, - ds_b, ds_t, ds_tb, ds_v, dS_h, dS_v) - -# Useful constants -from math import e, pi +from ufl.matrix import Matrix +from ufl.measure import Measure, custom_integral_types, integral_types, register_integral_type +from ufl.objects import (dc, dC, dI, dO, dP, ds, dS, ds_b, dS_h, ds_t, ds_tb, ds_v, dS_v, dx, dX, facet, hexahedron, i, + interval, j, k, l, p, pentatope, prism, pyramid, q, quadrilateral, r, s, tesseract, + tetrahedron, triangle, vertex) +from ufl.operators import (And, Dn, Dx, Not, Or, acos, asin, atan, atan2, avg, bessel_I, bessel_J, bessel_K, bessel_Y, + cell_avg, cofac, conditional, conj, cos, cosh, cross, curl, det, dev, diag, diag_vector, + diff, div, dot, elem_div, elem_mult, elem_op, elem_pow, eq, erf, exp, exterior_derivative, + facet_avg, ge, grad, gt, imag, inner, inv, jump, le, ln, lt, max_value, min_value, nabla_div, + nabla_grad, ne, outer, perp, rank, real, rot, shape, sign, sin, sinh, skew, sqrt, sym, tan, + tanh, tr, transpose, variable) +from ufl.pullback import (AbstractPullback, MixedPullback, SymmetricPullback, contravariant_piola, covariant_piola, + double_contravariant_piola, double_covariant_piola, identity_pullback, l2_piola) +from ufl.sobolevspace import H1, H2, L2, HCurl, HDiv, HDivDiv, HEin, HInf +from ufl.split_functions import split +from ufl.tensors import as_matrix, as_tensor, as_vector, unit_matrices, unit_matrix, unit_vector, unit_vectors +from ufl.utils.sequences import product __all__ = [ 'product', 'as_cell', 'AbstractCell', 'Cell', 'TensorProductCell', - 'as_domain', 'AbstractDomain', 'Mesh', 'MeshView', 'TensorProductMesh', + 'AbstractDomain', 'Mesh', 'MeshView', 'L2', 'H1', 'H2', 'HCurl', 'HDiv', 'HInf', 'HEin', 'HDivDiv', + 'identity_pullback', 'l2_piola', 'contravariant_piola', 'covariant_piola', + 'double_contravariant_piola', 'double_covariant_piola', + 'l2_piola', 'MixedPullback', 'SymmetricPullback', 'AbstractPullback', 'SpatialCoordinate', 'CellVolume', 'CellDiameter', 'Circumradius', 'MinCellEdgeLength', 'MaxCellEdgeLength', 'FacetArea', 'MinFacetEdgeLength', 'MaxFacetEdgeLength', 'FacetNormal', 'CellNormal', 'Jacobian', 'JacobianDeterminant', 'JacobianInverse', - 'FiniteElementBase', 'FiniteElement', - 'MixedElement', 'VectorElement', 'TensorElement', 'EnrichedElement', - 'NodalEnrichedElement', 'RestrictedElement', 'TensorProductElement', - 'HDivElement', 'HCurlElement', - 'BrokenElement', "WithMapping", - 'register_element', 'show_elements', + 'AbstractFiniteElement', 'FunctionSpace', 'MixedFunctionSpace', 'Argument', 'Coargument', 'TestFunction', 'TrialFunction', 'Arguments', 'TestFunctions', 'TrialFunctions', diff --git a/ufl/action.py b/ufl/action.py index 6051b4b7c..5d6004994 100644 --- a/ufl/action.py +++ b/ufl/action.py @@ -7,14 +7,14 @@ # # Modified by Nacime Bouziani, 2021-2022. -from ufl.form import BaseForm, FormSum, Form, ZeroBaseForm -from ufl.core.ufl_type import ufl_type from ufl.algebra import Sum -from ufl.constantvalue import Zero from ufl.argument import Argument, Coargument from ufl.coefficient import BaseCoefficient, Coefficient, Cofunction -from ufl.differentiation import CoefficientDerivative +from ufl.constantvalue import Zero from ufl.core.base_form_operator import BaseFormOperator +from ufl.core.ufl_type import ufl_type +from ufl.differentiation import CoefficientDerivative +from ufl.form import BaseForm, Form, FormSum, ZeroBaseForm from ufl.matrix import Matrix # --- The Action class represents the action of a numerical object that needs @@ -120,6 +120,7 @@ def _analyze_form_arguments(self): def _analyze_domains(self): """Analyze which domains can be found in Action.""" from ufl.domain import join_domains + # Collect unique domains self._domains = join_domains([e.ufl_domain() for e in self.ufl_operands]) diff --git a/ufl/adjoint.py b/ufl/adjoint.py index e0f86ec22..38f003870 100644 --- a/ufl/adjoint.py +++ b/ufl/adjoint.py @@ -8,9 +8,10 @@ # # Modified by Nacime Bouziani, 2021-2022. -from ufl.form import BaseForm, FormSum, ZeroBaseForm from ufl.argument import Coargument from ufl.core.ufl_type import ufl_type +from ufl.form import BaseForm, FormSum, ZeroBaseForm + # --- The Adjoint class represents the adjoint of a numerical object that # needs to be computed at assembly time --- @@ -87,6 +88,7 @@ def _analyze_form_arguments(self): def _analyze_domains(self): """Analyze which domains can be found in Adjoint.""" from ufl.domain import join_domains + # Collect unique domains self._domains = join_domains([e.ufl_domain() for e in self.ufl_operands]) diff --git a/ufl/algebra.py b/ufl/algebra.py index 1bd1493be..376c16d02 100644 --- a/ufl/algebra.py +++ b/ufl/algebra.py @@ -7,14 +7,14 @@ # # Modified by Anders Logg, 2008 -from ufl.core.ufl_type import ufl_type +from ufl.checks import is_true_ufl_scalar, is_ufl_scalar +from ufl.constantvalue import ComplexValue, IntValue, ScalarValue, Zero, as_ufl, zero from ufl.core.expr import ufl_err_str from ufl.core.operator import Operator -from ufl.constantvalue import Zero, zero, ScalarValue, IntValue, ComplexValue, as_ufl -from ufl.checks import is_ufl_scalar, is_true_ufl_scalar +from ufl.core.ufl_type import ufl_type from ufl.index_combination_utils import merge_unique_indices -from ufl.sorting import sorted_expr from ufl.precedence import parstr +from ufl.sorting import sorted_expr # --- Algebraic operators --- diff --git a/ufl/algorithms/__init__.py b/ufl/algorithms/__init__.py index 690b237f8..8e3ce5a1a 100644 --- a/ufl/algorithms/__init__.py +++ b/ufl/algorithms/__init__.py @@ -56,74 +56,25 @@ "load_forms", ] -# Utilities for traversing over expression trees in different ways -# from ufl.algorithms.traversal import iter_expressions - -# Keeping these imports here for backwards compatibility, doesn't cost -# anything. Prefer importing from ufl.corealg.traversal in future -# code. -# from ufl.corealg.traversal import pre_traversal -from ufl.corealg.traversal import post_traversal -# from ufl.corealg.traversal import traverse_terminals, traverse_unique_terminals - - -# Utilities for extracting information from forms and expressions -from ufl.algorithms.analysis import ( - extract_type, - extract_arguments, - extract_coefficients, - # extract_arguments_and_coefficients, - extract_base_form_operators, - extract_elements, - extract_unique_elements, - extract_sub_elements, - sort_elements, -) - - -# Preprocessing a form to extract various meta data -# from ufl.algorithms.formdata import FormData -from ufl.algorithms.compute_form_data import compute_form_data, preprocess_form - -# Utilities for checking properties of forms -from ufl.algorithms.signature import compute_form_signature - -# Utilities for error checking of forms -from ufl.algorithms.checks import validate_form - -# Utilites for modifying expressions and forms -from ufl.corealg.multifunction import MultiFunction -from ufl.algorithms.transformer import Transformer, ReuseTransformer -# from ufl.algorithms.transformer import is_post_handler -from ufl.algorithms.transformer import apply_transformer -from ufl.algorithms.transformer import strip_variables -from ufl.algorithms.strip_terminal_data import strip_terminal_data -from ufl.algorithms.strip_terminal_data import replace_terminal_data -# from ufl.algorithms.replace import Replacer -from ufl.algorithms.replace import replace +from ufl.algorithms.ad import expand_derivatives +from ufl.algorithms.analysis import (extract_arguments, extract_base_form_operators, extract_coefficients, + extract_elements, extract_sub_elements, extract_type, extract_unique_elements, + sort_elements) from ufl.algorithms.change_to_reference import change_to_reference_grad -from ufl.algorithms.expand_compounds import expand_compounds -# from ufl.algorithms.estimate_degrees import SumDegreeEstimator +from ufl.algorithms.checks import validate_form +from ufl.algorithms.compute_form_data import compute_form_data, preprocess_form from ufl.algorithms.estimate_degrees import estimate_total_polynomial_degree +from ufl.algorithms.expand_compounds import expand_compounds from ufl.algorithms.expand_indices import expand_indices - -# Utilities for transforming complete Forms into other Forms -from ufl.algorithms.formtransformations import compute_form_adjoint -from ufl.algorithms.formtransformations import compute_form_action -from ufl.algorithms.formtransformations import compute_energy_norm -from ufl.algorithms.formtransformations import compute_form_lhs -from ufl.algorithms.formtransformations import compute_form_rhs -from ufl.algorithms.formtransformations import compute_form_functional -from ufl.algorithms.formtransformations import compute_form_arities - +from ufl.algorithms.formfiles import load_forms, load_ufl_file, read_ufl_file from ufl.algorithms.formsplitter import FormSplitter - -# Utilities for Automatic Functional Differentiation -from ufl.algorithms.ad import expand_derivatives - -# Utilities for form file handling -from ufl.algorithms.formfiles import read_ufl_file -from ufl.algorithms.formfiles import load_ufl_file -from ufl.algorithms.formfiles import load_forms - +from ufl.algorithms.formtransformations import (compute_energy_norm, compute_form_action, compute_form_adjoint, + compute_form_arities, compute_form_functional, compute_form_lhs, + compute_form_rhs) +from ufl.algorithms.replace import replace +from ufl.algorithms.signature import compute_form_signature +from ufl.algorithms.strip_terminal_data import replace_terminal_data, strip_terminal_data +from ufl.algorithms.transformer import ReuseTransformer, Transformer, apply_transformer, strip_variables +from ufl.corealg.multifunction import MultiFunction +from ufl.corealg.traversal import post_traversal from ufl.utils.formatting import tree_format diff --git a/ufl/algorithms/analysis.py b/ufl/algorithms/analysis.py index a758351ce..ba3afe6c6 100644 --- a/ufl/algorithms/analysis.py +++ b/ufl/algorithms/analysis.py @@ -11,21 +11,20 @@ from itertools import chain -from ufl.utils.sorting import sorted_by_count, topological_sorting - -from ufl.core.terminal import Terminal -from ufl.core.base_form_operator import BaseFormOperator +from ufl.algorithms.traversal import iter_expressions from ufl.argument import BaseArgument, Coargument from ufl.coefficient import BaseCoefficient from ufl.constant import Constant +from ufl.core.base_form_operator import BaseFormOperator +from ufl.core.terminal import Terminal +from ufl.corealg.traversal import traverse_unique_terminals, unique_pre_traversal from ufl.form import BaseForm, Form -from ufl.algorithms.traversal import iter_expressions -from ufl.corealg.traversal import unique_pre_traversal, traverse_unique_terminals - +from ufl.utils.sorting import sorted_by_count, topological_sorting # TODO: Some of these can possibly be optimised by implementing # inlined stack based traversal algorithms + def _sorted_by_number_and_part(seq): """Sort items by number and part.""" return sorted(seq, key=lambda x: (x.number(), x.part())) @@ -237,7 +236,7 @@ def extract_unique_elements(form): def extract_sub_elements(elements): """Build sorted tuple of all sub elements (including parent element).""" - sub_elements = tuple(chain(*[e.sub_elements() for e in elements])) + sub_elements = tuple(chain(*[e.sub_elements for e in elements])) if not sub_elements: return tuple(elements) return tuple(elements) + extract_sub_elements(sub_elements) @@ -253,12 +252,12 @@ def sort_elements(elements): The ordering is based on sorting a directed acyclic graph. """ # Set nodes - nodes = sorted(elements) + nodes = list(elements) # Set edges edges = dict((node, []) for node in nodes) for element in elements: - for sub_element in element.sub_elements(): + for sub_element in element.sub_elements: edges[element].append(sub_element) # Sort graph diff --git a/ufl/algorithms/apply_algebra_lowering.py b/ufl/algorithms/apply_algebra_lowering.py index 998a139e6..e7dc4cd46 100644 --- a/ufl/algorithms/apply_algebra_lowering.py +++ b/ufl/algorithms/apply_algebra_lowering.py @@ -8,14 +8,12 @@ # # Modified by Anders Logg, 2009-2010 -from ufl.classes import Product, Grad, Conj -from ufl.core.multiindex import indices, Index -from ufl.tensors import as_tensor, as_matrix, as_vector - -from ufl.compound_expressions import deviatoric_expr, determinant_expr, cofactor_expr, inverse_expr - -from ufl.corealg.multifunction import MultiFunction from ufl.algorithms.map_integrands import map_integrand_dags +from ufl.classes import Conj, Grad, Product +from ufl.compound_expressions import cofactor_expr, determinant_expr, deviatoric_expr, inverse_expr +from ufl.core.multiindex import Index, indices +from ufl.corealg.multifunction import MultiFunction +from ufl.tensors import as_matrix, as_tensor, as_vector class LowerCompoundAlgebra(MultiFunction): diff --git a/ufl/algorithms/apply_derivatives.py b/ufl/algorithms/apply_derivatives.py index d34540904..7ef8a0a5f 100644 --- a/ufl/algorithms/apply_derivatives.py +++ b/ufl/algorithms/apply_derivatives.py @@ -10,34 +10,30 @@ from collections import defaultdict from math import pi +from ufl.action import Action from ufl.algorithms.analysis import extract_arguments from ufl.algorithms.map_integrands import map_integrand_dags from ufl.algorithms.replace_derivative_nodes import replace_derivative_nodes +from ufl.argument import BaseArgument from ufl.checks import is_cellwise_constant -from ufl.classes import (Coefficient, ComponentTensor, Conj, ConstantValue, - ExprList, ExprMapping, FloatValue, FormArgument, Grad, - Identity, Imag, Indexed, IndexSum, JacobianInverse, - ListTensor, Product, Real, ReferenceGrad, - ReferenceValue, SpatialCoordinate, Sum, Variable, - Zero) +from ufl.classes import (Coefficient, ComponentTensor, Conj, ConstantValue, ExprList, ExprMapping, FloatValue, + FormArgument, Grad, Identity, Imag, Indexed, IndexSum, JacobianInverse, ListTensor, Product, + Real, ReferenceGrad, ReferenceValue, SpatialCoordinate, Sum, Variable, Zero) from ufl.constantvalue import is_true_ufl_scalar, is_ufl_scalar +from ufl.core.base_form_operator import BaseFormOperator from ufl.core.expr import ufl_err_str from ufl.core.multiindex import FixedIndex, MultiIndex, indices from ufl.core.terminal import Terminal from ufl.corealg.map_dag import map_expr_dag from ufl.corealg.multifunction import MultiFunction -from ufl.differentiation import CoordinateDerivative, BaseFormCoordinateDerivative, BaseFormOperatorDerivative +from ufl.differentiation import BaseFormCoordinateDerivative, BaseFormOperatorDerivative, CoordinateDerivative from ufl.domain import extract_unique_domain -from ufl.operators import (bessel_I, bessel_J, bessel_K, bessel_Y, cell_avg, - conditional, cos, cosh, exp, facet_avg, ln, sign, - sin, sinh, sqrt) -from ufl.tensors import (as_scalar, as_scalars, as_tensor, unit_indexed_tensor, - unwrap_list_tensor) - -from ufl.argument import BaseArgument -from ufl.action import Action from ufl.form import Form, ZeroBaseForm -from ufl.core.base_form_operator import BaseFormOperator +from ufl.operators import (bessel_I, bessel_J, bessel_K, bessel_Y, cell_avg, conditional, cos, cosh, exp, facet_avg, ln, + sign, sin, sinh, sqrt) +from ufl.pullback import CustomPullback, PhysicalPullback +from ufl.tensors import as_scalar, as_scalars, as_tensor, unit_indexed_tensor, unwrap_list_tensor + # TODO: Add more rulesets? # - DivRuleset # - CurlRuleset @@ -597,7 +593,7 @@ def reference_value(self, o): """Differentiate a reference_value.""" # grad(o) == grad(rv(f)) -> K_ji*rgrad(rv(f))_rj f = o.ufl_operands[0] - if f.ufl_element().mapping() == "physical": + if isinstance(f.ufl_element().pullback, PhysicalPullback): # TODO: Do we need to be more careful for immersed things? return ReferenceGrad(o) @@ -836,7 +832,7 @@ def reference_value(self, o): # d/dv(o) == d/dv(rv(f)) = 0 if v is not f, or rv(dv/df) v = self._variable if isinstance(v, Coefficient) and o.ufl_operands[0] == v: - if v.ufl_element().mapping() != "identity": + if not v.ufl_element().pullback.is_identity: # FIXME: This is a bit tricky, instead of Identity it is # actually inverse(transform), or we should rather not # convert to reference frame in the first place @@ -1641,7 +1637,7 @@ def coordinate_derivative(self, o, f, w, v, cd): """Apply to a coordinate_derivative.""" from ufl.algorithms import extract_unique_elements for space in extract_unique_elements(o): - if space.mapping() == "custom": + if isinstance(space.pullback, CustomPullback): raise NotImplementedError( "CoordinateDerivative is not supported for elements with custom pull back.") diff --git a/ufl/algorithms/apply_function_pullbacks.py b/ufl/algorithms/apply_function_pullbacks.py index ebd66866d..a8c1b8acf 100644 --- a/ufl/algorithms/apply_function_pullbacks.py +++ b/ufl/algorithms/apply_function_pullbacks.py @@ -6,152 +6,9 @@ # # SPDX-License-Identifier: LGPL-3.0-or-later -from itertools import accumulate, chain, repeat - -import numpy - from ufl.algorithms.map_integrands import map_integrand_dags -from ufl.classes import (Jacobian, JacobianDeterminant, JacobianInverse, - ReferenceValue) -from ufl.core.multiindex import indices +from ufl.classes import ReferenceValue from ufl.corealg.multifunction import MultiFunction, memoized_handler -from ufl.domain import extract_unique_domain -from ufl.tensors import as_tensor, as_vector -from ufl.utils.sequences import product - - -def sub_elements_with_mappings(element): - """Return an ordered list of the largest subelements that have a defined mapping.""" - if element.mapping() != "undefined": - return [element] - elements = [] - for subelm in element.sub_elements(): - if subelm.mapping() != "undefined": - elements.append(subelm) - else: - elements.extend(sub_elements_with_mappings(subelm)) - return elements - - -def apply_known_single_pullback(r, element): - """Apply pullback with given mapping. - - Args: - r: Expression wrapped in ReferenceValue - element: The element defining the mapping - """ - # Need to pass in r rather than the physical space thing, because - # the latter may be a ListTensor or similar, rather than a - # Coefficient/Argument (in the case of mixed elements, see below - # in apply_single_function_pullbacks), to which we cannot apply ReferenceValue - mapping = element.mapping() - domain = extract_unique_domain(r) - if mapping == "physical": - return r - elif mapping == "identity" or mapping == "custom": - return r - elif mapping == "contravariant Piola": - J = Jacobian(domain) - detJ = JacobianDeterminant(J) - transform = (1.0 / detJ) * J - # Apply transform "row-wise" to TensorElement(PiolaMapped, ...) - *k, i, j = indices(len(r.ufl_shape) + 1) - kj = (*k, j) - f = as_tensor(transform[i, j] * r[kj], (*k, i)) - return f - elif mapping == "covariant Piola": - K = JacobianInverse(domain) - # Apply transform "row-wise" to TensorElement(PiolaMapped, ...) - *k, i, j = indices(len(r.ufl_shape) + 1) - kj = (*k, j) - f = as_tensor(K[j, i] * r[kj], (*k, i)) - return f - elif mapping == "L2 Piola": - detJ = JacobianDeterminant(domain) - return r / detJ - elif mapping == "double contravariant Piola": - J = Jacobian(domain) - detJ = JacobianDeterminant(J) - transform = (1.0 / detJ) * J - # Apply transform "row-wise" to TensorElement(PiolaMapped, ...) - *k, i, j, m, n = indices(len(r.ufl_shape) + 2) - kmn = (*k, m, n) - f = as_tensor((1.0 / detJ)**2 * J[i, m] * r[kmn] * J[j, n], (*k, i, j)) - return f - elif mapping == "double covariant Piola": - K = JacobianInverse(domain) - # Apply transform "row-wise" to TensorElement(PiolaMapped, ...) - *k, i, j, m, n = indices(len(r.ufl_shape) + 2) - kmn = (*k, m, n) - f = as_tensor(K[m, i] * r[kmn] * K[n, j], (*k, i, j)) - return f - else: - raise ValueError(f"Unsupported mapping: {mapping}.") - - -def apply_single_function_pullbacks(r, element): - """Apply an appropriate pullback to something in physical space. - - Args: - r: An expression wrapped in ReferenceValue. - element: The element this expression lives in. - - Returns: - a pulled back expression. - """ - mapping = element.mapping() - if r.ufl_shape != element.reference_value_shape(): - raise ValueError( - f"Expecting reference space expression with shape '{element.reference_value_shape()}', " - f"got '{r.ufl_shape}'") - if mapping in {"physical", "identity", - "contravariant Piola", "covariant Piola", - "double contravariant Piola", "double covariant Piola", - "L2 Piola", "custom"}: - # Base case in recursion through elements. If the element - # advertises a mapping we know how to handle, do that - # directly. - f = apply_known_single_pullback(r, element) - if f.ufl_shape != element.value_shape(): - raise ValueError(f"Expecting pulled back expression with shape '{element.value_shape()}', " - f"got '{f.ufl_shape}'") - return f - elif mapping in {"symmetries", "undefined"}: - # Need to pull back each unique piece of the reference space thing - gsh = element.value_shape() - rsh = r.ufl_shape - if mapping == "symmetries": - subelem = element.sub_elements()[0] - fcm = element.flattened_sub_element_mapping() - offsets = (product(subelem.reference_value_shape()) * i for i in fcm) - elements = repeat(subelem) - else: - elements = sub_elements_with_mappings(element) - # Python >= 3.8 has an initial keyword argument to - # accumulate, but 3.7 does not. - offsets = chain([0], - accumulate(product(e.reference_value_shape()) - for e in elements)) - rflat = as_vector([r[idx] for idx in numpy.ndindex(rsh)]) - g_components = [] - # For each unique piece in reference space, apply the appropriate pullback - for offset, subelem in zip(offsets, elements): - sub_rsh = subelem.reference_value_shape() - rm = product(sub_rsh) - rsub = [rflat[offset + i] for i in range(rm)] - rsub = as_tensor(numpy.asarray(rsub).reshape(sub_rsh)) - rmapped = apply_single_function_pullbacks(rsub, subelem) - # Flatten into the pulled back expression for the whole thing - g_components.extend([rmapped[idx] - for idx in numpy.ndindex(rmapped.ufl_shape)]) - # And reshape appropriately - f = as_tensor(numpy.asarray(g_components).reshape(gsh)) - if f.ufl_shape != element.value_shape(): - raise ValueError(f"Expecting pulled back expression with shape '{element.value_shape()}', " - f"got '{f.ufl_shape}'") - return f - else: - raise ValueError(f"Unsupported mapping type: {mapping}") class FunctionPullbackApplier(MultiFunction): @@ -172,7 +29,18 @@ def form_argument(self, o): """Apply to a form_argument.""" # Represent 0-derivatives of form arguments on reference # element - f = apply_single_function_pullbacks(ReferenceValue(o), o.ufl_element()) + r = ReferenceValue(o) + element = o.ufl_element() + + if r.ufl_shape != element.reference_value_shape: + raise ValueError( + f"Expecting reference space expression with shape '{element.reference_value_shape}', " + f"got '{r.ufl_shape}'") + f = element.pullback.apply(r) + if f.ufl_shape != element.value_shape: + raise ValueError(f"Expecting pulled back expression with shape '{element.value_shape}', " + f"got '{f.ufl_shape}'") + assert f.ufl_shape == o.ufl_shape return f diff --git a/ufl/algorithms/apply_geometry_lowering.py b/ufl/algorithms/apply_geometry_lowering.py index 21ea99f3c..c1a39d863 100644 --- a/ufl/algorithms/apply_geometry_lowering.py +++ b/ufl/algorithms/apply_geometry_lowering.py @@ -14,14 +14,10 @@ from functools import reduce from itertools import combinations -from ufl.classes import (CellCoordinate, CellEdgeVectors, CellFacetJacobian, - CellOrientation, CellOrigin, CellVertices, CellVolume, - Expr, FacetEdgeVectors, FacetJacobian, - FacetJacobianDeterminant, FloatValue, Form, Integral, - Jacobian, JacobianDeterminant, JacobianInverse, - MaxCellEdgeLength, ReferenceCellVolume, - ReferenceFacetVolume, ReferenceGrad, ReferenceNormal, - SpatialCoordinate) +from ufl.classes import (CellCoordinate, CellEdgeVectors, CellFacetJacobian, CellOrientation, CellOrigin, CellVertices, + CellVolume, Expr, FacetEdgeVectors, FacetJacobian, FacetJacobianDeterminant, FloatValue, Form, + Integral, Jacobian, JacobianDeterminant, JacobianInverse, MaxCellEdgeLength, + ReferenceCellVolume, ReferenceFacetVolume, ReferenceGrad, ReferenceNormal, SpatialCoordinate) from ufl.compound_expressions import cross_expr, determinant_expr, inverse_expr from ufl.core.multiindex import Index, indices from ufl.corealg.map_dag import map_expr_dag @@ -54,7 +50,7 @@ def jacobian(self, o): if self._preserve_types[o._ufl_typecode_]: return o domain = extract_unique_domain(o) - if domain.ufl_coordinate_element().mapping() != "identity": + if not domain.ufl_coordinate_element().pullback.is_identity: raise ValueError("Piola mapped coordinates are not implemented.") # Note: No longer supporting domain.coordinates(), always # preserving SpatialCoordinate object. However if Jacobians @@ -155,7 +151,7 @@ def spatial_coordinate(self, o): """ if self._preserve_types[o._ufl_typecode_]: return o - if extract_unique_domain(o).ufl_coordinate_element().mapping() != "identity": + if not extract_unique_domain(o).ufl_coordinate_element().pullback.is_identity: raise ValueError("Piola mapped coordinates are not implemented.") # No longer supporting domain.coordinates(), always preserving # SpatialCoordinate object. @@ -284,7 +280,7 @@ def _reduce_cell_edge_length(self, o, reduction_op): domain = extract_unique_domain(o) - if not domain.ufl_coordinate_element().degree() == 1: + if domain.ufl_coordinate_element().embedded_superdegree > 1: # Don't lower bendy cells, instead leave it to form compiler warnings.warn("Only know how to compute cell edge lengths of P1 or Q1 cell.") return o @@ -309,7 +305,7 @@ def cell_diameter(self, o): domain = extract_unique_domain(o) - if not domain.ufl_coordinate_element().degree() in {1, (1, 1)}: + if domain.ufl_coordinate_element().embedded_superdegree > 1: # Don't lower bendy cells, instead leave it to form compiler warnings.warn("Only know how to compute cell diameter of P1 or Q1 cell.") return o @@ -346,7 +342,7 @@ def _reduce_facet_edge_length(self, o, reduction_op): if domain.ufl_cell().topological_dimension() < 3: raise ValueError("Facet edge lengths only make sense for topological dimension >= 3.") - elif not domain.ufl_coordinate_element().degree() == 1: + elif domain.ufl_coordinate_element().embedded_superdegree > 1: # Don't lower bendy cells, instead leave it to form compiler warnings.warn("Only know how to compute facet edge lengths of P1 or Q1 cell.") return o diff --git a/ufl/algorithms/apply_integral_scaling.py b/ufl/algorithms/apply_integral_scaling.py index de5cc9fa5..7e5e35ff2 100644 --- a/ufl/algorithms/apply_integral_scaling.py +++ b/ufl/algorithms/apply_integral_scaling.py @@ -6,11 +6,11 @@ # # SPDX-License-Identifier: LGPL-3.0-or-later -from ufl.classes import JacobianDeterminant, FacetJacobianDeterminant, QuadratureWeight, Form, Integral -from ufl.measure import custom_integral_types, point_integral_types -from ufl.differentiation import CoordinateDerivative from ufl.algorithms.apply_geometry_lowering import apply_geometry_lowering from ufl.algorithms.estimate_degrees import estimate_total_polynomial_degree +from ufl.classes import FacetJacobianDeterminant, Form, Integral, JacobianDeterminant, QuadratureWeight +from ufl.differentiation import CoordinateDerivative +from ufl.measure import custom_integral_types, point_integral_types def compute_integrand_scaling_factor(integral): diff --git a/ufl/algorithms/apply_restrictions.py b/ufl/algorithms/apply_restrictions.py index cd98315ae..8f788a009 100644 --- a/ufl/algorithms/apply_restrictions.py +++ b/ufl/algorithms/apply_restrictions.py @@ -157,7 +157,7 @@ def facet_normal(self, o): gd = D.geometric_dimension() td = D.topological_dimension() - if e._is_linear() and gd == td: + if e.embedded_superdegree <= 1 and e in H1 and gd == td: # For meshes with a continuous linear non-manifold # coordinate field, the facet normal from side - points in # the opposite direction of the one from side +. We must diff --git a/ufl/algorithms/balancing.py b/ufl/algorithms/balancing.py index fdb6682c5..477ec3f6f 100644 --- a/ufl/algorithms/balancing.py +++ b/ufl/algorithms/balancing.py @@ -6,8 +6,8 @@ # # SPDX-License-Identifier: LGPL-3.0-or-later -from ufl.classes import (CellAvg, FacetAvg, Grad, Indexed, NegativeRestricted, - PositiveRestricted, ReferenceGrad, ReferenceValue) +from ufl.classes import (CellAvg, FacetAvg, Grad, Indexed, NegativeRestricted, PositiveRestricted, ReferenceGrad, + ReferenceValue) from ufl.corealg.map_dag import map_expr_dag from ufl.corealg.multifunction import MultiFunction diff --git a/ufl/algorithms/change_to_reference.py b/ufl/algorithms/change_to_reference.py index 452f0d896..c232d3c7b 100644 --- a/ufl/algorithms/change_to_reference.py +++ b/ufl/algorithms/change_to_reference.py @@ -6,18 +6,14 @@ # # SPDX-License-Identifier: LGPL-3.0-or-later -from ufl.core.multiindex import indices -from ufl.corealg.multifunction import MultiFunction -from ufl.corealg.map_dag import map_expr_dag - -from ufl.classes import ReferenceGrad, Grad, Restricted, ReferenceValue, JacobianInverse - -from ufl.tensors import as_tensor - from ufl.algorithms.apply_function_pullbacks import apply_function_pullbacks from ufl.algorithms.apply_geometry_lowering import apply_geometry_lowering from ufl.checks import is_cellwise_constant - +from ufl.classes import Grad, JacobianInverse, ReferenceGrad, ReferenceValue, Restricted +from ufl.core.multiindex import indices +from ufl.corealg.map_dag import map_expr_dag +from ufl.corealg.multifunction import MultiFunction +from ufl.tensors import as_tensor """ # Some notes: diff --git a/ufl/algorithms/check_arities.py b/ufl/algorithms/check_arities.py index 80a917e03..c93727ad8 100644 --- a/ufl/algorithms/check_arities.py +++ b/ufl/algorithms/check_arities.py @@ -1,10 +1,10 @@ """Check arities.""" from itertools import chain -from ufl.corealg.traversal import traverse_unique_terminals -from ufl.corealg.multifunction import MultiFunction -from ufl.corealg.map_dag import map_expr_dag from ufl.classes import Argument, Zero +from ufl.corealg.map_dag import map_expr_dag +from ufl.corealg.multifunction import MultiFunction +from ufl.corealg.traversal import traverse_unique_terminals class ArityMismatch(BaseException): diff --git a/ufl/algorithms/check_restrictions.py b/ufl/algorithms/check_restrictions.py index d2c4ecd7e..df75e279e 100644 --- a/ufl/algorithms/check_restrictions.py +++ b/ufl/algorithms/check_restrictions.py @@ -6,8 +6,8 @@ # # SPDX-License-Identifier: LGPL-3.0-or-later -from ufl.corealg.multifunction import MultiFunction from ufl.corealg.map_dag import map_expr_dag +from ufl.corealg.multifunction import MultiFunction class RestrictionChecker(MultiFunction): diff --git a/ufl/algorithms/checks.py b/ufl/algorithms/checks.py index b5f6aa109..9257b160d 100644 --- a/ufl/algorithms/checks.py +++ b/ufl/algorithms/checks.py @@ -9,18 +9,17 @@ # Modified by Anders Logg, 2008-2009. # Modified by Mehdi Nikbakht, 2010. -# UFL classes -from ufl.core.expr import ufl_err_str -from ufl.form import Form +from ufl.algorithms.check_restrictions import check_restrictions +# UFL algorithms +from ufl.algorithms.traversal import iter_expressions from ufl.argument import Argument from ufl.coefficient import Coefficient from ufl.constantvalue import is_true_ufl_scalar - -# UFL algorithms -from ufl.algorithms.traversal import iter_expressions +# UFL classes +from ufl.core.expr import ufl_err_str from ufl.corealg.traversal import traverse_unique_terminals -from ufl.algorithms.check_restrictions import check_restrictions from ufl.domain import extract_unique_domain +from ufl.form import Form def validate_form(form): # TODO: Can we make this return a list of errors instead of raising exception? diff --git a/ufl/algorithms/comparison_checker.py b/ufl/algorithms/comparison_checker.py index 32a9098fc..e61aeecf3 100644 --- a/ufl/algorithms/comparison_checker.py +++ b/ufl/algorithms/comparison_checker.py @@ -1,10 +1,10 @@ """Algorithm to check for 'comparison' nodes in a form when the user is in 'complex mode'.""" -from ufl.corealg.multifunction import MultiFunction -from ufl.algorithms.map_integrands import map_integrand_dags from ufl.algebra import Real -from ufl.constantvalue import RealValue, Zero +from ufl.algorithms.map_integrands import map_integrand_dags from ufl.argument import Argument +from ufl.constantvalue import RealValue, Zero +from ufl.corealg.multifunction import MultiFunction from ufl.geometry import GeometricQuantity diff --git a/ufl/algorithms/compute_form_data.py b/ufl/algorithms/compute_form_data.py index 5a393987a..f8c9df4ac 100644 --- a/ufl/algorithms/compute_form_data.py +++ b/ufl/algorithms/compute_form_data.py @@ -11,30 +11,26 @@ from itertools import chain -from ufl.utils.sequences import max_degree - -from ufl.classes import GeometricFacetQuantity, Coefficient, Form, FunctionSpace -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 -from ufl.algorithms.formtransformations import compute_form_arities -from ufl.algorithms.check_arities import check_form_arity - +from ufl.algorithms.apply_algebra_lowering import apply_algebra_lowering +from ufl.algorithms.apply_derivatives import apply_coordinate_derivatives, apply_derivatives # These are the main symbolic processing steps: from ufl.algorithms.apply_function_pullbacks import apply_function_pullbacks -from ufl.algorithms.apply_algebra_lowering import apply_algebra_lowering -from ufl.algorithms.apply_derivatives import apply_derivatives, apply_coordinate_derivatives -from ufl.algorithms.apply_integral_scaling import apply_integral_scaling from ufl.algorithms.apply_geometry_lowering import apply_geometry_lowering -from ufl.algorithms.apply_restrictions import apply_restrictions, apply_default_restrictions -from ufl.algorithms.estimate_degrees import estimate_total_polynomial_degree -from ufl.algorithms.remove_complex_nodes import remove_complex_nodes +from ufl.algorithms.apply_integral_scaling import apply_integral_scaling +from ufl.algorithms.apply_restrictions import apply_default_restrictions, apply_restrictions +from ufl.algorithms.check_arities import check_form_arity from ufl.algorithms.comparison_checker import do_comparison_check - # 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 +from ufl.algorithms.domain_analysis import (build_integral_data, group_form_integrals, + reconstruct_form_from_integral_data) +from ufl.algorithms.estimate_degrees import estimate_total_polynomial_degree +from ufl.algorithms.formdata import FormData +from ufl.algorithms.formtransformations import compute_form_arities +from ufl.algorithms.remove_complex_nodes import remove_complex_nodes +from ufl.classes import Coefficient, Form, FunctionSpace, GeometricFacetQuantity +from ufl.corealg.traversal import traverse_unique_terminals +from ufl.utils.sequences import max_degree def _auto_select_degree(elements): @@ -46,7 +42,7 @@ def _auto_select_degree(elements): """ # Use max degree of all elements, at least 1 (to work with # Lagrange elements) - return max_degree({e.degree() for e in elements} - {None} | {1}) + return max_degree({e.embedded_superdegree for e in elements} - {None} | {1}) def _compute_element_mapping(form): @@ -74,7 +70,7 @@ def _compute_element_mapping(form): reconstruct = False # Set cell - cell = element.cell() + cell = element.cell if cell is None: domains = form.ufl_domains() if not all(domains[0].ufl_cell() == d.ufl_cell() @@ -84,7 +80,7 @@ def _compute_element_mapping(form): reconstruct = True # Set degree - degree = element.degree() + degree = element.embedded_superdegree if degree is None: degree = common_degree reconstruct = True @@ -138,7 +134,7 @@ def _check_elements(form_data): """Check elements.""" for element in chain(form_data.unique_elements, form_data.unique_sub_elements): - if element.cell() is None: + if element.cell is None: raise ValueError(f"Found element with undefined cell: {element}") diff --git a/ufl/algorithms/coordinate_derivative_helpers.py b/ufl/algorithms/coordinate_derivative_helpers.py index d94a8eed1..77b1c19bf 100644 --- a/ufl/algorithms/coordinate_derivative_helpers.py +++ b/ufl/algorithms/coordinate_derivative_helpers.py @@ -9,10 +9,10 @@ # # SPDX-License-Identifier: LGPL-3.0-or-later -from ufl.differentiation import CoordinateDerivative -from ufl.corealg.multifunction import MultiFunction -from ufl.corealg.map_dag import map_expr_dags from ufl.classes import Integral +from ufl.corealg.map_dag import map_expr_dags +from ufl.corealg.multifunction import MultiFunction +from ufl.differentiation import CoordinateDerivative class CoordinateDerivativeIsOutermostChecker(MultiFunction): diff --git a/ufl/algorithms/domain_analysis.py b/ufl/algorithms/domain_analysis.py index 86fc603d8..3a11b123a 100644 --- a/ufl/algorithms/domain_analysis.py +++ b/ufl/algorithms/domain_analysis.py @@ -6,18 +6,17 @@ # # SPDX-License-Identifier: LGPL-3.0-or-later +import numbers +import typing from collections import defaultdict import ufl -from ufl.integral import Integral +from ufl.algorithms.coordinate_derivative_helpers import attach_coordinate_derivatives, strip_coordinate_derivatives from ufl.form import Form +from ufl.integral import Integral from ufl.protocols import id_or_none from ufl.sorting import cmp_expr, sorted_expr from ufl.utils.sorting import canonicalize_metadata, sorted_by_key -from ufl.algorithms.coordinate_derivative_helpers import ( - attach_coordinate_derivatives, strip_coordinate_derivatives) -import numbers -import typing class IntegralData(object): diff --git a/ufl/algorithms/estimate_degrees.py b/ufl/algorithms/estimate_degrees.py index a5889fe2c..ccaf64c7e 100644 --- a/ufl/algorithms/estimate_degrees.py +++ b/ufl/algorithms/estimate_degrees.py @@ -11,10 +11,10 @@ import warnings -from ufl.corealg.multifunction import MultiFunction from ufl.checks import is_cellwise_constant from ufl.constantvalue import IntValue from ufl.corealg.map_dag import map_expr_dags +from ufl.corealg.multifunction import MultiFunction from ufl.domain import extract_unique_domain from ufl.form import Form from ufl.integral import Integral @@ -52,14 +52,14 @@ def geometric_quantity(self, v): return 0 else: # As a heuristic, just returning domain degree to bump up degree somewhat - return extract_unique_domain(v).ufl_coordinate_element().degree() + return extract_unique_domain(v).ufl_coordinate_element().embedded_superdegree def spatial_coordinate(self, v): """Apply to spatial_coordinate. A coordinate provides additional degrees depending on coordinate field of domain. """ - return extract_unique_domain(v).ufl_coordinate_element().degree() + return extract_unique_domain(v).ufl_coordinate_element().embedded_superdegree def cell_coordinate(self, v): """Apply to cell_coordinate. @@ -74,7 +74,7 @@ def argument(self, v): A form argument provides a degree depending on the element, or the default degree if the element has no degree. """ - return v.ufl_element().degree() # FIXME: Use component to improve accuracy for mixed elements + return v.ufl_element().embedded_superdegree # FIXME: Use component to improve accuracy for mixed elements def coefficient(self, v): """Apply to coefficient. @@ -84,7 +84,7 @@ def coefficient(self, v): """ e = v.ufl_element() e = self.element_replace_map.get(e, e) - d = e.degree() # FIXME: Use component to improve accuracy for mixed elements + d = e.embedded_superdegree # FIXME: Use component to improve accuracy for mixed elements if d is None: d = self.default_degree return d diff --git a/ufl/algorithms/expand_compounds.py b/ufl/algorithms/expand_compounds.py index 04290729b..97eecc499 100644 --- a/ufl/algorithms/expand_compounds.py +++ b/ufl/algorithms/expand_compounds.py @@ -9,6 +9,7 @@ # Modified by Anders Logg, 2009-2010 import warnings + from ufl.algorithms.apply_algebra_lowering import apply_algebra_lowering diff --git a/ufl/algorithms/expand_indices.py b/ufl/algorithms/expand_indices.py index b36923ac5..1523bd4c4 100644 --- a/ufl/algorithms/expand_indices.py +++ b/ufl/algorithms/expand_indices.py @@ -11,12 +11,12 @@ # # Modified by Anders Logg, 2009. -from ufl.utils.stacks import Stack, StackDict +from ufl.algorithms.transformer import ReuseTransformer, apply_transformer from ufl.classes import Terminal from ufl.constantvalue import Zero -from ufl.core.multiindex import Index, FixedIndex, MultiIndex +from ufl.core.multiindex import FixedIndex, Index, MultiIndex from ufl.differentiation import Grad -from ufl.algorithms.transformer import ReuseTransformer, apply_transformer +from ufl.utils.stacks import Stack, StackDict class IndexExpander(ReuseTransformer): @@ -58,10 +58,10 @@ def form_argument(self, x): raise ValueError("Component size mismatch.") # Map it through an eventual symmetry mapping - s = e.symmetry() - c = s.get(c, c) - if r != len(c): - raise ValueError("Component size mismatch after symmetry mapping.") + if len(e.components) > 1: + c = min(i for i, j in e.components.items() if j == e.components[c]) + if r != len(c): + raise ValueError("Component size mismatch after symmetry mapping.") return x[c] diff --git a/ufl/algorithms/formdata.py b/ufl/algorithms/formdata.py index c5162c140..6f1048aec 100644 --- a/ufl/algorithms/formdata.py +++ b/ufl/algorithms/formdata.py @@ -8,7 +8,7 @@ # # Modified by Anders Logg, 2008. -from ufl.utils.formatting import lstr, tstr, estr +from ufl.utils.formatting import estr, lstr, tstr class FormData(object): diff --git a/ufl/algorithms/formfiles.py b/ufl/algorithms/formfiles.py index 0bb4f38a5..7927f3a11 100644 --- a/ufl/algorithms/formfiles.py +++ b/ufl/algorithms/formfiles.py @@ -12,13 +12,14 @@ import io import os import re -from ufl.utils.sorting import sorted_by_key -from ufl.form import Form -from ufl.finiteelement import FiniteElementBase -from ufl.core.expr import Expr -from ufl.constant import Constant + from ufl.argument import Argument from ufl.coefficient import Coefficient +from ufl.constant import Constant +from ufl.core.expr import Expr +from ufl.finiteelement import AbstractFiniteElement +from ufl.form import Form +from ufl.utils.sorting import sorted_by_key class FileData(object): @@ -93,7 +94,7 @@ def interpret_ufl_namespace(namespace): # Object to hold all returned data ufd = FileData() - # Extract object names for Form, Coefficient and FiniteElementBase objects + # Extract object names for Form, Coefficient and AbstractFiniteElement objects # The use of id(obj) as key in object_names is necessary # because we need to distinguish between instances, # and not just between objects with different values. @@ -106,7 +107,7 @@ def interpret_ufl_namespace(namespace): # FIXME: Remove after FFC is updated to use reserved_objects: ufd.object_names[name] = value ufd.object_by_name[name] = value - elif isinstance(value, (FiniteElementBase, Coefficient, Constant, Argument, Form, Expr)): + elif isinstance(value, (AbstractFiniteElement, Coefficient, Constant, Argument, Form, Expr)): # Store instance <-> name mappings for important objects # without a reserved name ufd.object_names[id(value)] = name @@ -149,8 +150,8 @@ def get_form(name): # Validate types if not isinstance(ufd.elements, (list, tuple)): raise ValueError(f"Expecting 'elements' to be a list or tuple, not '{type(ufd.elements)}''.") - if not all(isinstance(e, FiniteElementBase) for e in ufd.elements): - raise ValueError("Expecting 'elements' to be a list of FiniteElementBase instances.") + if not all(isinstance(e, AbstractFiniteElement) for e in ufd.elements): + raise ValueError("Expecting 'elements' to be a list of AbstractFiniteElement instances.") # Get list of exported coefficients functions = [] diff --git a/ufl/algorithms/formsplitter.py b/ufl/algorithms/formsplitter.py index 755b479df..ef9b2bbef 100644 --- a/ufl/algorithms/formsplitter.py +++ b/ufl/algorithms/formsplitter.py @@ -8,12 +8,12 @@ # # Modified by Cecile Daversin-Catty, 2018 -from ufl.corealg.multifunction import MultiFunction from ufl.algorithms.map_integrands import map_integrand_dags -from ufl.constantvalue import Zero -from ufl.tensors import as_vector from ufl.argument import Argument +from ufl.constantvalue import Zero +from ufl.corealg.multifunction import MultiFunction from ufl.functionspace import FunctionSpace +from ufl.tensors import as_vector class FormSplitter(MultiFunction): diff --git a/ufl/algorithms/formtransformations.py b/ufl/algorithms/formtransformations.py index 6a473c71a..58d168c14 100644 --- a/ufl/algorithms/formtransformations.py +++ b/ufl/algorithms/formtransformations.py @@ -13,17 +13,16 @@ import warnings from logging import debug -# All classes: -from ufl.core.expr import ufl_err_str -from ufl.argument import Argument -from ufl.coefficient import Coefficient -from ufl.constantvalue import Zero from ufl.algebra import Conj - # Other algorithms: from ufl.algorithms.map_integrands import map_integrands -from ufl.algorithms.transformer import Transformer from ufl.algorithms.replace import replace +from ufl.algorithms.transformer import Transformer +from ufl.argument import Argument +from ufl.coefficient import Coefficient +from ufl.constantvalue import Zero +# All classes: +from ufl.core.expr import ufl_err_str # FIXME: Don't use this below, it makes partextracter more expensive than necessary diff --git a/ufl/algorithms/map_integrands.py b/ufl/algorithms/map_integrands.py index 250360f83..55266dfb9 100644 --- a/ufl/algorithms/map_integrands.py +++ b/ufl/algorithms/map_integrands.py @@ -10,13 +10,13 @@ # as part of a careful refactoring process, and this file depends on ufl.form # which drags in a lot of stuff. -from ufl.core.expr import Expr -from ufl.corealg.map_dag import map_expr_dag -from ufl.integral import Integral -from ufl.form import Form, BaseForm, FormSum, ZeroBaseForm from ufl.action import Action from ufl.adjoint import Adjoint from ufl.constantvalue import Zero +from ufl.core.expr import Expr +from ufl.corealg.map_dag import map_expr_dag +from ufl.form import BaseForm, Form, FormSum, ZeroBaseForm +from ufl.integral import Integral def map_integrands(function, form, only_integral_type=None): diff --git a/ufl/algorithms/remove_complex_nodes.py b/ufl/algorithms/remove_complex_nodes.py index 12a5b34e1..7d97a5731 100644 --- a/ufl/algorithms/remove_complex_nodes.py +++ b/ufl/algorithms/remove_complex_nodes.py @@ -1,8 +1,8 @@ """Algorithm for removing conj, real, and imag nodes from a form for when the user is in 'real mode'.""" -from ufl.corealg.multifunction import MultiFunction -from ufl.constantvalue import ComplexValue from ufl.algorithms.map_integrands import map_integrand_dags +from ufl.constantvalue import ComplexValue +from ufl.corealg.multifunction import MultiFunction class ComplexNodeRemoval(MultiFunction): diff --git a/ufl/algorithms/renumbering.py b/ufl/algorithms/renumbering.py index 2f3d94a21..303457dd6 100644 --- a/ufl/algorithms/renumbering.py +++ b/ufl/algorithms/renumbering.py @@ -5,11 +5,11 @@ # # SPDX-License-Identifier: LGPL-3.0-or-later -from ufl.core.expr import Expr -from ufl.core.multiindex import Index, FixedIndex, MultiIndex -from ufl.variable import Label, Variable from ufl.algorithms.transformer import ReuseTransformer, apply_transformer from ufl.classes import Zero +from ufl.core.expr import Expr +from ufl.core.multiindex import FixedIndex, Index, MultiIndex +from ufl.variable import Label, Variable class VariableRenumberingTransformer(ReuseTransformer): diff --git a/ufl/algorithms/replace.py b/ufl/algorithms/replace.py index fb62095cd..e96ecc577 100644 --- a/ufl/algorithms/replace.py +++ b/ufl/algorithms/replace.py @@ -8,11 +8,13 @@ # # Modified by Anders Logg, 2009-2010 -from ufl.classes import CoefficientDerivative, Interpolate, ExternalOperator, Form +from ufl.algorithms.analysis import has_exact_type +from ufl.algorithms.map_integrands import map_integrand_dags +from ufl.classes import CoefficientDerivative, Form from ufl.constantvalue import as_ufl +from ufl.core.external_operator import ExternalOperator +from ufl.core.interpolate import Interpolate from ufl.corealg.multifunction import MultiFunction -from ufl.algorithms.map_integrands import map_integrand_dags -from ufl.algorithms.analysis import has_exact_type class Replacer(MultiFunction): diff --git a/ufl/algorithms/replace_derivative_nodes.py b/ufl/algorithms/replace_derivative_nodes.py index d779a790f..9c822fafb 100644 --- a/ufl/algorithms/replace_derivative_nodes.py +++ b/ufl/algorithms/replace_derivative_nodes.py @@ -1,11 +1,11 @@ """Algorithm for replacing derivative nodes in a BaseForm or Expr.""" import ufl -from ufl.corealg.multifunction import MultiFunction -from ufl.algorithms.map_integrands import map_integrand_dags from ufl.algorithms.analysis import extract_arguments -from ufl.tensors import ListTensor +from ufl.algorithms.map_integrands import map_integrand_dags from ufl.constantvalue import as_ufl +from ufl.corealg.multifunction import MultiFunction +from ufl.tensors import ListTensor class DerivativeNodeReplacer(MultiFunction): diff --git a/ufl/algorithms/signature.py b/ufl/algorithms/signature.py index da3c516dc..7f9dca8b8 100644 --- a/ufl/algorithms/signature.py +++ b/ufl/algorithms/signature.py @@ -6,13 +6,11 @@ # SPDX-License-Identifier: LGPL-3.0-or-later import hashlib -from ufl.classes import (Label, - Index, MultiIndex, - Coefficient, Argument, - GeometricQuantity, ConstantValue, Constant, - ExprList, ExprMapping) -from ufl.corealg.traversal import traverse_unique_terminals, unique_post_traversal + from ufl.algorithms.domain_analysis import canonicalize_metadata +from ufl.classes import (Argument, Coefficient, Constant, ConstantValue, ExprList, ExprMapping, GeometricQuantity, + Index, Label, MultiIndex) +from ufl.corealg.traversal import traverse_unique_terminals, unique_post_traversal def compute_multiindex_hashdata(expr, index_numbering): diff --git a/ufl/algorithms/strip_terminal_data.py b/ufl/algorithms/strip_terminal_data.py index 04e48336d..4909febe4 100644 --- a/ufl/algorithms/strip_terminal_data.py +++ b/ufl/algorithms/strip_terminal_data.py @@ -3,11 +3,9 @@ In the stripped version, any data-carrying objects have been extracted to a mapping. """ -from ufl.classes import Form, Integral -from ufl.classes import Argument, Coefficient, Constant -from ufl.classes import FunctionSpace, TensorProductFunctionSpace, MixedFunctionSpace -from ufl.classes import Mesh, MeshView, TensorProductMesh from ufl.algorithms.replace import replace +from ufl.classes import (Argument, Coefficient, Constant, Form, FunctionSpace, Integral, Mesh, MeshView, + MixedFunctionSpace, TensorProductFunctionSpace) from ufl.corealg.map_dag import map_expr_dag from ufl.corealg.multifunction import MultiFunction @@ -123,8 +121,5 @@ def strip_domain(domain): elif isinstance(domain, MeshView): return MeshView(strip_domain(domain.ufl_mesh()), domain.topological_dimension(), domain.ufl_id()) - elif isinstance(domain, TensorProductMesh): - meshes = [strip_domain(mesh) for mesh in domain.ufl_meshes()] - return TensorProductMesh(meshes, domain.ufl_id()) else: raise NotImplementedError(f"{type(domain)} cannot be stripped") diff --git a/ufl/algorithms/traversal.py b/ufl/algorithms/traversal.py index 7a5c39918..78a0f1b2f 100644 --- a/ufl/algorithms/traversal.py +++ b/ufl/algorithms/traversal.py @@ -8,14 +8,12 @@ # # Modified by Anders Logg, 2008 -from ufl.core.expr import Expr -from ufl.integral import Integral from ufl.action import Action from ufl.adjoint import Adjoint -from ufl.form import Form, FormSum, BaseForm - +from ufl.core.expr import Expr +from ufl.form import BaseForm, Form, FormSum +from ufl.integral import Integral -# --- Traversal utilities --- def iter_expressions(a): """Utility function to handle Form, Integral and any Expr the same way when inspecting expressions. diff --git a/ufl/argument.py b/ufl/argument.py index 876354301..81b94b9ec 100644 --- a/ufl/argument.py +++ b/ufl/argument.py @@ -14,17 +14,14 @@ # Modified by Cecile Daversin-Catty, 2018. # Modified by Ignacia Fierro-Piccardo 2023. -import warnings import numbers -from ufl.core.ufl_type import ufl_type from ufl.core.terminal import FormArgument -from ufl.split_functions import split -from ufl.finiteelement import FiniteElementBase -from ufl.domain import default_domain +from ufl.core.ufl_type import ufl_type +from ufl.duals import is_dual, is_primal from ufl.form import BaseForm -from ufl.functionspace import AbstractFunctionSpace, FunctionSpace, MixedFunctionSpace -from ufl.duals import is_primal, is_dual +from ufl.functionspace import AbstractFunctionSpace, MixedFunctionSpace +from ufl.split_functions import split # Export list for ufl.classes (TODO: not actually classes: drop? these are in ufl.*) __all_classes__ = ["TestFunction", "TrialFunction", "TestFunctions", "TrialFunctions"] @@ -44,19 +41,11 @@ def __getnewargs__(self): def __init__(self, function_space, number, part=None): """initialise.""" - if isinstance(function_space, FiniteElementBase): - # For legacy support for UFL files using cells, we map the cell to - # the default Mesh - element = function_space - domain = default_domain(element.cell()) - function_space = FunctionSpace(domain, element) - warnings.warn("The use of FiniteElement as an input to Argument will be deprecated by December 2023. " - "Please, use FunctionSpace instead", FutureWarning) - elif not isinstance(function_space, AbstractFunctionSpace): - raise ValueError("Expecting a FunctionSpace or FiniteElement.") + if not isinstance(function_space, AbstractFunctionSpace): + raise ValueError("Expecting a FunctionSpace.") self._ufl_function_space = function_space - self._ufl_shape = function_space.ufl_element().value_shape() + self._ufl_shape = function_space.ufl_element().value_shape if not isinstance(number, numbers.Integral): raise ValueError(f"Expecting an int for number, not {number}") diff --git a/ufl/averaging.py b/ufl/averaging.py index dc6801cc6..899cc3ca8 100644 --- a/ufl/averaging.py +++ b/ufl/averaging.py @@ -6,9 +6,9 @@ # # SPDX-License-Identifier: LGPL-3.0-or-later +from ufl.constantvalue import ConstantValue from ufl.core.operator import Operator from ufl.core.ufl_type import ufl_type -from ufl.constantvalue import ConstantValue @ufl_type(inherit_shape_from_operand=0, diff --git a/ufl/cell.py b/ufl/cell.py index 85a556474..e7df446fe 100644 --- a/ufl/cell.py +++ b/ufl/cell.py @@ -7,14 +7,14 @@ # SPDX-License-Identifier: LGPL-3.0-or-later from __future__ import annotations + import functools import numbers import typing import weakref - -from ufl.core.ufl_type import UFLObject from abc import abstractmethod +from ufl.core.ufl_type import UFLObject __all_classes__ = ["AbstractCell", "Cell", "TensorProductCell"] diff --git a/ufl/checks.py b/ufl/checks.py index cd47a272c..5b4dc8ce1 100644 --- a/ufl/checks.py +++ b/ufl/checks.py @@ -9,7 +9,10 @@ # Modified by Anders Logg, 2008-2009 from ufl.core.expr import Expr +from ufl.core.terminal import FormArgument from ufl.corealg.traversal import traverse_unique_terminals +from ufl.geometry import GeometricQuantity +from ufl.sobolevspace import H1 def is_python_scalar(expression): @@ -30,32 +33,23 @@ def is_true_ufl_scalar(expression): def is_cellwise_constant(expr): """Return whether expression is constant over a single cell.""" # TODO: Implement more accurately considering e.g. derivatives? - return all(t.is_cellwise_constant() for t in traverse_unique_terminals(expr)) + return all(e.is_cellwise_constant() for e in traverse_unique_terminals(expr)) -def is_globally_constant(expr): - """Check if an expression is globally constant. +def is_scalar_constant_expression(expr): + """Check if an expression is a globally constant scalar expression.""" + if is_python_scalar(expr): + return True + if expr.ufl_shape: + return False - This includes spatially independent constant coefficients that - are not known before assembly time. - """ # TODO: This does not consider gradients of coefficients, so false # negatives are possible. - # from ufl.argument import Argument - # from ufl.coefficient import Coefficient - from ufl.core.terminal import FormArgument - from ufl.geometry import GeometricQuantity for e in traverse_unique_terminals(expr): # Return False if any single terminal is not constant - if e._ufl_is_literal_: - # Accept literals first, they are the most common - # terminals - continue - elif isinstance(e, FormArgument): - # Accept only Real valued Arguments and Coefficients - if e.ufl_element()._is_globally_constant(): - continue - else: + if isinstance(e, FormArgument): + # Accept only globally constant Arguments and Coefficients + if e.ufl_element().embedded_superdegree > 0 or e.ufl_element() not in H1: return False elif isinstance(e, GeometricQuantity): # Reject all geometric quantities, they all vary over @@ -64,12 +58,3 @@ def is_globally_constant(expr): # All terminals passed constant check return True - - -def is_scalar_constant_expression(expr): - """Check if an expression is a globally constant scalar expression.""" - if is_python_scalar(expr): - return True - if expr.ufl_shape: - return False - return is_globally_constant(expr) diff --git a/ufl/classes.py b/ufl/classes.py index 64d12284e..b65c802d4 100644 --- a/ufl/classes.py +++ b/ufl/classes.py @@ -19,42 +19,42 @@ # This will be populated part by part below __all__ = [] - # Import all submodules, triggering execution of the ufl_type class # decorator for each Expr class. -# Base classes of Expr type hierarchy -import ufl.core.expr -import ufl.core.terminal -import ufl.core.operator - -# Terminal types -import ufl.constantvalue +import ufl.algebra import ufl.argument +import ufl.averaging +import ufl.cell import ufl.coefficient +import ufl.conditional +import ufl.constantvalue +import ufl.core.expr +import ufl.core.multiindex +import ufl.core.operator +import ufl.core.terminal +import ufl.differentiation +import ufl.domain +import ufl.equation +import ufl.exprcontainers +import ufl.finiteelement +import ufl.form +import ufl.functionspace import ufl.geometry - -# Operator types -import ufl.averaging import ufl.indexed import ufl.indexsum -import ufl.variable -import ufl.tensors -import ufl.algebra -import ufl.tensoralgebra +import ufl.integral import ufl.mathfunctions -import ufl.differentiation -import ufl.conditional -import ufl.restriction -import ufl.exprcontainers +import ufl.measure +import ufl.pullback import ufl.referencevalue - -# Make sure we import exproperators which attaches special functions -# to Expr +import ufl.restriction +import ufl.sobolevspace +import ufl.tensoralgebra +import ufl.tensors +import ufl.variable from ufl import exproperators as __exproperators -# Make sure to import modules with new Expr subclasses here! - # Collect all classes in sets automatically classified by some properties all_ufl_classes = set(ufl.core.expr.Expr._ufl_all_classes_) abstract_classes = set(c for c in all_ufl_classes if c._ufl_is_abstract_) @@ -96,32 +96,15 @@ def populate_namespace_with_module_classes(mod, loc): return names -import ufl.cell # noqa E401 __all__ += populate_namespace_with_module_classes(ufl.cell, locals()) - -import ufl.finiteelement # noqa E401 __all__ += populate_namespace_with_module_classes(ufl.finiteelement, locals()) - -import ufl.domain # noqa E401 __all__ += populate_namespace_with_module_classes(ufl.domain, locals()) - -import ufl.functionspace # noqa E401 __all__ += populate_namespace_with_module_classes(ufl.functionspace, locals()) - -import ufl.core.multiindex # noqa E401 __all__ += populate_namespace_with_module_classes(ufl.core.multiindex, locals()) - -import ufl.argument # noqa E401 __all__ += populate_namespace_with_module_classes(ufl.argument, locals()) - -import ufl.measure # noqa E401 __all__ += populate_namespace_with_module_classes(ufl.measure, locals()) - -import ufl.integral # noqa E401 __all__ += populate_namespace_with_module_classes(ufl.integral, locals()) - -import ufl.form # noqa E401 __all__ += populate_namespace_with_module_classes(ufl.form, locals()) - -import ufl.equation # noqa E401 __all__ += populate_namespace_with_module_classes(ufl.equation, locals()) +__all__ += populate_namespace_with_module_classes(ufl.pullback, locals()) +__all__ += populate_namespace_with_module_classes(ufl.sobolevspace, locals()) diff --git a/ufl/coefficient.py b/ufl/coefficient.py index c5ba8dac7..584d06c3f 100644 --- a/ufl/coefficient.py +++ b/ufl/coefficient.py @@ -10,19 +10,15 @@ # Modified by Massimiliano Leoni, 2016. # Modified by Cecile Daversin-Catty, 2018. # Modified by Ignacia Fierro-Piccardo 2023. -import warnings -from ufl.core.ufl_type import ufl_type -from ufl.core.terminal import FormArgument from ufl.argument import Argument -from ufl.finiteelement import FiniteElementBase -from ufl.domain import default_domain -from ufl.functionspace import AbstractFunctionSpace, FunctionSpace, MixedFunctionSpace +from ufl.core.terminal import FormArgument +from ufl.core.ufl_type import ufl_type +from ufl.duals import is_dual, is_primal from ufl.form import BaseForm +from ufl.functionspace import AbstractFunctionSpace, MixedFunctionSpace from ufl.split_functions import split from ufl.utils.counted import Counted -from ufl.duals import is_primal, is_dual - # --- The Coefficient class represents a coefficient in a form --- @@ -45,19 +41,11 @@ def __init__(self, function_space, count=None): """Initalise.""" Counted.__init__(self, count, Coefficient) - if isinstance(function_space, FiniteElementBase): - # For legacy support for .ufl files using cells, we map - # the cell to The Default Mesh - element = function_space - domain = default_domain(element.cell()) - function_space = FunctionSpace(domain, element) - warnings.warn("The use of FiniteElement as an input to Coefficient will be deprecated by December 2023. " - "Please, use FunctionSpace instead", FutureWarning) - elif not isinstance(function_space, AbstractFunctionSpace): - raise ValueError("Expecting a FunctionSpace or FiniteElement.") + if not isinstance(function_space, AbstractFunctionSpace): + raise ValueError("Expecting a FunctionSpace.") self._ufl_function_space = function_space - self._ufl_shape = function_space.ufl_element().value_shape() + self._ufl_shape = function_space.ufl_element().value_shape self._repr = "BaseCoefficient(%s, %s)" % ( repr(self._ufl_function_space), repr(self._count)) diff --git a/ufl/compound_expressions.py b/ufl/compound_expressions.py index 246ce765f..6b9c63402 100644 --- a/ufl/compound_expressions.py +++ b/ufl/compound_expressions.py @@ -9,10 +9,10 @@ import warnings -from ufl.core.multiindex import indices, Index -from ufl.tensors import as_tensor, as_matrix, as_vector -from ufl.operators import sqrt from ufl.constantvalue import Zero, zero +from ufl.core.multiindex import Index, indices +from ufl.operators import sqrt +from ufl.tensors import as_matrix, as_tensor, as_vector # Note: To avoid typing errors, the expressions for cofactor and # deviatoric parts below were created with the script diff --git a/ufl/conditional.py b/ufl/conditional.py index 422be6ff2..b9f9bda16 100644 --- a/ufl/conditional.py +++ b/ufl/conditional.py @@ -7,13 +7,13 @@ import warnings +from ufl.checks import is_true_ufl_scalar +from ufl.constantvalue import as_ufl from ufl.core.expr import ufl_err_str -from ufl.core.ufl_type import ufl_type from ufl.core.operator import Operator -from ufl.constantvalue import as_ufl -from ufl.precedence import parstr +from ufl.core.ufl_type import ufl_type from ufl.exprequals import expr_equals -from ufl.checks import is_true_ufl_scalar +from ufl.precedence import parstr # --- Condition classes --- diff --git a/ufl/constant.py b/ufl/constant.py index 2edb21c8e..74eb81932 100644 --- a/ufl/constant.py +++ b/ufl/constant.py @@ -6,8 +6,8 @@ # # SPDX-License-Identifier: LGPL-3.0-or-later -from ufl.core.ufl_type import ufl_type from ufl.core.terminal import Terminal +from ufl.core.ufl_type import ufl_type from ufl.domain import as_domain from ufl.utils.counted import Counted diff --git a/ufl/constantvalue.py b/ufl/constantvalue.py index 64a44276a..82d9ed84e 100644 --- a/ufl/constantvalue.py +++ b/ufl/constantvalue.py @@ -12,15 +12,13 @@ from math import atan2 import ufl +# --- Helper functions imported here for compatibility--- +from ufl.checks import is_python_scalar, is_true_ufl_scalar, is_ufl_scalar # noqa: F401 from ufl.core.expr import Expr +from ufl.core.multiindex import FixedIndex, Index from ufl.core.terminal import Terminal -from ufl.core.multiindex import Index, FixedIndex from ufl.core.ufl_type import ufl_type -# --- Helper functions imported here for compatibility--- -from ufl.checks import is_python_scalar, is_ufl_scalar, is_true_ufl_scalar # noqa: F401 - - # Precision for float formatting precision = None diff --git a/ufl/core/base_form_operator.py b/ufl/core/base_form_operator.py index e2aa2475d..a136244cb 100644 --- a/ufl/core/base_form_operator.py +++ b/ufl/core/base_form_operator.py @@ -15,10 +15,10 @@ from collections import OrderedDict from ufl.argument import Argument, Coargument +from ufl.constantvalue import as_ufl from ufl.core.operator import Operator -from ufl.form import BaseForm from ufl.core.ufl_type import ufl_type -from ufl.constantvalue import as_ufl +from ufl.form import BaseForm from ufl.functionspace import AbstractFunctionSpace from ufl.utils.counted import Counted diff --git a/ufl/core/expr.py b/ufl/core/expr.py index 9f1851643..e6b2ce3d8 100644 --- a/ufl/core/expr.py +++ b/ufl/core/expr.py @@ -21,8 +21,6 @@ from ufl.core.ufl_type import UFLType, update_ufl_type_attributes -# --- The base object for all UFL expression tree nodes --- - class Expr(object, metaclass=UFLType): """Base class for all UFL expression types. @@ -208,10 +206,6 @@ def __init__(self): # "__str__", # "__repr__", - - # TODO: Add checks for methods/properties of terminals only? - # Required for terminals: - # "is_cellwise_constant", # TODO: Rename to ufl_is_cellwise_constant? ) # --- Global variables for collecting all types --- diff --git a/ufl/core/interpolate.py b/ufl/core/interpolate.py index e751c66c9..86198c680 100644 --- a/ufl/core/interpolate.py +++ b/ufl/core/interpolate.py @@ -8,14 +8,14 @@ # # Modified by Nacime Bouziani, 2021-2022 -from ufl.core.ufl_type import ufl_type -from ufl.constantvalue import as_ufl -from ufl.functionspace import AbstractFunctionSpace -from ufl.argument import Coargument, Argument +from ufl.argument import Argument, Coargument from ufl.coefficient import Cofunction -from ufl.form import Form +from ufl.constantvalue import as_ufl from ufl.core.base_form_operator import BaseFormOperator +from ufl.core.ufl_type import ufl_type from ufl.duals import is_dual +from ufl.form import Form +from ufl.functionspace import AbstractFunctionSpace @ufl_type(num_ops="varying", is_differential=True) diff --git a/ufl/core/multiindex.py b/ufl/core/multiindex.py index ca66a619a..81409ce7d 100644 --- a/ufl/core/multiindex.py +++ b/ufl/core/multiindex.py @@ -9,9 +9,9 @@ # Modified by Massimiliano Leoni, 2016. -from ufl.utils.counted import Counted -from ufl.core.ufl_type import ufl_type from ufl.core.terminal import Terminal +from ufl.core.ufl_type import ufl_type +from ufl.utils.counted import Counted # Export list for ufl.classes __all_classes__ = ["IndexBase", "FixedIndex", "Index"] diff --git a/ufl/core/operator.py b/ufl/core/operator.py index 7a8de1cc1..c2e31b87e 100644 --- a/ufl/core/operator.py +++ b/ufl/core/operator.py @@ -12,8 +12,6 @@ from ufl.core.ufl_type import ufl_type -# --- Base class for operator objects --- - @ufl_type(is_abstract=True, is_terminal=False) class Operator(Expr): """Base class for all operators, i.e. non-terminal expression types.""" diff --git a/ufl/core/ufl_type.py b/ufl/core/ufl_type.py index b83d30094..b3bfa0bd6 100644 --- a/ufl/core/ufl_type.py +++ b/ufl/core/ufl_type.py @@ -9,14 +9,15 @@ # Modified by Matthew Scroggs, 2023 from __future__ import annotations + import typing import warnings - -from ufl.core.compute_expr_hash import compute_expr_hash -from ufl.utils.formatting import camel2underscore from abc import ABC, abstractmethod + # Avoid circular import import ufl.core as core +from ufl.core.compute_expr_hash import compute_expr_hash +from ufl.utils.formatting import camel2underscore class UFLObject(ABC): diff --git a/ufl/corealg/map_dag.py b/ufl/corealg/map_dag.py index 946a86ff8..f299130c1 100644 --- a/ufl/corealg/map_dag.py +++ b/ufl/corealg/map_dag.py @@ -8,8 +8,8 @@ # Modified by Massimiliano Leoni, 2016 from ufl.core.expr import Expr -from ufl.corealg.traversal import unique_post_traversal, cutoff_unique_post_traversal from ufl.corealg.multifunction import MultiFunction +from ufl.corealg.traversal import cutoff_unique_post_traversal, unique_post_traversal def map_expr_dag(function, expression, compress=True, vcache=None, rcache=None): diff --git a/ufl/differentiation.py b/ufl/differentiation.py index e3820603a..34c003b85 100644 --- a/ufl/differentiation.py +++ b/ufl/differentiation.py @@ -6,13 +6,13 @@ # # SPDX-License-Identifier: LGPL-3.0-or-later +from ufl.argument import Argument, Coargument from ufl.checks import is_cellwise_constant from ufl.coefficient import Coefficient -from ufl.argument import Argument, Coargument from ufl.constantvalue import Zero +from ufl.core.base_form_operator import BaseFormOperator from ufl.core.expr import Expr from ufl.core.operator import Operator -from ufl.core.base_form_operator import BaseFormOperator from ufl.core.terminal import Terminal from ufl.core.ufl_type import ufl_type from ufl.domain import extract_unique_domain, find_geometric_dimension @@ -101,7 +101,7 @@ def __init__(self, base_form, coefficients, arguments, def _analyze_form_arguments(self): """Collect the arguments of the corresponding BaseForm.""" - from ufl.algorithms.analysis import extract_type, extract_coefficients + from ufl.algorithms.analysis import extract_coefficients, extract_type base_form, _, arguments, _ = self.ufl_operands def arg_type(x): diff --git a/ufl/domain.py b/ufl/domain.py index 59de4f121..ecdea376a 100644 --- a/ufl/domain.py +++ b/ufl/domain.py @@ -9,14 +9,14 @@ import numbers import warnings -from ufl.cell import AbstractCell, TensorProductCell, as_cell +from ufl.cell import AbstractCell from ufl.core.ufl_id import attach_ufl_id from ufl.core.ufl_type import attach_operators_from_hash_data from ufl.corealg.traversal import traverse_unique_terminals -from ufl.finiteelement.tensorproductelement import TensorProductElement +from ufl.sobolevspace import H1 # Export list for ufl.classes -__all_classes__ = ["AbstractDomain", "Mesh", "MeshView", "TensorProductMesh"] +__all_classes__ = ["AbstractDomain", "Mesh", "MeshView"] class AbstractDomain(object): @@ -68,22 +68,15 @@ def __init__(self, coordinate_element, ufl_id=None, cargo=None): # No longer accepting coordinates provided as a Coefficient from ufl.coefficient import Coefficient - if isinstance(coordinate_element, Coefficient): + if isinstance(coordinate_element, (Coefficient, AbstractCell)): raise ValueError("Expecting a coordinate element in the ufl.Mesh construct.") - # Accept a cell in place of an element for brevity Mesh(triangle) - if isinstance(coordinate_element, AbstractCell): - from ufl.finiteelement import VectorElement - cell = coordinate_element - coordinate_element = VectorElement("Lagrange", cell, 1, - dim=cell.geometric_dimension()) - # Store coordinate element self._ufl_coordinate_element = coordinate_element # Derive dimensions from element - gdim, = coordinate_element.value_shape() - tdim = coordinate_element.cell().topological_dimension() + gdim, = coordinate_element.value_shape + tdim = coordinate_element.cell.topological_dimension() AbstractDomain.__init__(self, tdim, gdim) def ufl_cargo(self): @@ -96,11 +89,12 @@ def ufl_coordinate_element(self): def ufl_cell(self): """Get the cell.""" - return self._ufl_coordinate_element.cell() + return self._ufl_coordinate_element.cell def is_piecewise_linear_simplex_domain(self): """Check if the domain is a piecewise linear simplex.""" - return (self._ufl_coordinate_element.degree() == 1) and self.ufl_cell().is_simplex() + ce = self._ufl_coordinate_element + return ce.embedded_superdegree <= 1 and ce in H1 and self.ufl_cell().is_simplex() def __repr__(self): """Representation.""" @@ -142,8 +136,8 @@ def __init__(self, mesh, topological_dimension, ufl_id=None): # Derive dimensions from element coordinate_element = mesh.ufl_coordinate_element() - gdim, = coordinate_element.value_shape() - tdim = coordinate_element.cell().topological_dimension() + gdim, = coordinate_element.value_shape + tdim = coordinate_element.cell.topological_dimension() AbstractDomain.__init__(self, tdim, gdim) def ufl_mesh(self): @@ -187,107 +181,6 @@ def _ufl_sort_key_(self): "MeshView", typespecific) -@attach_operators_from_hash_data -@attach_ufl_id -class TensorProductMesh(AbstractDomain): - """Symbolic representation of a mesh.""" - - def __init__(self, meshes, ufl_id=None): - """Initialise.""" - self._ufl_id = self._init_ufl_id(ufl_id) - - # TODO: Error checking of meshes - self._ufl_meshes = meshes - - # TODO: Is this what we want to do? - # Build cell from mesh cells - self._ufl_cell = TensorProductCell(*[mesh.ufl_cell() for mesh in meshes]) - - # TODO: Is this what we want to do? - # Build coordinate element from mesh coordinate elements - self._ufl_coordinate_element = TensorProductElement([mesh.ufl_coordinate_element() for mesh in meshes]) - - # Derive dimensions from meshes - gdim = sum(mesh.geometric_dimension() for mesh in meshes) - tdim = sum(mesh.topological_dimension() for mesh in meshes) - - AbstractDomain.__init__(self, tdim, gdim) - - def ufl_coordinate_element(self): - """Get the coordinate element.""" - return self._ufl_coordinate_element - - def ufl_cell(self): - """Get the cell.""" - return self._ufl_cell - - def ufl_meshes(self): - """Get the UFL meshes.""" - return self._ufl_meshes - - def is_piecewise_linear_simplex_domain(self): - """Check if the domain is a piecewise linear simplex.""" - return False # TODO: Any cases this is True - - def __repr__(self): - """Representation.""" - r = "TensorProductMesh(%s, %s)" % (repr(self._ufl_meshes), repr(self._ufl_id)) - return r - - def __str__(self): - """Format as a string.""" - return "" % ( - self._ufl_id, self._ufl_meshes) - - def _ufl_hash_data_(self): - """UFL hash data.""" - return (self._ufl_id,) + tuple(mesh._ufl_hash_data_() for mesh in self._ufl_meshes) - - def _ufl_signature_data_(self, renumbering): - """UFL signature data.""" - return ("TensorProductMesh",) + tuple(mesh._ufl_signature_data_(renumbering) for mesh in self._ufl_meshes) - - # NB! Dropped __lt__ here, don't want users to write 'mesh1 < - # mesh2'. - def _ufl_sort_key_(self): - """UFL sort key.""" - typespecific = (self._ufl_id, tuple(mesh._ufl_sort_key_() for mesh in self._ufl_meshes)) - return (self.geometric_dimension(), self.topological_dimension(), - "TensorProductMesh", typespecific) - - -# --- Utility conversion functions - -def affine_mesh(cell, ufl_id=None): - """Create a Mesh over a given cell type with an affine geometric parameterization.""" - from ufl.finiteelement import VectorElement - cell = as_cell(cell) - gdim = cell.geometric_dimension() - degree = 1 - coordinate_element = VectorElement("Lagrange", cell, degree, dim=gdim) - return Mesh(coordinate_element, ufl_id=ufl_id) - - -_default_domains = {} - - -def default_domain(cell): - """Create a singular default Mesh from a cell, always returning the same Mesh object for the same cell.""" - global _default_domains - - warnings.warn("default_domain is deprecated.", FutureWarning) - - assert isinstance(cell, AbstractCell) - domain = _default_domains.get(cell) - if domain is None: - # Create one and only one affine Mesh with a negative ufl_id - # to avoid id collision - ufl_id = -(len(_default_domains) + 1) - domain = affine_mesh(cell, ufl_id=ufl_id) - _default_domains[cell] = domain - return domain - - def as_domain(domain): """Convert any valid object to an AbstractDomain type.""" if isinstance(domain, AbstractDomain): @@ -297,15 +190,7 @@ def as_domain(domain): try: return extract_unique_domain(domain) except AttributeError: - try: - # Legacy UFL files - # TODO: Make this conversion in the relevant constructors - # closer to the user interface? - # TODO: Make this configurable to be an error from the dolfin side? - cell = as_cell(domain) - return default_domain(cell) - except ValueError: - return domain.ufl_domain() + return domain.ufl_domain() def sort_domains(domains): @@ -388,7 +273,7 @@ def find_geometric_dimension(expr): if hasattr(t, "ufl_element"): element = t.ufl_element() if element is not None: - cell = element.cell() + cell = element.cell if cell is not None: gdims.add(cell.geometric_dimension()) diff --git a/ufl/exprcontainers.py b/ufl/exprcontainers.py index 60ae13f6f..8da36299c 100644 --- a/ufl/exprcontainers.py +++ b/ufl/exprcontainers.py @@ -5,15 +5,15 @@ # # SPDX-License-Identifier: LGPL-3.0-or-later +from ufl.argument import Coargument +from ufl.coefficient import Cofunction from ufl.core.expr import Expr from ufl.core.operator import Operator from ufl.core.ufl_type import ufl_type -from ufl.coefficient import Cofunction -from ufl.argument import Coargument - # --- Non-tensor types --- + @ufl_type(num_ops="varying") class ExprList(Operator): """List of Expr objects. For internal use, never to be created by end users.""" diff --git a/ufl/exproperators.py b/ufl/exproperators.py index f0b38ec79..60eb87566 100644 --- a/ufl/exproperators.py +++ b/ufl/exproperators.py @@ -14,25 +14,23 @@ import numbers -from ufl.utils.stacks import StackDict -from ufl.core.expr import Expr +from ufl.algebra import Abs, Division, Power, Product, Sum +from ufl.conditional import GE, GT, LE, LT from ufl.constantvalue import Zero, as_ufl -from ufl.algebra import Sum, Product, Division, Power, Abs -from ufl.tensoralgebra import Transposed, Inner -from ufl.core.multiindex import MultiIndex, Index, indices -from ufl.indexed import Indexed -from ufl.indexsum import IndexSum -from ufl.tensors import as_tensor, ComponentTensor -from ufl.restriction import PositiveRestricted, NegativeRestricted +from ufl.core.expr import Expr +from ufl.core.multiindex import Index, MultiIndex, indices from ufl.differentiation import Grad -from ufl.index_combination_utils import create_slice_indices, merge_overlapping_indices - from ufl.exprequals import expr_equals +from ufl.index_combination_utils import create_slice_indices, merge_overlapping_indices +from ufl.indexed import Indexed +from ufl.indexsum import IndexSum +from ufl.restriction import NegativeRestricted, PositiveRestricted +from ufl.tensoralgebra import Inner, Transposed +from ufl.tensors import ComponentTensor, as_tensor +from ufl.utils.stacks import StackDict # --- Boolean operators --- -from ufl.conditional import LE, GE, LT, GT - def _le(left, right): """A boolean expresion (left <= right) for use with conditional.""" diff --git a/ufl/finiteelement.py b/ufl/finiteelement.py new file mode 100644 index 000000000..9a4e43d6e --- /dev/null +++ b/ufl/finiteelement.py @@ -0,0 +1,368 @@ +"""This module defines the UFL finite element classes.""" +# Copyright (C) 2008-2016 Martin Sandve Alnæs +# +# This file is part of UFL (https://www.fenicsproject.org) +# +# SPDX-License-Identifier: LGPL-3.0-or-later +# +# Modified by Kristian B. Oelgaard +# Modified by Marie E. Rognes 2010, 2012 +# Modified by Massimiliano Leoni, 2016 +# Modified by Matthew Scroggs, 2023 + +from __future__ import annotations + +import abc as _abc +import typing as _typing + +import numpy as np + +from ufl.cell import Cell as _Cell +from ufl.pullback import AbstractPullback as _AbstractPullback +from ufl.pullback import IdentityPullback as _IdentityPullback +from ufl.pullback import MixedPullback as _MixedPullback +from ufl.pullback import SymmetricPullback as _SymmetricPullback +from ufl.sobolevspace import SobolevSpace as _SobolevSpace +from ufl.utils.sequences import product + +__all_classes__ = ["AbstractFiniteElement", "FiniteElement", "MixedElement", "SymmetricElement"] + + +class AbstractFiniteElement(_abc.ABC): + """Base class for all finite elements. + + To make your element library compatible with UFL, you should make a subclass of AbstractFiniteElement + and provide implementions of all the abstract methods and properties. All methods and properties + that are not marked as abstract are implemented here and should not need to be overwritten in your + subclass. + + An example of how the methods in your subclass could be implemented can be found in Basix; see + https://github.com/FEniCS/basix/blob/main/python/basix/ufl.py + """ + + @_abc.abstractmethod + def __repr__(self) -> str: + """Format as string for evaluation as Python object.""" + + @_abc.abstractmethod + def __str__(self) -> str: + """Format as string for nice printing.""" + + @_abc.abstractmethod + def __hash__(self) -> int: + """Return a hash.""" + + @_abc.abstractmethod + def __eq__(self, other: AbstractFiniteElement) -> bool: + """Check if this element is equal to another element.""" + + @_abc.abstractproperty + def sobolev_space(self) -> _SobolevSpace: + """Return the underlying Sobolev space.""" + + @_abc.abstractproperty + def pullback(self) -> _AbstractPullback: + """Return the pullback for this element.""" + + @_abc.abstractproperty + def embedded_superdegree(self) -> _typing.Union[int, None]: + """Return the degree of the minimum degree Lagrange space that spans this element. + + This returns the degree of the lowest degree Lagrange space such that the polynomial + space of the Lagrange space is a superspace of this element's polynomial space. If this + element contains basis functions that are not in any Lagrange space, this function should + return None. + + Note that on a simplex cells, the polynomial space of Lagrange space is a complete polynomial + space, but on other cells this is not true. For example, on quadrilateral cells, the degree 1 + Lagrange space includes the degree 2 polynomial xy. + """ + + @_abc.abstractproperty + def embedded_subdegree(self) -> int: + """Return the degree of the maximum degree Lagrange space that is spanned by this element. + + This returns the degree of the highest degree Lagrange space such that the polynomial + space of the Lagrange space is a subspace of this element's polynomial space. If this + element's polynomial space does not include the constant function, this function should + return -1. + + Note that on a simplex cells, the polynomial space of Lagrange space is a complete polynomial + space, but on other cells this is not true. For example, on quadrilateral cells, the degree 1 + Lagrange space includes the degree 2 polynomial xy. + """ + + @_abc.abstractproperty + def cell(self) -> _Cell: + """Return the cell of the finite element.""" + + @_abc.abstractproperty + def reference_value_shape(self) -> _typing.Tuple[int, ...]: + """Return the shape of the value space on the reference cell.""" + + @_abc.abstractproperty + def sub_elements(self) -> _typing.List: + """Return list of sub-elements. + + This function does not recurse: ie it does not extract the sub-elements + of sub-elements. + """ + + def __ne__(self, other: AbstractFiniteElement) -> bool: + """Check if this element is different to another element.""" + return not self.__eq__(other) + + def is_cellwise_constant(self) -> bool: + """Check whether this element is spatially constant over each cell.""" + return self.embedded_superdegree == 0 + + def _ufl_hash_data_(self) -> str: + """Return UFL hash data.""" + return repr(self) + + def _ufl_signature_data_(self) -> str: + """Return UFL signature data.""" + return repr(self) + + @property + def components(self) -> _typing.Dict[_typing.Tuple[int, ...], int]: + """Get the numbering of the components of the element. + + Returns: + A map from the components of the values on a physical cell (eg (0, 1)) + to flat component numbers on the reference cell (eg 1) + """ + if isinstance(self.pullback, _SymmetricPullback): + return self.pullback._symmetry + + if len(self.sub_elements) == 0: + return {(): 0} + + components = {} + offset = 0 + c_offset = 0 + for e in self.sub_elements: + for i, j in enumerate(np.ndindex(e.value_shape)): + components[(offset + i, )] = c_offset + e.components[j] + c_offset += max(e.components.values()) + 1 + offset += e.value_size + return components + + @property + def value_shape(self) -> _typing.Tuple[int, ...]: + """Return the shape of the value space on the physical domain.""" + return self.pullback.physical_value_shape(self) + + @property + def value_size(self) -> int: + """Return the integer product of the value shape.""" + return product(self.value_shape) + + @property + def reference_value_size(self) -> int: + """Return the integer product of the reference value shape.""" + return product(self.reference_value_shape) + + @property + def num_sub_elements(self) -> int: + """Return number of sub-elements. + + This function does not recurse: ie it does not count the sub-elements of + sub-elements. + """ + return len(self.sub_elements) + + +class FiniteElement(AbstractFiniteElement): + """A directly defined finite element.""" + __slots__ = ("_repr", "_str", "_family", "_cell", "_degree", + "_reference_value_shape", "_pullback", "_sobolev_space", + "_sub_elements", "_subdegree") + + def __init__( + self, family: str, cell: _Cell, degree: int, + reference_value_shape: _typing.Tuple[int, ...], pullback: _AbstractPullback, + sobolev_space: _SobolevSpace, sub_elements=[], + _repr: _typing.Optional[str] = None, _str: _typing.Optional[str] = None, + subdegree: _typing.Optional[int] = None, + ): + """Initialise a finite element. + + This class should only be used for testing + + Args: + family: The family name of the element + cell: The cell on which the element is defined + degree: The polynomial degree of the element + reference_value_shape: The reference value shape of the element + pullback: The pullback to use + sobolev_space: The Sobolev space containing this element + sub_elements: Sub elements of this element + _repr: A string representation of this elements + _str: A string for printing + subdegree: The embedded subdegree of this element + """ + if subdegree is None: + self._subdegree = degree + else: + self._subdegree = subdegree + if _repr is None: + if len(sub_elements) > 0: + self._repr = ( + f"ufl.finiteelement.FiniteElement(\"{family}\", {cell}, {degree}, " + f"{reference_value_shape}, {pullback}, {sobolev_space}, {sub_elements!r})") + else: + self._repr = ( + f"ufl.finiteelement.FiniteElement(\"{family}\", {cell}, {degree}, " + f"{reference_value_shape}, {pullback}, {sobolev_space})") + else: + self._repr = _repr + if _str is None: + self._str = f"<{family}{degree} on a {cell}>" + else: + self._str = _str + self._family = family + self._cell = cell + self._degree = degree + self._reference_value_shape = reference_value_shape + self._pullback = pullback + self._sobolev_space = sobolev_space + self._sub_elements = sub_elements + + def __repr__(self) -> str: + """Format as string for evaluation as Python object.""" + return self._repr + + def __str__(self) -> str: + """Format as string for nice printing.""" + return self._str + + def __hash__(self) -> int: + """Return a hash.""" + return hash(f"{self!r}") + + def __eq__(self, other) -> bool: + """Check if this element is equal to another element.""" + return type(self) is type(other) and repr(self) == repr(other) + + @property + def sobolev_space(self) -> _SobolevSpace: + """Return the underlying Sobolev space.""" + return self._sobolev_space + + @property + def pullback(self) -> _AbstractPullback: + """Return the pullback for this element.""" + return self._pullback + + @property + def embedded_superdegree(self) -> _typing.Union[int, None]: + """Return the degree of the minimum degree Lagrange space that spans this element. + + This returns the degree of the lowest degree Lagrange space such that the polynomial + space of the Lagrange space is a superspace of this element's polynomial space. If this + element contains basis functions that are not in any Lagrange space, this function should + return None. + + Note that on a simplex cells, the polynomial space of Lagrange space is a complete polynomial + space, but on other cells this is not true. For example, on quadrilateral cells, the degree 1 + Lagrange space includes the degree 2 polynomial xy. + """ + return self._degree + + @property + def embedded_subdegree(self) -> int: + """Return the degree of the maximum degree Lagrange space that is spanned by this element. + + This returns the degree of the highest degree Lagrange space such that the polynomial + space of the Lagrange space is a subspace of this element's polynomial space. If this + element's polynomial space does not include the constant function, this function should + return -1. + + Note that on a simplex cells, the polynomial space of Lagrange space is a complete polynomial + space, but on other cells this is not true. For example, on quadrilateral cells, the degree 1 + Lagrange space includes the degree 2 polynomial xy. + """ + return self._subdegree + + @property + def cell(self) -> _Cell: + """Return the cell of the finite element.""" + return self._cell + + @property + def reference_value_shape(self) -> _typing.Tuple[int, ...]: + """Return the shape of the value space on the reference cell.""" + return self._reference_value_shape + + @property + def sub_elements(self) -> _typing.List: + """Return list of sub-elements. + + This function does not recurse: ie it does not extract the sub-elements + of sub-elements. + """ + return self._sub_elements + + +class SymmetricElement(FiniteElement): + """A symmetric finite element.""" + + def __init__( + self, + symmetry: _typing.Dict[_typing.Tuple[int, ...], int], + sub_elements: _typing.List[AbstractFiniteElement] + ): + """Initialise a symmetric element. + + This class should only be used for testing + + Args: + symmetry: Map from physical components to reference components + sub_elements: Sub-elements of this element + """ + self._sub_elements = sub_elements + pullback = _SymmetricPullback(self, symmetry) + reference_value_shape = (sum(e.reference_value_size for e in sub_elements), ) + degree = max(e.embedded_superdegree for e in sub_elements) + cell = sub_elements[0].cell + for e in sub_elements: + if e.cell != cell: + raise ValueError("All sub-elements must be defined on the same cell") + sobolev_space = max(e.sobolev_space for e in sub_elements) + + super().__init__( + "Symmetric element", cell, degree, reference_value_shape, pullback, + sobolev_space, sub_elements=sub_elements, + _repr=(f"ufl.finiteelement.SymmetricElement({symmetry!r}, {sub_elements!r})"), + _str=f"") + + +class MixedElement(FiniteElement): + """A mixed element.""" + + def __init__(self, sub_elements): + """Initialise a mixed element. + + This class should only be used for testing + + Args: + sub_elements: Sub-elements of this element + """ + sub_elements = [MixedElement(e) if isinstance(e, list) else e for e in sub_elements] + cell = sub_elements[0].cell + for e in sub_elements: + assert e.cell == cell + degree = max(e.embedded_superdegree for e in sub_elements) + reference_value_shape = (sum(e.reference_value_size for e in sub_elements), ) + if all(isinstance(e.pullback, _IdentityPullback) for e in sub_elements): + pullback = _IdentityPullback() + else: + pullback = _MixedPullback(self) + sobolev_space = max(e.sobolev_space for e in sub_elements) + + super().__init__( + "Mixed element", cell, degree, reference_value_shape, pullback, sobolev_space, + sub_elements=sub_elements, + _repr=f"ufl.finiteelement.MixedElement({sub_elements!r})", + _str=f"") diff --git a/ufl/finiteelement/__init__.py b/ufl/finiteelement/__init__.py deleted file mode 100644 index f7a7c94a3..000000000 --- a/ufl/finiteelement/__init__.py +++ /dev/null @@ -1,42 +0,0 @@ -# flake8: noqa -"""This module defines the UFL finite element classes.""" - -# Copyright (C) 2008-2016 Martin Sandve Alnæs -# -# This file is part of UFL (https://www.fenicsproject.org) -# -# SPDX-License-Identifier: LGPL-3.0-or-later -# -# Modified by Kristian B. Oelgaard -# Modified by Marie E. Rognes 2010, 2012 -# Modified by Andrew T. T. McRae 2014 -# Modified by Lawrence Mitchell 2014 - -from ufl.finiteelement.finiteelementbase import FiniteElementBase -from ufl.finiteelement.finiteelement import FiniteElement -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, WithMapping -from ufl.finiteelement.brokenelement import BrokenElement - -# Export list for ufl.classes -__all_classes__ = [ - "FiniteElementBase", - "FiniteElement", - "MixedElement", - "VectorElement", - "TensorElement", - "EnrichedElement", - "NodalEnrichedElement", - "RestrictedElement", - "TensorProductElement", - "HDivElement", - "HCurlElement", - "BrokenElement", - "WithMapping" - ] diff --git a/ufl/form.py b/ufl/form.py index ecbd4b17d..4fb474f1c 100644 --- a/ufl/form.py +++ b/ufl/form.py @@ -831,6 +831,7 @@ def _analyze_form_arguments(self): def _analyze_domains(self): """Analyze which domains can be found in FormSum.""" from ufl.domain import join_domains + # Collect unique domains self._domains = join_domains([component.ufl_domain() for component in self._components]) diff --git a/ufl/formatting/ufl2unicode.py b/ufl/formatting/ufl2unicode.py index 842e78e3f..90a5d7bf7 100644 --- a/ufl/formatting/ufl2unicode.py +++ b/ufl/formatting/ufl2unicode.py @@ -3,11 +3,11 @@ import numbers import ufl -from ufl.corealg.multifunction import MultiFunction +from ufl.algorithms import compute_form_data +from ufl.core.multiindex import FixedIndex, Index from ufl.corealg.map_dag import map_expr_dag -from ufl.core.multiindex import Index, FixedIndex +from ufl.corealg.multifunction import MultiFunction from ufl.form import Form -from ufl.algorithms import compute_form_data try: import colorama diff --git a/ufl/formoperators.py b/ufl/formoperators.py index b71acd9d9..d5cc31117 100644 --- a/ufl/formoperators.py +++ b/ufl/formoperators.py @@ -9,37 +9,29 @@ # Modified by Massimiliano Leoni, 2016 # Modified by Cecile Daversin-Catty, 2018 -from ufl.form import Form, FormSum, BaseForm, ZeroBaseForm, as_form -from ufl.core.expr import Expr, ufl_err_str -from ufl.core.base_form_operator import BaseFormOperator -from ufl.split_functions import split -from ufl.exprcontainers import ExprList, ExprMapping -from ufl.variable import Variable -from ufl.finiteelement import MixedElement +from ufl.action import Action +from ufl.adjoint import Adjoint +from ufl.algorithms import replace # noqa: F401 +from ufl.algorithms import (compute_energy_norm, compute_form_action, compute_form_adjoint, compute_form_functional, + compute_form_lhs, compute_form_rhs, expand_derivatives, extract_arguments, formsplitter) from ufl.argument import Argument from ufl.coefficient import Coefficient, Cofunction -from ufl.adjoint import Adjoint -from ufl.action import Action -from ufl.differentiation import (CoefficientDerivative, CoordinateDerivative, - BaseFormDerivative, BaseFormCoordinateDerivative, - BaseFormOperatorDerivative, BaseFormOperatorCoordinateDerivative) -from ufl.constantvalue import is_true_ufl_scalar, as_ufl -from ufl.indexed import Indexed +from ufl.constantvalue import as_ufl, is_true_ufl_scalar +from ufl.core.base_form_operator import BaseFormOperator +from ufl.core.expr import Expr, ufl_err_str from ufl.core.multiindex import FixedIndex, MultiIndex -from ufl.tensors import as_tensor, ListTensor -from ufl.sorting import sorted_expr +from ufl.differentiation import (BaseFormCoordinateDerivative, BaseFormDerivative, BaseFormOperatorCoordinateDerivative, + BaseFormOperatorDerivative, CoefficientDerivative, CoordinateDerivative) +from ufl.exprcontainers import ExprList, ExprMapping +from ufl.finiteelement import MixedElement +from ufl.form import BaseForm, Form, FormSum, ZeroBaseForm, as_form from ufl.functionspace import FunctionSpace from ufl.geometry import SpatialCoordinate - -# An exception to the rule that ufl.* does not depend on ufl.algorithms.* ... -from ufl.algorithms import compute_form_adjoint, compute_form_action -from ufl.algorithms import compute_energy_norm -from ufl.algorithms import compute_form_lhs, compute_form_rhs, compute_form_functional -from ufl.algorithms import expand_derivatives, extract_arguments -from ufl.algorithms import formsplitter - -# Part of the external interface -from ufl.algorithms import replace # noqa +from ufl.indexed import Indexed +from ufl.sorting import sorted_expr +from ufl.split_functions import split +from ufl.tensors import ListTensor, as_tensor +from ufl.variable import Variable def extract_blocks(form, i=None, j=None): @@ -223,14 +215,14 @@ def _handle_derivative_arguments(form, coefficient, argument): # Create argument and split it if in a mixed space function_spaces = [c.ufl_function_space() for c in coefficients] - domains = [fs.ufl_domain() for fs in function_spaces] - elements = [fs.ufl_element() for fs in function_spaces] if len(function_spaces) == 1: arguments = (Argument(function_spaces[0], number, part),) else: # Create in mixed space over assumed (for now) same domain + domains = [fs.ufl_domain() for fs in function_spaces] + elements = [fs.ufl_element() for fs in function_spaces] assert all(fs.ufl_domain() == domains[0] for fs in function_spaces) - elm = MixedElement(*elements) + elm = MixedElement(elements) fs = FunctionSpace(domains[0], elm) arguments = split(Argument(fs, number, part)) else: diff --git a/ufl/functionspace.py b/ufl/functionspace.py index 7c3cfda43..e2cc86dbc 100644 --- a/ufl/functionspace.py +++ b/ufl/functionspace.py @@ -42,7 +42,7 @@ def __init__(self, domain, element): """Initialise.""" if domain is None: # DOLFIN hack - # TODO: Is anything expected from element.cell() in this case? + # TODO: Is anything expected from element.cell in this case? pass else: try: @@ -50,7 +50,7 @@ def __init__(self, domain, element): except AttributeError: raise ValueError("Expected non-abstract domain for initalization of function space.") else: - if element.cell() != domain_cell: + if element.cell != domain_cell: raise ValueError("Non-matching cell of finite element and domain.") AbstractFunctionSpace.__init__(self) diff --git a/ufl/geometry.py b/ufl/geometry.py index e06ec7793..a8f6c7720 100644 --- a/ufl/geometry.py +++ b/ufl/geometry.py @@ -9,6 +9,7 @@ from ufl.core.terminal import Terminal from ufl.core.ufl_type import ufl_type from ufl.domain import as_domain, extract_unique_domain +from ufl.sobolevspace import H1 """ Possible coordinate bootstrapping: @@ -675,7 +676,8 @@ def is_cellwise_constant(self): # facets. Seems like too much work to fix right now. Only # true for a piecewise linear coordinate field with simplex # _facets_. - is_piecewise_linear = self._domain.ufl_coordinate_element().degree() == 1 + ce = self._domain.ufl_coordinate_element() + is_piecewise_linear = ce.embedded_superdegree <= 1 and ce in H1 return is_piecewise_linear and self._domain.ufl_cell().has_simplex_facets() diff --git a/ufl/index_combination_utils.py b/ufl/index_combination_utils.py index 6d820244e..1f7682ae0 100644 --- a/ufl/index_combination_utils.py +++ b/ufl/index_combination_utils.py @@ -7,10 +7,10 @@ from ufl.core.multiindex import FixedIndex, Index, indices - # FIXME: Some of these might be merged into one function, some might # be optimized + def unique_sorted_indices(indices): """Get unique sorted indices. diff --git a/ufl/indexed.py b/ufl/indexed.py index 43bae7f10..b22f3d7ef 100644 --- a/ufl/indexed.py +++ b/ufl/indexed.py @@ -8,18 +8,16 @@ from ufl.constantvalue import Zero from ufl.core.expr import Expr, ufl_err_str -from ufl.core.ufl_type import ufl_type +from ufl.core.multiindex import FixedIndex, Index, MultiIndex from ufl.core.operator import Operator -from ufl.core.multiindex import Index, FixedIndex, MultiIndex +from ufl.core.ufl_type import ufl_type from ufl.index_combination_utils import unique_sorted_indices from ufl.precedence import parstr -# --- Indexed expression --- - @ufl_type(is_shaping=True, num_ops=2, is_terminal_modifier=True) class Indexed(Operator): - """Indexed.""" + """Indexed expression.""" __slots__ = ( "ufl_free_indices", diff --git a/ufl/indexsum.py b/ufl/indexsum.py index 3df273255..b13829484 100644 --- a/ufl/indexsum.py +++ b/ufl/indexsum.py @@ -7,16 +7,16 @@ # SPDX-License-Identifier: LGPL-3.0-or-later -from ufl.core.ufl_type import ufl_type +from ufl.constantvalue import Zero from ufl.core.expr import Expr, ufl_err_str -from ufl.core.operator import Operator from ufl.core.multiindex import MultiIndex +from ufl.core.operator import Operator +from ufl.core.ufl_type import ufl_type from ufl.precedence import parstr -from ufl.constantvalue import Zero - # --- Sum over an index --- + @ufl_type(num_ops=2) class IndexSum(Operator): """Index sum.""" diff --git a/ufl/integral.py b/ufl/integral.py index f92aefbc5..d680f5a77 100644 --- a/ufl/integral.py +++ b/ufl/integral.py @@ -10,8 +10,8 @@ # Modified by Massimiliano Leoni, 2016. import ufl -from ufl.core.expr import Expr from ufl.checks import is_python_scalar, is_scalar_constant_expression +from ufl.core.expr import Expr from ufl.measure import Measure # noqa from ufl.protocols import id_or_none diff --git a/ufl/legacy/__init__.py b/ufl/legacy/__init__.py new file mode 100644 index 000000000..06a0b12ca --- /dev/null +++ b/ufl/legacy/__init__.py @@ -0,0 +1,25 @@ +"""Legacy UFL features.""" +# Copyright (C) 2008-2016 Martin Sandve Alnæs +# +# This file is part of UFL (https://www.fenicsproject.org) +# +# SPDX-License-Identifier: LGPL-3.0-or-later +# +# Modified by Kristian B. Oelgaard +# Modified by Marie E. Rognes 2010, 2012 +# Modified by Andrew T. T. McRae 2014 +# Modified by Lawrence Mitchell 2014 + +import warnings as _warnings + +from ufl.legacy.brokenelement import BrokenElement +from ufl.legacy.enrichedelement import EnrichedElement, NodalEnrichedElement +from ufl.legacy.finiteelement import FiniteElement +from ufl.legacy.finiteelementbase import FiniteElementBase +from ufl.legacy.hdivcurl import HCurlElement, HDivElement, WithMapping +from ufl.legacy.mixedelement import MixedElement, TensorElement, VectorElement +from ufl.legacy.restrictedelement import RestrictedElement +from ufl.legacy.tensorproductelement import TensorProductElement + +_warnings.warn("The features in ufl.legacy are deprecated and will be removed in a future version.", + FutureWarning) diff --git a/ufl/finiteelement/brokenelement.py b/ufl/legacy/brokenelement.py similarity index 86% rename from ufl/finiteelement/brokenelement.py rename to ufl/legacy/brokenelement.py index 2467e9d0b..2563b868f 100644 --- a/ufl/finiteelement/brokenelement.py +++ b/ufl/legacy/brokenelement.py @@ -8,7 +8,7 @@ # # Modified by Massimiliano Leoni, 2016 -from ufl.finiteelement.finiteelementbase import FiniteElementBase +from ufl.legacy.finiteelementbase import FiniteElementBase from ufl.sobolevspace import L2 @@ -19,11 +19,11 @@ def __init__(self, element): self._element = element family = "BrokenElement" - cell = element.cell() + cell = element.cell degree = element.degree() quad_scheme = element.quadrature_scheme() - value_shape = element.value_shape() - reference_value_shape = element.reference_value_shape() + 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) @@ -35,6 +35,7 @@ def mapping(self): """Doc.""" return self._element.mapping() + @property def sobolev_space(self): """Return the underlying Sobolev space.""" return L2 diff --git a/ufl/finiteelement/elementlist.py b/ufl/legacy/elementlist.py similarity index 99% rename from ufl/finiteelement/elementlist.py rename to ufl/legacy/elementlist.py index 90c783d4f..712b94bb1 100644 --- a/ufl/finiteelement/elementlist.py +++ b/ufl/legacy/elementlist.py @@ -16,12 +16,12 @@ # Modified by Robert Kloefkorn, 2022 import warnings + from numpy import asarray -from ufl.sobolevspace import L2, H1, H2, HDiv, HCurl, HEin, HDivDiv, HInf -from ufl.utils.formatting import istr from ufl.cell import Cell, TensorProductCell - +from ufl.sobolevspace import H1, H2, L2, HCurl, HDiv, HDivDiv, HEin, HInf +from ufl.utils.formatting import istr # List of valid elements ufl_elements = {} diff --git a/ufl/finiteelement/enrichedelement.py b/ufl/legacy/enrichedelement.py similarity index 83% rename from ufl/finiteelement/enrichedelement.py rename to ufl/legacy/enrichedelement.py index 81e2d88cc..76420f00b 100644 --- a/ufl/finiteelement/enrichedelement.py +++ b/ufl/legacy/enrichedelement.py @@ -10,7 +10,7 @@ # Modified by Marie E. Rognes 2010, 2012 # Modified by Massimiliano Leoni, 2016 -from ufl.finiteelement.finiteelementbase import FiniteElementBase +from ufl.legacy.finiteelementbase import FiniteElementBase class EnrichedElementBase(FiniteElementBase): @@ -20,8 +20,8 @@ def __init__(self, *elements): """Doc.""" self._elements = elements - cell = elements[0].cell() - if not all(e.cell() == cell for e in elements[1:]): + cell = elements[0].cell + if not all(e.cell == cell for e in elements[1:]): raise ValueError("Cell mismatch for sub elements of enriched element.") if isinstance(elements[0].degree(), int): @@ -38,12 +38,12 @@ def __init__(self, *elements): if not all(qs == quad_scheme for qs in quad_schemes): raise ValueError("Quadrature scheme mismatch.") - value_shape = elements[0].value_shape() - if not all(e.value_shape() == value_shape for e in elements[1:]): + value_shape = elements[0].value_shape + if not all(e.value_shape == value_shape for e in elements[1:]): raise ValueError("Element value shape mismatch.") - reference_value_shape = elements[0].reference_value_shape() - if not all(e.reference_value_shape() == reference_value_shape for e in elements[1:]): + reference_value_shape = elements[0].reference_value_shape + if not all(e.reference_value_shape == reference_value_shape for e in elements[1:]): raise ValueError("Element reference value shape mismatch.") # mapping = elements[0].mapping() # FIXME: This fails for a mixed subelement here. @@ -62,15 +62,16 @@ def mapping(self): """Doc.""" return self._elements[0].mapping() + @property 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() + if all(e.sobolev_space == elements[0].sobolev_space for e in elements): - return elements[0].sobolev_space() + return elements[0].sobolev_space else: # Find smallest shared Sobolev space over all sub elements - spaces = [e.sobolev_space() for e in 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(): @@ -92,6 +93,22 @@ def reconstruct(self, **kwargs): """Doc.""" return type(self)(*[e.reconstruct(**kwargs) for e in self._elements]) + @property + def embedded_subdegree(self): + """Return embedded subdegree.""" + if isinstance(self._degree, int): + return self._degree + else: + return min(e.embedded_subdegree for e in self._elements) + + @property + def embedded_superdegree(self): + """Return embedded superdegree.""" + if isinstance(self._degree, int): + return self._degree + else: + return max(e.embedded_superdegree for e in self._elements) + class EnrichedElement(EnrichedElementBase): r"""The vector sum of several finite element spaces. diff --git a/ufl/finiteelement/finiteelement.py b/ufl/legacy/finiteelement.py similarity index 93% rename from ufl/finiteelement/finiteelement.py rename to ufl/legacy/finiteelement.py index 0962e82a3..1166ed741 100644 --- a/ufl/finiteelement/finiteelement.py +++ b/ufl/legacy/finiteelement.py @@ -10,12 +10,10 @@ # Modified by Anders Logg 2014 # Modified by Massimiliano Leoni, 2016 +from ufl.cell import TensorProductCell, as_cell +from ufl.legacy.elementlist import canonical_element_description, simplices +from ufl.legacy.finiteelementbase import FiniteElementBase from ufl.utils.formatting import istr -from ufl.cell import as_cell - -from ufl.cell import TensorProductCell -from ufl.finiteelement.elementlist import canonical_element_description, simplices -from ufl.finiteelement.finiteelementbase import FiniteElementBase class FiniteElement(FiniteElementBase): @@ -37,9 +35,10 @@ def __new__(cls, if isinstance(cell, TensorProductCell): # 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 + from ufl.legacy.enrichedelement import EnrichedElement + from ufl.legacy.hdivcurl import HCurlElement as HCurl + from ufl.legacy.hdivcurl import HDivElement as HDiv + from ufl.legacy.tensorproductelement import TensorProductElement family, short_name, degree, value_shape, reference_value_shape, sobolev_space, mapping = \ canonical_element_description(family, cell, degree, form_degree) @@ -171,7 +170,7 @@ def __init__(self, else: var_str = ", variant=%s" % repr(v) self._repr = "FiniteElement(%s, %s, %s%s%s)" % ( - repr(self.family()), repr(self.cell()), repr(self.degree()), quad_str, var_str) + repr(self.family()), repr(self.cell), repr(self.degree()), quad_str, var_str) assert '"' not in self._repr def __repr__(self): @@ -190,6 +189,7 @@ def mapping(self): """Return the mapping type for this element .""" return self._mapping + @property def sobolev_space(self): """Return the underlying Sobolev space.""" return self._sobolev_space @@ -203,7 +203,7 @@ def reconstruct(self, family=None, cell=None, degree=None, quad_scheme=None, var if family is None: family = self.family() if cell is None: - cell = self.cell() + cell = self.cell if degree is None: degree = self.degree() if quad_scheme is None: @@ -219,7 +219,7 @@ def __str__(self): 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()) + qs, v, self.cell) def shortstr(self): """Format as string for pretty printing.""" @@ -228,7 +228,7 @@ def shortstr(self): def __getnewargs__(self): """Return the arguments which pickle needs to recreate the object.""" return (self.family(), - self.cell(), + self.cell, self.degree(), None, self.quadrature_scheme(), diff --git a/ufl/finiteelement/finiteelementbase.py b/ufl/legacy/finiteelementbase.py similarity index 81% rename from ufl/finiteelement/finiteelementbase.py rename to ufl/legacy/finiteelementbase.py index 7f34252fc..88eba6c8c 100644 --- a/ufl/finiteelement/finiteelementbase.py +++ b/ufl/legacy/finiteelementbase.py @@ -10,15 +10,18 @@ # Modified by Marie E. Rognes 2010, 2012 # Modified by Massimiliano Leoni, 2016 -from ufl.utils.sequences import product +from abc import abstractmethod, abstractproperty + +from ufl import pullback from ufl.cell import AbstractCell, as_cell -from abc import ABC, abstractmethod +from ufl.finiteelement import AbstractFiniteElement +from ufl.utils.sequences import product -class FiniteElementBase(ABC): +class FiniteElementBase(AbstractFiniteElement): """Base class for all finite elements.""" __slots__ = ("_family", "_cell", "_degree", "_quad_scheme", - "_value_shape", "_reference_value_shape", "__weakref__") + "_value_shape", "_reference_value_shape") # TODO: Not all these should be in the base class! In particular # family, degree, and quad_scheme do not belong here. @@ -49,7 +52,7 @@ def __repr__(self): """Format as string for evaluation as Python object.""" pass - @abstractmethod + @abstractproperty def sobolev_space(self): """Return the underlying Sobolev space.""" pass @@ -104,14 +107,13 @@ def variant(self): def degree(self, component=None): """Return polynomial degree of finite element.""" - # FIXME: Consider embedded_degree concept for more accurate - # degree, see blueprint return self._degree def quadrature_scheme(self): """Return quadrature scheme of finite element.""" return self._quad_scheme + @property def cell(self): """Return cell of finite element.""" return self._cell @@ -120,21 +122,25 @@ def is_cellwise_constant(self, component=None): """Return whether the basis functions of this element is spatially constant over each cell.""" return self._is_globally_constant() or self.degree() == 0 + @property def value_shape(self): """Return the shape of the value space on the global domain.""" return self._value_shape + @property def reference_value_shape(self): """Return the shape of the value space on the reference cell.""" return self._reference_value_shape + @property def value_size(self): """Return the integer product of the value shape.""" - return product(self.value_shape()) + return product(self.value_shape) + @property def reference_value_size(self): """Return the integer product of the reference value shape.""" - return product(self.reference_value_shape()) + return product(self.reference_value_shape) def symmetry(self): # FIXME: different approach r"""Return the symmetry dict. @@ -148,7 +154,7 @@ def symmetry(self): # FIXME: different approach def _check_component(self, i): """Check that component index i is valid.""" - sh = self.value_shape() + sh = self.value_shape r = len(sh) if not (len(i) == r and all(j < k for (j, k) in zip(i, sh))): raise ValueError( @@ -174,7 +180,7 @@ def extract_component(self, i): def _check_reference_component(self, i): """Check that reference component index i is valid.""" - sh = self.value_shape() + sh = self.value_shape r = len(sh) if not (len(i) == r and all(j < k for (j, k) in zip(i, sh))): raise ValueError( @@ -201,10 +207,12 @@ def extract_reference_component(self, i): self._check_reference_component(i) return (i, self) + @property def num_sub_elements(self): """Return number of sub-elements.""" return 0 + @property def sub_elements(self): """Return list of sub-elements.""" return [] @@ -213,20 +221,20 @@ def __add__(self, other): """Add two elements, creating an enriched element.""" if not isinstance(other, FiniteElementBase): raise ValueError(f"Can't add element and {other.__class__}.") - from ufl.finiteelement import EnrichedElement + from ufl.legacy import EnrichedElement return EnrichedElement(self, other) def __mul__(self, other): """Multiply two elements, creating a mixed element.""" if not isinstance(other, FiniteElementBase): raise ValueError("Can't multiply element and {other.__class__}.") - from ufl.finiteelement import MixedElement + from ufl.legacy import MixedElement return MixedElement(self, other) def __getitem__(self, index): """Restrict finite element to a subdomain, subcomponent or topology (cell).""" if index in ("facet", "interior"): - from ufl.finiteelement import RestrictedElement + from ufl.legacy import RestrictedElement return RestrictedElement(self, index) else: raise KeyError(f"Invalid index for restriction: {repr(index)}") @@ -234,3 +242,35 @@ def __getitem__(self, index): def __iter__(self): """Iter.""" raise TypeError(f"'{type(self).__name__}' object is not iterable") + + @property + def embedded_superdegree(self): + """Doc.""" + return self.degree() + + @property + def embedded_subdegree(self): + """Doc.""" + return self.degree() + + @property + def pullback(self): + """Get the pull back.""" + if self.mapping() == "identity": + return pullback.identity_pullback + elif self.mapping() == "L2 Piola": + return pullback.l2_piola + elif self.mapping() == "covariant Piola": + return pullback.covariant_piola + elif self.mapping() == "contravariant Piola": + return pullback.contravariant_piola + elif self.mapping() == "double covariant Piola": + return pullback.double_covariant_piola + elif self.mapping() == "double contravariant Piola": + return pullback.double_contravariant_piola + elif self.mapping() == "custom": + return pullback.custom_pullback + elif self.mapping() == "physical": + return pullback.physical_pullback + + raise ValueError(f"Unsupported mapping: {self.mapping()}") diff --git a/ufl/finiteelement/hdivcurl.py b/ufl/legacy/hdivcurl.py similarity index 81% rename from ufl/finiteelement/hdivcurl.py rename to ufl/legacy/hdivcurl.py index fde6ff33b..3f693017e 100644 --- a/ufl/finiteelement/hdivcurl.py +++ b/ufl/legacy/hdivcurl.py @@ -7,8 +7,8 @@ # # Modified by Massimiliano Leoni, 2016 -from ufl.finiteelement.finiteelementbase import FiniteElementBase -from ufl.sobolevspace import HDiv, HCurl, L2 +from ufl.legacy.finiteelementbase import FiniteElementBase +from ufl.sobolevspace import L2, HCurl, HDiv class HDivElement(FiniteElementBase): @@ -20,11 +20,11 @@ def __init__(self, element): self._element = element family = "TensorProductElement" - cell = element.cell() + cell = element.cell degree = element.degree() quad_scheme = element.quadrature_scheme() - value_shape = (element.cell().geometric_dimension(),) - reference_value_shape = (element.cell().topological_dimension(),) + value_shape = (element.cell.geometric_dimension(),) + reference_value_shape = (element.cell.topological_dimension(),) # Skipping TensorProductElement constructor! Bad code smell, refactor to avoid this non-inheritance somehow. FiniteElementBase.__init__(self, family, cell, degree, @@ -38,6 +38,7 @@ def mapping(self): """Doc.""" return "contravariant Piola" + @property def sobolev_space(self): """Return the underlying Sobolev space.""" return HDiv @@ -58,6 +59,16 @@ def shortstr(self): """Format as string for pretty printing.""" return f"HDivElement({self._element.shortstr()})" + @property + def embedded_subdegree(self): + """Return embedded subdegree.""" + return self._element.embedded_subdegree + + @property + def embedded_superdegree(self): + """Return embedded superdegree.""" + return self._element.embedded_superdegree + class HCurlElement(FiniteElementBase): """A curl-conforming version of an outer product element, assuming this makes mathematical sense.""" @@ -68,10 +79,10 @@ def __init__(self, element): self._element = element family = "TensorProductElement" - cell = element.cell() + cell = element.cell degree = element.degree() quad_scheme = element.quadrature_scheme() - cell = element.cell() + cell = element.cell value_shape = (cell.geometric_dimension(),) reference_value_shape = (cell.topological_dimension(),) # TODO: Is this right? # Skipping TensorProductElement constructor! Bad code smell, @@ -87,6 +98,7 @@ def mapping(self): """Doc.""" return "covariant Piola" + @property def sobolev_space(self): """Return the underlying Sobolev space.""" return HCurl @@ -136,36 +148,39 @@ def __repr__(self): """Doc.""" return f"WithMapping({repr(self.wrapee)}, '{self._mapping}')" + @property def value_shape(self): """Doc.""" - gdim = self.cell().geometric_dimension() + gdim = self.cell.geometric_dimension() mapping = self.mapping() if mapping in {"covariant Piola", "contravariant Piola"}: return (gdim,) elif mapping in {"double covariant Piola", "double contravariant Piola"}: return (gdim, gdim) else: - return self.wrapee.value_shape() + return self.wrapee.value_shape + @property def reference_value_shape(self): """Doc.""" - tdim = self.cell().topological_dimension() + tdim = self.cell.topological_dimension() mapping = self.mapping() if mapping in {"covariant Piola", "contravariant Piola"}: return (tdim,) elif mapping in {"double covariant Piola", "double contravariant Piola"}: return (tdim, tdim) else: - return self.wrapee.reference_value_shape() + return self.wrapee.reference_value_shape def mapping(self): """Doc.""" return self._mapping + @property def sobolev_space(self): """Return the underlying Sobolev space.""" if self.wrapee.mapping() == self.mapping(): - return self.wrapee.sobolev_space() + return self.wrapee.sobolev_space else: return L2 @@ -186,3 +201,13 @@ def __str__(self): def shortstr(self): """Doc.""" return f"WithMapping({self.wrapee.shortstr()}, {self._mapping})" + + @property + def embedded_subdegree(self): + """Return embedded subdegree.""" + return self._element.embedded_subdegree + + @property + def embedded_superdegree(self): + """Return embedded superdegree.""" + return self._element.embedded_superdegree diff --git a/ufl/finiteelement/mixedelement.py b/ufl/legacy/mixedelement.py similarity index 86% rename from ufl/finiteelement/mixedelement.py rename to ufl/legacy/mixedelement.py index ca16a3d80..97f194e55 100644 --- a/ufl/finiteelement/mixedelement.py +++ b/ufl/legacy/mixedelement.py @@ -10,13 +10,15 @@ # Modified by Anders Logg 2014 # Modified by Massimiliano Leoni, 2016 -from ufl.permutation import compute_indices -from ufl.utils.sequences import product, max_degree -from ufl.utils.indexflattening import flatten_multiindex, unflatten_index, shape_to_strides -from ufl.cell import as_cell +import numpy as np -from ufl.finiteelement.finiteelementbase import FiniteElementBase -from ufl.finiteelement.finiteelement import FiniteElement +from ufl.cell import as_cell +from ufl.legacy.finiteelement import FiniteElement +from ufl.legacy.finiteelementbase import FiniteElementBase +from ufl.permutation import compute_indices +from ufl.pullback import IdentityPullback, MixedPullback, SymmetricPullback +from ufl.utils.indexflattening import flatten_multiindex, shape_to_strides, unflatten_index +from ufl.utils.sequences import max_degree, product class MixedElement(FiniteElementBase): @@ -38,7 +40,7 @@ def __init__(self, *elements, **kwargs): self._sub_elements = elements # Pick the first cell, for now all should be equal - cells = tuple(sorted(set(element.cell() for element in elements) - set([None]))) + cells = tuple(sorted(set(element.cell for element in elements) - set([None]))) self._cells = cells if cells: cell = cells[0] @@ -58,8 +60,8 @@ def __init__(self, *elements, **kwargs): raise ValueError("Quadrature scheme mismatch for sub elements of mixed element.") # Compute value sizes in global and reference configurations - value_size_sum = sum(product(s.value_shape()) for s in self._sub_elements) - reference_value_size_sum = sum(product(s.reference_value_shape()) for s in self._sub_elements) + value_size_sum = sum(product(s.value_shape) for s in self._sub_elements) + reference_value_size_sum = sum(product(s.reference_value_shape) for s in self._sub_elements) # Default value shape: Treated simply as all subelement values # unpacked in a vector. @@ -110,7 +112,7 @@ def symmetry(self): # Base index of the current subelement into mixed value j = 0 for e in self._sub_elements: - sh = e.value_shape() + sh = e.value_shape st = shape_to_strides(sh) # Map symmetries of subelement into index space of this # element @@ -120,13 +122,14 @@ def symmetry(self): sm[(j0,)] = (j1,) # Update base index for next element j += product(sh) - if j != product(self.value_shape()): + if j != product(self.value_shape): raise ValueError("Size mismatch in symmetry algorithm.") return sm or {} + @property def sobolev_space(self): """Doc.""" - return max(e.sobolev_space() for e in self._sub_elements) + return max(e.sobolev_space for e in self._sub_elements) def mapping(self): """Doc.""" @@ -135,10 +138,12 @@ def mapping(self): else: return "undefined" + @property def num_sub_elements(self): """Return number of sub elements.""" return len(self._sub_elements) + @property def sub_elements(self): """Return list of sub elements.""" return self._sub_elements @@ -153,14 +158,14 @@ def extract_subelement_component(self, i): self._check_component(i) # Select between indexing modes - if len(self.value_shape()) == 1: + if len(self.value_shape) == 1: # Indexing into a long vector of flattened subelement # shapes j, = i # Find subelement for this index for sub_element_index, e in enumerate(self._sub_elements): - sh = e.value_shape() + sh = e.value_shape si = product(sh) if j < si: break @@ -198,13 +203,13 @@ def extract_subelement_reference_component(self, i): self._check_reference_component(i) # Select between indexing modes - assert len(self.reference_value_shape()) == 1 + assert len(self.reference_value_shape) == 1 # Indexing into a long vector of flattened subelement shapes j, = i # Find subelement for this index for sub_element_index, e in enumerate(self._sub_elements): - sh = e.reference_value_shape() + sh = e.reference_value_shape si = product(sh) if j < si: break @@ -228,7 +233,7 @@ def extract_reference_component(self, i): def is_cellwise_constant(self, component=None): """Return whether the basis functions of this element is spatially constant over each cell.""" if component is None: - return all(e.is_cellwise_constant() for e in self.sub_elements()) + return all(e.is_cellwise_constant() for e in self.sub_elements) else: i, e = self.extract_component(component) return e.is_cellwise_constant() @@ -241,14 +246,30 @@ def degree(self, component=None): i, e = self.extract_component(component) return e.degree() + @property + def embedded_subdegree(self): + """Return embedded subdegree.""" + if isinstance(self._degree, int): + return self._degree + else: + return min(e.embedded_subdegree for e in self.sub_elements) + + @property + def embedded_superdegree(self): + """Return embedded superdegree.""" + if isinstance(self._degree, int): + return self._degree + else: + return max(e.embedded_superdegree for e in self.sub_elements) + def reconstruct(self, **kwargs): """Doc.""" - return MixedElement(*[e.reconstruct(**kwargs) for e in self.sub_elements()]) + return MixedElement(*[e.reconstruct(**kwargs) for e in self.sub_elements]) def variant(self): """Doc.""" try: - variant, = {e.variant() for e in self.sub_elements()} + variant, = {e.variant() for e in self.sub_elements} return variant except ValueError: return None @@ -263,6 +284,14 @@ def shortstr(self): tmp = ", ".join(element.shortstr() for element in self._sub_elements) return "Mixed<" + tmp + ">" + @property + def pullback(self): + """Get the pull back.""" + for e in self.sub_elements: + if not isinstance(e.pullback, IdentityPullback): + return MixedPullback(self) + return IdentityPullback() + class VectorElement(MixedElement): """A special case of a mixed finite element where all elements are equal.""" @@ -274,7 +303,7 @@ def __init__(self, family, cell=None, degree=None, dim=None, """Create vector element (repeated mixed element).""" if isinstance(family, FiniteElementBase): sub_element = family - cell = sub_element.cell() + cell = sub_element.cell variant = sub_element.variant() else: if cell is not None: @@ -296,14 +325,14 @@ def __init__(self, family, cell=None, degree=None, dim=None, sub_elements = [sub_element] * dim # Compute value shapes - value_shape = (dim,) + sub_element.value_shape() - reference_value_shape = (dim,) + sub_element.reference_value_shape() + value_shape = (dim,) + sub_element.value_shape + reference_value_shape = (dim,) + sub_element.reference_value_shape # Initialize element data MixedElement.__init__(self, sub_elements, value_shape=value_shape, reference_value_shape=reference_value_shape) - FiniteElementBase.__init__(self, sub_element.family(), sub_element.cell(), sub_element.degree(), + FiniteElementBase.__init__(self, sub_element.family(), sub_element.cell, sub_element.degree(), sub_element.quadrature_scheme(), value_shape, reference_value_shape) self._sub_element = sub_element @@ -323,7 +352,7 @@ def __repr__(self): def reconstruct(self, **kwargs): """Doc.""" sub_element = self._sub_element.reconstruct(**kwargs) - return VectorElement(sub_element, dim=len(self.sub_elements())) + return VectorElement(sub_element, dim=len(self.sub_elements)) def variant(self): """Return the variant used to initialise the element.""" @@ -356,7 +385,7 @@ def __init__(self, family, cell=None, degree=None, shape=None, """Create tensor element (repeated mixed element with optional symmetries).""" if isinstance(family, FiniteElementBase): sub_element = family - cell = sub_element.cell() + cell = sub_element.cell variant = sub_element.variant() else: if cell is not None: @@ -423,8 +452,8 @@ def __init__(self, family, cell=None, degree=None, shape=None, reference_value_shape = shape self._mapping = sub_element.mapping() - value_shape = value_shape + sub_element.value_shape() - reference_value_shape = reference_value_shape + sub_element.reference_value_shape() + value_shape = value_shape + sub_element.value_shape + reference_value_shape = reference_value_shape + sub_element.reference_value_shape # Initialize element data MixedElement.__init__(self, sub_elements, value_shape=value_shape, reference_value_shape=reference_value_shape) @@ -445,6 +474,25 @@ def __init__(self, family, cell=None, degree=None, shape=None, self._repr = (f"TensorElement({repr(sub_element)}, shape={shape}, " f"symmetry={symmetry}{var_str})") + @property + def pullback(self): + """Get pull back.""" + if len(self._symmetry) > 0: + sub_element_value_shape = self.sub_elements[0].value_shape + for e in self.sub_elements: + if e.value_shape != sub_element_value_shape: + raise ValueError("Sub-elements must all have the same value size") + symmetry = {} + n = 0 + for i in np.ndindex(self.value_shape[:len(self.value_shape)-len(sub_element_value_shape)]): + if i in self._symmetry and self._symmetry[i] in symmetry: + symmetry[i] = symmetry[self._symmetry[i]] + else: + symmetry[i] = n + n += 1 + return SymmetricPullback(self, symmetry) + return super().pullback + def __repr__(self): """Doc.""" return self._repr @@ -501,7 +549,7 @@ def __str__(self): else: sym = "" return ("" % - (self.value_shape(), self._sub_element, sym)) + (self.value_shape, self._sub_element, sym)) def shortstr(self): """Format as string for pretty printing.""" @@ -510,5 +558,5 @@ def shortstr(self): sym = " with symmetries (%s)" % tmp else: sym = "" - return "Tensor<%s x %s%s>" % (self.value_shape(), + return "Tensor<%s x %s%s>" % (self.value_shape, self._sub_element.shortstr(), sym) diff --git a/ufl/finiteelement/restrictedelement.py b/ufl/legacy/restrictedelement.py similarity index 91% rename from ufl/finiteelement/restrictedelement.py rename to ufl/legacy/restrictedelement.py index 7ad71b3a7..767444087 100644 --- a/ufl/finiteelement/restrictedelement.py +++ b/ufl/legacy/restrictedelement.py @@ -10,7 +10,7 @@ # Modified by Marie E. Rognes 2010, 2012 # Modified by Massimiliano Leoni, 2016 -from ufl.finiteelement.finiteelementbase import FiniteElementBase +from ufl.legacy.finiteelementbase import FiniteElementBase from ufl.sobolevspace import L2 valid_restriction_domains = ("interior", "facet", "face", "edge", "vertex") @@ -26,11 +26,11 @@ def __init__(self, element, restriction_domain): if restriction_domain not in valid_restriction_domains: raise ValueError(f"Expecting one of the strings: {valid_restriction_domains}") - FiniteElementBase.__init__(self, "RestrictedElement", element.cell(), + FiniteElementBase.__init__(self, "RestrictedElement", element.cell, element.degree(), element.quadrature_scheme(), - element.value_shape(), - element.reference_value_shape()) + element.value_shape, + element.reference_value_shape) self._element = element @@ -40,12 +40,13 @@ def __repr__(self): """Doc.""" return f"RestrictedElement({repr(self._element)}, {repr(self._restriction_domain)})" + @property def sobolev_space(self): """Doc.""" if self._restriction_domain == "interior": return L2 else: - return self._element.sobolev_space() + return self._element.sobolev_space def is_cellwise_constant(self): """Return whether the basis functions of this element is spatially constant over each cell.""" @@ -89,13 +90,15 @@ def symmetry(self): """ return self._element.symmetry() + @property def num_sub_elements(self): """Return number of sub elements.""" - return self._element.num_sub_elements() + return self._element.num_sub_elements + @property def sub_elements(self): """Return list of sub elements.""" - return self._element.sub_elements() + return self._element.sub_elements def num_restricted_sub_elements(self): """Return number of restricted sub elements.""" diff --git a/ufl/finiteelement/tensorproductelement.py b/ufl/legacy/tensorproductelement.py similarity index 83% rename from ufl/finiteelement/tensorproductelement.py rename to ufl/legacy/tensorproductelement.py index 07aad6e8f..d9f1ff1bd 100644 --- a/ufl/finiteelement/tensorproductelement.py +++ b/ufl/legacy/tensorproductelement.py @@ -13,10 +13,9 @@ from itertools import chain from ufl.cell import TensorProductCell, as_cell +from ufl.legacy.finiteelementbase import FiniteElementBase from ufl.sobolevspace import DirectionalSobolevSpace -from ufl.finiteelement.finiteelementbase import FiniteElementBase - class TensorProductElement(FiniteElementBase): r"""The tensor product of :math:`d` element spaces. @@ -43,7 +42,7 @@ def __init__(self, *elements, **kwargs): if cell is None: # Define cell as the product of each elements cell - cell = TensorProductCell(*[e.cell() for e in elements]) + cell = TensorProductCell(*[e.cell for e in elements]) else: cell = as_cell(cell) @@ -54,8 +53,8 @@ def __init__(self, *elements, **kwargs): quad_scheme = None # match FIAT implementation - value_shape = tuple(chain(*[e.value_shape() for e in elements])) - reference_value_shape = tuple(chain(*[e.reference_value_shape() for e in elements])) + value_shape = tuple(chain(*[e.value_shape for e in elements])) + reference_value_shape = tuple(chain(*[e.reference_value_shape for e in elements])) if len(value_shape) > 1: raise ValueError("Product of vector-valued elements not supported") if len(reference_value_shape) > 1: @@ -80,39 +79,42 @@ def mapping(self): else: return "undefined" + @property 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() + if all(e.sobolev_space == elements[0].sobolev_space for e in elements): - return elements[0].sobolev_space() + 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 + e_dim = e.cell.geometric_dimension() + e_order = (e.sobolev_space._order,) * e_dim orders.extend(e_order) return DirectionalSobolevSpace(orders) + @property def num_sub_elements(self): """Return number of subelements.""" return len(self._sub_elements) + @property def sub_elements(self): """Return subelements (factors).""" return self._sub_elements def reconstruct(self, **kwargs): """Doc.""" - cell = kwargs.pop("cell", self.cell()) - return TensorProductElement(*[e.reconstruct(**kwargs) for e in self.sub_elements()], cell=cell) + cell = kwargs.pop("cell", self.cell) + return TensorProductElement(*[e.reconstruct(**kwargs) for e in self.sub_elements], cell=cell) def variant(self): """Doc.""" try: - variant, = {e.variant() for e in self.sub_elements()} + variant, = {e.variant() for e in self.sub_elements} return variant except ValueError: return None @@ -126,3 +128,13 @@ def shortstr(self): """Short pretty-print.""" return "TensorProductElement(%s, cell=%s)" \ % (', '.join([e.shortstr() for e in self._sub_elements]), str(self._cell)) + + @property + def embedded_superdegree(self): + """Doc.""" + return sum(self.degree()) + + @property + def embedded_subdegree(self): + """Doc.""" + return min(self.degree()) diff --git a/ufl/mathfunctions.py b/ufl/mathfunctions.py index 24ab0354d..704304d6c 100644 --- a/ufl/mathfunctions.py +++ b/ufl/mathfunctions.py @@ -8,15 +8,15 @@ # Modified by Anders Logg, 2008 # Modified by Kristian B. Oelgaard, 2011 -import math import cmath +import math import numbers import warnings +from ufl.constantvalue import (ComplexValue, ConstantValue, FloatValue, IntValue, RealValue, Zero, as_ufl, + is_true_ufl_scalar) from ufl.core.operator import Operator from ufl.core.ufl_type import ufl_type -from ufl.constantvalue import (is_true_ufl_scalar, Zero, RealValue, FloatValue, IntValue, ComplexValue, - ConstantValue, as_ufl) """ TODO: Include additional functions available in (need derivatives as well): diff --git a/ufl/matrix.py b/ufl/matrix.py index 7cf8e55bd..7fb1f5c07 100644 --- a/ufl/matrix.py +++ b/ufl/matrix.py @@ -7,15 +7,15 @@ # # Modified by Nacime Bouziani, 2021-2022. -from ufl.form import BaseForm -from ufl.core.ufl_type import ufl_type from ufl.argument import Argument +from ufl.core.ufl_type import ufl_type +from ufl.form import BaseForm from ufl.functionspace import AbstractFunctionSpace from ufl.utils.counted import Counted - # --- The Matrix class represents a matrix, an assembled two form --- + @ufl_type() class Matrix(BaseForm, Counted): """An assemble linear operator between two function spaces.""" @@ -68,6 +68,7 @@ def _analyze_form_arguments(self): def _analyze_domains(self): """Analyze which domains can be found in a Matrix.""" from ufl.domain import join_domains + # Collect unique domains self._domains = join_domains([fs.ufl_domain() for fs in self._ufl_function_spaces]) diff --git a/ufl/measure.py b/ufl/measure.py index e457842ab..97d719501 100644 --- a/ufl/measure.py +++ b/ufl/measure.py @@ -10,16 +10,14 @@ # Modified by Massimiliano Leoni, 2016. import numbers - from itertools import chain -from ufl.core.expr import Expr from ufl.checks import is_true_ufl_scalar from ufl.constantvalue import as_ufl -from ufl.domain import as_domain, AbstractDomain, extract_domains +from ufl.core.expr import Expr +from ufl.domain import AbstractDomain, as_domain, extract_domains from ufl.protocols import id_or_none - # Export list for ufl.classes __all_classes__ = ["Measure", "MeasureSum", "MeasureProduct"] @@ -123,9 +121,11 @@ def __init__(self, self._integral_type = as_integral_type(integral_type) # Check that we either have a proper AbstractDomain or none - self._domain = None if domain is None else as_domain(domain) - if not (self._domain is None or isinstance(self._domain, AbstractDomain)): - raise ValueError("Invalid domain.") + if domain is not None: + domain = as_domain(domain) + if not isinstance(domain, AbstractDomain): + raise ValueError("Invalid domain.") + self._domain = domain # Store subdomain data self._subdomain_data = subdomain_data @@ -231,11 +231,11 @@ def __call__(self, subdomain_id=None, metadata=None, domain=None, # over entire domain. To do this we need to hijack the first # argument: if subdomain_id is not None and ( - isinstance(subdomain_id, AbstractDomain) or hasattr(subdomain_id, 'ufl_domain') + isinstance(subdomain_id, AbstractDomain) or hasattr(subdomain_id, "ufl_domain") ): if domain is not None: raise ValueError("Ambiguous: setting domain both as keyword argument and first argument.") - subdomain_id, domain = "everywhere", as_domain(subdomain_id) + subdomain_id, domain = "everywhere", subdomain_id # If degree or scheme is set, inject into metadata. This is a # quick fix to enable the dx(..., degree=3) notation. @@ -344,8 +344,8 @@ def __rmul__(self, integrand): Integration properties are taken from this Measure object. """ # Avoid circular imports - from ufl.integral import Integral from ufl.form import Form + from ufl.integral import Integral # Allow python literals: 1*dx and 1.0*dx if isinstance(integrand, (int, float)): diff --git a/ufl/objects.py b/ufl/objects.py index 7e740cf72..fc57ea2ae 100644 --- a/ufl/objects.py +++ b/ufl/objects.py @@ -8,10 +8,9 @@ # Modified by Anders Logg, 2008 # Modified by Kristian Oelgaard, 2009 -from ufl.core.multiindex import indices from ufl.cell import Cell -from ufl.measure import Measure -from ufl.measure import integral_type_to_measure_name +from ufl.core.multiindex import indices +from ufl.measure import Measure, integral_type_to_measure_name # Default indices i, j, k, l = indices(4) # noqa: E741 diff --git a/ufl/operators.py b/ufl/operators.py index 8f99d501b..9dc5add03 100644 --- a/ufl/operators.py +++ b/ufl/operators.py @@ -14,30 +14,27 @@ # Modified by Kristian B. Oelgaard, 2011 # Modified by Massimiliano Leoni, 2016. -import warnings import operator +import warnings -from ufl.form import Form -from ufl.constantvalue import Zero, RealValue, ComplexValue, as_ufl -from ufl.differentiation import VariableDerivative, Grad, Div, Curl, NablaGrad, NablaDiv -from ufl.tensoralgebra import ( - Transposed, Inner, Outer, Dot, Cross, Perp, - Determinant, Inverse, Cofactor, Trace, Deviatoric, Skew, Sym) -from ufl.coefficient import Coefficient -from ufl.variable import Variable -from ufl.tensors import as_tensor, as_matrix, as_vector, ListTensor -from ufl.conditional import ( - EQ, NE, AndCondition, OrCondition, NotCondition, Conditional, MaxValue, MinValue) -from ufl.algebra import Conj, Real, Imag -from ufl.mathfunctions import ( - Sqrt, Exp, Ln, Erf, Cos, Sin, Tan, Cosh, Sinh, Tanh, Acos, Asin, Atan, Atan2, - BesselJ, BesselY, BesselI, BesselK) +from ufl import sobolevspace +from ufl.algebra import Conj, Imag, Real from ufl.averaging import CellAvg, FacetAvg -from ufl.indexed import Indexed -from ufl.geometry import SpatialCoordinate, FacetNormal from ufl.checks import is_cellwise_constant +from ufl.coefficient import Coefficient +from ufl.conditional import EQ, NE, AndCondition, Conditional, MaxValue, MinValue, NotCondition, OrCondition +from ufl.constantvalue import ComplexValue, RealValue, Zero, as_ufl +from ufl.differentiation import Curl, Div, Grad, NablaDiv, NablaGrad, VariableDerivative from ufl.domain import extract_domains -from ufl import sobolevspace +from ufl.form import Form +from ufl.geometry import FacetNormal, SpatialCoordinate +from ufl.indexed import Indexed +from ufl.mathfunctions import (Acos, Asin, Atan, Atan2, BesselI, BesselJ, BesselK, BesselY, Cos, Cosh, Erf, Exp, Ln, + Sin, Sinh, Sqrt, Tan, Tanh) +from ufl.tensoralgebra import (Cofactor, Cross, Determinant, Deviatoric, Dot, Inner, Inverse, Outer, Perp, Skew, Sym, + Trace, Transposed) +from ufl.tensors import ListTensor, as_matrix, as_tensor, as_vector +from ufl.variable import Variable # --- Basic operators --- @@ -671,7 +668,12 @@ def exterior_derivative(f): raise NotImplementedError index = int(indices[0]) element = expression.ufl_element() - element = element.extract_component(index)[1] + while index != 0: + for e in element.sub_elements: + if e.value_size > index: + element = e + break + index -= e.value_size elif isinstance(f, ListTensor): f0 = f.ufl_operands[0] f0expr, f0indices = f0.ufl_operands # FIXME: Assumption on type of f0!!! @@ -679,7 +681,12 @@ def exterior_derivative(f): raise NotImplementedError index = int(f0indices[0]) element = f0expr.ufl_element() - element = element.extract_component(index)[1] + while index != 0: + for e in element.sub_elements: + if e.value_size > index: + element = e + break + index -= e.value_size else: try: element = f.ufl_element() @@ -687,7 +694,7 @@ def exterior_derivative(f): raise ValueError(f"Unable to determine element from {f}") gdim = element.cell().geometric_dimension() - space = element.sobolev_space() + space = element.sobolev_space if space == sobolevspace.L2: return f diff --git a/ufl/precedence.py b/ufl/precedence.py index 2c3b705a1..0aea48b20 100644 --- a/ufl/precedence.py +++ b/ufl/precedence.py @@ -8,9 +8,9 @@ import warnings - # FIXME: This code is crap... + def parstr(child, parent, pre="(", post=")", format=str): """Parstr.""" # Execute when needed instead of on import, which leads to all @@ -41,8 +41,8 @@ def parstr(child, parent, pre="(", post=")", format=str): def build_precedence_list(): """Build precedence list.""" - from ufl.classes import (Operator, Terminal, Sum, IndexSum, Product, Division, Power, - MathFunction, BesselFunction, Abs, Indexed) + from ufl.classes import (Abs, BesselFunction, Division, Indexed, IndexSum, MathFunction, Operator, Power, Product, + Sum, Terminal) # TODO: Fill in other types... # Power <= Transposed @@ -74,7 +74,7 @@ def build_precedence_mapping(precedence_list): Utility function used by some external code. """ - from ufl.classes import Expr, all_ufl_classes, abstract_classes + from ufl.classes import Expr, abstract_classes, all_ufl_classes pm = {} missing = set() # Assign integer values for each precedence level diff --git a/ufl/pullback.py b/ufl/pullback.py new file mode 100644 index 000000000..dee12f0ce --- /dev/null +++ b/ufl/pullback.py @@ -0,0 +1,552 @@ +"""Pull back and push forward maps.""" +# Copyright (C) 2023 Matthew Scroggs, David Ham, Garth Wells +# +# This file is part of UFL (https://www.fenicsproject.org) +# +# SPDX-License-Identifier: LGPL-3.0-or-later + +from __future__ import annotations + +import typing +from abc import ABC, abstractmethod, abstractproperty +from typing import TYPE_CHECKING + +import numpy as np + +from ufl.core.expr import Expr +from ufl.core.multiindex import indices +from ufl.domain import extract_unique_domain +from ufl.tensors import as_tensor + +if TYPE_CHECKING: + from ufl.finiteelement import AbstractFiniteElement as _AbstractFiniteElement + +__all_classes__ = ["NonStandardPullbackException", "AbstractPullback", "IdentityPullback", + "ContravariantPiola", "CovariantPiola", "L2Piola", "DoubleContravariantPiola", + "DoubleCovariantPiola", "MixedPullback", "SymmetricPullback", + "PhysicalPullback", "CustomPullback", "UndefinedPullback"] + + +class NonStandardPullbackException(BaseException): + """Exception to raise if a map is non-standard.""" + pass + + +class AbstractPullback(ABC): + """An abstract pull back.""" + + @abstractmethod + def __repr__(self) -> str: + """Return a representation of the object.""" + + @abstractmethod + def physical_value_shape(self, element) -> typing.Tuple[int, ...]: + """Get the physical value shape when this pull back is applied to an element. + + Args: + element: The element that the pull back is applied to + + Returns: + The value shape when the pull back is applied to the given element + """ + + @abstractproperty + def is_identity(self) -> bool: + """Is this pull back the identity (or the identity applied to mutliple components).""" + + def apply(self, expr: Expr) -> Expr: + """Apply the pull back. + + Args: + expr: A function on a physical cell + + Returns: The function pulled back to the reference cell + """ + raise NonStandardPullbackException() + + +class IdentityPullback(AbstractPullback): + """The identity pull back.""" + + def __repr__(self) -> str: + """Return a representation of the object.""" + return "IdentityPullback()" + + @property + def is_identity(self) -> bool: + """Is this pull back the identity (or the identity applied to mutliple components).""" + return True + + def apply(self, expr): + """Apply the pull back. + + Args: + expr: A function on a physical cell + + Returns: The function pulled back to the reference cell + """ + return expr + + def physical_value_shape(self, element) -> typing.Tuple[int, ...]: + """Get the physical value shape when this pull back is applied to an element. + + Args: + element: The element that the pull back is applied to + + Returns: + The value shape when the pull back is applied to the given element + """ + return element.reference_value_shape + + +class ContravariantPiola(AbstractPullback): + """The contravariant Piola pull back.""" + + def __repr__(self) -> str: + """Return a representation of the object.""" + return "ContravariantPiola()" + + @property + def is_identity(self) -> bool: + """Is this pull back the identity (or the identity applied to mutliple components).""" + return False + + def apply(self, expr): + """Apply the pull back. + + Args: + expr: A function on a physical cell + + Returns: The function pulled back to the reference cell + """ + from ufl.classes import Jacobian, JacobianDeterminant + + domain = extract_unique_domain(expr) + J = Jacobian(domain) + detJ = JacobianDeterminant(J) + transform = (1.0 / detJ) * J + # Apply transform "row-wise" to TensorElement(PiolaMapped, ...) + *k, i, j = indices(len(expr.ufl_shape) + 1) + kj = (*k, j) + return as_tensor(transform[i, j] * expr[kj], (*k, i)) + + def physical_value_shape(self, element) -> typing.Tuple[int, ...]: + """Get the physical value shape when this pull back is applied to an element. + + Args: + element: The element that the pull back is applied to + + Returns: + The value shape when the pull back is applied to the given element + """ + gdim = element.cell.geometric_dimension() + return (gdim, ) + element.reference_value_shape[1:] + + +class CovariantPiola(AbstractPullback): + """The covariant Piola pull back.""" + + def __repr__(self) -> str: + """Return a representation of the object.""" + return "CovariantPiola()" + + @property + def is_identity(self) -> bool: + """Is this pull back the identity (or the identity applied to mutliple components).""" + return False + + def apply(self, expr): + """Apply the pull back. + + Args: + expr: A function on a physical cell + + Returns: The function pulled back to the reference cell + """ + from ufl.classes import JacobianInverse + + domain = extract_unique_domain(expr) + K = JacobianInverse(domain) + # Apply transform "row-wise" to TensorElement(PiolaMapped, ...) + *k, i, j = indices(len(expr.ufl_shape) + 1) + kj = (*k, j) + return as_tensor(K[j, i] * expr[kj], (*k, i)) + + def physical_value_shape(self, element) -> typing.Tuple[int, ...]: + """Get the physical value shape when this pull back is applied to an element. + + Args: + element: The element that the pull back is applied to + + Returns: + The value shape when the pull back is applied to the given element + """ + gdim = element.cell.geometric_dimension() + return (gdim, ) + element.reference_value_shape[1:] + + +class L2Piola(AbstractPullback): + """The L2 Piola pull back.""" + + def __repr__(self) -> str: + """Return a representation of the object.""" + return "L2Piola()" + + @property + def is_identity(self) -> bool: + """Is this pull back the identity (or the identity applied to mutliple components).""" + return False + + def apply(self, expr): + """Apply the pull back. + + Args: + expr: A function on a physical cell + + Returns: The function pulled back to the reference cell + """ + from ufl.classes import JacobianDeterminant + + domain = extract_unique_domain(expr) + detJ = JacobianDeterminant(domain) + return expr / detJ + + def physical_value_shape(self, element) -> typing.Tuple[int, ...]: + """Get the physical value shape when this pull back is applied to an element. + + Args: + element: The element that the pull back is applied to + + Returns: + The value shape when the pull back is applied to the given element + """ + return element.reference_value_shape + + +class DoubleContravariantPiola(AbstractPullback): + """The double contravariant Piola pull back.""" + + def __repr__(self) -> str: + """Return a representation of the object.""" + return "DoubleContravariantPiola()" + + @property + def is_identity(self) -> bool: + """Is this pull back the identity (or the identity applied to mutliple components).""" + return False + + def apply(self, expr): + """Apply the pull back. + + Args: + expr: A function on a physical cell + + Returns: The function pulled back to the reference cell + """ + from ufl.classes import Jacobian, JacobianDeterminant + + domain = extract_unique_domain(expr) + J = Jacobian(domain) + detJ = JacobianDeterminant(J) + # Apply transform "row-wise" to TensorElement(PiolaMapped, ...) + *k, i, j, m, n = indices(len(expr.ufl_shape) + 2) + kmn = (*k, m, n) + return as_tensor((1.0 / detJ)**2 * J[i, m] * expr[kmn] * J[j, n], (*k, i, j)) + + def physical_value_shape(self, element) -> typing.Tuple[int, ...]: + """Get the physical value shape when this pull back is applied to an element. + + Args: + element: The element that the pull back is applied to + + Returns: + The value shape when the pull back is applied to the given element + """ + gdim = element.cell.geometric_dimension() + return (gdim, gdim) + + +class DoubleCovariantPiola(AbstractPullback): + """The double covariant Piola pull back.""" + + def __repr__(self) -> str: + """Return a representation of the object.""" + return "DoubleCovariantPiola()" + + @property + def is_identity(self) -> bool: + """Is this pull back the identity (or the identity applied to mutliple components).""" + return False + + def apply(self, expr): + """Apply the pull back. + + Args: + expr: A function on a physical cell + + Returns: The function pulled back to the reference cell + """ + from ufl.classes import JacobianInverse + + domain = extract_unique_domain(expr) + K = JacobianInverse(domain) + # Apply transform "row-wise" to TensorElement(PiolaMapped, ...) + *k, i, j, m, n = indices(len(expr.ufl_shape) + 2) + kmn = (*k, m, n) + return as_tensor(K[m, i] * expr[kmn] * K[n, j], (*k, i, j)) + + def physical_value_shape(self, element) -> typing.Tuple[int, ...]: + """Get the physical value shape when this pull back is applied to an element. + + Args: + element: The element that the pull back is applied to + + Returns: + The value shape when the pull back is applied to the given element + """ + gdim = element.cell.geometric_dimension() + return (gdim, gdim) + + +class MixedPullback(AbstractPullback): + """Pull back for a mixed element.""" + + def __init__(self, element: _AbstractFiniteElement): + """Initalise. + + Args: + element: The mixed element + """ + self._element = element + + def __repr__(self) -> str: + """Return a representation of the object.""" + return f"MixedPullback({self._element!r})" + + @property + def is_identity(self) -> bool: + """Is this pull back the identity (or the identity applied to mutliple components).""" + return all(e.pullback.is_identity for e in self._element.sub_elements) + + def apply(self, expr): + """Apply the pull back. + + Args: + expr: A function on a physical cell + + Returns: The function pulled back to the reference cell + """ + rflat = [expr[idx] for idx in np.ndindex(expr.ufl_shape)] + g_components = [] + offset = 0 + # For each unique piece in reference space, apply the appropriate pullback + for subelem in self._element.sub_elements: + rsub = as_tensor(np.asarray( + rflat[offset: offset + subelem.reference_value_size] + ).reshape(subelem.reference_value_shape)) + rmapped = subelem.pullback.apply(rsub) + # Flatten into the pulled back expression for the whole thing + g_components.extend([rmapped[idx] for idx in np.ndindex(rmapped.ufl_shape)]) + offset += subelem.reference_value_size + # And reshape appropriately + f = as_tensor(np.asarray(g_components).reshape(self._element.value_shape)) + if f.ufl_shape != self._element.value_shape: + raise ValueError("Expecting pulled back expression with shape " + f"'{self._element.value_shape}', got '{f.ufl_shape}'") + return f + + def physical_value_shape(self, element) -> typing.Tuple[int, ...]: + """Get the physical value shape when this pull back is applied to an element. + + Args: + element: The element that the pull back is applied to + + Returns: + The value shape when the pull back is applied to the given element + """ + assert element == self._element + dim = sum(e.value_size for e in self._element.sub_elements) + return (dim, ) + + +class SymmetricPullback(AbstractPullback): + """Pull back for an element with symmetry.""" + + def __init__(self, element: _AbstractFiniteElement, symmetry: typing.Dict[typing.tuple[int, ...], int]): + """Initalise. + + Args: + element: The element + symmetry: A dictionary mapping from the component in physical space to the local component + """ + self._element = element + self._symmetry = symmetry + + self._sub_element_value_shape = element.sub_elements[0].value_shape + for e in element.sub_elements: + if e.value_shape != self._sub_element_value_shape: + raise ValueError("Sub-elements must all have the same value shape.") + self._block_shape = tuple(i + 1 for i in max(symmetry.keys())) + + def __repr__(self) -> str: + """Return a representation of the object.""" + return f"SymmetricPullback({self._element!r}, {self._symmetry!r})" + + @property + def is_identity(self) -> bool: + """Is this pull back the identity (or the identity applied to mutliple components).""" + return all(e.pullback.is_identity for e in self._element.sub_elements) + + def apply(self, expr): + """Apply the pull back. + + Args: + expr: A function on a physical cell + + Returns: The function pulled back to the reference cell + """ + rflat = [expr[idx] for idx in np.ndindex(expr.ufl_shape)] + g_components = [] + offsets = [0] + for subelem in self._element.sub_elements: + offsets.append(offsets[-1] + subelem.reference_value_size) + # For each unique piece in reference space, apply the appropriate pullback + for component in np.ndindex(self._block_shape): + i = self._symmetry[component] + subelem = self._element.sub_elements[i] + rsub = as_tensor(np.asarray( + rflat[offsets[i]:offsets[i+1]] + ).reshape(subelem.reference_value_shape)) + rmapped = subelem.pullback.apply(rsub) + # Flatten into the pulled back expression for the whole thing + g_components.extend([rmapped[idx] for idx in np.ndindex(rmapped.ufl_shape)]) + # And reshape appropriately + f = as_tensor(np.asarray(g_components).reshape(self._element.value_shape)) + if f.ufl_shape != self._element.value_shape: + raise ValueError(f"Expecting pulled back expression with shape " + f"'{self._element.value_shape}', got '{f.ufl_shape}'") + return f + + def physical_value_shape(self, element) -> typing.Tuple[int, ...]: + """Get the physical value shape when this pull back is applied to an element. + + Args: + element: The element that the pull back is applied to + + Returns: + The value shape when the pull back is applied to the given element + """ + assert element == self._element + return tuple(i + 1 for i in max(self._symmetry.keys())) + + +class PhysicalPullback(AbstractPullback): + """Physical pull back. + + This should probably be removed. + """ + + def __repr__(self) -> str: + """Return a representation of the object.""" + return "PhysicalPullback()" + + @property + def is_identity(self) -> bool: + """Is this pull back the identity (or the identity applied to mutliple components).""" + return True + + def apply(self, expr): + """Apply the pull back. + + Args: + expr: A function on a physical cell + + Returns: The function pulled back to the reference cell + """ + return expr + + def physical_value_shape(self, element) -> typing.Tuple[int, ...]: + """Get the physical value shape when this pull back is applied to an element. + + Args: + element: The element that the pull back is applied to + + Returns: + The value shape when the pull back is applied to the given element + """ + raise NotImplementedError() + + +class CustomPullback(AbstractPullback): + """Custom pull back. + + This should probably be removed. + """ + + def __repr__(self) -> str: + """Return a representation of the object.""" + return "CustomPullback()" + + @property + def is_identity(self) -> bool: + """Is this pull back the identity (or the identity applied to mutliple components).""" + return True + + def apply(self, expr): + """Apply the pull back. + + Args: + expr: A function on a physical cell + + Returns: The function pulled back to the reference cell + """ + return expr + + def physical_value_shape(self, element) -> typing.Tuple[int, ...]: + """Get the physical value shape when this pull back is applied to an element. + + Args: + element: The element that the pull back is applied to + + Returns: + The value shape when the pull back is applied to the given element + """ + raise NotImplementedError() + + +class UndefinedPullback(AbstractPullback): + """Undefined pull back. + + This should probably be removed. + """ + + def __repr__(self) -> str: + """Return a representation of the object.""" + return "UndefinedPullback()" + + @property + def is_identity(self) -> bool: + """Is this pull back the identity (or the identity applied to mutliple components).""" + return True + + def physical_value_shape(self, element) -> typing.Tuple[int, ...]: + """Get the physical value shape when this pull back is applied to an element. + + Args: + element: The element that the pull back is applied to + + Returns: + The value shape when the pull back is applied to the given element + """ + raise NotImplementedError() + + +identity_pullback = IdentityPullback() +covariant_piola = CovariantPiola() +contravariant_piola = ContravariantPiola() +l2_piola = L2Piola() +double_covariant_piola = DoubleCovariantPiola() +double_contravariant_piola = DoubleContravariantPiola() +physical_pullback = PhysicalPullback() +custom_pullback = CustomPullback() +undefined_pullback = UndefinedPullback() diff --git a/ufl/referencevalue.py b/ufl/referencevalue.py index c1d13bcf1..5a4c5bb11 100644 --- a/ufl/referencevalue.py +++ b/ufl/referencevalue.py @@ -5,9 +5,9 @@ # # SPDX-License-Identifier: LGPL-3.0-or-later -from ufl.core.ufl_type import ufl_type from ufl.core.operator import Operator from ufl.core.terminal import FormArgument +from ufl.core.ufl_type import ufl_type @ufl_type(num_ops=1, @@ -28,7 +28,7 @@ def __init__(self, f): @property def ufl_shape(self): """Get the UFL shape.""" - return self.ufl_operands[0].ufl_element().reference_value_shape() + return self.ufl_operands[0].ufl_element().reference_value_shape def evaluate(self, x, mapping, component, index_values, derivatives=()): """Get child from mapping and return the component asked for.""" diff --git a/ufl/restriction.py b/ufl/restriction.py index 77b03c71a..2871cd53f 100644 --- a/ufl/restriction.py +++ b/ufl/restriction.py @@ -6,12 +6,12 @@ # SPDX-License-Identifier: LGPL-3.0-or-later from ufl.core.operator import Operator -from ufl.precedence import parstr from ufl.core.ufl_type import ufl_type - +from ufl.precedence import parstr # --- Restriction operators --- + @ufl_type(is_abstract=True, num_ops=1, inherit_shape_from_operand=0, diff --git a/ufl/sobolevspace.py b/ufl/sobolevspace.py index b697a0685..9500ae422 100644 --- a/ufl/sobolevspace.py +++ b/ufl/sobolevspace.py @@ -18,6 +18,8 @@ from functools import total_ordering from math import inf, isinf +__all_classes__ = ["SobolevSpace", "DirectionalSobolevSpace"] + @total_ordering class SobolevSpace(object): @@ -83,24 +85,13 @@ def __contains__(self, other): 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 self in other.sobolev_space().parents + return (other.sobolev_space == self or + self in other.sobolev_space.parents) def __lt__(self, other): """In common with intrinsic Python sets, < indicates "is a proper subset of".""" return other in self.parents - def __call__(self, element): - """Syntax shortcut to create a HDivElement or HCurlElement.""" - if self.name == "HDiv": - from ufl.finiteelement import HDivElement - return HDivElement(element) - elif self.name == "HCurl": - from ufl.finiteelement import HCurlElement - return HCurlElement(element) - raise NotImplementedError( - "SobolevSpace has no call operator (only the specific HDiv and HCurl instances)." - ) - @total_ordering class DirectionalSobolevSpace(SobolevSpace): @@ -140,9 +131,9 @@ def __contains__(self, other): 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)) + return (other.sobolev_space == self or + all(self[i] in other.sobolev_space.parents + for i in self._spatial_indices)) def __eq__(self, other): """Check equality.""" diff --git a/ufl/sorting.py b/ufl/sorting.py index 445fa0739..5efe2a44a 100644 --- a/ufl/sorting.py +++ b/ufl/sorting.py @@ -14,7 +14,6 @@ from functools import cmp_to_key -from ufl.core.expr import Expr from ufl.argument import Argument from ufl.coefficient import Coefficient from ufl.core.multiindex import FixedIndex, MultiIndex @@ -98,7 +97,7 @@ def _cmp_terminal_by_repr(a, b): # Hack up a MultiFunction-like type dispatch for terminal comparisons -_terminal_cmps = [_cmp_terminal_by_repr] * Expr._ufl_num_typecodes_ +_terminal_cmps = {} _terminal_cmps[MultiIndex._ufl_typecode_] = _cmp_multi_index _terminal_cmps[Argument._ufl_typecode_] = _cmp_argument _terminal_cmps[Coefficient._ufl_typecode_] = _cmp_coefficient @@ -120,7 +119,11 @@ def cmp_expr(a, b): # Now we know that the type is the same, check further based # on type specific properties. if a._ufl_is_terminal_: - c = _terminal_cmps[x](a, b) + if x in _terminal_cmps: + c = _terminal_cmps[x](a, b) + else: + c = _cmp_terminal_by_repr(a, b) + if c: return c else: diff --git a/ufl/split_functions.py b/ufl/split_functions.py index f285f8ea5..886c98915 100644 --- a/ufl/split_functions.py +++ b/ufl/split_functions.py @@ -7,12 +7,11 @@ # # Modified by Anders Logg, 2008 -from ufl.utils.sequences import product -from ufl.finiteelement import TensorElement -from ufl.tensors import as_vector, as_matrix, ListTensor from ufl.indexed import Indexed from ufl.permutation import compute_indices +from ufl.tensors import ListTensor, as_matrix, as_vector from ufl.utils.indexflattening import flatten_multiindex, shape_to_strides +from ufl.utils.sequences import product def split(v): @@ -50,19 +49,15 @@ def split(v): # Special case: simple element, just return function in a tuple element = v.ufl_element() - if element.num_sub_elements() == 0: + if element.num_sub_elements == 0: assert end is None return (v,) - if isinstance(element, TensorElement): - if element.symmetry(): - raise ValueError("Split not implemented for symmetric tensor elements.") - if len(v.ufl_shape) != 1: raise ValueError("Don't know how to split tensor valued mixed functions without flattened index space.") # Compute value size and set default range end - value_size = product(element.value_shape()) + value_size = element.value_size if end is None: end = value_size else: @@ -70,19 +65,22 @@ def split(v): # corresponding to beginning of range j = begin while True: - sub_i, j = element.extract_subelement_component(j) - element = element.sub_elements()[sub_i] + for e in element.sub_elements: + if j < e.value_size: + element = e + break + j -= e.value_size # Then break when we find the subelement that covers the whole range - if product(element.value_shape()) == (end - begin): + if element.value_size == (end - begin): break # Build expressions representing the subfunction of v for each subelement offset = begin sub_functions = [] - for i, e in enumerate(element.sub_elements()): + for i, e in enumerate(element.sub_elements): # Get shape, size, indices, and v components # corresponding to subelement value - shape = e.value_shape() + shape = e.value_shape strides = shape_to_strides(shape) rank = len(shape) sub_size = product(shape) diff --git a/ufl/tensoralgebra.py b/ufl/tensoralgebra.py index a0291d060..733b24388 100644 --- a/ufl/tensoralgebra.py +++ b/ufl/tensoralgebra.py @@ -5,13 +5,13 @@ # # SPDX-License-Identifier: LGPL-3.0-or-later +from ufl.algebra import Conj, Operator +from ufl.constantvalue import Zero from ufl.core.expr import ufl_err_str from ufl.core.ufl_type import ufl_type -from ufl.constantvalue import Zero -from ufl.algebra import Operator, Conj +from ufl.index_combination_utils import merge_nonoverlapping_indices from ufl.precedence import parstr from ufl.sorting import sorted_expr -from ufl.index_combination_utils import merge_nonoverlapping_indices # Algebraic operations on tensors: # FloatValues: diff --git a/ufl/tensors.py b/ufl/tensors.py index d89045ce9..2fe829990 100644 --- a/ufl/tensors.py +++ b/ufl/tensors.py @@ -7,17 +7,17 @@ # # Modified by Massimiliano Leoni, 2016. -from ufl.core.ufl_type import ufl_type +from ufl.constantvalue import Zero, as_ufl from ufl.core.expr import Expr +from ufl.core.multiindex import FixedIndex, Index, MultiIndex, indices from ufl.core.operator import Operator -from ufl.constantvalue import as_ufl, Zero -from ufl.core.multiindex import Index, FixedIndex, MultiIndex, indices -from ufl.indexed import Indexed +from ufl.core.ufl_type import ufl_type from ufl.index_combination_utils import remove_indices - +from ufl.indexed import Indexed # --- Classes representing tensors of UFL expressions --- + @ufl_type(is_shaping=True, num_ops="varying", inherit_indices_from_operand=0) class ListTensor(Operator): """Wraps a list of expressions into a tensor valued expression of one higher rank.""" diff --git a/ufl/utils/sequences.py b/ufl/utils/sequences.py index 20ebe1dc9..9904287c4 100644 --- a/ufl/utils/sequences.py +++ b/ufl/utils/sequences.py @@ -7,6 +7,7 @@ # SPDX-License-Identifier: LGPL-3.0-or-later from functools import reduce + import numpy diff --git a/ufl/variable.py b/ufl/variable.py index ee8587119..84a795434 100644 --- a/ufl/variable.py +++ b/ufl/variable.py @@ -8,12 +8,12 @@ # # SPDX-License-Identifier: LGPL-3.0-or-later -from ufl.utils.counted import Counted +from ufl.constantvalue import as_ufl from ufl.core.expr import Expr -from ufl.core.ufl_type import ufl_type -from ufl.core.terminal import Terminal from ufl.core.operator import Operator -from ufl.constantvalue import as_ufl +from ufl.core.terminal import Terminal +from ufl.core.ufl_type import ufl_type +from ufl.utils.counted import Counted @ufl_type() From 4b6142a7c69f5360af10897c98aa9fec0f7d96ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B8rgen=20Schartum=20Dokken?= Date: Mon, 16 Oct 2023 16:39:34 +0200 Subject: [PATCH 05/16] Remove deprecated functionality (attach_operators_from_hash_data). (#220) * Remove deprecated functionality (attach_operators_from_hash_data). The deprecation was introduced 4 months ago (https://github.com/FEniCS/ufl/pull/168). Since all the core objects of UFL is using it (Mesh and FunctionSpace) it means that pytest is throwing tons of deprecation warnings at import: ```python python3 -W error::DeprecationWarning -c "import ufl.Mesh" Traceback (most recent call last): File "", line 1, in File "/root/shared/ufl/__init__.py", line 265, in from ufl.domain import as_domain, AbstractDomain, Mesh, MeshView, TensorProductMesh File "/root/shared/ufl/domain.py", line 190, in class TensorProductMesh(AbstractDomain): File "/root/shared/ufl/core/ufl_type.py", line 56, in attach_operators_from_hash_data warnings.warn("attach_operators_from_hash_data deprecated, please use UFLObject instead.", DeprecationWarning) DeprecationWarning: attach_operators_from_hash_data deprecated, please use UFLObject instead. ``` * Flake8 * implement __str__ for function spaces --------- Co-authored-by: Matthew Scroggs --- test/test_strip_forms.py | 8 +++--- ufl/core/ufl_id.py | 2 -- ufl/core/ufl_type.py | 29 ---------------------- ufl/domain.py | 8 +++--- ufl/functionspace.py | 53 +++++++++++++++++++++------------------- 5 files changed, 34 insertions(+), 66 deletions(-) diff --git a/test/test_strip_forms.py b/test/test_strip_forms.py index 9a74ac506..27e75869d 100644 --- a/test/test_strip_forms.py +++ b/test/test_strip_forms.py @@ -4,7 +4,7 @@ from ufl import Coefficient, Constant, FunctionSpace, Mesh, TestFunction, TrialFunction, dx, grad, inner, triangle from ufl.algorithms import replace_terminal_data, strip_terminal_data from ufl.core.ufl_id import attach_ufl_id -from ufl.core.ufl_type import attach_operators_from_hash_data +from ufl.core.ufl_type import UFLObject from ufl.finiteelement import FiniteElement from ufl.pullback import identity_pullback from ufl.sobolevspace import H1 @@ -13,16 +13,14 @@ """The minimum value returned by sys.getrefcount.""" -@attach_operators_from_hash_data @attach_ufl_id -class AugmentedMesh(Mesh): +class AugmentedMesh(Mesh, UFLObject): def __init__(self, *args, data): super().__init__(*args) self.data = data -@attach_operators_from_hash_data -class AugmentedFunctionSpace(FunctionSpace): +class AugmentedFunctionSpace(FunctionSpace, UFLObject): def __init__(self, *args, data): super().__init__(*args) self.data = data diff --git a/ufl/core/ufl_id.py b/ufl/core/ufl_id.py index 1af849ba1..bc1206761 100644 --- a/ufl/core/ufl_id.py +++ b/ufl/core/ufl_id.py @@ -54,8 +54,6 @@ def init_ufl_id(self, ufl_id): return init_ufl_id # Modify class: - if hasattr(cls, "__slots__"): - assert "_ufl_id" in cls.__slots__ cls._ufl_global_id = 0 cls.ufl_id = _get_ufl_id cls._init_ufl_id = _init_ufl_id(cls) diff --git a/ufl/core/ufl_type.py b/ufl/core/ufl_type.py index b3bfa0bd6..e19a9c340 100644 --- a/ufl/core/ufl_type.py +++ b/ufl/core/ufl_type.py @@ -11,10 +11,8 @@ from __future__ import annotations import typing -import warnings from abc import ABC, abstractmethod -# Avoid circular import import ufl.core as core from ufl.core.compute_expr_hash import compute_expr_hash from ufl.utils.formatting import camel2underscore @@ -48,33 +46,6 @@ def __ne__(self, other): return not self.__eq__(other) -def attach_operators_from_hash_data(cls): - """Class decorator to attach ``__hash__``, ``__eq__`` and ``__ne__`` implementations. - - These are implemented in terms of a ``._ufl_hash_data()`` method on the class, - which should return a tuple or hashable and comparable data. - """ - warnings.warn("attach_operators_from_hash_data deprecated, please use UFLObject instead.", DeprecationWarning) - assert hasattr(cls, "_ufl_hash_data_") - - def __hash__(self): - """__hash__ implementation attached in attach_operators_from_hash_data.""" - return hash(self._ufl_hash_data_()) - cls.__hash__ = __hash__ - - def __eq__(self, other): - """__eq__ implementation attached in attach_operators_from_hash_data.""" - return type(self) is type(other) and self._ufl_hash_data_() == other._ufl_hash_data_() - cls.__eq__ = __eq__ - - def __ne__(self, other): - """__ne__ implementation attached in attach_operators_from_hash_data.""" - return not self.__eq__(other) - cls.__ne__ = __ne__ - - return cls - - def get_base_attr(cls, name): """Return first non-``None`` attribute of given name among base classes.""" for base in cls.mro(): diff --git a/ufl/domain.py b/ufl/domain.py index ecdea376a..f438006d0 100644 --- a/ufl/domain.py +++ b/ufl/domain.py @@ -11,7 +11,7 @@ from ufl.cell import AbstractCell from ufl.core.ufl_id import attach_ufl_id -from ufl.core.ufl_type import attach_operators_from_hash_data +from ufl.core.ufl_type import UFLObject from ufl.corealg.traversal import traverse_unique_terminals from ufl.sobolevspace import H1 @@ -52,9 +52,8 @@ def topological_dimension(self): # AbstractDomain.__init__(self, geometric_dimension, geometric_dimension) -@attach_operators_from_hash_data @attach_ufl_id -class Mesh(AbstractDomain): +class Mesh(AbstractDomain, UFLObject): """Symbolic representation of a mesh.""" def __init__(self, coordinate_element, ufl_id=None, cargo=None): @@ -122,9 +121,8 @@ def _ufl_sort_key_(self): "Mesh", typespecific) -@attach_operators_from_hash_data @attach_ufl_id -class MeshView(AbstractDomain): +class MeshView(AbstractDomain, UFLObject): """Symbolic representation of a mesh.""" def __init__(self, mesh, topological_dimension, ufl_id=None): diff --git a/ufl/functionspace.py b/ufl/functionspace.py index e2cc86dbc..17de9d1a8 100644 --- a/ufl/functionspace.py +++ b/ufl/functionspace.py @@ -9,7 +9,7 @@ # Modified by Massimiliano Leoni, 2016 # Modified by Cecile Daversin-Catty, 2018 -from ufl.core.ufl_type import attach_operators_from_hash_data +from ufl.core.ufl_type import UFLObject from ufl.domain import join_domains from ufl.duals import is_dual, is_primal @@ -29,13 +29,11 @@ class AbstractFunctionSpace(object): def ufl_sub_spaces(self): """Return ufl sub spaces.""" raise NotImplementedError( - "Missing implementation of IFunctionSpace.ufl_sub_spaces in %s." - % self.__class__.__name__ + f"Missing implementation of ufl_sub_spaces in {self.__class__.__name__}." ) -@attach_operators_from_hash_data -class BaseFunctionSpace(AbstractFunctionSpace): +class BaseFunctionSpace(AbstractFunctionSpace, UFLObject): """Base function space.""" def __init__(self, domain, element): @@ -109,13 +107,10 @@ def _ufl_signature_data_(self, renumbering, name=None): def __repr__(self): """Representation.""" - r = "BaseFunctionSpace(%s, %s)" % (repr(self._ufl_domain), - repr(self._ufl_element)) - return r + return f"BaseFunctionSpace({self._ufl_domain!r}, {self._ufl_element!r})" -@attach_operators_from_hash_data -class FunctionSpace(BaseFunctionSpace): +class FunctionSpace(BaseFunctionSpace, UFLObject): """Representation of a Function space.""" _primal = True @@ -135,13 +130,14 @@ def _ufl_signature_data_(self, renumbering): def __repr__(self): """Representation.""" - r = "FunctionSpace(%s, %s)" % (repr(self._ufl_domain), - repr(self._ufl_element)) - return r + return f"FunctionSpace({self._ufl_domain!r}, {self._ufl_element!r})" + def __str__(self): + """String.""" + return f"FunctionSpace({self._ufl_domain}, {self._ufl_element})" -@attach_operators_from_hash_data -class DualSpace(BaseFunctionSpace): + +class DualSpace(BaseFunctionSpace, UFLObject): """Representation of a Dual space.""" _primal = False @@ -165,13 +161,14 @@ def _ufl_signature_data_(self, renumbering): def __repr__(self): """Representation.""" - r = "DualSpace(%s, %s)" % (repr(self._ufl_domain), - repr(self._ufl_element)) - return r + return f"DualSpace({self._ufl_domain!r}, {self._ufl_element!r})" + + def __str__(self): + """String.""" + return f"DualSpace({self._ufl_domain}, {self._ufl_element})" -@attach_operators_from_hash_data -class TensorProductFunctionSpace(AbstractFunctionSpace): +class TensorProductFunctionSpace(AbstractFunctionSpace, UFLObject): """Tensor product function space.""" def __init__(self, *function_spaces): @@ -196,12 +193,14 @@ def _ufl_signature_data_(self, renumbering): def __repr__(self): """Representation.""" - r = "TensorProductFunctionSpace(*%s)" % repr(self._ufl_function_spaces) - return r + return f"TensorProductFunctionSpace(*{self._ufl_function_spaces!r})" + def __str__(self): + """String.""" + return self.__repr__() -@attach_operators_from_hash_data -class MixedFunctionSpace(AbstractFunctionSpace): + +class MixedFunctionSpace(AbstractFunctionSpace, UFLObject): """Mixed function space.""" def __init__(self, *args): @@ -297,4 +296,8 @@ def _ufl_signature_data_(self, renumbering): def __repr__(self): """Representation.""" - return f"MixedFunctionSpace(*{self._ufl_function_spaces})" + return f"MixedFunctionSpace(*{self._ufl_function_spaces!r})" + + def __str__(self): + """String.""" + return self.__repr__() From f132188759a248d6edfc6eaf05e623b898bfcffd Mon Sep 17 00:00:00 2001 From: "Garth N. Wells" Date: Mon, 16 Oct 2023 17:22:16 +0100 Subject: [PATCH 06/16] Remove `setup.cfg` (#223) * Remove setup.cfg * Fixes * Update doc check * CI fix * Update --- .flake8 | 6 +++ .github/workflows/pythonapp.yml | 2 +- pyproject.toml | 50 ++++++++++++++++++++++- setup.cfg | 71 --------------------------------- 4 files changed, 56 insertions(+), 73 deletions(-) create mode 100644 .flake8 delete mode 100644 setup.cfg diff --git a/.flake8 b/.flake8 new file mode 100644 index 000000000..cd8e21ed8 --- /dev/null +++ b/.flake8 @@ -0,0 +1,6 @@ +[flake8] +max-line-length = 120 +builtins = ufl +exclude = doc/sphinx/source/conf.py +per-file-ignores = + */__init__.py: F401 diff --git a/.github/workflows/pythonapp.yml b/.github/workflows/pythonapp.yml index 1d916b7b5..032bec747 100644 --- a/.github/workflows/pythonapp.yml +++ b/.github/workflows/pythonapp.yml @@ -34,7 +34,7 @@ jobs: flake8 --statistics . - name: Check documentation style run: | - python -m pip install pydocstyle + python -m pip install pydocstyle[toml] python -m pydocstyle ufl/ - name: Install UFL run: python -m pip install .[ci] diff --git a/pyproject.toml b/pyproject.toml index cfcfb19c9..4fcaab701 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,4 +1,52 @@ [build-system] requires = ["setuptools>=62", "wheel"] - build-backend = "setuptools.build_meta" + +[project] +name = "fenics-ufl" +version = "2023.3.0.dev0" +authors = [{email="fenics-dev@googlegroups.com"}, {name="FEniCS Project"}] +maintainers = [{email="fenics-dev@googlegroups.com"}, {name="FEniCS Project Steering Council"}] +description = "Unified Form Language" +readme = "README.rst" +license = {file = "LICENSE"} +requires-python = ">=3.8.0" +dependencies = ["numpy"] + +[project.urls] +homepage = "https://fenicsproject.org" +repository = "https://github.com/fenics/ufl.git" +documentation = "https://docs.fenicsproject.org" +issues = "https://github.com/FEniCS/ufl/issues" +funding = "https://numfocus.org/donate" + +[project.optional-dependencies] +lint = ["flake8", "pydocstyle[toml]"] +docs = ["sphinx", "sphinx_rtd_theme"] +test = ["pytest"] +ci = [ + "coveralls", + "coverage", + "pytest-cov", + "pytest-xdist", + "fenics-ufl[docs]", + "fenics-ufl[lint]", + "fenics-ufl[test]", +] + +[tool.setuptools] +packages = [ + "ufl", + "ufl.algorithms", + "ufl.core", + "ufl.corealg", + "ufl.formatting", + "ufl.legacy", + "ufl.utils", +] + +[tool.pydocstyle] +convention = "google" + +[itool.sort] +line_length = 120 diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index 4bbece177..000000000 --- a/setup.cfg +++ /dev/null @@ -1,71 +0,0 @@ -# Setuptools does not yet support modern pyproject.toml but will do so in the -# future -[metadata] -name = fenics-ufl -version = 2023.3.0.dev0 -author = FEniCS Project Contributors -email = fenics-dev@googlegroups.com -maintainer = FEniCS Project Steering Council -description = Unified Form Language -url = https://github.com/FEniCS/ufl -project_urls = - Homepage = https://fenicsproject.org - Documentation = https://fenics.readthedocs.io/projects/ufl - Issues = https://github.com/FEniCS/ufl/issues - Funding = https://numfocus.org/donate -long_description = file: README.rst -long_description_content_type = text/x-rst -license=LGPL-3.0-or-later -classifiers = - Development Status :: 5 - Production/Stable - Intended Audience :: Developers - Intended Audience :: Science/Research - License :: OSI Approved :: GNU Lesser General Public License v3 or later (LGPLv3+) - Operating System :: POSIX - Operating System :: POSIX :: Linux - Operating System :: MacOS :: MacOS X - Programming Language :: Python - Programming Language :: Python :: 3 - Programming Language :: Python :: 3.8 - Programming Language :: Python :: 3.9 - Programming Language :: Python :: 3.10 - Programming Language :: Python :: 3.11 - Topic :: Scientific/Engineering :: Mathematics - Topic :: Software Development :: Libraries :: Python Modules - -[options] -packages = find: -include_package_data = True -zip_safe = False -python_requires = >= 3.8 -setup_requires = - setuptools >= 62 - wheel -install_requires = - numpy - -[options.extras_require] -docs = sphinx; sphinx_rtd_theme -lint = flake8; pydocstyle[toml] -test = pytest -ci = - coverage - coveralls - pytest-cov - pytest-xdist - fenics-ufl[docs] - fenics-ufl[lint] - fenics-ufl[test] - -[flake8] -max-line-length = 120 -builtins = ufl -exclude = doc/sphinx/source/conf.py -per-file-ignores = - */__init__.py: F401 - -[pydocstyle] -convention = google - -[isort] -line_length = 120 From e1e8e5627b0c7db17687bdb32857820ac4ddc072 Mon Sep 17 00:00:00 2001 From: Matthew Scroggs Date: Wed, 18 Oct 2023 14:46:03 +0100 Subject: [PATCH 07/16] Fix coefficients optional kwarg when calling a Form (#226) * None * fix typo --- ufl/form.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ufl/form.py b/ufl/form.py index 4fb474f1c..ed3040477 100644 --- a/ufl/form.py +++ b/ufl/form.py @@ -227,7 +227,7 @@ def __call__(self, *args, **kwargs): raise ValueError(f"Need {len(arguments)} arguments to form(), got {len(args)}.") repdict.update(zip(arguments, args)) - coefficients = kwargs.pop("coefficients") + coefficients = kwargs.pop("coefficients", None) if kwargs: raise ValueError(f"Unknown kwargs {list(kwargs)}.") From 1bb16223c594f615ccad9406a726452f14920535 Mon Sep 17 00:00:00 2001 From: Connor Pierce Date: Thu, 26 Oct 2023 05:09:39 -0500 Subject: [PATCH 08/16] Replace ineffective `continue` with `break` (#229) --- ufl/utils/sorting.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ufl/utils/sorting.py b/ufl/utils/sorting.py index cb177737d..bf02cfdf6 100644 --- a/ufl/utils/sorting.py +++ b/ufl/utils/sorting.py @@ -22,7 +22,7 @@ def topological_sorting(nodes, edges): for es in edges.values(): if node in es and node in S: S.remove(node) - continue + break while S: node = S.pop(0) From a4c7280574789b7bda9b779fff65bc7d3bf32644 Mon Sep 17 00:00:00 2001 From: Francesco Ballarin Date: Fri, 27 Oct 2023 11:48:23 +0200 Subject: [PATCH 09/16] Fix isort section in pyproject.toml (#231) --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 4fcaab701..ec823e1d9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -48,5 +48,5 @@ packages = [ [tool.pydocstyle] convention = "google" -[itool.sort] +[tool.isort] line_length = 120 From 6cf3e61dfa3449b848a714a57247580fd34a7c7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B8rgen=20Schartum=20Dokken?= Date: Fri, 27 Oct 2023 11:56:34 +0200 Subject: [PATCH 10/16] We do require pip>=22.3 for editable installs (PEP 660). (#227) This does not quite resolve issues with users trying to install with an outdated pip version, as the upgrade happens at runtime, and doesn't change the current version of pip that is executed. Co-authored-by: Jack S. Hale --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index ec823e1d9..28cf84116 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,5 @@ [build-system] -requires = ["setuptools>=62", "wheel"] +requires = ["setuptools>=62", "wheel", "pip>=22.3"] build-backend = "setuptools.build_meta" [project] From 1c1bfbe514ec664218cb1a5cdffcdbe22a89ee3e Mon Sep 17 00:00:00 2001 From: "Jack S. Hale" Date: Fri, 27 Oct 2023 12:02:05 +0200 Subject: [PATCH 11/16] Update wheel builder action. (#230) --- .github/workflows/build-wheels.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build-wheels.yml b/.github/workflows/build-wheels.yml index e7f65cb77..f1d86945d 100644 --- a/.github/workflows/build-wheels.yml +++ b/.github/workflows/build-wheels.yml @@ -40,7 +40,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout UFL - uses: actions/checkout@v2 + uses: actions/checkout@v4 with: ref: ${{ github.event.inputs.ufl_ref }} @@ -50,7 +50,7 @@ jobs: - name: Build sdist and wheel run: python -m build . - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@v3 with: path: dist/* @@ -65,7 +65,7 @@ jobs: path: dist - name: Push to PyPI - uses: pypa/gh-action-pypi-publish@v1.5.0 + uses: pypa/gh-action-pypi-publish@release/v1 if: ${{ github.event.inputs.pypi_publish == 'true' }} with: user: __token__ @@ -73,7 +73,7 @@ jobs: repository_url: https://upload.pypi.org/legacy/ - name: Push to Test PyPI - uses: pypa/gh-action-pypi-publish@v1.5.0 + uses: pypa/gh-action-pypi-publish@release/v1 if: ${{ github.event.inputs.test_pypi_publish == 'true' }} with: user: __token__ From 41f30d0970de4b84d5743ce537640cb082fd6e78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Delaporte-Mathurin?= <40028739+RemDelaporteMathurin@users.noreply.github.com> Date: Thu, 2 Nov 2023 03:31:52 -0400 Subject: [PATCH 12/16] fixed typo (#233) --- ufl/conditional.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ufl/conditional.py b/ufl/conditional.py index b9f9bda16..5d0fb910d 100644 --- a/ufl/conditional.py +++ b/ufl/conditional.py @@ -267,7 +267,7 @@ class Conditional(Operator): def __init__(self, condition, true_value, false_value): """Initialise.""" if not isinstance(condition, Condition): - raise ValueError("Expectiong condition as first argument.") + raise ValueError("Expecting condition as first argument.") true_value = as_ufl(true_value) false_value = as_ufl(false_value) tsh = true_value.ufl_shape From b210795ce798e93be27f3fb72b476d930d6522d3 Mon Sep 17 00:00:00 2001 From: Matthew Scroggs Date: Thu, 2 Nov 2023 07:32:53 +0000 Subject: [PATCH 13/16] Remove ufl.legacy (#224) * remove ufl.legacy * finat branch * remove test of legacy elements * remove .legacy --- .github/workflows/tsfc-tests.yml | 4 +- pyproject.toml | 1 - test/test_legacy.py | 254 ------------- ufl/legacy/__init__.py | 25 -- ufl/legacy/brokenelement.py | 53 --- ufl/legacy/elementlist.py | 481 ------------------------ ufl/legacy/enrichedelement.py | 164 --------- ufl/legacy/finiteelement.py | 235 ------------ ufl/legacy/finiteelementbase.py | 276 -------------- ufl/legacy/hdivcurl.py | 213 ----------- ufl/legacy/mixedelement.py | 562 ----------------------------- ufl/legacy/restrictedelement.py | 113 ------ ufl/legacy/tensorproductelement.py | 140 ------- 13 files changed, 2 insertions(+), 2519 deletions(-) delete mode 100644 test/test_legacy.py delete mode 100644 ufl/legacy/__init__.py delete mode 100644 ufl/legacy/brokenelement.py delete mode 100644 ufl/legacy/elementlist.py delete mode 100644 ufl/legacy/enrichedelement.py delete mode 100644 ufl/legacy/finiteelement.py delete mode 100644 ufl/legacy/finiteelementbase.py delete mode 100644 ufl/legacy/hdivcurl.py delete mode 100644 ufl/legacy/mixedelement.py delete mode 100644 ufl/legacy/restrictedelement.py delete mode 100644 ufl/legacy/tensorproductelement.py diff --git a/.github/workflows/tsfc-tests.yml b/.github/workflows/tsfc-tests.yml index a861b4728..45752de0c 100644 --- a/.github/workflows/tsfc-tests.yml +++ b/.github/workflows/tsfc-tests.yml @@ -32,14 +32,14 @@ jobs: with: path: ./tsfc repository: firedrakeproject/tsfc - ref: mscroggs/newfl-legacy + ref: mscroggs/newfl-legacy2 - name: Install tsfc run: | cd tsfc pip install -r requirements-ext.txt pip install git+https://github.com/coneoproject/COFFEE.git#egg=coffee pip install git+https://github.com/firedrakeproject/fiat.git#egg=fenics-fiat - pip install git+https://github.com/FInAT/FInAT.git#egg=finat + pip install git+https://github.com/FInAT/FInAT.git@mscroggs/ufl-elements pip install git+https://github.com/firedrakeproject/loopy.git#egg=loopy pip install .[ci] pip install pytest diff --git a/pyproject.toml b/pyproject.toml index 28cf84116..b3751e708 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -41,7 +41,6 @@ packages = [ "ufl.core", "ufl.corealg", "ufl.formatting", - "ufl.legacy", "ufl.utils", ] diff --git a/test/test_legacy.py b/test/test_legacy.py deleted file mode 100644 index fd3de052f..000000000 --- a/test/test_legacy.py +++ /dev/null @@ -1,254 +0,0 @@ -from ufl import (H1, Coefficient, FunctionSpace, Mesh, dx, hexahedron, identity_pullback, inner, interval, - quadrilateral, tetrahedron, triangle) -from ufl.legacy import FiniteElement, MixedElement, TensorElement, VectorElement, WithMapping - -all_cells = (interval, triangle, tetrahedron, quadrilateral, hexahedron) - - -def test_legacy_vs_new(): - from ufl.finiteelement import FiniteElement as NewFiniteElement - e = FiniteElement("Lagrange", triangle, 1) - new_e = NewFiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1) - assert e.sobolev_space == new_e.sobolev_space - assert e.pullback == new_e.pullback - assert e.embedded_superdegree == new_e.embedded_superdegree - assert e.embedded_subdegree == new_e.embedded_subdegree - assert e.cell == new_e.cell - assert e.reference_value_shape == new_e.reference_value_shape - assert e.value_shape == new_e.value_shape - assert e.reference_value_size == new_e.reference_value_size - assert e.value_size == new_e.value_size - assert e.num_sub_elements == new_e.num_sub_elements - assert e.sub_elements == new_e.sub_elements - assert e.is_cellwise_constant() == new_e.is_cellwise_constant() - - -def test_scalar_galerkin(): - for cell in all_cells: - for p in range(1, 10): - for family in ("Lagrange", "CG", "Discontinuous Lagrange", "DG", "Discontinuous Lagrange L2", "DG L2"): - element = FiniteElement(family, cell, p) - assert element.value_shape == () - assert element == eval(repr(element)) - for p in range(1, 10): - for family in ("TDG", "Discontinuous Taylor"): - element = FiniteElement(family, interval, p) - assert element.value_shape == () - - -def test_vector_galerkin(): - for cell in all_cells: - dim = cell.geometric_dimension() - # shape = () if dim == 1 else (dim,) - shape = (dim,) - for p in range(1, 10): - for family in ("Lagrange", "CG", "Discontinuous Lagrange", "DG", "Discontinuous Lagrange L2", "DG L2"): - element = VectorElement(family, cell, p) - assert element.value_shape == shape - assert element == eval(repr(element)) - for i in range(dim): - c = element.extract_component(i) - assert c[0] == () - - -def test_tensor_galerkin(): - for cell in all_cells: - dim = cell.geometric_dimension() - # shape = () if dim == 1 else (dim,dim) - shape = (dim, dim) - for p in range(1, 10): - for family in ("Lagrange", "CG", "Discontinuous Lagrange", "DG", "Discontinuous Lagrange L2", "DG L2"): - element = TensorElement(family, cell, p) - assert element.value_shape == shape - assert element == eval(repr(element)) - for i in range(dim): - for j in range(dim): - c = element.extract_component((i, j)) - assert c[0] == () - - -def test_tensor_symmetry(): - for cell in all_cells: - dim = cell.geometric_dimension() - for p in range(1, 10): - for s in (None, True, {(0, 1): (1, 0)}): - # Symmetry dict is invalid for interval cell - if isinstance(s, dict) and cell == interval: - continue - - for family in ("Lagrange", "CG", "Discontinuous Lagrange", "DG", "Discontinuous Lagrange L2", "DG L2"): - if isinstance(s, dict): - element = TensorElement( - family, cell, p, shape=(dim, dim), symmetry=s) - else: - element = TensorElement(family, cell, p, symmetry=s) - assert element.value_shape, (dim == dim) - assert element == eval(repr(element)) - for i in range(dim): - for j in range(dim): - c = element.extract_component((i, j)) - assert c[0] == () - - -def test_mixed_tensor_symmetries(): - from ufl.algorithms import expand_compounds, expand_indices - - S = FiniteElement('CG', triangle, 1) - V = VectorElement('CG', triangle, 1) - T = TensorElement('CG', triangle, 1, symmetry=True) - - print(T.pullback) - - # M has dimension 4+1, symmetries are 2->1 - M = T * S - domain = Mesh(VectorElement("Lagrange", triangle, 1)) - m_space = FunctionSpace(domain, M) - P = Coefficient(m_space) - M = inner(P, P) * dx - - M2 = expand_indices(expand_compounds(M)) - assert '[1]' in str(M2) - assert '[2]' not in str(M2) - - # M has dimension 2+(1+4), symmetries are 5->4 - M = V * (S * T) - m_space = FunctionSpace(domain, M) - P = Coefficient(m_space) - M = inner(P, P) * dx - - M2 = expand_indices(expand_compounds(M)) - assert '[4]' in str(M2) - assert '[5]' not in str(M2) - - -def test_bdm(): - for cell in (triangle, tetrahedron): - dim = cell.geometric_dimension() - element = FiniteElement("BDM", cell, 1) - assert element.value_shape == (dim,) - assert element == eval(repr(element)) - - -def test_vector_bdm(): - for cell in (triangle, tetrahedron): - dim = cell.geometric_dimension() - element = VectorElement("BDM", cell, 1) - assert element.value_shape, (dim == dim) - assert element == eval(repr(element)) - - -def test_mtw(): - cell = triangle - element = FiniteElement("MTW", cell, 3) - assert element.value_shape == (cell.geometric_dimension(), ) - assert element == eval(repr(element)) - assert element.mapping() == "contravariant Piola" - - -def test_mixed(): - for cell in (triangle, tetrahedron): - dim = cell.geometric_dimension() - velement = VectorElement("CG", cell, 2) - pelement = FiniteElement("CG", cell, 1) - TH1 = MixedElement(velement, pelement) - TH2 = velement * pelement - assert TH1.value_shape == (dim + 1,) - assert TH2.value_shape == (dim + 1,) - assert repr(TH1) == repr(TH2) - assert TH1 == eval(repr(TH2)) - assert TH2 == eval(repr(TH1)) - - -def test_nested_mixed(): - for cell in (triangle, tetrahedron): - dim = cell.geometric_dimension() - velement = VectorElement("CG", cell, 2) - pelement = FiniteElement("CG", cell, 1) - TH1 = MixedElement((velement, pelement), pelement) - TH2 = velement * pelement * pelement - assert TH1.value_shape == (dim + 2,) - assert TH2.value_shape == (dim + 2,) - assert repr(TH1) == repr(TH2) - assert TH1 == eval(repr(TH2)) - assert TH2 == eval(repr(TH1)) - - -def test_quadrature_scheme(): - for cell in (triangle, tetrahedron): - for q in (None, 1, 2, 3): - element = FiniteElement("CG", cell, 1, quad_scheme=q) - assert element.quadrature_scheme() == q - assert element == eval(repr(element)) - - -def test_missing_cell(): - # These special cases are here to allow missing - # cell in PyDOLFIN Constant and Expression - for cell in (triangle, None): - element = FiniteElement("Real", cell, 0) - assert element == eval(repr(element)) - element = FiniteElement("Undefined", cell, None) - assert element == eval(repr(element)) - element = VectorElement("Lagrange", cell, 1, dim=2) - assert element == eval(repr(element)) - element = TensorElement("DG", cell, 1, shape=(2, 2)) - assert element == eval(repr(element)) - element = TensorElement("DG L2", cell, 1, shape=(2, 2)) - assert element == eval(repr(element)) - - -def test_invalid_degree(): - cell = triangle - for degree in (1, None): - element = FiniteElement("CG", cell, degree) - assert element == eval(repr(element)) - element = VectorElement("CG", cell, degree) - assert element == eval(repr(element)) - - -def test_lobatto(): - cell = interval - for degree in (1, 2, None): - element = FiniteElement("Lob", cell, degree) - assert element == eval(repr(element)) - - element = FiniteElement("Lobatto", cell, degree) - assert element == eval(repr(element)) - - -def test_radau(): - cell = interval - for degree in (0, 1, 2, None): - element = FiniteElement("Rad", cell, degree) - assert element == eval(repr(element)) - - element = FiniteElement("Radau", cell, degree) - assert element == eval(repr(element)) - - -def test_mse(): - for degree in (2, 3, 4, 5): - element = FiniteElement('EGL', interval, degree) - assert element == eval(repr(element)) - - element = FiniteElement('EGL-Edge', interval, degree - 1) - assert element == eval(repr(element)) - - element = FiniteElement('EGL-Edge L2', interval, degree - 1) - assert element == eval(repr(element)) - - for degree in (1, 2, 3, 4, 5): - element = FiniteElement('GLL', interval, degree) - assert element == eval(repr(element)) - - element = FiniteElement('GLL-Edge', interval, degree - 1) - assert element == eval(repr(element)) - - element = FiniteElement('GLL-Edge L2', interval, degree - 1) - assert element == eval(repr(element)) - - -def test_withmapping(): - base = FiniteElement("CG", interval, 1) - element = WithMapping(base, "identity") - assert element == eval(repr(element)) diff --git a/ufl/legacy/__init__.py b/ufl/legacy/__init__.py deleted file mode 100644 index 06a0b12ca..000000000 --- a/ufl/legacy/__init__.py +++ /dev/null @@ -1,25 +0,0 @@ -"""Legacy UFL features.""" -# Copyright (C) 2008-2016 Martin Sandve Alnæs -# -# This file is part of UFL (https://www.fenicsproject.org) -# -# SPDX-License-Identifier: LGPL-3.0-or-later -# -# Modified by Kristian B. Oelgaard -# Modified by Marie E. Rognes 2010, 2012 -# Modified by Andrew T. T. McRae 2014 -# Modified by Lawrence Mitchell 2014 - -import warnings as _warnings - -from ufl.legacy.brokenelement import BrokenElement -from ufl.legacy.enrichedelement import EnrichedElement, NodalEnrichedElement -from ufl.legacy.finiteelement import FiniteElement -from ufl.legacy.finiteelementbase import FiniteElementBase -from ufl.legacy.hdivcurl import HCurlElement, HDivElement, WithMapping -from ufl.legacy.mixedelement import MixedElement, TensorElement, VectorElement -from ufl.legacy.restrictedelement import RestrictedElement -from ufl.legacy.tensorproductelement import TensorProductElement - -_warnings.warn("The features in ufl.legacy are deprecated and will be removed in a future version.", - FutureWarning) diff --git a/ufl/legacy/brokenelement.py b/ufl/legacy/brokenelement.py deleted file mode 100644 index 2563b868f..000000000 --- a/ufl/legacy/brokenelement.py +++ /dev/null @@ -1,53 +0,0 @@ -"""Element.""" -# -*- coding: utf-8 -*- -# Copyright (C) 2014 Andrew T. T. McRae -# -# This file is part of UFL (https://www.fenicsproject.org) -# -# SPDX-License-Identifier: LGPL-3.0-or-later -# -# Modified by Massimiliano Leoni, 2016 - -from ufl.legacy.finiteelementbase import FiniteElementBase -from ufl.sobolevspace import L2 - - -class BrokenElement(FiniteElementBase): - """The discontinuous version of an existing Finite Element space.""" - def __init__(self, element): - """Init.""" - self._element = element - - family = "BrokenElement" - 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 __repr__(self): - """Doc.""" - return f"BrokenElement({repr(self._element)})" - - def mapping(self): - """Doc.""" - return self._element.mapping() - - @property - def sobolev_space(self): - """Return the underlying Sobolev space.""" - return L2 - - def reconstruct(self, **kwargs): - """Doc.""" - return BrokenElement(self._element.reconstruct(**kwargs)) - - def __str__(self): - """Doc.""" - return f"BrokenElement({repr(self._element)})" - - def shortstr(self): - """Format as string for pretty printing.""" - return f"BrokenElement({repr(self._element)})" diff --git a/ufl/legacy/elementlist.py b/ufl/legacy/elementlist.py deleted file mode 100644 index 712b94bb1..000000000 --- a/ufl/legacy/elementlist.py +++ /dev/null @@ -1,481 +0,0 @@ -"""Element. - -This module provides an extensive list of predefined finite element -families. Users or, more likely, form compilers, may register new -elements by calling the function register_element. -""" -# Copyright (C) 2008-2016 Martin Sandve Alnæs and Anders Logg -# -# This file is part of UFL (https://www.fenicsproject.org) -# -# SPDX-License-Identifier: LGPL-3.0-or-later -# -# Modified by Marie E. Rognes , 2010 -# Modified by Lizao Li , 2015, 2016 -# Modified by Massimiliano Leoni, 2016 -# Modified by Robert Kloefkorn, 2022 - -import warnings - -from numpy import asarray - -from ufl.cell import Cell, TensorProductCell -from ufl.sobolevspace import H1, H2, L2, HCurl, HDiv, HDivDiv, HEin, HInf -from ufl.utils.formatting import istr - -# List of valid elements -ufl_elements = {} - -# Aliases: aliases[name] (...) -> (standard_name, ...) -aliases = {} - - -# Function for registering new elements -def register_element(family, short_name, value_rank, sobolev_space, mapping, - degree_range, cellnames): - """Register new finite element family.""" - if family in ufl_elements: - raise ValueError(f"Finite element '{family}%s' has already been registered.") - ufl_elements[family] = (family, short_name, value_rank, sobolev_space, - mapping, degree_range, cellnames) - if short_name is not None: - ufl_elements[short_name] = (family, short_name, value_rank, sobolev_space, - mapping, degree_range, cellnames) - - -def register_alias(alias, to): - """Doc.""" - aliases[alias] = to - - -def show_elements(): - """Shows all registered elements.""" - print("Showing all registered elements:") - print("================================") - shown = set() - for k in sorted(ufl_elements.keys()): - data = ufl_elements[k] - if data in shown: - continue - shown.add(data) - (family, short_name, value_rank, sobolev_space, mapping, degree_range, cellnames) = data - print(f"Finite element family: '{family}', '{short_name}'") - print(f"Sobolev space: {sobolev_space}%s") - print(f"Mapping: {mapping}") - print(f"Degree range: {degree_range}") - print(f"Value rank: {value_rank}") - print(f"Defined on cellnames: {cellnames}") - print() - - -# FIXME: Consider cleanup of element names. Use notation from periodic -# table as the main, keep old names as compatibility aliases. - -# NOTE: Any element with polynomial degree 0 will be considered L2, -# independent of the space passed to register_element. - -# NOTE: The mapping of the element basis functions -# from reference to physical representation is -# chosen based on the sobolev space: -# HDiv = contravariant Piola, -# HCurl = covariant Piola, -# H1/L2 = no mapping. - -# TODO: If determining mapping from sobolev_space isn't sufficient in -# the future, add mapping name as another element property. - -# Cell groups -simplices = ("interval", "triangle", "tetrahedron", "pentatope") -cubes = ("interval", "quadrilateral", "hexahedron", "tesseract") -any_cell = (None, - "vertex", "interval", - "triangle", "tetrahedron", "prism", - "pyramid", "quadrilateral", "hexahedron", "pentatope", "tesseract") - -# Elements in the periodic table # TODO: Register these as aliases of -# periodic table element description instead of the other way around -register_element("Lagrange", "CG", 0, H1, "identity", (1, None), - any_cell) # "P" -register_element("Brezzi-Douglas-Marini", "BDM", 1, HDiv, - "contravariant Piola", (1, None), simplices[1:]) # "BDMF" (2d), "N2F" (3d) -register_element("Discontinuous Lagrange", "DG", 0, L2, "identity", (0, None), - any_cell) # "DP" -register_element("Discontinuous Taylor", "TDG", 0, L2, "identity", (0, None), simplices) -register_element("Nedelec 1st kind H(curl)", "N1curl", 1, HCurl, - "covariant Piola", (1, None), simplices[1:]) # "RTE" (2d), "N1E" (3d) -register_element("Nedelec 2nd kind H(curl)", "N2curl", 1, HCurl, - "covariant Piola", (1, None), simplices[1:]) # "BDME" (2d), "N2E" (3d) -register_element("Raviart-Thomas", "RT", 1, HDiv, "contravariant Piola", - (1, None), simplices[1:]) # "RTF" (2d), "N1F" (3d) - -# Elements not in the periodic table -register_element("Argyris", "ARG", 0, H2, "custom", (5, 5), ("triangle",)) -register_element("Bell", "BELL", 0, H2, "custom", (5, 5), ("triangle",)) -register_element("Brezzi-Douglas-Fortin-Marini", "BDFM", 1, HDiv, - "contravariant Piola", (1, None), simplices[1:]) -register_element("Crouzeix-Raviart", "CR", 0, L2, "identity", (1, 1), - simplices[1:]) -# TODO: Implement generic Tear operator for elements instead of this: -register_element("Discontinuous Raviart-Thomas", "DRT", 1, L2, - "contravariant Piola", (1, None), simplices[1:]) -register_element("Hermite", "HER", 0, H1, "custom", (3, 3), simplices) -register_element("Kong-Mulder-Veldhuizen", "KMV", 0, H1, "identity", (1, None), - simplices[1:]) -register_element("Mardal-Tai-Winther", "MTW", 1, H1, "contravariant Piola", (3, 3), - ("triangle",)) -register_element("Morley", "MOR", 0, H2, "custom", (2, 2), ("triangle",)) - -# Special elements -register_element("Boundary Quadrature", "BQ", 0, L2, "identity", (0, None), - any_cell) -register_element("Bubble", "B", 0, H1, "identity", (2, None), simplices) -register_element("FacetBubble", "FB", 0, H1, "identity", (2, None), simplices) -register_element("Quadrature", "Quadrature", 0, L2, "identity", (0, None), - any_cell) -register_element("Real", "R", 0, HInf, "identity", (0, 0), - any_cell + ("TensorProductCell",)) -register_element("Undefined", "U", 0, L2, "identity", (0, None), any_cell) -register_element("Radau", "Rad", 0, L2, "identity", (0, None), ("interval",)) -register_element("Regge", "Regge", 2, HEin, "double covariant Piola", - (0, None), simplices[1:]) -register_element("HDiv Trace", "HDivT", 0, L2, "identity", (0, None), any_cell) -register_element("Hellan-Herrmann-Johnson", "HHJ", 2, HDivDiv, - "double contravariant Piola", (0, None), ("triangle",)) -register_element("Nonconforming Arnold-Winther", "AWnc", 2, HDivDiv, - "double contravariant Piola", (2, 2), ("triangle", "tetrahedron")) -register_element("Conforming Arnold-Winther", "AWc", 2, HDivDiv, - "double contravariant Piola", (3, None), ("triangle", "tetrahedron")) -# Spectral elements. -register_element("Gauss-Legendre", "GL", 0, L2, "identity", (0, None), - ("interval",)) -register_element("Gauss-Lobatto-Legendre", "GLL", 0, H1, "identity", (1, None), - ("interval",)) -register_alias("Lobatto", - lambda family, dim, order, degree: ("Gauss-Lobatto-Legendre", order)) -register_alias("Lob", - lambda family, dim, order, degree: ("Gauss-Lobatto-Legendre", order)) - -register_element("Bernstein", None, 0, H1, "identity", (1, None), simplices) - - -# Let Nedelec H(div) elements be aliases to BDMs/RTs -register_alias("Nedelec 1st kind H(div)", - lambda family, dim, order, degree: ("Raviart-Thomas", order)) -register_alias("N1div", - lambda family, dim, order, degree: ("Raviart-Thomas", order)) - -register_alias("Nedelec 2nd kind H(div)", - lambda family, dim, order, degree: ("Brezzi-Douglas-Marini", - order)) -register_alias("N2div", - lambda family, dim, order, degree: ("Brezzi-Douglas-Marini", - order)) - -# Let Discontinuous Lagrange Trace element be alias to HDiv Trace -register_alias("Discontinuous Lagrange Trace", - lambda family, dim, order, degree: ("HDiv Trace", order)) -register_alias("DGT", - lambda family, dim, order, degree: ("HDiv Trace", order)) - -# New elements introduced for the periodic table 2014 -register_element("Q", None, 0, H1, "identity", (1, None), cubes) -register_element("DQ", None, 0, L2, "identity", (0, None), cubes) -register_element("RTCE", None, 1, HCurl, "covariant Piola", (1, None), - ("quadrilateral",)) -register_element("RTCF", None, 1, HDiv, "contravariant Piola", (1, None), - ("quadrilateral",)) -register_element("NCE", None, 1, HCurl, "covariant Piola", (1, None), - ("hexahedron",)) -register_element("NCF", None, 1, HDiv, "contravariant Piola", (1, None), - ("hexahedron",)) - -register_element("S", None, 0, H1, "identity", (1, None), cubes) -register_element("DPC", None, 0, L2, "identity", (0, None), cubes) -register_element("BDMCE", None, 1, HCurl, "covariant Piola", (1, None), - ("quadrilateral",)) -register_element("BDMCF", None, 1, HDiv, "contravariant Piola", (1, None), - ("quadrilateral",)) -register_element("SminusE", "SminusE", 1, HCurl, "covariant Piola", (1, None), cubes[1:3]) -register_element("SminusF", "SminusF", 1, HDiv, "contravariant Piola", (1, None), cubes[1:2]) -register_element("SminusDiv", "SminusDiv", 1, HDiv, "contravariant Piola", (1, None), cubes[1:3]) -register_element("SminusCurl", "SminusCurl", 1, HCurl, "covariant Piola", (1, None), cubes[1:3]) -register_element("AAE", None, 1, HCurl, "covariant Piola", (1, None), - ("hexahedron",)) -register_element("AAF", None, 1, HDiv, "contravariant Piola", (1, None), - ("hexahedron",)) - -# New aliases introduced for the periodic table 2014 -register_alias("P", lambda family, dim, order, degree: ("Lagrange", order)) -register_alias("DP", lambda family, dim, order, - degree: ("Discontinuous Lagrange", order)) -register_alias("RTE", lambda family, dim, order, - degree: ("Nedelec 1st kind H(curl)", order)) -register_alias("RTF", lambda family, dim, order, - degree: ("Raviart-Thomas", order)) -register_alias("N1E", lambda family, dim, order, - degree: ("Nedelec 1st kind H(curl)", order)) -register_alias("N1F", lambda family, dim, order, degree: ("Raviart-Thomas", - order)) - -register_alias("BDME", lambda family, dim, order, - degree: ("Nedelec 2nd kind H(curl)", order)) -register_alias("BDMF", lambda family, dim, order, - degree: ("Brezzi-Douglas-Marini", order)) -register_alias("N2E", lambda family, dim, order, - degree: ("Nedelec 2nd kind H(curl)", order)) -register_alias("N2F", lambda family, dim, order, - degree: ("Brezzi-Douglas-Marini", order)) - -# discontinuous elements using l2 pullbacks -register_element("DPC L2", None, 0, L2, "L2 Piola", (1, None), cubes) -register_element("DQ L2", None, 0, L2, "L2 Piola", (0, None), cubes) -register_element("Gauss-Legendre L2", "GL L2", 0, L2, "L2 Piola", (0, None), - ("interval",)) -register_element("Discontinuous Lagrange L2", "DG L2", 0, L2, "L2 Piola", (0, None), - any_cell) # "DP" - -register_alias("DP L2", lambda family, dim, order, - degree: ("Discontinuous Lagrange L2", order)) - -register_alias("P- Lambda L2", lambda family, dim, order, - degree: feec_element_l2(family, dim, order, degree)) -register_alias("P Lambda L2", lambda family, dim, order, - degree: feec_element_l2(family, dim, order, degree)) -register_alias("Q- Lambda L2", lambda family, dim, order, - degree: feec_element_l2(family, dim, order, degree)) -register_alias("S Lambda L2", lambda family, dim, order, - degree: feec_element_l2(family, dim, order, degree)) - -register_alias("P- L2", lambda family, dim, order, - degree: feec_element_l2(family, dim, order, degree)) -register_alias("Q- L2", lambda family, dim, order, - degree: feec_element_l2(family, dim, order, degree)) - -# mimetic spectral elements - primal and dual complexs -register_element("Extended-Gauss-Legendre", "EGL", 0, H1, "identity", (2, None), - ("interval",)) -register_element("Extended-Gauss-Legendre Edge", "EGL-Edge", 0, L2, "identity", (1, None), - ("interval",)) -register_element("Extended-Gauss-Legendre Edge L2", "EGL-Edge L2", 0, L2, "L2 Piola", (1, None), - ("interval",)) -register_element("Gauss-Lobatto-Legendre Edge", "GLL-Edge", 0, L2, "identity", (0, None), - ("interval",)) -register_element("Gauss-Lobatto-Legendre Edge L2", "GLL-Edge L2", 0, L2, "L2 Piola", (0, None), - ("interval",)) - -# directly-defined serendipity elements ala Arbogast -# currently the theory is only really worked out for quads. -register_element("Direct Serendipity", "Sdirect", 0, H1, "physical", (1, None), - ("quadrilateral",)) -register_element("Direct Serendipity Full H(div)", "Sdirect H(div)", 1, HDiv, "physical", (1, None), - ("quadrilateral",)) -register_element("Direct Serendipity Reduced H(div)", "Sdirect H(div) red", 1, HDiv, "physical", (1, None), - ("quadrilateral",)) - - -# NOTE- the edge elements for primal mimetic spectral elements are accessed by using -# variant='mse' in the appropriate places - -def feec_element(family, n, r, k): - """Finite element exterior calculus notation. - - n = topological dimension of domain - r = polynomial order - k = form_degree - """ - # Note: We always map to edge elements in 2D, don't know how to - # differentiate otherwise? - - # Mapping from (feec name, domain dimension, form degree) to - # (family name, polynomial order) - _feec_elements = { - "P- Lambda": ( - (("P", r), ("DP", r - 1)), - (("P", r), ("RTE", r), ("DP", r - 1)), - (("P", r), ("N1E", r), ("N1F", r), ("DP", r - 1)), - ), - "P Lambda": ( - (("P", r), ("DP", r)), - (("P", r), ("BDME", r), ("DP", r)), - (("P", r), ("N2E", r), ("N2F", r), ("DP", r)), - ), - "Q- Lambda": ( - (("Q", r), ("DQ", r - 1)), - (("Q", r), ("RTCE", r), ("DQ", r - 1)), - (("Q", r), ("NCE", r), ("NCF", r), ("DQ", r - 1)), - ), - "S Lambda": ( - (("S", r), ("DPC", r)), - (("S", r), ("BDMCE", r), ("DPC", r)), - (("S", r), ("AAE", r), ("AAF", r), ("DPC", r)), - ), - } - - # New notation, old verbose notation (including "Lambda") might be - # removed - _feec_elements["P-"] = _feec_elements["P- Lambda"] - _feec_elements["P"] = _feec_elements["P Lambda"] - _feec_elements["Q-"] = _feec_elements["Q- Lambda"] - _feec_elements["S"] = _feec_elements["S Lambda"] - - family, r = _feec_elements[family][n - 1][k] - - return family, r - - -def feec_element_l2(family, n, r, k): - """Finite element exterior calculus notation. - - n = topological dimension of domain - r = polynomial order - k = form_degree - """ - # Note: We always map to edge elements in 2D, don't know how to - # differentiate otherwise? - - # Mapping from (feec name, domain dimension, form degree) to - # (family name, polynomial order) - _feec_elements = { - "P- Lambda L2": ( - (("P", r), ("DP L2", r - 1)), - (("P", r), ("RTE", r), ("DP L2", r - 1)), - (("P", r), ("N1E", r), ("N1F", r), ("DP L2", r - 1)), - ), - "P Lambda L2": ( - (("P", r), ("DP L2", r)), - (("P", r), ("BDME", r), ("DP L2", r)), - (("P", r), ("N2E", r), ("N2F", r), ("DP L2", r)), - ), - "Q- Lambda L2": ( - (("Q", r), ("DQ L2", r - 1)), - (("Q", r), ("RTCE", r), ("DQ L2", r - 1)), - (("Q", r), ("NCE", r), ("NCF", r), ("DQ L2", r - 1)), - ), - "S Lambda L2": ( - (("S", r), ("DPC L2", r)), - (("S", r), ("BDMCE", r), ("DPC L2", r)), - (("S", r), ("AAE", r), ("AAF", r), ("DPC L2", r)), - ), - } - - # New notation, old verbose notation (including "Lambda") might be - # removed - _feec_elements["P- L2"] = _feec_elements["P- Lambda L2"] - _feec_elements["P L2"] = _feec_elements["P Lambda L2"] - _feec_elements["Q- L2"] = _feec_elements["Q- Lambda L2"] - _feec_elements["S L2"] = _feec_elements["S Lambda L2"] - - family, r = _feec_elements[family][n - 1][k] - - return family, r - - -# General FEEC notation, old verbose (can be removed) -register_alias("P- Lambda", lambda family, dim, order, - degree: feec_element(family, dim, order, degree)) -register_alias("P Lambda", lambda family, dim, order, - degree: feec_element(family, dim, order, degree)) -register_alias("Q- Lambda", lambda family, dim, order, - degree: feec_element(family, dim, order, degree)) -register_alias("S Lambda", lambda family, dim, order, - degree: feec_element(family, dim, order, degree)) - -# General FEEC notation, new compact notation -register_alias("P-", lambda family, dim, order, - degree: feec_element(family, dim, order, degree)) -register_alias("Q-", lambda family, dim, order, - degree: feec_element(family, dim, order, degree)) - - -def canonical_element_description(family, cell, order, form_degree): - """Given basic element information, return corresponding element information on canonical form. - - Input: family, cell, (polynomial) order, form_degree - Output: family (canonical), short_name (for printing), order, value shape, - reference value shape, sobolev_space. - - This is used by the FiniteElement constructor to ved input - data against the element list and aliases defined in ufl. - """ - # Get domain dimensions - if cell is not None: - tdim = cell.topological_dimension() - gdim = cell.geometric_dimension() - if isinstance(cell, Cell): - cellname = cell.cellname() - else: - cellname = None - else: - tdim = None - gdim = None - cellname = None - - # Catch general FEEC notation "P" and "S" - if form_degree is not None and family in ("P", "S"): - family, order = feec_element(family, tdim, order, form_degree) - - if form_degree is not None and family in ("P L2", "S L2"): - family, order = feec_element_l2(family, tdim, order, form_degree) - - # Check whether this family is an alias for something else - while family in aliases: - if tdim is None: - raise ValueError("Need dimension to handle element aliases.") - (family, order) = aliases[family](family, tdim, order, form_degree) - - # Check that the element family exists - if family not in ufl_elements: - raise ValueError(f"Unknown finite element '{family}'.") - - # Check that element data is valid (and also get common family - # name) - (family, short_name, value_rank, sobolev_space, mapping, krange, cellnames) = ufl_elements[family] - - # Accept CG/DG on all kind of cells, but use Q/DQ on "product" cells - if cellname in set(cubes) - set(simplices) or isinstance(cell, TensorProductCell): - if family == "Lagrange": - family = "Q" - elif family == "Discontinuous Lagrange": - if order >= 1: - warnings.warn("Discontinuous Lagrange element requested on %s, creating DQ element." % cell.cellname()) - family = "DQ" - elif family == "Discontinuous Lagrange L2": - if order >= 1: - warnings.warn(f"Discontinuous Lagrange L2 element requested on {cell.cellname()}, " - "creating DQ L2 element.") - family = "DQ L2" - - # Validate cellname if a valid cell is specified - if not (cellname is None or cellname in cellnames): - raise ValueError(f"Cellname '{cellname}' invalid for '{family}' finite element.") - - # Validate order if specified - if order is not None: - if krange is None: - raise ValueError(f"Order {order} invalid for '{family}' finite element, should be None.") - kmin, kmax = krange - if not (kmin is None or (asarray(order) >= kmin).all()): - raise ValueError(f"Order {order} invalid for '{family}' finite element.") - if not (kmax is None or (asarray(order) <= kmax).all()): - raise ValueError(f"Order {istr(order)} invalid for '{family}' finite element.") - - if value_rank == 2: - # Tensor valued fundamental elements in HEin have this shape - if gdim is None or tdim is None: - raise ValueError("Cannot infer shape of element without topological and geometric dimensions.") - reference_value_shape = (tdim, tdim) - value_shape = (gdim, gdim) - elif value_rank == 1: - # Vector valued fundamental elements in HDiv and HCurl have a shape - if gdim is None or tdim is None: - raise ValueError("Cannot infer shape of element without topological and geometric dimensions.") - reference_value_shape = (tdim,) - value_shape = (gdim,) - elif value_rank == 0: - # All other elements are scalar values - reference_value_shape = () - value_shape = () - else: - raise ValueError(f"Invalid value rank {value_rank}.") - - return family, short_name, order, value_shape, reference_value_shape, sobolev_space, mapping diff --git a/ufl/legacy/enrichedelement.py b/ufl/legacy/enrichedelement.py deleted file mode 100644 index 76420f00b..000000000 --- a/ufl/legacy/enrichedelement.py +++ /dev/null @@ -1,164 +0,0 @@ -"""This module defines the UFL finite element classes.""" - -# Copyright (C) 2008-2016 Martin Sandve Alnæs -# -# This file is part of UFL (https://www.fenicsproject.org) -# -# SPDX-License-Identifier: LGPL-3.0-or-later -# -# Modified by Kristian B. Oelgaard -# Modified by Marie E. Rognes 2010, 2012 -# Modified by Massimiliano Leoni, 2016 - -from ufl.legacy.finiteelementbase import FiniteElementBase - - -class EnrichedElementBase(FiniteElementBase): - """The vector sum of several finite element spaces.""" - - def __init__(self, *elements): - """Doc.""" - self._elements = elements - - cell = elements[0].cell - if not all(e.cell == cell for e in elements[1:]): - raise ValueError("Cell mismatch for sub elements of enriched element.") - - if isinstance(elements[0].degree(), int): - degrees = {e.degree() for e in elements} - {None} - degree = max(degrees) if degrees else None - else: - degree = tuple(map(max, zip(*[e.degree() for e in elements]))) - - # We can allow the scheme not to be defined, but all defined - # should be equal - quad_schemes = [e.quadrature_scheme() for e in elements] - quad_schemes = [qs for qs in quad_schemes if qs is not None] - quad_scheme = quad_schemes[0] if quad_schemes else None - if not all(qs == quad_scheme for qs in quad_schemes): - raise ValueError("Quadrature scheme mismatch.") - - value_shape = elements[0].value_shape - if not all(e.value_shape == value_shape for e in elements[1:]): - raise ValueError("Element value shape mismatch.") - - reference_value_shape = elements[0].reference_value_shape - if not all(e.reference_value_shape == reference_value_shape for e in elements[1:]): - raise ValueError("Element reference value shape mismatch.") - - # mapping = elements[0].mapping() # FIXME: This fails for a mixed subelement here. - # if not all(e.mapping() == mapping for e in elements[1:]): - # raise ValueError("Element mapping mismatch.") - - # Get name of subclass: EnrichedElement or NodalEnrichedElement - class_name = self.__class__.__name__ - - # Initialize element data - FiniteElementBase.__init__(self, class_name, cell, degree, - quad_scheme, value_shape, - reference_value_shape) - - def mapping(self): - """Doc.""" - return self._elements[0].mapping() - - @property - 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 variant(self): - """Doc.""" - try: - variant, = {e.variant() for e in self._elements} - return variant - except ValueError: - return None - - def reconstruct(self, **kwargs): - """Doc.""" - return type(self)(*[e.reconstruct(**kwargs) for e in self._elements]) - - @property - def embedded_subdegree(self): - """Return embedded subdegree.""" - if isinstance(self._degree, int): - return self._degree - else: - return min(e.embedded_subdegree for e in self._elements) - - @property - def embedded_superdegree(self): - """Return embedded superdegree.""" - if isinstance(self._degree, int): - return self._degree - else: - return max(e.embedded_superdegree for e in self._elements) - - -class EnrichedElement(EnrichedElementBase): - r"""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 __repr__(self): - """Doc.""" - return "EnrichedElement(" + ", ".join(repr(e) for e in self._elements) + ")" - - def __str__(self): - """Format as string for pretty printing.""" - return "<%s>" % " + ".join(str(e) for e in self._elements) - - def shortstr(self): - """Format as string for pretty printing.""" - return "<%s>" % " + ".join(e.shortstr() for e in self._elements) - - -class NodalEnrichedElement(EnrichedElementBase): - r"""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 __repr__(self): - """Doc.""" - return "NodalEnrichedElement(" + ", ".join(repr(e) for e in self._elements) + ")" - - def __str__(self): - """Format as string for pretty printing.""" - return "" % ", ".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/legacy/finiteelement.py b/ufl/legacy/finiteelement.py deleted file mode 100644 index 1166ed741..000000000 --- a/ufl/legacy/finiteelement.py +++ /dev/null @@ -1,235 +0,0 @@ -"""This module defines the UFL finite element classes.""" -# Copyright (C) 2008-2016 Martin Sandve Alnæs -# -# This file is part of UFL (https://www.fenicsproject.org) -# -# SPDX-License-Identifier: LGPL-3.0-or-later -# -# Modified by Kristian B. Oelgaard -# Modified by Marie E. Rognes 2010, 2012 -# Modified by Anders Logg 2014 -# Modified by Massimiliano Leoni, 2016 - -from ufl.cell import TensorProductCell, as_cell -from ufl.legacy.elementlist import canonical_element_description, simplices -from ufl.legacy.finiteelementbase import FiniteElementBase -from ufl.utils.formatting import istr - - -class FiniteElement(FiniteElementBase): - """The basic finite element class for all simple finite elements.""" - # TODO: Move these to base? - __slots__ = ("_short_name", "_sobolev_space", - "_mapping", "_variant", "_repr") - - def __new__(cls, - family, - cell=None, - degree=None, - form_degree=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): - # Delay import to avoid circular dependency at module load time - from ufl.legacy.enrichedelement import EnrichedElement - from ufl.legacy.hdivcurl import HCurlElement as HCurl - from ufl.legacy.hdivcurl import HDivElement as HDiv - from ufl.legacy.tensorproductelement import TensorProductElement - - 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"]: - cell_h, cell_v = cell.sub_cells() - if cell_h.cellname() != "interval": - raise ValueError(f"{family} is available on TensorProductCell(interval, interval) only.") - if cell_v.cellname() != "interval": - raise ValueError(f"{family} is available on TensorProductCell(interval, interval) only.") - - 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) - - if family == "RTCF": - return EnrichedElement(HDiv(CxD_elt), HDiv(DxC_elt)) - if family == "RTCE": - return EnrichedElement(HCurl(CxD_elt), HCurl(DxC_elt)) - - elif family == "NCF": - cell_h, cell_v = cell.sub_cells() - if cell_h.cellname() != "quadrilateral": - raise ValueError(f"{family} is available on TensorProductCell(quadrilateral, interval) only.") - if cell_v.cellname() != "interval": - raise ValueError(f"{family} is available on TensorProductCell(quadrilateral, interval) only.") - - Qc_elt = FiniteElement("RTCF", "quadrilateral", degree, variant=variant) - Qd_elt = FiniteElement("DQ", "quadrilateral", degree - 1, variant=variant) - - 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": - cell_h, cell_v = cell.sub_cells() - if cell_h.cellname() != "quadrilateral": - raise ValueError(f"{family} is available on TensorProductCell(quadrilateral, interval) only.") - if cell_v.cellname() != "interval": - raise ValueError(f"{family} is available on TensorProductCell(quadrilateral, interval) only.") - - Qc_elt = FiniteElement("Q", "quadrilateral", degree, variant=variant) - Qd_elt = FiniteElement("RTCE", "quadrilateral", degree, variant=variant) - - 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", c, degree, variant=variant) - for c in cell.sub_cells()], - cell=cell) - - elif family == "DQ": - def dq_family(cell): - """Doc.""" - 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) - - elif family == "DQ L2": - def dq_family_l2(cell): - """Doc.""" - return "DG L2" if cell.cellname() in simplices else "DQ L2" - return TensorProductElement(*[FiniteElement(dq_family_l2(c), c, degree, variant=variant) - for c in cell.sub_cells()], - cell=cell) - - return super(FiniteElement, cls).__new__(cls) - - def __init__(self, - family, - cell=None, - degree=None, - form_degree=None, - quad_scheme=None, - variant=None): - """Create finite element. - - Args: - family: The finite element family - cell: The geometric cell - degree: The polynomial degree (optional) - form_degree: The form degree (FEEC notation, used when field is - 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: - if cell is not None: - cell = as_cell(cell) - - ( - family, short_name, degree, value_shape, reference_value_shape, sobolev_space, mapping - ) = canonical_element_description(family, cell, degree, form_degree) - - # TODO: Move these to base? Might be better to instead - # simplify base though. - self._sobolev_space = sobolev_space - self._mapping = mapping - self._short_name = short_name or family - self._variant = variant - - # 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) - - # Cache repr string - qs = self.quadrature_scheme() - if qs is None: - quad_str = "" - else: - quad_str = ", quad_scheme=%s" % repr(qs) - v = self.variant() - if v is None: - var_str = "" - else: - var_str = ", variant=%s" % repr(v) - self._repr = "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 __repr__(self): - """Format as string for evaluation as Python object.""" - return self._repr - - def _is_globally_constant(self): - """Doc.""" - return self.family() == "Real" - - def _is_linear(self): - """Doc.""" - return self.family() == "Lagrange" and self.degree() == 1 - - def mapping(self): - """Return the mapping type for this element .""" - return self._mapping - - @property - def sobolev_space(self): - """Return the underlying Sobolev space.""" - return self._sobolev_space - - def variant(self): - """Return the variant used to initialise the element.""" - return self._variant - - def reconstruct(self, family=None, cell=None, degree=None, quad_scheme=None, variant=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() - if quad_scheme is None: - quad_scheme = self.quadrature_scheme() - if variant is None: - variant = self.variant() - return FiniteElement(family, cell, degree, quad_scheme=quad_scheme, variant=variant) - - def __str__(self): - """Format as string for pretty printing.""" - qs = self.quadrature_scheme() - qs = "" if qs is None else "(%s)" % qs - 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 f"{self._short_name}{istr(self.degree())}({self.quadrature_scheme()},{istr(self.variant())})" - - def __getnewargs__(self): - """Return the arguments which pickle needs to recreate the object.""" - return (self.family(), - self.cell, - self.degree(), - None, - self.quadrature_scheme(), - self.variant()) diff --git a/ufl/legacy/finiteelementbase.py b/ufl/legacy/finiteelementbase.py deleted file mode 100644 index 88eba6c8c..000000000 --- a/ufl/legacy/finiteelementbase.py +++ /dev/null @@ -1,276 +0,0 @@ -"""This module defines the UFL finite element classes.""" - -# Copyright (C) 2008-2016 Martin Sandve Alnæs -# -# This file is part of UFL (https://www.fenicsproject.org) -# -# SPDX-License-Identifier: LGPL-3.0-or-later -# -# Modified by Kristian B. Oelgaard -# Modified by Marie E. Rognes 2010, 2012 -# Modified by Massimiliano Leoni, 2016 - -from abc import abstractmethod, abstractproperty - -from ufl import pullback -from ufl.cell import AbstractCell, as_cell -from ufl.finiteelement import AbstractFiniteElement -from ufl.utils.sequences import product - - -class FiniteElementBase(AbstractFiniteElement): - """Base class for all finite elements.""" - __slots__ = ("_family", "_cell", "_degree", "_quad_scheme", - "_value_shape", "_reference_value_shape") - - # TODO: Not all these should be in the base class! In particular - # family, degree, and quad_scheme do not belong here. - def __init__(self, family, cell, degree, quad_scheme, value_shape, - reference_value_shape): - """Initialize basic finite element data.""" - if not (degree is None or isinstance(degree, (int, tuple))): - raise ValueError("Invalid degree type.") - if not isinstance(value_shape, tuple): - raise ValueError("Invalid value_shape type.") - if not isinstance(reference_value_shape, tuple): - raise ValueError("Invalid reference_value_shape type.") - - if cell is not None: - cell = as_cell(cell) - if not isinstance(cell, AbstractCell): - raise ValueError("Invalid cell type.") - - self._family = family - self._cell = cell - self._degree = degree - self._value_shape = value_shape - self._reference_value_shape = reference_value_shape - self._quad_scheme = quad_scheme - - @abstractmethod - def __repr__(self): - """Format as string for evaluation as Python object.""" - pass - - @abstractproperty - def sobolev_space(self): - """Return the underlying Sobolev space.""" - pass - - @abstractmethod - def mapping(self): - """Return the mapping type for this element.""" - pass - - def _is_globally_constant(self): - """Check if the element is a global constant. - - For Real elements, this should return True. - """ - return False - - def _is_linear(self): - """Check if the element is Lagrange degree 1.""" - return False - - def _ufl_hash_data_(self): - """Doc.""" - return repr(self) - - def _ufl_signature_data_(self): - """Doc.""" - return repr(self) - - def __hash__(self): - """Compute hash code for insertion in hashmaps.""" - return hash(self._ufl_hash_data_()) - - def __eq__(self, other): - """Compute element equality for insertion in hashmaps.""" - return type(self) is type(other) and self._ufl_hash_data_() == other._ufl_hash_data_() - - def __ne__(self, other): - """Compute element inequality for insertion in hashmaps.""" - return not self.__eq__(other) - - def __lt__(self, other): - """Compare elements by repr, to give a natural stable sorting.""" - return repr(self) < repr(other) - - def family(self): # FIXME: Undefined for base? - """Return finite element family.""" - return self._family - - def variant(self): - """Return the variant used to initialise the element.""" - return None - - def degree(self, component=None): - """Return polynomial degree of finite element.""" - return self._degree - - def quadrature_scheme(self): - """Return quadrature scheme of finite element.""" - return self._quad_scheme - - @property - def cell(self): - """Return cell of finite element.""" - return self._cell - - def is_cellwise_constant(self, component=None): - """Return whether the basis functions of this element is spatially constant over each cell.""" - return self._is_globally_constant() or self.degree() == 0 - - @property - def value_shape(self): - """Return the shape of the value space on the global domain.""" - return self._value_shape - - @property - def reference_value_shape(self): - """Return the shape of the value space on the reference cell.""" - return self._reference_value_shape - - @property - def value_size(self): - """Return the integer product of the value shape.""" - return product(self.value_shape) - - @property - def reference_value_size(self): - """Return the integer product of the reference value shape.""" - return product(self.reference_value_shape) - - def symmetry(self): # FIXME: different approach - r"""Return the symmetry dict. - - This is a mapping :math:`c_0 \\to c_1` - meaning that component :math:`c_0` is represented by component - :math:`c_1`. - A component is a tuple of one or more ints. - """ - return {} - - def _check_component(self, i): - """Check that component index i is valid.""" - sh = self.value_shape - r = len(sh) - if not (len(i) == r and all(j < k for (j, k) in zip(i, sh))): - raise ValueError( - f"Illegal component index {i} (value rank {len(i)}) " - f"for element (value rank {r}).") - - def extract_subelement_component(self, i): - """Extract direct subelement index and subelement relative component index for a given component index.""" - if isinstance(i, int): - i = (i,) - self._check_component(i) - return (None, i) - - def extract_component(self, i): - """Recursively extract component index relative to a (simple) element. - - and that element for given value component index. - """ - if isinstance(i, int): - i = (i,) - self._check_component(i) - return (i, self) - - def _check_reference_component(self, i): - """Check that reference component index i is valid.""" - sh = self.value_shape - r = len(sh) - if not (len(i) == r and all(j < k for (j, k) in zip(i, sh))): - raise ValueError( - f"Illegal component index {i} (value rank {len(i)}) " - f"for element (value rank {r}).") - - def extract_subelement_reference_component(self, i): - """Extract direct subelement index and subelement relative. - - reference component index for a given reference component index. - """ - if isinstance(i, int): - i = (i,) - self._check_reference_component(i) - return (None, i) - - def extract_reference_component(self, i): - """Recursively extract reference component index relative to a (simple) element. - - and that element for given reference value component index. - """ - if isinstance(i, int): - i = (i,) - self._check_reference_component(i) - return (i, self) - - @property - def num_sub_elements(self): - """Return number of sub-elements.""" - return 0 - - @property - def sub_elements(self): - """Return list of sub-elements.""" - return [] - - def __add__(self, other): - """Add two elements, creating an enriched element.""" - if not isinstance(other, FiniteElementBase): - raise ValueError(f"Can't add element and {other.__class__}.") - from ufl.legacy import EnrichedElement - return EnrichedElement(self, other) - - def __mul__(self, other): - """Multiply two elements, creating a mixed element.""" - if not isinstance(other, FiniteElementBase): - raise ValueError("Can't multiply element and {other.__class__}.") - from ufl.legacy import MixedElement - return MixedElement(self, other) - - def __getitem__(self, index): - """Restrict finite element to a subdomain, subcomponent or topology (cell).""" - if index in ("facet", "interior"): - from ufl.legacy import RestrictedElement - return RestrictedElement(self, index) - else: - raise KeyError(f"Invalid index for restriction: {repr(index)}") - - def __iter__(self): - """Iter.""" - raise TypeError(f"'{type(self).__name__}' object is not iterable") - - @property - def embedded_superdegree(self): - """Doc.""" - return self.degree() - - @property - def embedded_subdegree(self): - """Doc.""" - return self.degree() - - @property - def pullback(self): - """Get the pull back.""" - if self.mapping() == "identity": - return pullback.identity_pullback - elif self.mapping() == "L2 Piola": - return pullback.l2_piola - elif self.mapping() == "covariant Piola": - return pullback.covariant_piola - elif self.mapping() == "contravariant Piola": - return pullback.contravariant_piola - elif self.mapping() == "double covariant Piola": - return pullback.double_covariant_piola - elif self.mapping() == "double contravariant Piola": - return pullback.double_contravariant_piola - elif self.mapping() == "custom": - return pullback.custom_pullback - elif self.mapping() == "physical": - return pullback.physical_pullback - - raise ValueError(f"Unsupported mapping: {self.mapping()}") diff --git a/ufl/legacy/hdivcurl.py b/ufl/legacy/hdivcurl.py deleted file mode 100644 index 3f693017e..000000000 --- a/ufl/legacy/hdivcurl.py +++ /dev/null @@ -1,213 +0,0 @@ -"""Doc.""" -# Copyright (C) 2008-2016 Andrew T. T. McRae -# -# This file is part of UFL (https://www.fenicsproject.org) -# -# SPDX-License-Identifier: LGPL-3.0-or-later -# -# Modified by Massimiliano Leoni, 2016 - -from ufl.legacy.finiteelementbase import FiniteElementBase -from ufl.sobolevspace import L2, HCurl, HDiv - - -class HDivElement(FiniteElementBase): - """A div-conforming version of an outer product element, assuming this makes mathematical sense.""" - __slots__ = ("_element", ) - - def __init__(self, element): - """Doc.""" - self._element = element - - family = "TensorProductElement" - cell = element.cell - degree = element.degree() - quad_scheme = element.quadrature_scheme() - value_shape = (element.cell.geometric_dimension(),) - reference_value_shape = (element.cell.topological_dimension(),) - - # Skipping TensorProductElement constructor! Bad code smell, refactor to avoid this non-inheritance somehow. - FiniteElementBase.__init__(self, family, cell, degree, - quad_scheme, value_shape, reference_value_shape) - - def __repr__(self): - """Doc.""" - return f"HDivElement({repr(self._element)})" - - def mapping(self): - """Doc.""" - return "contravariant Piola" - - @property - def sobolev_space(self): - """Return the underlying Sobolev space.""" - return HDiv - - def reconstruct(self, **kwargs): - """Doc.""" - return HDivElement(self._element.reconstruct(**kwargs)) - - def variant(self): - """Doc.""" - return self._element.variant() - - def __str__(self): - """Doc.""" - return f"HDivElement({repr(self._element)})" - - def shortstr(self): - """Format as string for pretty printing.""" - return f"HDivElement({self._element.shortstr()})" - - @property - def embedded_subdegree(self): - """Return embedded subdegree.""" - return self._element.embedded_subdegree - - @property - def embedded_superdegree(self): - """Return embedded superdegree.""" - return self._element.embedded_superdegree - - -class HCurlElement(FiniteElementBase): - """A curl-conforming version of an outer product element, assuming this makes mathematical sense.""" - __slots__ = ("_element",) - - def __init__(self, element): - """Doc.""" - self._element = element - - family = "TensorProductElement" - cell = element.cell - degree = element.degree() - quad_scheme = element.quadrature_scheme() - cell = element.cell - value_shape = (cell.geometric_dimension(),) - reference_value_shape = (cell.topological_dimension(),) # TODO: Is this right? - # Skipping TensorProductElement constructor! Bad code smell, - # refactor to avoid this non-inheritance somehow. - FiniteElementBase.__init__(self, family, cell, degree, quad_scheme, - value_shape, reference_value_shape) - - def __repr__(self): - """Doc.""" - return f"HCurlElement({repr(self._element)})" - - def mapping(self): - """Doc.""" - return "covariant Piola" - - @property - def sobolev_space(self): - """Return the underlying Sobolev space.""" - return HCurl - - def reconstruct(self, **kwargs): - """Doc.""" - return HCurlElement(self._element.reconstruct(**kwargs)) - - def variant(self): - """Doc.""" - return self._element.variant() - - def __str__(self): - """Doc.""" - return f"HCurlElement({repr(self._element)})" - - def shortstr(self): - """Format as string for pretty printing.""" - return f"HCurlElement({self._element.shortstr()})" - - -class WithMapping(FiniteElementBase): - """Specify an alternative mapping for the wrappee. - - For example, - to use identity mapping instead of Piola map with an element E, - write - remapped = WithMapping(E, "identity") - """ - - def __init__(self, wrapee, mapping): - """Doc.""" - if mapping == "symmetries": - raise ValueError("Can't change mapping to 'symmetries'") - self._mapping = mapping - self.wrapee = wrapee - - def __getattr__(self, attr): - """Doc.""" - try: - return getattr(self.wrapee, attr) - except AttributeError: - raise AttributeError("'%s' object has no attribute '%s'" % - (type(self).__name__, attr)) - - def __repr__(self): - """Doc.""" - return f"WithMapping({repr(self.wrapee)}, '{self._mapping}')" - - @property - def value_shape(self): - """Doc.""" - gdim = self.cell.geometric_dimension() - mapping = self.mapping() - if mapping in {"covariant Piola", "contravariant Piola"}: - return (gdim,) - elif mapping in {"double covariant Piola", "double contravariant Piola"}: - return (gdim, gdim) - else: - return self.wrapee.value_shape - - @property - def reference_value_shape(self): - """Doc.""" - tdim = self.cell.topological_dimension() - mapping = self.mapping() - if mapping in {"covariant Piola", "contravariant Piola"}: - return (tdim,) - elif mapping in {"double covariant Piola", "double contravariant Piola"}: - return (tdim, tdim) - else: - return self.wrapee.reference_value_shape - - def mapping(self): - """Doc.""" - return self._mapping - - @property - def sobolev_space(self): - """Return the underlying Sobolev space.""" - if self.wrapee.mapping() == self.mapping(): - return self.wrapee.sobolev_space - else: - return L2 - - def reconstruct(self, **kwargs): - """Doc.""" - mapping = kwargs.pop("mapping", self._mapping) - wrapee = self.wrapee.reconstruct(**kwargs) - return type(self)(wrapee, mapping) - - def variant(self): - """Doc.""" - return self.wrapee.variant() - - def __str__(self): - """Doc.""" - return f"WithMapping({repr(self.wrapee)}, {self._mapping})" - - def shortstr(self): - """Doc.""" - return f"WithMapping({self.wrapee.shortstr()}, {self._mapping})" - - @property - def embedded_subdegree(self): - """Return embedded subdegree.""" - return self._element.embedded_subdegree - - @property - def embedded_superdegree(self): - """Return embedded superdegree.""" - return self._element.embedded_superdegree diff --git a/ufl/legacy/mixedelement.py b/ufl/legacy/mixedelement.py deleted file mode 100644 index 97f194e55..000000000 --- a/ufl/legacy/mixedelement.py +++ /dev/null @@ -1,562 +0,0 @@ -"""This module defines the UFL finite element classes.""" -# Copyright (C) 2008-2016 Martin Sandve Alnæs -# -# This file is part of UFL (https://www.fenicsproject.org) -# -# SPDX-License-Identifier: LGPL-3.0-or-later -# -# Modified by Kristian B. Oelgaard -# Modified by Marie E. Rognes 2010, 2012 -# Modified by Anders Logg 2014 -# Modified by Massimiliano Leoni, 2016 - -import numpy as np - -from ufl.cell import as_cell -from ufl.legacy.finiteelement import FiniteElement -from ufl.legacy.finiteelementbase import FiniteElementBase -from ufl.permutation import compute_indices -from ufl.pullback import IdentityPullback, MixedPullback, SymmetricPullback -from ufl.utils.indexflattening import flatten_multiindex, shape_to_strides, unflatten_index -from ufl.utils.sequences import max_degree, product - - -class MixedElement(FiniteElementBase): - """A finite element composed of a nested hierarchy of mixed or simple elements.""" - __slots__ = ("_sub_elements", "_cells") - - def __init__(self, *elements, **kwargs): - """Create mixed finite element from given list of elements.""" - if type(self) is MixedElement: - if kwargs: - raise ValueError("Not expecting keyword arguments to MixedElement constructor.") - - # Un-nest arguments if we get a single argument with a list of elements - if len(elements) == 1 and isinstance(elements[0], (tuple, list)): - elements = elements[0] - # Interpret nested tuples as sub-mixedelements recursively - elements = [MixedElement(e) if isinstance(e, (tuple, list)) else e - for e in elements] - self._sub_elements = elements - - # Pick the first cell, for now all should be equal - cells = tuple(sorted(set(element.cell for element in elements) - set([None]))) - self._cells = cells - if cells: - cell = cells[0] - # Require that all elements are defined on the same cell - if not all(c == cell for c in cells[1:]): - raise ValueError("Sub elements must live on the same cell.") - else: - cell = None - - # Check that all elements use the same quadrature scheme TODO: - # We can allow the scheme not to be defined. - if len(elements) == 0: - quad_scheme = None - else: - quad_scheme = elements[0].quadrature_scheme() - if not all(e.quadrature_scheme() == quad_scheme for e in elements): - raise ValueError("Quadrature scheme mismatch for sub elements of mixed element.") - - # Compute value sizes in global and reference configurations - value_size_sum = sum(product(s.value_shape) for s in self._sub_elements) - reference_value_size_sum = sum(product(s.reference_value_shape) for s in self._sub_elements) - - # Default value shape: Treated simply as all subelement values - # unpacked in a vector. - value_shape = kwargs.get('value_shape', (value_size_sum,)) - - # Default reference value shape: Treated simply as all - # subelement reference values unpacked in a vector. - reference_value_shape = kwargs.get('reference_value_shape', (reference_value_size_sum,)) - - # Validate value_shape (deliberately not for subclasses - # VectorElement and TensorElement) - if type(self) is MixedElement: - # This is not valid for tensor elements with symmetries, - # assume subclasses deal with their own validation - if product(value_shape) != value_size_sum: - raise ValueError("Provided value_shape doesn't match the " - "total value size of all subelements.") - - # Initialize element data - degrees = {e.degree() for e in self._sub_elements} - {None} - degree = max_degree(degrees) if degrees else None - FiniteElementBase.__init__(self, "Mixed", cell, degree, quad_scheme, - value_shape, reference_value_shape) - - def __repr__(self): - """Doc.""" - return "MixedElement(" + ", ".join(repr(e) for e in self._sub_elements) + ")" - - def _is_linear(self): - """Doc.""" - return all(i._is_linear() for i in self._sub_elements) - - def reconstruct_from_elements(self, *elements): - """Reconstruct a mixed element from new subelements.""" - if all(a == b for (a, b) in zip(elements, self._sub_elements)): - return self - return MixedElement(*elements) - - def symmetry(self): - r"""Return the symmetry dict, which is a mapping :math:`c_0 \\to c_1`. - - meaning that component :math:`c_0` is represented by component - :math:`c_1`. - A component is a tuple of one or more ints. - """ - # Build symmetry map from symmetries of subelements - sm = {} - # Base index of the current subelement into mixed value - j = 0 - for e in self._sub_elements: - sh = e.value_shape - st = shape_to_strides(sh) - # Map symmetries of subelement into index space of this - # element - for c0, c1 in e.symmetry().items(): - j0 = flatten_multiindex(c0, st) + j - j1 = flatten_multiindex(c1, st) + j - sm[(j0,)] = (j1,) - # Update base index for next element - j += product(sh) - if j != product(self.value_shape): - raise ValueError("Size mismatch in symmetry algorithm.") - return sm or {} - - @property - def sobolev_space(self): - """Doc.""" - return max(e.sobolev_space for e in self._sub_elements) - - def mapping(self): - """Doc.""" - if all(e.mapping() == "identity" for e in self._sub_elements): - return "identity" - else: - return "undefined" - - @property - def num_sub_elements(self): - """Return number of sub elements.""" - return len(self._sub_elements) - - @property - def sub_elements(self): - """Return list of sub elements.""" - return self._sub_elements - - def extract_subelement_component(self, i): - """Extract direct subelement index and subelement relative. - - component index for a given component index. - """ - if isinstance(i, int): - i = (i,) - self._check_component(i) - - # Select between indexing modes - if len(self.value_shape) == 1: - # Indexing into a long vector of flattened subelement - # shapes - j, = i - - # Find subelement for this index - for sub_element_index, e in enumerate(self._sub_elements): - sh = e.value_shape - si = product(sh) - if j < si: - break - j -= si - if j < 0: - raise ValueError("Moved past last value component!") - - # Convert index into a shape tuple - st = shape_to_strides(sh) - component = unflatten_index(j, st) - else: - # Indexing into a multidimensional tensor where subelement - # index is first axis - sub_element_index = i[0] - if sub_element_index >= len(self._sub_elements): - raise ValueError(f"Illegal component index (dimension {sub_element_index}).") - component = i[1:] - return (sub_element_index, component) - - def extract_component(self, i): - """Recursively extract component index relative to a (simple) element. - - and that element for given value component index. - """ - sub_element_index, component = self.extract_subelement_component(i) - return self._sub_elements[sub_element_index].extract_component(component) - - def extract_subelement_reference_component(self, i): - """Extract direct subelement index and subelement relative. - - reference_component index for a given reference_component index. - """ - if isinstance(i, int): - i = (i,) - self._check_reference_component(i) - - # Select between indexing modes - assert len(self.reference_value_shape) == 1 - # Indexing into a long vector of flattened subelement shapes - j, = i - - # Find subelement for this index - for sub_element_index, e in enumerate(self._sub_elements): - sh = e.reference_value_shape - si = product(sh) - if j < si: - break - j -= si - if j < 0: - raise ValueError("Moved past last value reference_component!") - - # Convert index into a shape tuple - st = shape_to_strides(sh) - reference_component = unflatten_index(j, st) - return (sub_element_index, reference_component) - - def extract_reference_component(self, i): - """Recursively extract reference_component index relative to a (simple) element. - - and that element for given value reference_component index. - """ - sub_element_index, reference_component = self.extract_subelement_reference_component(i) - return self._sub_elements[sub_element_index].extract_reference_component(reference_component) - - def is_cellwise_constant(self, component=None): - """Return whether the basis functions of this element is spatially constant over each cell.""" - if component is None: - return all(e.is_cellwise_constant() for e in self.sub_elements) - else: - i, e = self.extract_component(component) - return e.is_cellwise_constant() - - def degree(self, component=None): - """Return polynomial degree of finite element.""" - if component is None: - return self._degree # from FiniteElementBase, computed as max of subelements in __init__ - else: - i, e = self.extract_component(component) - return e.degree() - - @property - def embedded_subdegree(self): - """Return embedded subdegree.""" - if isinstance(self._degree, int): - return self._degree - else: - return min(e.embedded_subdegree for e in self.sub_elements) - - @property - def embedded_superdegree(self): - """Return embedded superdegree.""" - if isinstance(self._degree, int): - return self._degree - else: - return max(e.embedded_superdegree for e in self.sub_elements) - - def reconstruct(self, **kwargs): - """Doc.""" - return MixedElement(*[e.reconstruct(**kwargs) for e in self.sub_elements]) - - def variant(self): - """Doc.""" - try: - variant, = {e.variant() for e in self.sub_elements} - return variant - except ValueError: - return None - - def __str__(self): - """Format as string for pretty printing.""" - tmp = ", ".join(str(element) for element in self._sub_elements) - return "" - - def shortstr(self): - """Format as string for pretty printing.""" - tmp = ", ".join(element.shortstr() for element in self._sub_elements) - return "Mixed<" + tmp + ">" - - @property - def pullback(self): - """Get the pull back.""" - for e in self.sub_elements: - if not isinstance(e.pullback, IdentityPullback): - return MixedPullback(self) - return IdentityPullback() - - -class VectorElement(MixedElement): - """A special case of a mixed finite element where all elements are equal.""" - - __slots__ = ("_repr", "_mapping", "_sub_element") - - def __init__(self, family, cell=None, degree=None, dim=None, - form_degree=None, quad_scheme=None, variant=None): - """Create vector element (repeated mixed element).""" - if isinstance(family, FiniteElementBase): - sub_element = family - cell = sub_element.cell - variant = sub_element.variant() - else: - if cell is not None: - cell = as_cell(cell) - # Create sub element - sub_element = FiniteElement(family, cell, degree, - form_degree=form_degree, - quad_scheme=quad_scheme, - variant=variant) - - # Set default size if not specified - if dim is None: - if cell is None: - raise ValueError("Cannot infer vector dimension without a cell.") - dim = cell.geometric_dimension() - - self._mapping = sub_element.mapping() - # Create list of sub elements for mixed element constructor - sub_elements = [sub_element] * dim - - # Compute value shapes - value_shape = (dim,) + sub_element.value_shape - reference_value_shape = (dim,) + sub_element.reference_value_shape - - # Initialize element data - MixedElement.__init__(self, sub_elements, value_shape=value_shape, - reference_value_shape=reference_value_shape) - - FiniteElementBase.__init__(self, sub_element.family(), sub_element.cell, sub_element.degree(), - sub_element.quadrature_scheme(), value_shape, reference_value_shape) - - self._sub_element = sub_element - - if variant is None: - var_str = "" - else: - var_str = ", variant='" + variant + "'" - - # Cache repr string - self._repr = f"VectorElement({repr(sub_element)}, dim={dim}{var_str})" - - def __repr__(self): - """Doc.""" - return self._repr - - def reconstruct(self, **kwargs): - """Doc.""" - sub_element = self._sub_element.reconstruct(**kwargs) - return VectorElement(sub_element, dim=len(self.sub_elements)) - - def variant(self): - """Return the variant used to initialise the element.""" - return self._sub_element.variant() - - def mapping(self): - """Doc.""" - return self._mapping - - def __str__(self): - """Format as string for pretty printing.""" - return ("" % - (len(self._sub_elements), self._sub_element)) - - def shortstr(self): - """Format as string for pretty printing.""" - return "Vector<%d x %s>" % (len(self._sub_elements), - self._sub_element.shortstr()) - - -class TensorElement(MixedElement): - """A special case of a mixed finite element where all elements are equal.""" - __slots__ = ("_sub_element", "_shape", "_symmetry", - "_sub_element_mapping", - "_flattened_sub_element_mapping", - "_mapping", "_repr") - - def __init__(self, family, cell=None, degree=None, shape=None, - symmetry=None, quad_scheme=None, variant=None): - """Create tensor element (repeated mixed element with optional symmetries).""" - if isinstance(family, FiniteElementBase): - sub_element = family - cell = sub_element.cell - variant = sub_element.variant() - else: - if cell is not None: - cell = as_cell(cell) - # Create scalar sub element - sub_element = FiniteElement(family, cell, degree, quad_scheme=quad_scheme, - variant=variant) - - # Set default shape if not specified - if shape is None: - if cell is None: - raise ValueError("Cannot infer tensor shape without a cell.") - dim = cell.geometric_dimension() - shape = (dim, dim) - - if symmetry is None: - symmetry = {} - elif symmetry is True: - # Construct default symmetry dict for matrix elements - if not (len(shape) == 2 and shape[0] == shape[1]): - raise ValueError("Cannot set automatic symmetry for non-square tensor.") - symmetry = dict(((i, j), (j, i)) for i in range(shape[0]) - for j in range(shape[1]) if i > j) - else: - if not isinstance(symmetry, dict): - raise ValueError("Expecting symmetry to be None (unset), True, or dict.") - - # Validate indices in symmetry dict - for i, j in symmetry.items(): - if len(i) != len(j): - raise ValueError("Non-matching length of symmetry index tuples.") - for k in range(len(i)): - if not (i[k] >= 0 and j[k] >= 0 and i[k] < shape[k] and j[k] < shape[k]): - raise ValueError("Symmetry dimensions out of bounds.") - - # Compute all index combinations for given shape - indices = compute_indices(shape) - - # Compute mapping from indices to sub element number, - # accounting for symmetry - sub_elements = [] - sub_element_mapping = {} - for index in indices: - if index in symmetry: - continue - sub_element_mapping[index] = len(sub_elements) - sub_elements += [sub_element] - - # Update mapping for symmetry - for index in indices: - if index in symmetry: - sub_element_mapping[index] = sub_element_mapping[symmetry[index]] - flattened_sub_element_mapping = [sub_element_mapping[index] for i, - index in enumerate(indices)] - - # Compute value shape - value_shape = shape - - # Compute reference value shape based on symmetries - if symmetry: - reference_value_shape = (product(shape) - len(symmetry),) - self._mapping = "symmetries" - else: - reference_value_shape = shape - self._mapping = sub_element.mapping() - - value_shape = value_shape + sub_element.value_shape - reference_value_shape = reference_value_shape + sub_element.reference_value_shape - # Initialize element data - MixedElement.__init__(self, sub_elements, value_shape=value_shape, - reference_value_shape=reference_value_shape) - self._family = sub_element.family() - self._degree = sub_element.degree() - self._sub_element = sub_element - self._shape = shape - self._symmetry = symmetry - self._sub_element_mapping = sub_element_mapping - self._flattened_sub_element_mapping = flattened_sub_element_mapping - - if variant is None: - var_str = "" - else: - var_str = ", variant='" + variant + "'" - - # Cache repr string - self._repr = (f"TensorElement({repr(sub_element)}, shape={shape}, " - f"symmetry={symmetry}{var_str})") - - @property - def pullback(self): - """Get pull back.""" - if len(self._symmetry) > 0: - sub_element_value_shape = self.sub_elements[0].value_shape - for e in self.sub_elements: - if e.value_shape != sub_element_value_shape: - raise ValueError("Sub-elements must all have the same value size") - symmetry = {} - n = 0 - for i in np.ndindex(self.value_shape[:len(self.value_shape)-len(sub_element_value_shape)]): - if i in self._symmetry and self._symmetry[i] in symmetry: - symmetry[i] = symmetry[self._symmetry[i]] - else: - symmetry[i] = n - n += 1 - return SymmetricPullback(self, symmetry) - return super().pullback - - def __repr__(self): - """Doc.""" - return self._repr - - def variant(self): - """Return the variant used to initialise the element.""" - return self._sub_element.variant() - - def mapping(self): - """Doc.""" - return self._mapping - - def flattened_sub_element_mapping(self): - """Doc.""" - return self._flattened_sub_element_mapping - - def extract_subelement_component(self, i): - """Extract direct subelement index and subelement relative. - - component index for a given component index. - """ - if isinstance(i, int): - i = (i,) - self._check_component(i) - - i = self.symmetry().get(i, i) - l = len(self._shape) # noqa: E741 - ii = i[:l] - jj = i[l:] - if ii not in self._sub_element_mapping: - raise ValueError(f"Illegal component index {i}.") - k = self._sub_element_mapping[ii] - return (k, jj) - - def symmetry(self): - r"""Return the symmetry dict, which is a mapping :math:`c_0 \\to c_1`. - - meaning that component :math:`c_0` is represented by component - :math:`c_1`. - A component is a tuple of one or more ints. - """ - return self._symmetry - - def reconstruct(self, **kwargs): - """Doc.""" - 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: - tmp = ", ".join("%s -> %s" % (a, b) for (a, b) in self._symmetry.items()) - sym = " with symmetries (%s)" % tmp - else: - sym = "" - return ("" % - (self.value_shape, self._sub_element, sym)) - - def shortstr(self): - """Format as string for pretty printing.""" - if self._symmetry: - tmp = ", ".join("%s -> %s" % (a, b) for (a, b) in self._symmetry.items()) - sym = " with symmetries (%s)" % tmp - else: - sym = "" - return "Tensor<%s x %s%s>" % (self.value_shape, - self._sub_element.shortstr(), sym) diff --git a/ufl/legacy/restrictedelement.py b/ufl/legacy/restrictedelement.py deleted file mode 100644 index 767444087..000000000 --- a/ufl/legacy/restrictedelement.py +++ /dev/null @@ -1,113 +0,0 @@ -"""This module defines the UFL finite element classes.""" - -# Copyright (C) 2008-2016 Martin Sandve Alnæs -# -# This file is part of UFL (https://www.fenicsproject.org) -# -# SPDX-License-Identifier: LGPL-3.0-or-later -# -# Modified by Kristian B. Oelgaard -# Modified by Marie E. Rognes 2010, 2012 -# Modified by Massimiliano Leoni, 2016 - -from ufl.legacy.finiteelementbase import FiniteElementBase -from ufl.sobolevspace import L2 - -valid_restriction_domains = ("interior", "facet", "face", "edge", "vertex") - - -class RestrictedElement(FiniteElementBase): - """Represents the restriction of a finite element to a type of cell entity.""" - - def __init__(self, element, restriction_domain): - """Doc.""" - if not isinstance(element, FiniteElementBase): - raise ValueError("Expecting a finite element instance.") - if restriction_domain not in valid_restriction_domains: - raise ValueError(f"Expecting one of the strings: {valid_restriction_domains}") - - FiniteElementBase.__init__(self, "RestrictedElement", element.cell, - element.degree(), - element.quadrature_scheme(), - element.value_shape, - element.reference_value_shape) - - self._element = element - - self._restriction_domain = restriction_domain - - def __repr__(self): - """Doc.""" - return f"RestrictedElement({repr(self._element)}, {repr(self._restriction_domain)})" - - @property - def sobolev_space(self): - """Doc.""" - if self._restriction_domain == "interior": - return L2 - else: - return self._element.sobolev_space - - def is_cellwise_constant(self): - """Return whether the basis functions of this element is spatially constant over each cell.""" - return self._element.is_cellwise_constant() - - def _is_linear(self): - """Doc.""" - return self._element._is_linear() - - def sub_element(self): - """Return the element which is restricted.""" - return self._element - - def mapping(self): - """Doc.""" - return self._element.mapping() - - def restriction_domain(self): - """Return the domain onto which the element is restricted.""" - return self._restriction_domain - - def reconstruct(self, **kwargs): - """Doc.""" - 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) - - def shortstr(self): - """Format as string for pretty printing.""" - return "<%s>|_{%s}" % (self._element.shortstr(), - self._restriction_domain) - - def symmetry(self): - r"""Return the symmetry dict, which is a mapping :math:`c_0 \\to c_1`. - - meaning that component :math:`c_0` is represented by component - :math:`c_1`. A component is a tuple of one or more ints. - """ - return self._element.symmetry() - - @property - def num_sub_elements(self): - """Return number of sub elements.""" - return self._element.num_sub_elements - - @property - def sub_elements(self): - """Return list of sub elements.""" - return self._element.sub_elements - - def num_restricted_sub_elements(self): - """Return number of restricted sub elements.""" - return 1 - - def restricted_sub_elements(self): - """Return list of restricted sub elements.""" - return (self._element,) - - def variant(self): - """Doc.""" - return self._element.variant() diff --git a/ufl/legacy/tensorproductelement.py b/ufl/legacy/tensorproductelement.py deleted file mode 100644 index d9f1ff1bd..000000000 --- a/ufl/legacy/tensorproductelement.py +++ /dev/null @@ -1,140 +0,0 @@ -"""This module defines the UFL finite element classes.""" - -# Copyright (C) 2008-2016 Martin Sandve Alnæs -# -# This file is part of UFL (https://www.fenicsproject.org) -# -# SPDX-License-Identifier: LGPL-3.0-or-later -# -# Modified by Kristian B. Oelgaard -# Modified by Marie E. Rognes 2010, 2012 -# Modified by Massimiliano Leoni, 2016 - -from itertools import chain - -from ufl.cell import TensorProductCell, as_cell -from ufl.legacy.finiteelementbase import FiniteElementBase -from ufl.sobolevspace import DirectionalSobolevSpace - - -class TensorProductElement(FiniteElementBase): - r"""The tensor product of :math:`d` element spaces. - - .. math:: V = V_1 \otimes V_2 \otimes ... \otimes V_d - - Given bases :math:`\{\phi_{j_i}\}` of the spaces :math:`V_i` for :math:`i = 1, ...., d`, - :math:`\{ \phi_{j_1} \otimes \phi_{j_2} \otimes \cdots \otimes \phi_{j_d} - \}` forms a basis for :math:`V`. - """ - __slots__ = ("_sub_elements", "_cell") - - def __init__(self, *elements, **kwargs): - """Create TensorProductElement from a given list of elements.""" - if not elements: - raise ValueError("Cannot create TensorProductElement from empty list.") - - keywords = list(kwargs.keys()) - if keywords and keywords != ["cell"]: - raise ValueError("TensorProductElement got an unexpected keyword argument '%s'" % keywords[0]) - cell = kwargs.get("cell") - - family = "TensorProductElement" - - if cell is None: - # Define cell as the product of each elements cell - cell = TensorProductCell(*[e.cell for e in elements]) - else: - cell = as_cell(cell) - - # Define polynomial degree as a tuple of sub-degrees - degree = tuple(e.degree() for e in elements) - - # No quadrature scheme defined - quad_scheme = None - - # match FIAT implementation - value_shape = tuple(chain(*[e.value_shape for e in elements])) - reference_value_shape = tuple(chain(*[e.reference_value_shape for e in elements])) - if len(value_shape) > 1: - raise ValueError("Product of vector-valued elements not supported") - if len(reference_value_shape) > 1: - raise ValueError("Product of vector-valued elements not supported") - - FiniteElementBase.__init__(self, family, cell, degree, - quad_scheme, value_shape, - reference_value_shape) - self._sub_elements = elements - self._cell = cell - - def __repr__(self): - """Doc.""" - return "TensorProductElement(" + ", ".join(repr(e) for e in self._sub_elements) + f", cell={repr(self._cell)})" - - def mapping(self): - """Doc.""" - if all(e.mapping() == "identity" for e in self._sub_elements): - return "identity" - elif all(e.mapping() == "L2 Piola" for e in self._sub_elements): - return "L2 Piola" - else: - return "undefined" - - @property - 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) - - @property - def num_sub_elements(self): - """Return number of subelements.""" - return len(self._sub_elements) - - @property - def sub_elements(self): - """Return subelements (factors).""" - return self._sub_elements - - def reconstruct(self, **kwargs): - """Doc.""" - cell = kwargs.pop("cell", self.cell) - return TensorProductElement(*[e.reconstruct(**kwargs) for e in self.sub_elements], cell=cell) - - def variant(self): - """Doc.""" - try: - variant, = {e.variant() for e in self.sub_elements} - return variant - except ValueError: - return None - - def __str__(self): - """Pretty-print.""" - return "TensorProductElement(%s, cell=%s)" \ - % (', '.join([str(e) for e in self._sub_elements]), str(self._cell)) - - def shortstr(self): - """Short pretty-print.""" - return "TensorProductElement(%s, cell=%s)" \ - % (', '.join([e.shortstr() for e in self._sub_elements]), str(self._cell)) - - @property - def embedded_superdegree(self): - """Doc.""" - return sum(self.degree()) - - @property - def embedded_subdegree(self): - """Doc.""" - return min(self.degree()) From 23c383e59dae0728df75c572d67b595d8a244f06 Mon Sep 17 00:00:00 2001 From: "Jack S. Hale" Date: Fri, 3 Nov 2023 06:31:09 +0100 Subject: [PATCH 14/16] Change DOLFINx integration test image (#235) * Change DOLFINx integration test image * Update PETSC_ARCH to new scheme * Update pip install. * Update fenicsx-tests.yml * Update fenicsx-tests.yml --- .github/workflows/fenicsx-tests.yml | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/.github/workflows/fenicsx-tests.yml b/.github/workflows/fenicsx-tests.yml index 2075498b0..b2a47af67 100644 --- a/.github/workflows/fenicsx-tests.yml +++ b/.github/workflows/fenicsx-tests.yml @@ -37,7 +37,7 @@ jobs: python3 -m pip install git+https://github.com/FEniCS/basix.git - name: Clone FFCx - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: path: ./ffcx repository: FEniCS/ffcx @@ -53,11 +53,10 @@ jobs: dolfinx-tests: name: Run DOLFINx tests runs-on: ubuntu-latest - container: fenicsproject/test-env:nightly-openmpi + container: ghcr.io/fenics/test-env:current-openmpi env: - - PETSC_ARCH: linux-gnu-complex-32 + PETSC_ARCH: linux-gnu-complex128-32 OMPI_ALLOW_RUN_AS_ROOT: 1 OMPI_ALLOW_RUN_AS_ROOT_CONFIRM: 1 OMPI_MCA_rmaps_base_oversubscribe: 1 @@ -67,10 +66,7 @@ jobs: OMPI_MCA_hwloc_base_binding_policy: none steps: - - uses: actions/checkout@v3 - - name: Install dependencies (Python) - run: | - python3 -m pip install --upgrade pip + - uses: actions/checkout@v4 - name: Install UFL run: | @@ -82,7 +78,7 @@ jobs: python3 -m pip install git+https://github.com/FEniCS/ffcx.git - name: Clone DOLFINx - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: path: ./dolfinx repository: FEniCS/dolfinx @@ -92,6 +88,6 @@ jobs: cmake -G Ninja -DCMAKE_BUILD_TYPE=Developer -B build -S dolfinx/cpp/ cmake --build build cmake --install build - pip3 -v install --global-option build --global-option --debug dolfinx/python/ + python3 -m pip -v install --no-build-isolation --check-build-dependencies --config-settings=cmake.build-type="Developer" dolfinx/python/ - name: Run DOLFINx unit tests run: python3 -m pytest -n auto dolfinx/python/test/unit From 372a93b2c875ceaf0651e79a1a099f0567eeb9a0 Mon Sep 17 00:00:00 2001 From: Nacime Bouziani <48448063+nbouziani@users.noreply.github.com> Date: Fri, 3 Nov 2023 15:20:47 +0000 Subject: [PATCH 15/16] Update BaseForm's __call__ (#232) * Add trimmed serendipity * Fix value shape for trimmed serendipity * ufl plumbing update for trimmed serendipity. * Plumbing for SminusDiv.py * Adding in element stuff for SminusCurl. * Fix typo * remove spurioius names * Fix BaseForm's call * Add test * Update test with new FiniteElement interface --------- Co-authored-by: Rob Kirby Co-authored-by: Justincrum Co-authored-by: David A. Ham Co-authored-by: ksagiyam Co-authored-by: ksagiyam <46749170+ksagiyam@users.noreply.github.com> Co-authored-by: Connor Ward Co-authored-by: Matthew Scroggs --- test/test_duals.py | 11 +++++++++++ ufl/form.py | 49 ++++------------------------------------------ 2 files changed, 15 insertions(+), 45 deletions(-) diff --git a/test/test_duals.py b/test/test_duals.py index b82736e0f..53e230f3f 100644 --- a/test/test_duals.py +++ b/test/test_duals.py @@ -305,3 +305,14 @@ def test_zero_base_form_mult(): Zu = Z * u assert Zu == action(Z, u) assert action(Zu, u) == ZeroBaseForm(()) + + +def test_base_form_call(): + domain_2d = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)) + f_2d = FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1) + V = FunctionSpace(domain_2d, f_2d) + + # Check duality pairing + f = Coefficient(V) + c = Cofunction(V.dual()) + assert c(f) == action(c, f) diff --git a/ufl/form.py b/ufl/form.py index ed3040477..f4b546b41 100644 --- a/ufl/form.py +++ b/ufl/form.py @@ -198,51 +198,10 @@ def __ne__(self, other): """Immediately evaluate the != operator (as opposed to the == operator).""" return not self.equals(other) - def __call__(self, *args, **kwargs): - """Evaluate form by replacing arguments and coefficients. - - Replaces form.arguments() with given positional arguments in - same number and ordering. Number of positional arguments must - be 0 or equal to the number of Arguments in the form. - - The optional keyword argument coefficients can be set to a dict - to replace Coefficients with expressions of matching shapes. - - Example: - V = FiniteElement("CG", triangle, 1) - v = TestFunction(V) - u = TrialFunction(V) - f = Coefficient(V) - g = Coefficient(V) - a = g*inner(grad(u), grad(v))*dx - M = a(f, f, coefficients={ g: 1 }) - - Is equivalent to M == grad(f)**2*dx. - """ - repdict = {} - - if args: - arguments = self.arguments() - if len(arguments) != len(args): - raise ValueError(f"Need {len(arguments)} arguments to form(), got {len(args)}.") - repdict.update(zip(arguments, args)) - - coefficients = kwargs.pop("coefficients", None) - if kwargs: - raise ValueError(f"Unknown kwargs {list(kwargs)}.") - - if coefficients is not None: - coeffs = self.coefficients() - for f in coefficients: - if f in coeffs: - repdict[f] = coefficients[f] - else: - warnings("Coefficient %s is not in form." % ufl_err_str(f)) - if repdict: - from ufl.formoperators import replace - return replace(self, repdict) - else: - return self + def __call__(self, x): + """Take the action of this form on ``x``.""" + from ufl.formoperators import action + return action(self, x) def _ufl_compute_hash_(self): """Compute the hash.""" From 0fe782c97bcd1f7cc054a21861a45d7eaf1761ed Mon Sep 17 00:00:00 2001 From: Daiane Iglesia Dolci <63597005+Ig-dolci@users.noreply.github.com> Date: Fri, 3 Nov 2023 15:21:01 +0000 Subject: [PATCH 16/16] ufl2unicode fail (#237) * In a recent change to UFL, subdomain_id was transformed from a single value to a tuple. This broke some assumptions in ufl2unicode that I have fixed here. * Update ufl/formatting/ufl2unicode.py Co-authored-by: Connor Ward * Update ufl/formatting/ufl2unicode.py Co-authored-by: Connor Ward * Update ufl/formatting/ufl2unicode.py Co-authored-by: Connor Ward * Update ufl/formatting/ufl2unicode.py Co-authored-by: Connor Ward --------- Co-authored-by: Connor Ward --- ufl/formatting/ufl2unicode.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/ufl/formatting/ufl2unicode.py b/ufl/formatting/ufl2unicode.py index 90a5d7bf7..0a5f0549f 100644 --- a/ufl/formatting/ufl2unicode.py +++ b/ufl/formatting/ufl2unicode.py @@ -300,14 +300,15 @@ def get_integral_symbol(integral_type, domain, subdomain_id): # TODO: Render domain description - if isinstance(subdomain_id, numbers.Integral): - istr += subscript_number(int(subdomain_id)) - elif subdomain_id == "everywhere": - pass - elif subdomain_id == "otherwise": - istr += "[rest of domain]" - elif isinstance(subdomain_id, tuple): - istr += ",".join([subscript_number(int(i)) for i in subdomain_id]) + subdomain_strs = [] + for subdomain in subdomain_id: + if isinstance(subdomain, numbers.Integral): + subdomain_strs.append(subscript_number(int(subdomain))) + elif subdomain == "everywhere": + pass + elif subdomain == "otherwise": + subdomain_strs.append("[rest of domain]") + istr += ",".join(subdomain_strs) dxstr = ufl.measure.integral_type_to_measure_name[integral_type] dxstr = measure_font(dxstr)