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/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__ 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 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/.github/workflows/spack.yml b/.github/workflows/spack.yml new file mode 100644 index 000000000..7af85df62 --- /dev/null +++ b/.github/workflows/spack.yml @@ -0,0 +1,61 @@ +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: 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 + 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 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 --test=root + + - 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 --test=root diff --git a/.github/workflows/tsfc-tests.yml b/.github/workflows/tsfc-tests.yml index 06b10a13d..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: master + 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/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/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 ======= 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/pyproject.toml b/pyproject.toml index cfcfb19c9..b3751e708 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,4 +1,51 @@ [build-system] -requires = ["setuptools>=62", "wheel"] - +requires = ["setuptools>=62", "wheel", "pip>=22.3"] 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.utils", +] + +[tool.pydocstyle] +convention = "google" + +[tool.isort] +line_length = 120 diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index 7bcb914d4..000000000 --- a/setup.cfg +++ /dev/null @@ -1,68 +0,0 @@ -# Setuptools does not yet support modern pyproject.toml but will do so in the -# future -[metadata] -name = fenics-ufl -version = 2023.2.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 -exclude = doc/sphinx/source/conf.py - -[pydocstyle] -convention = google - -[isort] -line_length = 120 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..53e230f3f 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)) @@ -302,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/test/test_elements.py b/test/test_elements.py deleted file mode 100755 index 370679662..000000000 --- a/test/test_elements.py +++ /dev/null @@ -1,235 +0,0 @@ -from ufl import (Coefficient, FiniteElement, FunctionSpace, Mesh, MixedElement, TensorElement, VectorElement, - WithMapping, dx, hexahedron, inner, interval, quadrilateral, tetrahedron, triangle) - -all_cells = (interval, triangle, tetrahedron, quadrilateral, hexahedron) - -# TODO: cover all valid element definitions - - -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) - - # 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/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_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..27e75869d 100644 --- a/test/test_strip_forms.py +++ b/test/test_strip_forms.py @@ -1,26 +1,26 @@ 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.core.ufl_type import UFLObject +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.""" -@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 @@ -51,8 +51,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 +89,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..5d0fb910d 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 --- @@ -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 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_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 b83d30094..e19a9c340 100644 --- a/ufl/core/ufl_type.py +++ b/ufl/core/ufl_type.py @@ -9,14 +9,13 @@ # Modified by Matthew Scroggs, 2023 from __future__ import annotations + import typing -import warnings +from abc import ABC, abstractmethod +import ufl.core as core 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 class UFLObject(ABC): @@ -47,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/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..f438006d0 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.core.ufl_type import UFLObject 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): @@ -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): @@ -68,22 +67,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 +88,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.""" @@ -128,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): @@ -142,8 +134,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 +179,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 +188,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 +271,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/finiteelement/brokenelement.py b/ufl/finiteelement/brokenelement.py deleted file mode 100644 index 2467e9d0b..000000000 --- a/ufl/finiteelement/brokenelement.py +++ /dev/null @@ -1,52 +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.finiteelement.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() - - 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/finiteelement/elementlist.py b/ufl/finiteelement/elementlist.py deleted file mode 100644 index 90c783d4f..000000000 --- a/ufl/finiteelement/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.sobolevspace import L2, H1, H2, HDiv, HCurl, HEin, HDivDiv, HInf -from ufl.utils.formatting import istr -from ufl.cell import Cell, TensorProductCell - - -# 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/finiteelement/enrichedelement.py b/ufl/finiteelement/enrichedelement.py deleted file mode 100644 index 81e2d88cc..000000000 --- a/ufl/finiteelement/enrichedelement.py +++ /dev/null @@ -1,147 +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.finiteelement.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() - - 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]) - - -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/finiteelement/finiteelement.py b/ufl/finiteelement/finiteelement.py deleted file mode 100644 index 0962e82a3..000000000 --- a/ufl/finiteelement/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.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): - """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.finiteelement.tensorproductelement import TensorProductElement - from ufl.finiteelement.enrichedelement import EnrichedElement - from ufl.finiteelement.hdivcurl import HDivElement as HDiv, HCurlElement as HCurl - - family, short_name, degree, value_shape, reference_value_shape, sobolev_space, mapping = \ - canonical_element_description(family, cell, degree, form_degree) - - if family in ["RTCF", "RTCE"]: - 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 - - 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/finiteelement/finiteelementbase.py b/ufl/finiteelement/finiteelementbase.py deleted file mode 100644 index 7f34252fc..000000000 --- a/ufl/finiteelement/finiteelementbase.py +++ /dev/null @@ -1,236 +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.utils.sequences import product -from ufl.cell import AbstractCell, as_cell -from abc import ABC, abstractmethod - - -class FiniteElementBase(ABC): - """Base class for all finite elements.""" - __slots__ = ("_family", "_cell", "_degree", "_quad_scheme", - "_value_shape", "_reference_value_shape", "__weakref__") - - # 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 - - @abstractmethod - 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.""" - # 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 - - 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 - - def value_shape(self): - """Return the shape of the value space on the global domain.""" - return self._value_shape - - def reference_value_shape(self): - """Return the shape of the value space on the reference cell.""" - return self._reference_value_shape - - def value_size(self): - """Return the integer product of the value shape.""" - return product(self.value_shape()) - - def reference_value_size(self): - """Return the integer product of the reference value shape.""" - return product(self.reference_value_shape()) - - def symmetry(self): # FIXME: different approach - 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) - - def num_sub_elements(self): - """Return number of sub-elements.""" - return 0 - - 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.finiteelement 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 - 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 - 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") diff --git a/ufl/finiteelement/hdivcurl.py b/ufl/finiteelement/hdivcurl.py deleted file mode 100644 index fde6ff33b..000000000 --- a/ufl/finiteelement/hdivcurl.py +++ /dev/null @@ -1,188 +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.finiteelement.finiteelementbase import FiniteElementBase -from ufl.sobolevspace import HDiv, HCurl, L2 - - -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" - - 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()})" - - -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" - - 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}')" - - 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() - - 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 - - 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})" diff --git a/ufl/finiteelement/mixedelement.py b/ufl/finiteelement/mixedelement.py deleted file mode 100644 index ca16a3d80..000000000 --- a/ufl/finiteelement/mixedelement.py +++ /dev/null @@ -1,514 +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.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 - -from ufl.finiteelement.finiteelementbase import FiniteElementBase -from ufl.finiteelement.finiteelement import FiniteElement - - -class MixedElement(FiniteElementBase): - """A finite element composed of a nested hierarchy of mixed or simple elements.""" - __slots__ = ("_sub_elements", "_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 {} - - 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" - - def num_sub_elements(self): - """Return number of sub elements.""" - return len(self._sub_elements) - - 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() - - 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 + ">" - - -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})") - - 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/finiteelement/restrictedelement.py b/ufl/finiteelement/restrictedelement.py deleted file mode 100644 index 7ad71b3a7..000000000 --- a/ufl/finiteelement/restrictedelement.py +++ /dev/null @@ -1,110 +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.finiteelement.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)})" - - 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() - - def num_sub_elements(self): - """Return number of sub elements.""" - return self._element.num_sub_elements() - - 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/finiteelement/tensorproductelement.py b/ufl/finiteelement/tensorproductelement.py deleted file mode 100644 index 07aad6e8f..000000000 --- a/ufl/finiteelement/tensorproductelement.py +++ /dev/null @@ -1,128 +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.sobolevspace import DirectionalSobolevSpace - -from ufl.finiteelement.finiteelementbase import FiniteElementBase - - -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" - - def sobolev_space(self): - """Return the underlying Sobolev space of the TensorProductElement.""" - elements = self._sub_elements - if all(e.sobolev_space() == elements[0].sobolev_space() - for e in elements): - return elements[0].sobolev_space() - else: - # Generate a DirectionalSobolevSpace which contains - # continuity information parametrized by spatial index - orders = [] - for e in elements: - e_dim = e.cell().geometric_dimension() - e_order = (e.sobolev_space()._order,) * e_dim - orders.extend(e_order) - return DirectionalSobolevSpace(orders) - - def num_sub_elements(self): - """Return number of subelements.""" - return len(self._sub_elements) - - 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)) diff --git a/ufl/form.py b/ufl/form.py index ecbd4b17d..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") - 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.""" @@ -831,6 +790,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..0a5f0549f 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 @@ -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) 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..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,20 +29,18 @@ 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): """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 +48,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) @@ -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__() 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/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/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) 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()